diff --git a/.github/workflows/install_deps/action.yml b/.github/workflows/install_deps/action.yml index 99b77dc8157..dfa167b1dc7 100644 --- a/.github/workflows/install_deps/action.yml +++ b/.github/workflows/install_deps/action.yml @@ -8,6 +8,11 @@ inputs: description: The version of Python to use in order to run unit tests required: false default: '3.10' + install_numpy_1: + description: Indicate if numpy 1 should be installed or not + required: false + type: boolean + default: false install_jax: description: Indicate if JAX should be installed or not required: false @@ -15,7 +20,7 @@ inputs: jax_version: description: The version of JAX to install for any job that requires JAX required: false - default: '0.4.23' + default: '0.4.28' install_tensorflow: description: Indicate if TensorFlow should be installed or not required: false @@ -67,6 +72,11 @@ runs: pip install -r requirements-ci.txt --upgrade pip install -r requirements-dev.txt --upgrade + - name: Install numpy 1 version + shell: bash + if: inputs.install_numpy_1 == 'true' + run: pip install "numpy==1.26.4" + - name: Install PyTorch shell: bash if: inputs.install_pytorch == 'true' @@ -86,7 +96,7 @@ runs: if: inputs.install_jax == 'true' env: JAX_VERSION: ${{ inputs.jax_version != '' && format('=={0}', inputs.jax_version) || '' }} - run: pip install "jax${{ env.JAX_VERSION}}" "jaxlib${{ env.JAX_VERSION }}" scipy~=1.12.0 + run: pip install "jax${{ env.JAX_VERSION}}" "jaxlib${{ env.JAX_VERSION }}" - name: Install additional PIP packages shell: bash diff --git a/.github/workflows/interface-unit-tests.yml b/.github/workflows/interface-unit-tests.yml index 70104146499..1492ff4370c 100644 --- a/.github/workflows/interface-unit-tests.yml +++ b/.github/workflows/interface-unit-tests.yml @@ -270,7 +270,7 @@ jobs: install_pennylane_lightning_master: true pytest_coverage_flags: ${{ inputs.pytest_coverage_flags }} pytest_markers: jax and not qcut and not finite-diff and not param-shift - pytest_additional_args: --splits 5 --group ${{ matrix.group }} + pytest_additional_args: --dist=loadscope --splits 5 --group ${{ matrix.group }} pytest_durations_file_path: '.github/workflows/jax_tests_durations.json' pytest_store_durations: ${{ inputs.pytest_store_durations }} additional_pip_packages: pytest-split diff --git a/.github/workflows/numpy_1_tests.yml b/.github/workflows/numpy_1_tests.yml new file mode 100644 index 00000000000..0c76d43148d --- /dev/null +++ b/.github/workflows/numpy_1_tests.yml @@ -0,0 +1,19 @@ +name: NumPy 1 - Tests +on: + + schedule: + # Runs daily at 6 AM UTC (1 AM Toronto in winter, 2 AM in summer) + - cron: '0 6 * * *' + + # Allows to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: numpy-1-unit-tests-${{ github.ref }} + cancel-in-progress: true + +jobs: + tests: + uses: ./.github/workflows/numpy_1_unit-tests.yml + with: + branch: ${{ github.ref }} diff --git a/.github/workflows/numpy_1_unit-tests.yml b/.github/workflows/numpy_1_unit-tests.yml new file mode 100644 index 00000000000..c24ac043eb5 --- /dev/null +++ b/.github/workflows/numpy_1_unit-tests.yml @@ -0,0 +1,230 @@ +# TODO: remove this workflow once compatibility with NumPy 1 +# will no longer be tested. + +name: NumPy 1 - Unit Tests - Interfaces +on: + workflow_call: + inputs: + branch: + description: The PennyLane branch to checkout and run unit tests for + required: true + type: string + pipeline_mode: + description: The pipeline mode can be unit-tests, benchmarks, or reference-benchmarks + required: false + type: string + default: 'unit-tests' + disable_new_opmath: + description: Whether to disable the new op_math or not when running the tests + required: false + type: string + default: "False" + +jobs: + setup-ci-load: + runs-on: ubuntu-latest + + steps: + - name: Setup Python Versions + id: python_versions + + run: | + cat >python_versions.json <<-EOF + { + "default": ["3.10", "3.11", "3.12"], + "torch-tests": ["3.10", "3.12"], + "jax-tests": ["3.10", "3.12"], + "external-libraries-tests": ["3.10"], + "data-tests": ["3.10"] + } + EOF + + jq . python_versions.json + echo "python_versions=$(jq -r tostring python_versions.json)" >> $GITHUB_OUTPUT + + - name: Set NumPy Version + id: numpy_version + run: echo "NUMPY_VERSION=1.26" >> $GITHUB_ENV + + - name: Setup Matrix Max Parallel + id: max_parallel + run: | + cat >matrix_max_parallel.json <<-EOF + { + "default": 1, + "core-tests": 5, + "jax-tests": 10, + "torch-tests": 2 + } + EOF + + jq . matrix_max_parallel.json + echo "matrix_max_parallel=$(jq -r tostring matrix_max_parallel.json)" >> $GITHUB_OUTPUT + + outputs: + matrix-max-parallel: ${{ steps.max_parallel.outputs.matrix_max_parallel }} + python-version: ${{ steps.python_versions.outputs.python_versions }} + + torch-tests: + needs: + - setup-ci-load + strategy: + max-parallel: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).torch-tests + || fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).default + }} + matrix: + python-version: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.python-version).torch-tests + || fromJSON(needs.setup-ci-load.outputs.python-version).default + }} + uses: ./.github/workflows/unit-test.yml + with: + job_name: torch-tests (${{ matrix.python-version }}, numpy-1.26) + branch: ${{ inputs.branch }} + coverage_artifact_name: core-interfaces-coverage-torch-${{ matrix.python-version }}-numpy-1.26 + python_version: ${{ matrix.python-version }} + pipeline_mode: ${{ inputs.pipeline_mode }} + install_numpy_1: true + install_jax: false + install_tensorflow: false + install_pytorch: true + install_pennylane_lightning_master: true + pytest_markers: torch and not qcut and not finite-diff and not param-shift + requirements_file: ${{ github.event_name == 'schedule' && strategy.job-index == 0 && 'torch.txt' || '' }} + disable_new_opmath: ${{ inputs.disable_new_opmath }} + + + autograd-tests: + needs: + - setup-ci-load + strategy: + max-parallel: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).autograd-tests + || fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).default + }} + matrix: + python-version: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.python-version).autograd-tests + || fromJSON(needs.setup-ci-load.outputs.python-version).default + }} + uses: ./.github/workflows/unit-test.yml + with: + job_name: autograd-tests (${{ matrix.python-version }}, numpy-1.26) + branch: ${{ inputs.branch }} + coverage_artifact_name: core-interfaces-coverage-autograd-${{ matrix.python-version }}-numpy-1.26 + python_version: ${{ matrix.python-version }} + pipeline_mode: ${{ inputs.pipeline_mode }} + install_numpy_1: true + install_jax: false + install_tensorflow: false + install_pytorch: false + install_pennylane_lightning_master: true + pytest_markers: autograd and not qcut and not finite-diff and not param-shift + disable_new_opmath: ${{ inputs.disable_new_opmath }} + + + jax-tests: + needs: + - setup-ci-load + strategy: + max-parallel: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).jax-tests + || fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).default + }} + matrix: + group: [1, 2, 3, 4, 5] + python-version: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.python-version).jax-tests + || fromJSON(needs.setup-ci-load.outputs.python-version).default + }} + uses: ./.github/workflows/unit-test.yml + with: + job_name: jax-tests (${{ matrix.group }}, ${{ matrix.python-version }}, numpy-1.26) + branch: ${{ inputs.branch }} + coverage_artifact_name: core-interfaces-coverage-jax-${{ matrix.python-version }}-${{ matrix.group }}-numpy-1.26 + python_version: ${{ matrix.python-version }} + pipeline_mode: ${{ inputs.pipeline_mode }} + install_numpy_1: true + install_jax: true + install_tensorflow: false + install_pytorch: false + install_pennylane_lightning_master: true + pytest_markers: jax and not qcut and not finite-diff and not param-shift + pytest_additional_args: --dist=loadscope --splits 5 --group ${{ matrix.group }} + additional_pip_packages: pytest-split + requirements_file: ${{ github.event_name == 'schedule' && strategy.job-index == 0 && 'jax.txt' || '' }} + disable_new_opmath: ${{ inputs.disable_new_opmath }} + + + core-tests: + needs: + - setup-ci-load + strategy: + max-parallel: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).core-tests + || fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).default + }} + matrix: + group: [1, 2, 3, 4, 5] + python-version: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.python-version).core-tests + || fromJSON(needs.setup-ci-load.outputs.python-version).default + }} + uses: ./.github/workflows/unit-test.yml + with: + job_name: core-tests (${{ matrix.group }}, ${{ matrix.python-version }}, numpy-1.26) + branch: ${{ inputs.branch }} + coverage_artifact_name: core-interfaces-coverage-core-${{ matrix.python-version }}-${{ matrix.group }}-numpy-1.26 + python_version: ${{ matrix.python-version }} + pipeline_mode: ${{ inputs.pipeline_mode }} + install_numpy_1: true + install_jax: false + install_tensorflow: false + install_pytorch: false + install_pennylane_lightning_master: true + pytest_markers: core and not qcut and not finite-diff and not param-shift + pytest_additional_args: --splits 5 --group ${{ matrix.group }} + additional_pip_packages: pytest-split + requirements_file: ${{ github.event_name == 'schedule' && strategy.job-index == 0 && 'core.txt' || '' }} + disable_new_opmath: ${{ inputs.disable_new_opmath }} + + + data-tests: + needs: + - setup-ci-load + strategy: + max-parallel: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).data-tests + || fromJSON(needs.setup-ci-load.outputs.matrix-max-parallel).default + }} + matrix: + python-version: >- + ${{ + fromJSON(needs.setup-ci-load.outputs.python-version).data-tests + || fromJSON(needs.setup-ci-load.outputs.python-version).default + }} + uses: ./.github/workflows/unit-test.yml + with: + job_name: data-tests (${{ matrix.python-version }}, numpy-1.26) + branch: ${{ inputs.branch }} + coverage_artifact_name: data-coverage-${{ matrix.python-version }}-numpy-1.26 + python_version: ${{ matrix.python-version }} + pipeline_mode: ${{ inputs.pipeline_mode }} + install_numpy_1: true + install_jax: false + install_tensorflow: false + install_pytorch: false + install_pennylane_lightning_master: true + pytest_markers: data + additional_pip_packages: h5py + disable_new_opmath: ${{ inputs.disable_new_opmath }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 8e11cf86473..36cdb6d3f43 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -24,6 +24,11 @@ on: required: false type: string default: '3.10' + install_numpy_1: + description: Indicate if numpy 1 should be installed or not + required: false + type: boolean + default: false pipeline_mode: description: The pipeline mode can be unit-tests, benchmarks, or reference-benchmark required: false @@ -158,6 +163,7 @@ jobs: uses: ./.github/workflows/install_deps with: python_version: ${{ inputs.python_version }} + install_numpy_1: ${{ inputs.install_numpy_1 }} install_pytorch: ${{ inputs.install_pytorch }} install_tensorflow: ${{ inputs.install_tensorflow }} install_jax: ${{ inputs.install_jax }} diff --git a/doc/code/qml_qinfo.rst b/doc/code/qml_qinfo.rst index 04563167e2c..02674bbce41 100644 --- a/doc/code/qml_qinfo.rst +++ b/doc/code/qml_qinfo.rst @@ -4,6 +4,13 @@ qml.qinfo Overview -------- +.. warning:: + + The ``qinfo`` module is deprecated and scheduled to be removed in v0.40. Most quantum information transforms + are available as measurement processes (see the :mod:`~pennylane.measurements` module for more details). + Additionally, the transforms are also available as standalone functions in the :mod:`~pennylane.math` and + :mod:`~pennylane.gradients` modules. + This module provides a collection of methods to return quantum information quantities from :class:`~.QNode` returning :func:`~pennylane.state`. @@ -15,8 +22,4 @@ Transforms .. automodapi:: pennylane.qinfo.transforms :no-heading: :no-inherited-members: - :skip: metric_tensor - :skip: adjoint_metric_tensor :skip: transform - :skip: classical_fisher - :skip: quantum_fisher diff --git a/doc/development/adding_operators.rst b/doc/development/adding_operators.rst index 728ec26604a..f8391929c5b 100644 --- a/doc/development/adding_operators.rst +++ b/doc/development/adding_operators.rst @@ -204,7 +204,7 @@ knows a native implementation for ``FlipAndRotate``). It also defines an adjoint # as the class differs from the standard `__init__` call signature of # (*data, wires=wires, **hyperparameters), the _unflatten method that # must be defined as well - # _unflatten recreates a opeartion from the serialized data and metadata of ``Operator._flatten`` + # _unflatten recreates a operation from the serialized data and metadata of ``Operator._flatten`` # copied_op = type(op)._unflatten(*op._flatten()) wires = metadata[0] hyperparams = dict(metadata[1]) @@ -245,12 +245,7 @@ If the above operator omitted the ``_unflatten`` custom definition, it would rai The new gate can be used with PennyLane devices. Device support for an operation can be checked via ``dev.stopping_condition(op)``. If ``True``, then the device supports the operation. -``DefaultQubitLegacy`` first checks if the operator has a matrix using the :attr:`~.Operator.has_matrix` property. -If the Operator doesn't have a matrix, the device then checks if the name of the Operator is explicitly specified in -:attr:`~DefaultQubitLegacy.operations` or :attr:`~DefaultQubitLegacy.observables`. - -Other devices that do not inherit from ``DefaultQubitLegacy`` only check if the name is explicitly specified in the ``operations`` -property. +``DefaultQubit`` first checks if the operator has a matrix using the :attr:`~.Operator.has_matrix` property. - If the device registers support for an operation with the same name, PennyLane leaves the gate implementation up to the device. The device diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index c91979963a9..b7e58cb2279 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -9,14 +9,37 @@ deprecations are listed below. Pending deprecations -------------------- +* The ``'ancilla'`` argument for :func:`~pennylane.iterative_qpe` has been deprecated. Instead, use the ``'aux_wire'`` + argument. + + - Deprecated in v0.39 + - Will be removed in v0.40 + +* The ``qml.shadows.shadow_expval`` transform has been deprecated. Instead, please use the + ``qml.shadow_expval`` measurement process. + + - Deprecated in v0.39 + - Will be removed in v0.40 + +* ``qml.broadcast`` has been deprecated. Users should use ``for`` loops instead. + + - Deprecated in v0.39 + - Will be removed in v0.40 + +* The ``qml.qinfo`` module has been deprecated. Please see the respective functions in the ``qml.math`` and ``qml.measurements`` + modules instead. + + - Deprecated in v0.39 + - Will be removed in v0.40 + * ``Device``, ``QubitDevice``, and ``QutritDevice`` will no longer be imported top level in v0.40. They instead we be available as ``qml.devices.LegacyDevice``, ``qml.devices.QubitDevice``, and ``qml.devices.QutritDevice`` respectively. - Deprecated top level access in v0.39 - - Top level access removed in v0.40 + - Top level access will be removed in v0.40 -* `QNode.gradient_fn` is deprecated. Please use `QNode.diff_method` instead. `QNode.get_gradient_fn` can also be used to +* ``QNode.gradient_fn`` is deprecated. Please use ``QNode.diff_method`` instead. ``QNode.get_gradient_fn`` can also be used to process the diff method. - Deprecated in v0.39 @@ -27,18 +50,21 @@ Pending deprecations - Deprecated in v0.38 - Will be removed in v0.39 -* The functions ``qml.qinfo.classical_fisher`` and ``qml.qinfo.quantum_fisher`` are deprecated since they are being migrated - to the ``qml.gradients`` module. Therefore, ``qml.gradients.classical_fisher`` and ``qml.gradients.quantum_fisher`` should be used instead. - - - Deprecated and Duplicated in v0.38 - - Will be removed in v0.39 - * The ``simplify`` argument in ``qml.Hamiltonian`` and ``qml.ops.LinearCombination`` is deprecated. Instead, ``qml.simplify()`` can be called on the constructed operator. - Deprecated in v0.37 - Will be removed in v0.39 +* The :class:`~pennylane.BasisStatePreparation` template is deprecated. + Instead, use :class:`~pennylane.BasisState`. + +* The ``QubitStateVector`` template is deprecated. + Instead, use ``StatePrep``. + + - Deprecated in v0.39 + - Will be removed in v0.40 + New operator arithmetic deprecations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -83,6 +109,23 @@ Other deprecations Completed deprecation cycles ---------------------------- +* The ``simplify`` argument in ``qml.Hamiltonian`` and ``qml.ops.LinearCombination`` has been removed. + Instead, ``qml.simplify()`` can be called on the constructed operator. + + - Deprecated in v0.37 + - Removed in v0.39 + +* The ``decomp_depth`` argument in ``qml.device`` is removed. + + - Deprecated in v0.38 + - Removed in v0.39 + +* The functions ``qml.qinfo.classical_fisher`` and ``qml.qinfo.quantum_fisher`` have been removed and migrated to the ``qml.gradients`` + module. Therefore, ``qml.gradients.classical_fisher`` and ``qml.gradients.quantum_fisher`` should be used instead. + + - Deprecated in v0.38 + - Removed in v0.39 + * All of the legacy devices (any with the name ``default.qubit.{autograd,torch,tf,jax,legacy}``) are removed. Use ``default.qubit`` instead, as it supports backpropagation for the many backends the legacy devices support. diff --git a/doc/index.rst b/doc/index.rst index 85c20538942..045a381670b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -137,7 +137,7 @@ If you are having issues, please let us know by posting the issue on our GitHub We encourage contributions — simply fork the PennyLane repository, and then make a `pull request `_ containing -your contribution. All contributers to PennyLane will be listed as authors on the releases. +your contribution. All contributors to PennyLane will be listed as authors on the releases. To chat directly with the team designing and building PennyLane, as well as members of our community — ranging from quantum machine learning researchers, to students, to those diff --git a/doc/introduction/interfaces/numpy.rst b/doc/introduction/interfaces/numpy.rst index ebef5748c89..1b16a2053ad 100644 --- a/doc/introduction/interfaces/numpy.rst +++ b/doc/introduction/interfaces/numpy.rst @@ -170,7 +170,7 @@ the following QNode that accepts two arguments ``data`` and ``weights``: qml.CNOT(wires=[0, 2]) return qml.expval(qml.PauliZ(0)) - rng = np.random.default_rng(seed=42) # make the results reproducable + rng = np.random.default_rng(seed=42) # make the results reproducible data = rng.random([2 ** 3], requires_grad=False) weights = np.array([0.1, 0.2, 0.3], requires_grad=True) diff --git a/doc/introduction/measurements.rst b/doc/introduction/measurements.rst index 2f6e6c693fe..214a132951f 100644 --- a/doc/introduction/measurements.rst +++ b/doc/introduction/measurements.rst @@ -282,7 +282,7 @@ circuit: .. code-block:: python - # fix seed to make results reproducable + # fix seed to make results reproducible np.random.seed(1) dev = qml.device("default.qubit", wires=1) diff --git a/doc/introduction/templates.rst b/doc/introduction/templates.rst index 9593e203830..01a24cbf3e0 100644 --- a/doc/introduction/templates.rst +++ b/doc/introduction/templates.rst @@ -426,7 +426,7 @@ The shape can for example be used to construct random weights at the beginning o return qml.expval(qml.PauliZ(0)) shape = BasicEntanglerLayers.shape(n_layers=2, n_wires=n_wires) - np.random.seed(42) # to make the result reproducable + np.random.seed(42) # to make the result reproducible weights = np.random.random(size=shape) >>> circuit(weights) diff --git a/doc/news/new_opmath.rst b/doc/news/new_opmath.rst index 59a1c54aa0b..c90df6d8978 100644 --- a/doc/news/new_opmath.rst +++ b/doc/news/new_opmath.rst @@ -245,10 +245,10 @@ To help identify a fix, select the option below that describes your situation. >>> qml.Hamiltonian([0.5], [X(0) @ X(1)]) 0.5 * (X(0) @ X(1)) - The API of :class:`~.ops.op_math.LinearCombination` is identical to that of :class:`~.Hamiltonian`. We can group observables or simplify upon initialization. + The API of :class:`~.ops.op_math.LinearCombination` is identical to that of :class:`~.Hamiltonian`. We can group observables upon initialization. - >>> H1 = qml.Hamiltonian([0.5, 0.5, 0.5], [X(0) @ X(1), X(0), Y(0)], grouping_type="qwc", simplify=True) - >>> H2 = qml.ops.LinearCombination([0.5, 0.5, 0.5], [X(0) @ X(1), X(0), Y(0)], grouping_type="qwc", simplify=True) + >>> H1 = qml.Hamiltonian([0.5, 0.5, 0.5], [X(0) @ X(1), X(0), Y(0)], grouping_type="qwc") + >>> H2 = qml.ops.LinearCombination([0.5, 0.5, 0.5], [X(0) @ X(1), X(0), Y(0)], grouping_type="qwc") >>> H1 == H2 True diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index eabd76ecc1b..6db86325d6f 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -8,16 +8,24 @@ [Kitaev](https://arxiv.org/abs/cond-mat/0506438) model on a lattice. [(#6174)](https://github.com/PennyLaneAI/pennylane/pull/6174) +* A new `qml.vn_entanglement_entropy` measurement process has been added which measures the + Von Neumann entanglement entropy of a quantum state. + [(#5911)](https://github.com/PennyLaneAI/pennylane/pull/5911) + +

Improvements 🛠

* PennyLane is now compatible with NumPy 2.0. [(#6061)](https://github.com/PennyLaneAI/pennylane/pull/6061) [(#6258)](https://github.com/PennyLaneAI/pennylane/pull/6258) +* PennyLane is now compatible with Jax 0.4.28. + [(#6255)](https://github.com/PennyLaneAI/pennylane/pull/6255) + * `qml.qchem.excitations` now optionally returns fermionic operators. [(#6171)](https://github.com/PennyLaneAI/pennylane/pull/6171) -* The `diagonalize_measurements` transform now uses a more efficient method of diagonalization +* The `diagonalize_measurements` transform now uses a more efficient method of diagonalization when possible, based on the `pauli_rep` of the relevant observables. [#6113](https://github.com/PennyLaneAI/pennylane/pull/6113/) @@ -41,6 +49,9 @@ `from pennylane.capture.primitives import *`. [(#6129)](https://github.com/PennyLaneAI/pennylane/pull/6129) +* `FermiWord` class now has a method to apply anti-commutator relations. + [(#6196)](https://github.com/PennyLaneAI/pennylane/pull/6196) + * `FermiWord` and `FermiSentence` classes now have methods to compute adjoints. [(#6166)](https://github.com/PennyLaneAI/pennylane/pull/6166) @@ -55,12 +66,23 @@ unique representation of the object. [(#6167)](https://github.com/PennyLaneAI/pennylane/pull/6167) +* A `ReferenceQubit` is introduced for testing purposes and as a reference for future plugin development. + [(#6181)](https://github.com/PennyLaneAI/pennylane/pull/6181) + * The `to_mat` methods for `FermiWord` and `FermiSentence` now optionally return a sparse matrix. [(#6173)](https://github.com/PennyLaneAI/pennylane/pull/6173)

Breaking changes 💔

+* The `simplify` argument in `qml.Hamiltonian` and `qml.ops.LinearCombination` has been removed. + Instead, `qml.simplify()` can be called on the constructed operator. + [(#6279)](https://github.com/PennyLaneAI/pennylane/pull/6279) + +* The functions `qml.qinfo.classical_fisher` and `qml.qinfo.quantum_fisher` have been removed and migrated to the `qml.gradients` + module. Therefore, `qml.gradients.classical_fisher` and `qml.gradients.quantum_fisher` should be used instead. + [(#5911)](https://github.com/PennyLaneAI/pennylane/pull/5911) + * Remove support for Python 3.9. [(#6223)](https://github.com/PennyLaneAI/pennylane/pull/6223) @@ -85,12 +107,36 @@ Please use `qml.transforms.split_non_commuting` instead. [(#6204)](https://github.com/PennyLaneAI/pennylane/pull/6204) +* The `decomp_depth` keyword argument to `qml.device` is removed. + [(#6234)](https://github.com/PennyLaneAI/pennylane/pull/6234) + * `Operator.expand` is now removed. Use `qml.tape.QuantumScript(op.deocomposition())` instead. [(#6227)](https://github.com/PennyLaneAI/pennylane/pull/6227)

Deprecations 👋

+* The `qml.BasisStatePreparation` template is deprecated. + Instead, use `qml.BasisState`. + [(#6021)](https://github.com/PennyLaneAI/pennylane/pull/6021) + +* The `'ancilla'` argument for `qml.iterative_qpe` has been deprecated. Instead, use the `'aux_wire'` argument. + [(#6277)](https://github.com/PennyLaneAI/pennylane/pull/6277) + +* `qml.shadows.shadow_expval` has been deprecated. Instead, use the `qml.shadow_expval` measurement + process. + [(#6277)](https://github.com/PennyLaneAI/pennylane/pull/6277) + +* `qml.broadcast` has been deprecated. Please use `for` loops instead. + [(#6277)](https://github.com/PennyLaneAI/pennylane/pull/6277) + +* The `qml.QubitStateVector` template is deprecated. Instead, use `qml.StatePrep`. + [(#6172)](https://github.com/PennyLaneAI/pennylane/pull/6172) + +* The `qml.qinfo` module has been deprecated. Please see the respective functions in the `qml.math` and + `qml.measurements` modules instead. + [(#5911)](https://github.com/PennyLaneAI/pennylane/pull/5911) + * `Device`, `QubitDevice`, and `QutritDevice` will no longer be accessible via top-level import in v0.40. They will still be accessible as `qml.devices.LegacyDevice`, `qml.devices.QubitDevice`, and `qml.devices.QutritDevice` respectively. @@ -101,24 +147,30 @@

Documentation 📝

+* Fixed spelling in a number of places across the documentation. + [(#6280)](https://github.com/PennyLaneAI/pennylane/pull/6280) + +* Add `work_wires` parameter to `qml.MultiControlledX` docstring signature. + [(#6271)](https://github.com/PennyLaneAI/pennylane/pull/6271) +

Bug fixes 🐛

* Fix a bug where zero-valued JVPs were calculated wrongly in the presence of shot vectors. [(#6219)](https://github.com/PennyLaneAI/pennylane/pull/6219) -* Fix `qml.PrepSelPrep` template to work with `torch`: +* Fix `qml.PrepSelPrep` template to work with `torch`. [(#6191)](https://github.com/PennyLaneAI/pennylane/pull/6191) * Now `qml.equal` compares correctly `qml.PrepSelPrep` operators. [(#6182)](https://github.com/PennyLaneAI/pennylane/pull/6182) -* The ``qml.QSVT`` template now orders the ``projector`` wires first and the ``UA`` wires second, which is the expected order of the decomposition. +* The `qml.QSVT` template now orders the `projector` wires first and the `UA` wires second, which is the expected order of the decomposition. [(#6212)](https://github.com/PennyLaneAI/pennylane/pull/6212) - -* The ``qml.Qubitization`` template now orders the ``control`` wires first and the ``hamiltonian`` wires second, which is the expected according to other templates. + +* The `qml.Qubitization` template now orders the `control` wires first and the `hamiltonian` wires second, which is the expected according to other templates. [(#6229)](https://github.com/PennyLaneAI/pennylane/pull/6229) -* The ``qml.FABLE`` template now returns the correct value when JIT is enabled. +* The `qml.FABLE` template now returns the correct value when JIT is enabled. [(#6263)](https://github.com/PennyLaneAI/pennylane/pull/6263) * Fixes a bug where a circuit using the `autograd` interface sometimes returns nested values that are not of the `autograd` interface. @@ -135,10 +187,12 @@ Guillermo Alonso, Utkarsh Azad, Diksha Dhawan, Astral Cai, +Isaac De Vlugt, Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, Christina Lee, William Maxwell, Lee J. O'Riordan, +Mudit Pandey, David Wierichs, diff --git a/pennylane/__init__.py b/pennylane/__init__.py index 3db66c953d2..7b7a32c751c 100644 --- a/pennylane/__init__.py +++ b/pennylane/__init__.py @@ -67,6 +67,7 @@ state, var, vn_entropy, + vn_entanglement_entropy, purity, mutual_info, classical_shadow, @@ -183,7 +184,7 @@ def __getattr__(name): if name == "QubitDevice": warn( "QubitDevice will no longer be accessible top level. Please access " - " the class as pennylane.devices.QubitDevice", + "the class as pennylane.devices.QubitDevice", PennyLaneDeprecationWarning, ) return pennylane.devices._qubit_device.QubitDevice # pylint:disable=protected-access @@ -191,7 +192,7 @@ def __getattr__(name): if name == "QutritDevice": warn( "QutritDevice will no longer be accessible top level. Please access " - " the class as pennylane.devices.QutritDevice", + "the class as pennylane.devices.QutritDevice", PennyLaneDeprecationWarning, ) return pennylane.devices._qutrit_device.QutritDevice # pylint:disable=protected-access @@ -199,7 +200,7 @@ def __getattr__(name): if name == "Device": warn( "Device will no longer be accessible top level. Please access " - " the class as pennylane.devices.LegacyDevice", + "the class as pennylane.devices.LegacyDevice", PennyLaneDeprecationWarning, ) return pennylane.devices._legacy_device.Device # pylint:disable=protected-access diff --git a/pennylane/_version.py b/pennylane/_version.py index 0c39c922ce2..77d60d43078 100644 --- a/pennylane/_version.py +++ b/pennylane/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.39.0-dev16" +__version__ = "0.39.0-dev19" diff --git a/pennylane/circuit_graph.py b/pennylane/circuit_graph.py index c93be7f74c4..7e8365eaecb 100644 --- a/pennylane/circuit_graph.py +++ b/pennylane/circuit_graph.py @@ -89,7 +89,7 @@ class CircuitGraph: par_info (Optional[list[dict]]): Parameter information. For each index, the entry is a dictionary containing an operation and an index into that operation's parameters. trainable_params (Optional[set[int]]): A set containing the indices of parameters that support - differentiability. The indices provided match the order of appearence in the + differentiability. The indices provided match the order of appearance in the quantum circuit. """ diff --git a/pennylane/compiler/qjit_api.py b/pennylane/compiler/qjit_api.py index c87d6f70ded..c849677a415 100644 --- a/pennylane/compiler/qjit_api.py +++ b/pennylane/compiler/qjit_api.py @@ -53,7 +53,7 @@ def qjit(fn=None, *args, compiler="catalyst", **kwargs): # pylint:disable=keywo Please see the :doc:`Catalyst documentation ` for more details on supported devices, operations, and measurements. - CUDA Quantum supports ``softwareq.qpp``, ``nvidida.custatevec``, and ``nvidia.cutensornet``. + CUDA Quantum supports ``softwareq.qpp``, ``nvidia.custatevec``, and ``nvidia.cutensornet``. Args: fn (Callable): Hybrid (quantum-classical) function to compile @@ -77,7 +77,7 @@ def qjit(fn=None, *args, compiler="catalyst", **kwargs): # pylint:disable=keywo elements of this list are named sequences of MLIR passes to be executed. A ``None`` value (the default) results in the execution of the default pipeline. This option is considered to be used by advanced users for low-level debugging purposes. - static_argnums(int or Seqence[Int]): an index or a sequence of indices that specifies the + static_argnums(int or Sequence[Int]): an index or a sequence of indices that specifies the positions of static arguments. abstracted_axes (Sequence[Sequence[str]] or Dict[int, str] or Sequence[Dict[int, str]]): An experimental option to specify dynamic tensor shapes. diff --git a/pennylane/data/__init__.py b/pennylane/data/__init__.py index e6bb4ba9baf..2f07debe26d 100644 --- a/pennylane/data/__init__.py +++ b/pennylane/data/__init__.py @@ -152,12 +152,12 @@ ... doc="The hamiltonian of the system")) >>> dataset.eigen = qml.data.attribute( ... {"eigvals": eigvals, "eigvecs": eigvecs}, -... doc="Eigenvalues and eigenvectors of the hamiltonain") +... doc="Eigenvalues and eigenvectors of the hamiltonian") This metadata can then be accessed using the :meth:`Dataset.attr_info` mapping: >>> dataset.attr_info["eigen"]["doc"] -'Eigenvalues and eigenvectors of the hamiltonain' +'Eigenvalues and eigenvectors of the hamiltonian' Declarative API diff --git a/pennylane/data/base/attribute.py b/pennylane/data/base/attribute.py index e1eefbf9b07..ebcbaa8110c 100644 --- a/pennylane/data/base/attribute.py +++ b/pennylane/data/base/attribute.py @@ -426,12 +426,12 @@ def attribute( ... doc="The hamiltonian of the system")) >>> dataset.eigen = qml.data.attribute( ... {"eigvals": eigvals, "eigvecs": eigvecs}, - ... doc="Eigenvalues and eigenvectors of the hamiltonain") + ... doc="Eigenvalues and eigenvectors of the hamiltonian") This metadata can then be accessed using the :meth:`~.Dataset.attr_info` mapping: >>> dataset.attr_info["eigen"]["doc"] - 'Eigenvalues and eigenvectors of the hamiltonain' + 'Eigenvalues and eigenvectors of the hamiltonian' """ return match_obj_type(val)(val, AttributeInfo(doc=doc, py_type=type(val), **kwargs)) diff --git a/pennylane/devices/__init__.py b/pennylane/devices/__init__.py index a542ba7df1d..98ae08ac642 100644 --- a/pennylane/devices/__init__.py +++ b/pennylane/devices/__init__.py @@ -26,7 +26,6 @@ default_qubit - default_qubit_legacy default_gaussian default_mixed default_qutrit @@ -37,6 +36,7 @@ _qubit_device _qutrit_device null_qubit + reference_qubit tests Next generation devices @@ -58,6 +58,7 @@ DefaultQubit DefaultTensor NullQubit + ReferenceQubit DefaultQutritMixed LegacyDeviceFacade @@ -154,12 +155,12 @@ def execute(self, circuits, execution_config = qml.devices.DefaultExecutionConfi # DefaultTensor is not imported here to avoid warnings # from quimb in case it is installed on the system. -from .default_qubit_legacy import DefaultQubitLegacy from .default_gaussian import DefaultGaussian from .default_mixed import DefaultMixed from .default_clifford import DefaultClifford from .default_tensor import DefaultTensor from .null_qubit import NullQubit +from .reference_qubit import ReferenceQubit from .default_qutrit import DefaultQutrit from .default_qutrit_mixed import DefaultQutritMixed from ._legacy_device import Device as LegacyDevice diff --git a/pennylane/devices/_legacy_device.py b/pennylane/devices/_legacy_device.py index b3642e084da..dd1b97dd4a5 100644 --- a/pennylane/devices/_legacy_device.py +++ b/pennylane/devices/_legacy_device.py @@ -296,7 +296,7 @@ def shot_vector(self): **Example** - >>> dev = qml.device("default.qubit.legacy", wires=2, shots=[3, 1, 2, 2, 2, 2, 6, 1, 1, 5, 12, 10, 10]) + >>> dev = qml.device("default.mixed", wires=2, shots=[3, 1, 2, 2, 2, 2, 6, 1, 1, 5, 12, 10, 10]) >>> dev.shots 57 >>> dev.shot_vector @@ -623,8 +623,6 @@ def custom_expand(self, fn): .. code-block:: python - dev = qml.device("default.qubit.legacy", wires=2) - @dev.custom_expand def my_expansion_function(self, tape, max_expansion=10): ... diff --git a/pennylane/devices/_qubit_device.py b/pennylane/devices/_qubit_device.py index ef9f41f0378..c4873bf2799 100644 --- a/pennylane/devices/_qubit_device.py +++ b/pennylane/devices/_qubit_device.py @@ -50,6 +50,7 @@ StateMeasurement, StateMP, VarianceMP, + VnEntanglementEntropyMP, VnEntropyMP, ) from pennylane.operation import Operation, operation_derivative @@ -176,28 +177,6 @@ def _const_mul(constant, array): * tape: quantum tape to transform - **Example:** - - Let's create a device that inherits from :class:`~pennylane.devices.DefaultQubitLegacy` and overrides the - logic of the `qml.sample` measurement. To do so we will need to update the ``measurement_map`` - dictionary: - - .. code-block:: python - - class NewDevice(DefaultQubitLegacy): - def __init__(self, wires, shots): - super().__init__(wires=wires, shots=shots) - self.measurement_map[SampleMP] = "sample_measurement" - - def sample_measurement(self, measurement, shot_range=None, bin_size=None): - return 2 - - >>> dev = NewDevice(wires=2, shots=1000) - >>> @qml.qnode(dev) - ... def circuit(): - ... return qml.sample() - >>> circuit() - tensor(2, requires_grad=True) """ def __init__( @@ -740,6 +719,29 @@ def statistics( ) result = self.vn_entropy(wires=obs.wires, log_base=obs.log_base) + elif isinstance(m, VnEntanglementEntropyMP): + if self.wires.labels != tuple(range(self.num_wires)): + raise qml.QuantumFunctionError( + "Returning the Von Neumann entanglement entropy is not supported when using custom wire labels" + ) + + if self._shot_vector is not None: + raise NotImplementedError( + "Returning the Von Neumann entanglement entropy is not supported with shot vectors." + ) + + if self.shots is not None: + warnings.warn( + "Requested Von Neumann entanglement entropy with finite shots; the returned " + "state information is analytic and is unaffected by sampling. To silence " + "this warning, set shots=None on the device.", + UserWarning, + ) + wires0, wires1 = obs.raw_wires + result = self.vn_entanglement_entropy( + wires0=wires0, wires1=wires1, log_base=obs.log_base + ) + elif isinstance(m, MutualInfoMP): if self.wires.labels != tuple(range(self.num_wires)): raise qml.QuantumFunctionError( @@ -799,6 +801,7 @@ def statistics( VarianceMP, ProbabilityMP, VnEntropyMP, + VnEntanglementEntropyMP, MutualInfoMP, ShadowExpvalMP, ), @@ -1030,7 +1033,7 @@ def vn_entropy(self, wires, log_base): """ try: state = self.density_matrix(wires=self.wires) - except qml.QuantumFunctionError as e: # pragma: no cover + except (qml.QuantumFunctionError, NotImplementedError) as e: # pragma: no cover raise NotImplementedError( f"Cannot compute the Von Neumman entropy with device {self.name} that is not capable of returning the " f"state. " @@ -1038,6 +1041,36 @@ def vn_entropy(self, wires, log_base): wires = wires.tolist() return qml.math.vn_entropy(state, indices=wires, c_dtype=self.C_DTYPE, base=log_base) + def vn_entanglement_entropy(self, wires0, wires1, log_base): + r"""Returns the Von Neumann entanglement entropy prior to measurement. + + .. math:: + + S(\rho_A) = -\text{Tr}[\rho_A \log \rho_A] = -\text{Tr}[\rho_B \log \rho_B] = S(\rho_B) + + Args: + wires0 (Sequence[int] or int): the wires of the first subsystem + wires1 (Sequence[int] or int): the wires of the second subsystem + log_base (float): Base for the logarithm. + + Returns: + float: returns the Von Neumann entropy + """ + try: + state = self.density_matrix(wires=self.wires) + except (qml.QuantumFunctionError, NotImplementedError) as e: # pragma: no cover + raise NotImplementedError( + f"Cannot compute the Von Neumman entropy with device {self.name} that is not capable of returning the " + f"state. " + ) from e + + wires0 = wires0.tolist() + wires1 = wires1.tolist() + + return qml.math.vn_entanglement_entropy( + state, indices0=wires0, indices1=wires1, c_dtype=self.C_DTYPE, base=log_base + ) + def mutual_info(self, wires0, wires1, log_base): r"""Returns the mutual information prior to measurement: @@ -1057,7 +1090,7 @@ def mutual_info(self, wires0, wires1, log_base): """ try: state = self.density_matrix(wires=self.wires) - except qml.QuantumFunctionError as e: # pragma: no cover + except (qml.QuantumFunctionError, NotImplementedError) as e: # pragma: no cover raise NotImplementedError( f"Cannot compute the mutual information with device {self.name} that is not capable of returning the " f"state. " @@ -1153,7 +1186,7 @@ def classical_shadow(self, obs, circuit): def shadow_expval(self, obs, circuit): r"""Compute expectation values using classical shadows in a differentiable manner. - Please refer to :func:`~.pennylane.shadow_expval` for detailed documentation. + Please refer to :func:`~pennylane.shadow_expval` for detailed documentation. Args: obs (~.pennylane.measurements.ClassicalShadowMP): The classical shadow expectation @@ -1476,7 +1509,7 @@ def _samples_to_counts(self, samples, mp: CountsMP, num_wires): **Example** >>> num_wires = 2 - >>> dev = qml.device("default.qubit.legacy", wires=num_wires) + >>> dev = qml.device("default.mixed", wires=num_wires) >>> mp = qml.counts() >>> samples = pnp.array([[0, 0], [0, 0], [1, 0]]) >>> dev._samples_to_counts(samples, mp, num_wires) diff --git a/pennylane/devices/_qutrit_device.py b/pennylane/devices/_qutrit_device.py index fad5dfc8ff3..c721bfe6901 100644 --- a/pennylane/devices/_qutrit_device.py +++ b/pennylane/devices/_qutrit_device.py @@ -183,6 +183,28 @@ def vn_entropy(self, wires, log_base): "Unsupported return type specified for observable Von Neumann entropy" ) + def vn_entanglement_entropy(self, wires0, wires1, log_base): + r"""Returns the Von Neumann entanglement entropy prior to measurement. + + .. math:: + + S(\rho_A) = -\text{Tr}[\rho_A \log \rho_A] = -\text{Tr}[\rho_B \log \rho_B] = S(\rho_B) + + Args: + wires0 (Sequence[int] or int): the wires of the first subsystem + wires1 (Sequence[int] or int): the wires of the second subsystem + log_base (float): Base for the logarithm. + + Returns: + float: returns the Von Neumann entropy + """ + # TODO: Add support for VnEntanglementEntropy return type. Currently, qml.math is hard coded to calculate this for qubit + # states (see `qml.math.vn_entanglement_entropy()`), so it needs to be updated before VnEntanglementEntropy can be supported for qutrits. + # For now, if a user tries to request this return type, an error will be raised. + raise qml.QuantumFunctionError( + "Unsupported return type specified for observable Von Neumann entanglement entropy" + ) + def mutual_info(self, wires0, wires1, log_base): r"""Returns the mutual information prior to measurement: @@ -230,7 +252,7 @@ def classical_shadow(self, obs, circuit): def shadow_expval(self, obs, circuit): r"""Compute expectation values using classical shadows in a differentiable manner. - Please refer to :func:`~.pennylane.shadow_expval` for detailed documentation. + Please refer to :func:`~pennylane.shadow_expval` for detailed documentation. .. seealso:: :func:`~pennylane.shadow_expval` diff --git a/pennylane/devices/default_mixed.py b/pennylane/devices/default_mixed.py index d7a3a55e0b5..dd902c557ea 100644 --- a/pennylane/devices/default_mixed.py +++ b/pennylane/devices/default_mixed.py @@ -41,6 +41,7 @@ SampleMP, StateMP, VarianceMP, + VnEntanglementEntropyMP, VnEntropyMP, ) from pennylane.operation import Channel @@ -637,6 +638,9 @@ def _snapshot_measurements(self, density_matrix, measurement): density_matrix, indices=map_wires, c_dtype=self.C_DTYPE, base=base ) + elif isinstance(measurement, VnEntanglementEntropyMP): + snap_result = measurement.process_density_matrix(density_matrix, wire_order=self.wires) + elif isinstance(measurement, MutualInfoMP): base = measurement.log_base wires0, wires1 = list(map(self.map_wires, measurement.raw_wires)) @@ -762,9 +766,10 @@ def execute(self, circuit, **kwargs): # not specified or all wires specified. self.measured_wires = self.wires return super().execute(circuit, **kwargs) - if isinstance(m, (VnEntropyMP, MutualInfoMP)): - # VnEntropy, MutualInfo: Computed for the state prior to measurement. So, readout - # error need not be applied on the corresponding device wires. + if isinstance(m, (VnEntropyMP, VnEntanglementEntropyMP, MutualInfoMP)): + # VnEntropy, VnEntanglementEntropyMP, MutualInfo: Computed for the state + # prior to measurement. So, readout error need not be applied on the + # corresponding device wires. continue wires_list.append(m.wires) self.measured_wires = qml.wires.Wires.all_wires(wires_list) diff --git a/pennylane/devices/default_qutrit.py b/pennylane/devices/default_qutrit.py index 3cb0708ee88..fef80204748 100644 --- a/pennylane/devices/default_qutrit.py +++ b/pennylane/devices/default_qutrit.py @@ -24,7 +24,6 @@ import numpy as np import pennylane as qml # pylint: disable=unused-import -from pennylane.devices.default_qubit_legacy import _get_slice from pennylane.logging import debug_logger, debug_logger_init from pennylane.wires import WireError @@ -40,6 +39,35 @@ OMEGA = qml.math.exp(2 * np.pi * 1j / 3) +def _get_slice(index, axis, num_axes): + """Allows slicing along an arbitrary axis of an array or tensor. + + Args: + index (int): the index to access + axis (int): the axis to slice into + num_axes (int): total number of axes + + Returns: + tuple[slice or int]: a tuple that can be used to slice into an array or tensor + + **Example:** + + Accessing the 2 index along axis 1 of a 3-axis array: + + >>> sl = _get_slice(2, 1, 3) + >>> sl + (slice(None, None, None), 2, slice(None, None, None)) + >>> a = np.arange(27).reshape((3, 3, 3)) + >>> a[sl] + array([[ 6, 7, 8], + [15, 16, 17], + [24, 25, 26]]) + """ + idx = [slice(None)] * num_axes + idx[axis] = index + return tuple(idx) + + # pylint: disable=too-many-arguments class DefaultQutrit(QutritDevice): """Default qutrit device for PennyLane. diff --git a/pennylane/devices/device_api.py b/pennylane/devices/device_api.py index 83bb43d0b2c..60d14afa8fb 100644 --- a/pennylane/devices/device_api.py +++ b/pennylane/devices/device_api.py @@ -63,7 +63,7 @@ class Device(abc.ABC): Shot information is no longer stored on the device, but instead specified on individual input :class:`~.QuantumTape`. The old devices defined a :meth:`~.Device.capabilities` dictionary that defined characteristics of the devices and controlled various - preprocessing and validation steps, such as ``"supports_broadcasting"``. These capabilites should now be handled by the + preprocessing and validation steps, such as ``"supports_broadcasting"``. These capabilities should now be handled by the :meth:`~.Device.preprocess` method. For example, if a device does not support broadcasting, ``preprocess`` should split a quantum script with broadcasted parameters into a batch of quantum scripts. If the device does not support mid circuit measurements, then ``preprocess`` should apply :func:`~.defer_measurements`. A set of default preprocessing steps will be available @@ -77,7 +77,7 @@ class Device(abc.ABC): to certain inputs. Versioning should be specified by the package containing the device. If an external package includes a PennyLane device, - then the package requirements should specify the minimium PennyLane version required to work with the device. + then the package requirements should specify the minimum PennyLane version required to work with the device. .. details:: :title: The relationship between preprocessing and execution diff --git a/pennylane/devices/device_constructor.py b/pennylane/devices/device_constructor.py index 00ad188d1d1..1fc4df2acff 100644 --- a/pennylane/devices/device_constructor.py +++ b/pennylane/devices/device_constructor.py @@ -14,7 +14,6 @@ """ This module contains code for the main device construction delegation logic. """ -import warnings from importlib import metadata from sys import version_info @@ -57,8 +56,7 @@ def refresh_devices(): # pylint: disable=protected-access def device(name, *args, **kwargs): - r""" - Load a device and return the instance. + r"""Load a device and return the instance. This function is used to load a particular quantum device, which can then be used to construct QNodes. @@ -105,12 +103,6 @@ def device(name, *args, **kwargs): that contains global and/or device specific configurations. custom_decomps (Dict[Union(str, Operator), Callable]): Custom decompositions to be applied by the device at runtime. - decomp_depth (int): For when custom decompositions are specified, - the maximum expansion depth used by the expansion function. - - .. warning:: - - The ``decomp_depth`` argument is deprecated and will be removed in version 0.39. All devices must be loaded by specifying their **short-name** as listed above, followed by the **wires** (subsystems) you wish to initialize. The ``wires`` @@ -122,10 +114,10 @@ def device(name, *args, **kwargs): dev = qml.device('default.qubit', wires=5) def circuit(): - qml.Hadamard(wires=1) - qml.Hadamard(wires=[0]) - qml.CNOT(wires=[3, 4]) - ... + qml.Hadamard(wires=1) + qml.Hadamard(wires=[0]) + qml.CNOT(wires=[3, 4]) + ... The ``wires`` argument can also be a sequence of unique numbers or strings, specifying custom wire labels that the user employs to address the wires: @@ -135,10 +127,10 @@ def circuit(): dev = qml.device('default.qubit', wires=['ancilla', 'q11', 'q12', -1, 1]) def circuit(): - qml.Hadamard(wires='q11') - qml.Hadamard(wires=['ancilla']) - qml.CNOT(wires=['q12', -1]) - ... + qml.Hadamard(wires='q11') + qml.Hadamard(wires=['ancilla']) + qml.CNOT(wires=['q12', -1]) + ... On some newer devices, such as ``default.qubit``, the ``wires`` argument can be omitted altogether, and instead the wires will be computed when executing a circuit depending on its contents. @@ -157,8 +149,8 @@ def circuit(): @qml.qnode(dev) def circuit(a): - qml.RX(a, wires=0) - return qml.sample(qml.Z(0)) + qml.RX(a, wires=0) + return qml.sample(qml.Z(0)) >>> circuit(0.8) # 10 samples are returned array([ 1, 1, 1, 1, -1, 1, 1, -1, 1, 1]) @@ -243,15 +235,6 @@ def run_cnot(): # Pop the custom decomposition keyword argument; we will use it here # only and not pass it to the device. custom_decomps = kwargs.pop("custom_decomps", None) - decomp_depth = kwargs.pop("decomp_depth", None) - - if decomp_depth is not None: - warnings.warn( - "The decomp_depth argument is deprecated and will be removed in version 0.39. ", - qml.PennyLaneDeprecationWarning, - ) - else: - decomp_depth = 10 kwargs.pop("config", None) options.update(kwargs) @@ -284,12 +267,12 @@ def _safe_specifier_set(version_str): if custom_decomps is not None: if isinstance(dev, qml.devices.LegacyDevice): custom_decomp_expand_fn = qml.transforms.create_decomp_expand_fn( - custom_decomps, dev, decomp_depth=decomp_depth + custom_decomps, dev, decomp_depth=10 ) dev.custom_expand(custom_decomp_expand_fn) else: custom_decomp_preprocess = qml.transforms.tape_expand._create_decomp_preprocessing( - custom_decomps, dev, decomp_depth=decomp_depth + custom_decomps, dev, decomp_depth=10 ) dev.preprocess = custom_decomp_preprocess diff --git a/pennylane/devices/legacy_facade.py b/pennylane/devices/legacy_facade.py index bd2190f0fe1..e0ce5b578f1 100644 --- a/pennylane/devices/legacy_facade.py +++ b/pennylane/devices/legacy_facade.py @@ -53,7 +53,6 @@ def _set_shots(device, shots): As a standard context manager: - >>> dev = qml.device("default.qubit.legacy", wires=2, shots=None) >>> with _set_shots(dev, shots=100): ... print(dev.shots) 100 diff --git a/pennylane/devices/qubit/measure.py b/pennylane/devices/qubit/measure.py index ae2ddb07c02..da4d51b7aec 100644 --- a/pennylane/devices/qubit/measure.py +++ b/pennylane/devices/qubit/measure.py @@ -18,6 +18,7 @@ from scipy.sparse import csr_matrix +import pennylane as qml from pennylane import math from pennylane.measurements import ( ExpectationMP, @@ -168,7 +169,7 @@ def sum_of_terms_method( ) -# pylint: disable=too-many-return-statements +# pylint: disable=too-many-return-statements,too-many-branches def get_measurement_function( measurementprocess: MeasurementProcess, state: TensorLike ) -> Callable[[MeasurementProcess, TensorLike], TensorLike]: @@ -195,13 +196,31 @@ def get_measurement_function( backprop_mode = math.get_interface(state, *measurementprocess.obs.data) != "numpy" if isinstance(measurementprocess.obs, (Hamiltonian, LinearCombination)): - # need to work out thresholds for when its faster to use "backprop mode" measurements - return sum_of_terms_method if backprop_mode else csr_dot_products + + # need to work out thresholds for when it's faster to use "backprop mode" + if backprop_mode: + return sum_of_terms_method + + if not all(obs.has_sparse_matrix for obs in measurementprocess.obs.terms()[1]): + return sum_of_terms_method + + # Hamiltonian.sparse_matrix raises a ValueError for this scenario. + if isinstance(measurementprocess.obs, Hamiltonian) and any( + any(len(o.wires) > 1 for o in qml.operation.Tensor(op).obs) + for op in measurementprocess.obs.ops + ): + return sum_of_terms_method + + return csr_dot_products if isinstance(measurementprocess.obs, Sum): if backprop_mode: # always use sum_of_terms_method for Sum observables in backprop mode return sum_of_terms_method + + if not all(obs.has_sparse_matrix for obs in measurementprocess.obs): + return sum_of_terms_method + if ( measurementprocess.obs.has_overlapping_wires and len(measurementprocess.obs.wires) > 7 diff --git a/pennylane/devices/qubit/simulate.py b/pennylane/devices/qubit/simulate.py index 89c041b8f3e..f535f5428e6 100644 --- a/pennylane/devices/qubit/simulate.py +++ b/pennylane/devices/qubit/simulate.py @@ -689,7 +689,8 @@ def prepend_state_prep(circuit, state, interface, wires): else state ) return qml.tape.QuantumScript( - [qml.StatePrep(state.ravel(), wires=wires, validate_norm=False)] + circuit.operations, + [qml.StatePrep(qml.math.ravel(state), wires=wires, validate_norm=False)] + + circuit.operations, circuit.measurements, shots=circuit.shots, ) @@ -942,7 +943,7 @@ def _(original_measurement: ProbabilityMP, measures): # pylint: disable=unused- @combine_measurements_core.register def _(original_measurement: SampleMP, measures): # pylint: disable=unused-argument - """The combined samples of two branches is obtained by concatenating the sample if each branch..""" + """The combined samples of two branches is obtained by concatenating the sample of each branch.""" new_sample = tuple( qml.math.atleast_1d(m[1]) for m in measures.values() if m[0] and not m[1] is tuple() ) @@ -955,6 +956,8 @@ def simulate_one_shot_native_mcm( ) -> Result: """Simulate a single shot of a single quantum script with native mid-circuit measurements. + Assumes that the circuit has been transformed by `dynamic_one_shot`. + Args: circuit (QuantumTape): The single circuit to simulate debugger (_Debugger): The debugger to use @@ -968,8 +971,8 @@ def simulate_one_shot_native_mcm( keep the same number of shots. Default is ``None``. Returns: - tuple(TensorLike): The results of the simulation - dict: The mid-circuit measurement results of the simulation + Result: The results of the simulation + """ prng_key = execution_kwargs.pop("prng_key", None) diff --git a/pennylane/devices/reference_qubit.py b/pennylane/devices/reference_qubit.py new file mode 100644 index 00000000000..49537d71a6e --- /dev/null +++ b/pennylane/devices/reference_qubit.py @@ -0,0 +1,154 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Contains the ReferenceQubit device, a minimal device that can be used for testing +and plugin development purposes. +""" + +import numpy as np + +import pennylane as qml + +from .device_api import Device +from .execution_config import DefaultExecutionConfig +from .modifiers import simulator_tracking, single_tape_support +from .preprocess import decompose, validate_device_wires, validate_measurements + + +def sample_state(state: np.ndarray, shots: int, seed=None): + """Generate samples from the provided state and number of shots.""" + + probs = np.imag(state) ** 2 + np.real(state) ** 2 + basis_states = np.arange(len(probs)) + + num_wires = int(np.log2(len(probs))) + + rng = np.random.default_rng(seed) + basis_samples = rng.choice(basis_states, shots, p=probs) + + # convert basis state integers to array of booleans + bin_strings = (format(s, f"0{num_wires}b") for s in basis_samples) + return np.array([[int(val) for val in s] for s in bin_strings]) + + +def simulate(tape: qml.tape.QuantumTape, seed=None) -> qml.typing.Result: + """Simulate a tape and turn it into results. + + Args: + tape (.QuantumTape): a representation of a circuit + seed (Any): A seed to use to control the generation of samples. + + """ + # 1) create the initial state + state = np.zeros(2 ** len(tape.wires)) + state[0] = 1.0 + + # 2) apply all the operations + for op in tape.operations: + op_mat = op.matrix(wire_order=tape.wires) + state = qml.math.matmul(op_mat, state) + + # 3) perform measurements + # note that shots are pulled from the tape, not from the device + if tape.shots: + samples = sample_state(state, shots=tape.shots.total_shots, seed=seed) + # Shot vector support + results = [] + for lower, upper in tape.shots.bins(): + sub_samples = samples[lower:upper] + results.append( + tuple(mp.process_samples(sub_samples, tape.wires) for mp in tape.measurements) + ) + if len(tape.measurements) == 1: + results = [res[0] for res in results] + if not tape.shots.has_partitioned_shots: + results = results[0] + else: + results = tuple(results) + else: + results = tuple(mp.process_state(state, tape.wires) for mp in tape.measurements) + if len(tape.measurements) == 1: + results = results[0] + + return results + + +operations = frozenset({"PauliX", "PauliY", "PauliZ", "Hadamard", "CNOT", "CZ", "RX", "RY", "RZ"}) + + +def supports_operation(op: qml.operation.Operator) -> bool: + """This function used by preprocessing determines what operations + are natively supported by the device. + + While in theory ``simulate`` can support any operation with a matrix, we limit the target + gate set for improved testing and reference purposes. + + """ + return getattr(op, "name", None) in operations + + +@simulator_tracking # update device.tracker with some relevant information +@single_tape_support # add support for device.execute(tape) in addition to device.execute((tape,)) +class ReferenceQubit(Device): + """A slimmed down numpy-based simulator for reference and testing purposes. + + Args: + wires (int, Iterable[Number, str]): Number of wires present on the device, or iterable that + contains unique labels for the wires as numbers (i.e., ``[-1, 0, 2]``) or strings + (``['aux', 'q1', 'q2']``). Default ``None`` if not specified. While this device allows + for ``wires`` to be unspecified at construction time, other devices may make this argument + mandatory. Devices can also implement additional restrictions on the possible wires. + shots (int, Sequence[int], Sequence[Union[int, Sequence[int]]]): The default number of shots + to use in executions involving this device. Note that during execution, shots + are pulled from the circuit, not from the device. + seed (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator, jax.random.PRNGKey]): A + seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. This is an optional + keyword argument added to follow recommend NumPy best practices. Other devices do not need + this parameter if it does not make sense for them. + + """ + + name = "reference.qubit" + + def __init__(self, wires=None, shots=None, seed=None): + super().__init__(wires=wires, shots=shots) + + # seed and rng not necessary for a device, but part of recommended + # numpy practices to use a local random number generator + self._rng = np.random.default_rng(seed) + + def preprocess(self, execution_config=DefaultExecutionConfig): + + # Here we convert an arbitrary tape into one natively supported by the device + program = qml.transforms.core.TransformProgram() + program.add_transform(validate_device_wires, wires=self.wires, name="reference.qubit") + program.add_transform(qml.defer_measurements) + program.add_transform(qml.transforms.split_non_commuting) + program.add_transform(qml.transforms.diagonalize_measurements) + program.add_transform( + decompose, + stopping_condition=supports_operation, + skip_initial_state_prep=False, + name="reference.qubit", + ) + program.add_transform(validate_measurements, name="reference.qubit") + program.add_transform(qml.transforms.broadcast_expand) + + # no need to preprocess the config as the device does not support derivatives + return program, execution_config + + def execute(self, circuits, execution_config=DefaultExecutionConfig): + for tape in circuits: + assert all(supports_operation(op) for op in tape.operations) + return tuple(simulate(tape, seed=self._rng) for tape in circuits) diff --git a/pennylane/devices/tests/__init__.py b/pennylane/devices/tests/__init__.py index 5c508e5e52a..e7b3f68e0c2 100644 --- a/pennylane/devices/tests/__init__.py +++ b/pennylane/devices/tests/__init__.py @@ -65,7 +65,7 @@ function: >>> from pennylane.devices.tests import test_device ->>> test_device("default.qubit.legacy") +>>> test_device("default.qubit") For more details on the available arguments, see the :func:`~.test_device` documentation. @@ -120,7 +120,7 @@ def test_device( **Example** >>> from pennylane.devices.tests import test_device - >>> test_device("default.qubit.legacy") + >>> test_device("default.qubit") ================================ test session starts ======================================= platform linux -- Python 3.7.7, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 rootdir: /home/josh/xanadu/pennylane/pennylane/devices/tests, inifile: pytest.ini diff --git a/pennylane/devices/tests/conftest.py b/pennylane/devices/tests/conftest.py index f462138c4d5..786b4584f59 100755 --- a/pennylane/devices/tests/conftest.py +++ b/pennylane/devices/tests/conftest.py @@ -14,7 +14,6 @@ """Contains shared fixtures for the device tests.""" import argparse import os -import warnings import numpy as np import pytest @@ -39,27 +38,6 @@ } -@pytest.fixture(scope="function", autouse=True) -def capture_legacy_device_deprecation_warnings(): - """Catches all warnings raised by a test and verifies that any Deprecation - warnings released are related to the legacy devices. Otherwise, it re-raises - any unrelated warnings""" - - with warnings.catch_warnings(record=True) as recwarn: - warnings.simplefilter("always") - yield - - for w in recwarn: - if isinstance(w, qml.PennyLaneDeprecationWarning): - assert "Use of 'default.qubit." in str(w.message) - assert "is deprecated" in str(w.message) - assert "use 'default.qubit'" in str(w.message) - - for w in recwarn: - if "Use of 'default.qubit." not in str(w.message): - warnings.warn(message=w.message, category=w.category) - - @pytest.fixture(scope="function") def tol(): """Numerical tolerance for equality tests. Returns a different tolerance for tests diff --git a/pennylane/devices/tests/test_templates.py b/pennylane/devices/tests/test_templates.py index 86e1101340a..c91304488ef 100644 --- a/pennylane/devices/tests/test_templates.py +++ b/pennylane/devices/tests/test_templates.py @@ -225,6 +225,9 @@ def circuit(): [math.fidelity_statevector(circuit(), exp_state)], [1.0], atol=tol(dev.shots) ) + @pytest.mark.filterwarnings( + "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning" + ) def test_BasisStatePreparation(self, device, tol): """Test the BasisStatePreparation template.""" dev = device(4) diff --git a/pennylane/fermi/fermionic.py b/pennylane/fermi/fermionic.py index 193cc0b4be8..b5f03020780 100644 --- a/pennylane/fermi/fermionic.py +++ b/pennylane/fermi/fermionic.py @@ -324,6 +324,110 @@ def to_mat(self, n_orbitals=None, format="dense", buffer_size=None): wire_order=list(range(largest_order)), format=format, buffer_size=buffer_size ) + def shift_operator(self, initial_position, final_position): + r"""Shifts an operator in the FermiWord from ``initial_position`` to ``final_position`` by applying the fermionic anti-commutation relations. + + There are three `anti-commutator relations `_: + + .. math:: + \left\{ a_i, a_j \right\} = 0, \quad \left\{ a^{\dagger}_i, a^{\dagger}_j \right\} = 0, \quad \left\{ a_i, a^{\dagger}_j \right\} = \delta_{ij}, + + + where + + .. math:: + \left\{a_i, a_j \right\} = a_i a_j + a_j a_i, + + and + + .. math:: + \delta_{ij} = \begin{cases} 1 & i = j \\ 0 & i \neq j \end{cases}. + + Args: + initial_position (int): The position of the operator to be shifted. + final_position (int): The desired position of the operator. + + Returns: + FermiSentence: The ``FermiSentence`` obtained after applying the anti-commutator relations. + + Raises: + TypeError: if ``initial_position`` or ``final_position`` is not an integer + ValueError: if ``initial_position`` or ``final_position`` are outside the range ``[0, len(fermiword) - 1]`` + where ``len(fermiword)`` is the number of operators in the FermiWord. + + + **Example** + + >>> w = qml.fermi.FermiWord({(0, 0): '+', (1, 1): '-'}) + >>> w.shift_operator(0, 1) + -1 * a(1) a⁺(0) + """ + + if not isinstance(initial_position, int) or not isinstance(final_position, int): + raise TypeError("Positions must be integers.") + + if initial_position < 0 or final_position < 0: + raise ValueError("Positions must be positive integers.") + + if initial_position > len(self.sorted_dic) - 1 or final_position > len(self.sorted_dic) - 1: + raise ValueError("Positions are out of range.") + + if initial_position == final_position: + return FermiSentence({self: 1}) + + fw = self + fs = FermiSentence({fw: 1}) + delta = 1 if initial_position < final_position else -1 + current = initial_position + + while current != final_position: + indices = list(fw.sorted_dic.keys()) + next = current + delta + curr_idx, curr_val = indices[current], fw[indices[current]] + next_idx, next_val = indices[next], fw[indices[next]] + + # commuting identical terms + if curr_idx[1] == next_idx[1] and curr_val == next_val: + current += delta + continue + + coeff = fs.pop(fw) + + fw = dict(fw) + fw[(current, next_idx[1])] = next_val + fw[(next, curr_idx[1])] = curr_val + + if curr_idx[1] != next_idx[1]: + del fw[curr_idx], fw[next_idx] + + fw = FermiWord(fw) + + # anti-commutator is 0 + if curr_val == next_val or curr_idx[1] != next_idx[1]: + current += delta + fs += -coeff * fw + continue + + # anti-commutator is 1 + _min = min(current, next) + _max = max(current, next) + items = list(fw.sorted_dic.items()) + + left = FermiWord({(i, key[1]): value for i, (key, value) in enumerate(items[:_min])}) + middle = FermiWord( + {(i, key[1]): value for i, (key, value) in enumerate(items[_min : _max + 1])} + ) + right = FermiWord( + {(i, key[1]): value for i, (key, value) in enumerate(items[_max + 1 :])} + ) + + terms = left * (1 - middle) * right + fs += coeff * terms + + current += delta + + return fs + # pylint: disable=useless-super-delegation class FermiSentence(dict): diff --git a/pennylane/fourier/qnode_spectrum.py b/pennylane/fourier/qnode_spectrum.py index 6eff1514503..366c9b7f16c 100644 --- a/pennylane/fourier/qnode_spectrum.py +++ b/pennylane/fourier/qnode_spectrum.py @@ -372,7 +372,7 @@ def circuit(x, y, z): Note that the values of the output are dictionaries instead of the spectrum lists, that they include the prefactors introduced by classical preprocessing, and that we would not be able to compute the advanced spectrum for ``x`` because it is - preprocessed non-linearily in the gate ``qml.RX(0.5*x**2, wires=0, id="x")``. + preprocessed non-linearly in the gate ``qml.RX(0.5*x**2, wires=0, id="x")``. """ # pylint: disable=too-many-branches,protected-access diff --git a/pennylane/gradients/fisher.py b/pennylane/gradients/fisher.py index d444ae46966..8d73a08dc75 100644 --- a/pennylane/gradients/fisher.py +++ b/pennylane/gradients/fisher.py @@ -17,7 +17,7 @@ import pennylane as qml from pennylane import transform -from pennylane.devices import DefaultQubit, DefaultQubitLegacy +from pennylane.devices import DefaultQubit from pennylane.gradients import adjoint_metric_tensor from pennylane.typing import PostprocessingFn @@ -245,14 +245,18 @@ def wrapper(*args, **kwargs): jac = jax.jacobian(new_qnode, argnums=argnums) - if interface == "torch": + elif interface == "torch": jac = _torch_jac(new_qnode) - if interface == "autograd": + elif interface == "autograd": jac = qml.jacobian(new_qnode) - if interface == "tf": + elif interface == "tf": jac = _tf_jac(new_qnode) + else: + raise ValueError( + f"Interface {interface} not supported for jacobian calculations." + ) # pragma: no cover j = jac(*args, **kwargs) p = new_qnode(*args, **kwargs) @@ -370,7 +374,7 @@ def circ(params): """ - if device.shots or not isinstance(device, (DefaultQubitLegacy, DefaultQubit)): + if device.shots or not isinstance(device, DefaultQubit): tapes, processing_fn = qml.gradients.metric_tensor(tape, *args, **kwargs) def processing_fn_multiply(res): diff --git a/pennylane/gradients/parameter_shift_cv.py b/pennylane/gradients/parameter_shift_cv.py index e76833d8546..481530e305c 100644 --- a/pennylane/gradients/parameter_shift_cv.py +++ b/pennylane/gradients/parameter_shift_cv.py @@ -561,7 +561,7 @@ def param_shift_cv( If ``None``, the default gradient recipe containing the two terms :math:`[c_0, a_0, s_0]=[1/2, 1, \pi/2]` and :math:`[c_1, a_1, s_1]=[-1/2, 1, -\pi/2]` is assumed for every parameter. - fallback_fn (None or Callable): a fallback grdient function to use for + fallback_fn (None or Callable): a fallback gradient function to use for any parameters that do not support the parameter-shift rule. f0 (tensor_like[float] or None): Output of the evaluated input tape. If provided, and the gradient recipe contains an unshifted term, this value is used, diff --git a/pennylane/gradients/parameter_shift_hessian.py b/pennylane/gradients/parameter_shift_hessian.py index b1dd2f1982e..704fa6c8919 100644 --- a/pennylane/gradients/parameter_shift_hessian.py +++ b/pennylane/gradients/parameter_shift_hessian.py @@ -490,7 +490,7 @@ def param_shift_hessian( in the QNode execution. - Note: By default a QNode with the keyword ``hybrid=True`` computes derivates with respect to + Note: By default a QNode with the keyword ``hybrid=True`` computes derivatives with respect to QNode arguments, which can include classical computations on those arguments before they are passed to quantum operations. The "purely quantum" Hessian can instead be obtained with ``hybrid=False``, which is then computed with respect to the gate arguments and produces a diff --git a/pennylane/gradients/pulse_gradient_odegen.py b/pennylane/gradients/pulse_gradient_odegen.py index 7c49ab5ac1d..2cd2ead6537 100644 --- a/pennylane/gradients/pulse_gradient_odegen.py +++ b/pennylane/gradients/pulse_gradient_odegen.py @@ -48,7 +48,7 @@ def _one_parameter_generators(op): r"""Compute the effective generators :math:`\{\Omega_k\}` of one-parameter groups that - reproduce the partial derivatives of a parameterized evolution. + reproduce the partial derivatives of a parametrized evolution. In particular, compute :math:`U` and :math:`\partial U / \partial \theta_k` and recombine them into :math:`\Omega_k = U^\dagger \partial U / \partial \theta_k` @@ -460,7 +460,7 @@ def pulse_odegen( **Example** - Consider the parameterized Hamiltonian + Consider the parametrized Hamiltonian :math:`\theta_0 Y_{0}+f(\boldsymbol{\theta_1}, t) Y_{1} + \theta_2 Z_{0}X_{1}` with parameters :math:`\theta_0 = \frac{1}{5}`, :math:`\boldsymbol{\theta_1}=\left(\frac{3}{5}, \frac{1}{5}\right)^{T}` and diff --git a/pennylane/io.py b/pennylane/io.py index f19bd150ddf..c003ab881e3 100644 --- a/pennylane/io.py +++ b/pennylane/io.py @@ -134,18 +134,18 @@ def circuit(): operator. See below for more information regarding how to translate more complex circuits from Qiskit to - PennyLane, including handling parameterized Qiskit circuits, mid-circuit measurements, and + PennyLane, including handling parametrized Qiskit circuits, mid-circuit measurements, and classical control flows. .. details:: - :title: Parameterized Quantum Circuits + :title: Parametrized Quantum Circuits - A Qiskit ``QuantumCircuit`` is parameterized if it contains + A Qiskit ``QuantumCircuit`` is parametrized if it contains `Parameter `__ or `ParameterVector `__ references that need to be given defined values to evaluate the circuit. These can be passed to the generated quantum function as keyword or positional arguments. If we define a - parameterized circuit: + parametrized circuit: .. code-block:: python @@ -182,7 +182,7 @@ def circuit(): >>> qml.grad(circuit, argnum=[0, 1])(np.pi/4, np.pi/6) (array(-0.61237244), array(-0.35355339)) - The ``QuantumCircuit`` may also be parameterized with a ``ParameterVector``. These can be + The ``QuantumCircuit`` may also be parametrized with a ``ParameterVector``. These can be similarly converted: .. code-block:: python @@ -363,7 +363,7 @@ def from_qiskit_op(qiskit_op, params=None, wires=None): .. details:: :title: Usage Details - You can convert a parameterized ``SparsePauliOp`` into a PennyLane operator by assigning + You can convert a parametrized ``SparsePauliOp`` into a PennyLane operator by assigning literal values to each coefficient parameter. For example, the script .. code-block:: python diff --git a/pennylane/math/multi_dispatch.py b/pennylane/math/multi_dispatch.py index 2c4684571f7..0468cfe6858 100644 --- a/pennylane/math/multi_dispatch.py +++ b/pennylane/math/multi_dispatch.py @@ -58,7 +58,7 @@ def eye(*args, like=None, **kwargs): def multi_dispatch(argnum=None, tensor_list=None): - r"""Decorater to dispatch arguments handled by the interface. + r"""Decorator to dispatch arguments handled by the interface. This helps simplify definitions of new functions inside PennyLane. We can decorate the function, indicating the arguments that are tensors handled diff --git a/pennylane/math/utils.py b/pennylane/math/utils.py index a03954b3746..9d716062382 100644 --- a/pennylane/math/utils.py +++ b/pennylane/math/utils.py @@ -194,7 +194,7 @@ def get_interface(*values): cannot both be present. * Autograd tensors *may* be present alongside Torch, TensorFlow and Jax tensors, - but Torch, TensorFlow and Jax take precendence; the autograd arrays will + but Torch, TensorFlow and Jax take precedence; the autograd arrays will be treated as non-differentiable NumPy arrays. A warning will be raised suggesting that vanilla NumPy be used instead. diff --git a/pennylane/measurements/__init__.py b/pennylane/measurements/__init__.py index 3129a92069d..d0eeaa5d7fd 100644 --- a/pennylane/measurements/__init__.py +++ b/pennylane/measurements/__init__.py @@ -288,6 +288,7 @@ def circuit(x): StateMeasurement, Variance, VnEntropy, + VnEntanglementEntropy, ) from .mid_measure import MeasurementValue, MidMeasureMP, measure, find_post_processed_mcms from .mutual_info import MutualInfoMP, mutual_info @@ -298,3 +299,4 @@ def circuit(x): from .state import DensityMatrixMP, StateMP, density_matrix, state from .var import VarianceMP, var from .vn_entropy import VnEntropyMP, vn_entropy +from .vn_entanglement_entropy import VnEntanglementEntropyMP, vn_entanglement_entropy diff --git a/pennylane/measurements/classical_shadow.py b/pennylane/measurements/classical_shadow.py index 200a48a25a5..4179ab02e09 100644 --- a/pennylane/measurements/classical_shadow.py +++ b/pennylane/measurements/classical_shadow.py @@ -479,7 +479,7 @@ def __copy__(self): class ShadowExpvalMP(MeasurementTransform): """Measures the expectation value of an operator using the classical shadow measurement process. - Please refer to :func:`shadow_expval` for detailed documentation. + Please refer to :func:`~pennylane.shadow_expval` for detailed documentation. Args: H (Operator, Sequence[Operator]): Operator or list of Operators to compute the expectation value over. diff --git a/pennylane/measurements/measurements.py b/pennylane/measurements/measurements.py index ab28f487415..6b7352b23e1 100644 --- a/pennylane/measurements/measurements.py +++ b/pennylane/measurements/measurements.py @@ -46,6 +46,7 @@ class ObservableReturnTypes(Enum): State = "state" MidMeasure = "measure" VnEntropy = "vnentropy" + VnEntanglementEntropy = "vnentanglemententropy" MutualInfo = "mutualinfo" Shadow = "shadow" ShadowExpval = "shadowexpval" @@ -90,6 +91,9 @@ def __repr__(self): VnEntropy = ObservableReturnTypes.VnEntropy """Enum: An enumeration which represents returning Von Neumann entropy before measurements.""" +VnEntanglementEntropy = ObservableReturnTypes.VnEntanglementEntropy +"""Enum: An enumeration which represents returning Von Neumann entanglement entropy before measurements.""" + MutualInfo = ObservableReturnTypes.MutualInfo """Enum: An enumeration which represents returning the mutual information before measurements.""" diff --git a/pennylane/measurements/mutual_info.py b/pennylane/measurements/mutual_info.py index 18335fe4306..81e39864896 100644 --- a/pennylane/measurements/mutual_info.py +++ b/pennylane/measurements/mutual_info.py @@ -74,7 +74,7 @@ def circuit_mutual(x): using the classical backpropagation differentiation method (``diff_method="backprop"``) with a compatible device and finite differences (``diff_method="finite-diff"``). - .. seealso:: :func:`~.vn_entropy`, :func:`pennylane.qinfo.transforms.mutual_info` and :func:`pennylane.math.mutual_info` + .. seealso:: :func:`~pennylane.vn_entropy`, :func:`pennylane.math.mutual_info` """ wires0 = qml.wires.Wires(wires0) wires1 = qml.wires.Wires(wires1) diff --git a/pennylane/measurements/purity.py b/pennylane/measurements/purity.py index 83e4166cbc9..4427f16231d 100644 --- a/pennylane/measurements/purity.py +++ b/pennylane/measurements/purity.py @@ -57,7 +57,7 @@ def circuit_purity(p): >>> circuit_purity(0.1) array(0.7048) - .. seealso:: :func:`pennylane.qinfo.transforms.purity` and :func:`pennylane.math.purity` + .. seealso:: :func:`pennylane.math.purity` """ wires = Wires(wires) return PurityMP(wires=wires) diff --git a/pennylane/measurements/vn_entanglement_entropy.py b/pennylane/measurements/vn_entanglement_entropy.py new file mode 100644 index 00000000000..758db6e49c2 --- /dev/null +++ b/pennylane/measurements/vn_entanglement_entropy.py @@ -0,0 +1,183 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# pylint: disable=protected-access +""" +This module contains the qml.vn_entanglement_entropy measurement. +""" +from copy import copy +from typing import Optional, Sequence + +import pennylane as qml +from pennylane.wires import Wires + +from .measurements import StateMeasurement, VnEntanglementEntropy + + +def vn_entanglement_entropy(wires0, wires1, log_base=None): + r"""Measures the `Von Neumann entanglement entropy `_ + of a quantum state: + + .. math:: + + S(\rho_A) = -\text{Tr}[\rho_A \log \rho_A] = -\text{Tr}[\rho_B \log \rho_B] = S(\rho_B) + + where :math:`S` is the Von Neumann entropy; :math:`\rho_A = \text{Tr}_B [\rho_{AB}]` and + :math:`\rho_B = \text{Tr}_A [\rho_{AB}]` are the reduced density matrices for each partition. + The Von Neumann entanglement entropy is a measure of the degree of quantum entanglement between + two subsystems constituting a pure bipartite quantum state. The entropy of entanglement is the + Von Neumann entropy of the reduced density matrix for any of the subsystems. If it is non-zero, + it indicates the two subsystems are entangled. + + Args: + wires0 (Sequence[int] or int): the wires of the first subsystem + wires1 (Sequence[int] or int): the wires of the second subsystem + log_base (float): Base for the logarithm. + + Returns: + VnEntanglementEntropyMP: measurement process instance + + **Example:** + + .. code-block:: python3 + + dev = qml.device("default.qubit", wires=2) + @qml.qnode(dev) + def circuit(x): + qml.RY(x, 0) + qml.Hadamard(0) + qml.CNOT([0, 1]) + return qml.vn_entanglement_entropy([0], [1]) + + Executing this QNode: + + >>> circuit(1.967) + 0.16389850379003218 + + It is also possible to get the gradient of the previous QNode: + + >>> param = np.array(np.pi/4, requires_grad=True) + >>> qml.grad(circuit)(param) + tensor(-0.62322524, requires_grad=True) + + .. note:: + + Calculating the derivative of :func:`~pennylane.vn_entanglement_entropy` is currently supported when + using the classical backpropagation differentiation method (``diff_method="backprop"``) + with a compatible device and finite differences (``diff_method="finite-diff"``). + + .. seealso:: :func:`~pennylane.vn_entropy` and :func:`pennylane.math.vn_entanglement_entropy` + """ + wires0 = qml.wires.Wires(wires0) + wires1 = qml.wires.Wires(wires1) + + # the subsystems cannot overlap + if not any(qml.math.is_abstract(w) for w in wires0 + wires1) and [ + wire for wire in wires0 if wire in wires1 + ]: + raise qml.QuantumFunctionError( + "Subsystems for computing entanglement entropy must not overlap." + ) + return VnEntanglementEntropyMP(wires=(wires0, wires1), log_base=log_base) + + +class VnEntanglementEntropyMP(StateMeasurement): + """Measurement process that computes the Von Neumann entanglement entropy between the provided wires. + Please refer to :func:`~pennylane.vn_entanglement_entropy` for detailed documentation. + + Args: + wires (Sequence[.Wires]): The wires the measurement process applies to. + id (str): custom label given to a measurement instance, can be useful for some applications + where the instance has to be identified + log_base (float): base for the logarithm + """ + + def _flatten(self): + metadata = (("wires", tuple(self.raw_wires)), ("log_base", self.log_base)) + return (None, None), metadata + + def __init__( + self, + wires: Optional[Sequence[Wires]] = None, + id: Optional[str] = None, + log_base: Optional[float] = None, + ): + self.log_base = log_base + super().__init__(wires=wires, id=id) + + # pylint: disable=arguments-differ + @classmethod + def _primitive_bind_call(cls, wires: Sequence, **kwargs): + if cls._wires_primitive is None: # pragma: no cover + # just a safety check + return type.__call__(cls, wires=wires, **kwargs) # pragma: no cover + return cls._wires_primitive.bind(*wires[0], *wires[1], n_wires0=len(wires[0]), **kwargs) + + def __repr__(self): + return f"VnEntanglementEntropy(wires0={self.raw_wires[0].tolist()}, wires1={self.raw_wires[1].tolist()}, log_base={self.log_base})" + + @property + def hash(self): + """int: returns an integer hash uniquely representing the measurement process""" + fingerprint = ( + self.__class__.__name__, + tuple(self.raw_wires[0].tolist()), + tuple(self.raw_wires[1].tolist()), + self.log_base, + ) + + return hash(fingerprint) + + @property + def return_type(self): + return VnEntanglementEntropy + + @property + def numeric_type(self): + return float + + def map_wires(self, wire_map: dict): + new_measurement = copy(self) + new_measurement._wires = [ + Wires([wire_map.get(wire, wire) for wire in wires]) for wires in self.raw_wires + ] + return new_measurement + + def shape( + self, shots: Optional[int] = None, num_device_wires: int = 0 + ): # pylint: disable=unused-argument + return () + + def process_state(self, state: Sequence[complex], wire_order: Wires): + density_matrix = qml.math.dm_from_state_vector(state) + return self.process_density_matrix(density_matrix=density_matrix, wire_order=wire_order) + + def process_density_matrix( + self, density_matrix: Sequence[complex], wire_order: Wires + ): # pylint: disable=unused-argument + return qml.math.vn_entanglement_entropy( + density_matrix, + indices0=list(self._wires[0]), + indices1=list(self._wires[1]), + c_dtype=density_matrix.dtype, + base=self.log_base, + ) + + +if VnEntanglementEntropyMP._wires_primitive is not None: + + @VnEntanglementEntropyMP._wires_primitive.def_impl + def _(*all_wires, n_wires0, **kwargs): + wires0 = all_wires[:n_wires0] + wires1 = all_wires[n_wires0:] + return type.__call__(VnEntanglementEntropyMP, wires=(wires0, wires1), **kwargs) diff --git a/pennylane/measurements/vn_entropy.py b/pennylane/measurements/vn_entropy.py index 79c1a875eae..e07789add66 100644 --- a/pennylane/measurements/vn_entropy.py +++ b/pennylane/measurements/vn_entropy.py @@ -61,11 +61,11 @@ def circuit_entropy(x): .. note:: - Calculating the derivative of :func:`~.vn_entropy` is currently supported when + Calculating the derivative of :func:`~pennylane.vn_entropy` is currently supported when using the classical backpropagation differentiation method (``diff_method="backprop"``) with a compatible device and finite differences (``diff_method="finite-diff"``). - .. seealso:: :func:`pennylane.qinfo.transforms.vn_entropy` and :func:`pennylane.math.vn_entropy` + .. seealso:: :func:`pennylane.math.vn_entropy` """ wires = Wires(wires) return VnEntropyMP(wires=wires, log_base=log_base) @@ -74,7 +74,7 @@ def circuit_entropy(x): class VnEntropyMP(StateMeasurement): """Measurement process that computes the Von Neumann entropy of the system prior to measurement. - Please refer to :func:`vn_entropy` for detailed documentation. + Please refer to :func:`~pennylane.vn_entropy` for detailed documentation. Args: wires (.Wires): The wires the measurement process applies to. diff --git a/pennylane/operation.py b/pennylane/operation.py index 77f989e6383..46a8d4e5bb7 100644 --- a/pennylane/operation.py +++ b/pennylane/operation.py @@ -870,6 +870,18 @@ def compute_sparse_matrix( """ raise SparseMatrixUndefinedError + # pylint: disable=no-self-argument, comparison-with-callable + @classproperty + def has_sparse_matrix(cls) -> bool: + r"""Bool: Whether the Operator returns a defined sparse matrix. + + Note: Child classes may have this as an instance property instead of as a class property. + """ + return ( + cls.compute_sparse_matrix != Operator.compute_sparse_matrix + or cls.sparse_matrix != Operator.sparse_matrix + ) + def sparse_matrix(self, wire_order: Optional[WiresLike] = None) -> csr_matrix: r"""Representation of the operator as a sparse matrix in the computational basis. diff --git a/pennylane/ops/cv.py b/pennylane/ops/cv.py index 38698477105..43258bb47fc 100644 --- a/pennylane/ops/cv.py +++ b/pennylane/ops/cv.py @@ -640,7 +640,7 @@ class InterferometerUnitary(CVOperation): This operation implements a **fixed** linear interferometer given a known unitary matrix. - If you instead wish to parameterize the interferometer, + If you instead wish to parametrize the interferometer, and calculate the gradient/optimize with respect to these parameters, consider instead the :func:`pennylane.template.Interferometer` template, which constructs an interferometer from a combination of beamsplitters diff --git a/pennylane/ops/functions/assert_valid.py b/pennylane/ops/functions/assert_valid.py index e3795ff6608..09a0821cb35 100644 --- a/pennylane/ops/functions/assert_valid.py +++ b/pennylane/ops/functions/assert_valid.py @@ -21,6 +21,7 @@ from string import ascii_lowercase import numpy as np +import scipy.sparse import pennylane as qml from pennylane.operation import EigvalsUndefinedError @@ -112,6 +113,23 @@ def _check_matrix(op): )() +def _check_sparse_matrix(op): + """Check that if the operation says it has a sparse matrix, it does. Otherwise a ``SparseMatrixUndefinedError`` should be raised.""" + if op.has_sparse_matrix: + mat = op.sparse_matrix() + assert isinstance(mat, scipy.sparse.csr_matrix), "matrix must be a TensorLike" + l = 2 ** len(op.wires) + failure_comment = f"matrix must be two dimensional with shape ({l}, {l})" + assert qml.math.shape(mat) == (l, l), failure_comment + else: + failure_comment = "If has_sparse_matrix is False, the matrix method must raise a ``SparseMatrixUndefinedError``." + _assert_error_raised( + op.sparse_matrix, + qml.operation.SparseMatrixUndefinedError, + failure_comment=failure_comment, + )() + + def _check_matrix_matches_decomp(op): """Check that if both the matrix and decomposition are defined, they match.""" if op.has_matrix and op.has_decomposition: @@ -332,5 +350,6 @@ def __init__(self, wires): _check_decomposition(op, skip_wire_mapping) _check_matrix(op) _check_matrix_matches_decomp(op) + _check_sparse_matrix(op) _check_eigendecomposition(op) _check_capture(op) diff --git a/pennylane/ops/functions/iterative_qpe.py b/pennylane/ops/functions/iterative_qpe.py index 8ccc429cfb0..eea5aefa408 100644 --- a/pennylane/ops/functions/iterative_qpe.py +++ b/pennylane/ops/functions/iterative_qpe.py @@ -15,12 +15,14 @@ This module contains the qml.iterative_qpe function. """ +from warnings import warn + import numpy as np import pennylane as qml -def iterative_qpe(base, ancilla, iters): +def iterative_qpe(base, aux_wire="unset", iters="unset", ancilla="unset"): r"""Performs the `iterative quantum phase estimation `_ circuit. Given a unitary :math:`U`, this function applies the circuit for iterative quantum phase @@ -28,8 +30,11 @@ def iterative_qpe(base, ancilla, iters): Args: base (Operator): the phase estimation unitary, specified as an :class:`~.Operator` - ancilla (Union[Wires, int, str]): the wire to be used for the estimation + aux_wire (Union[Wires, int, str]): the wire to be used for the estimation iters (int): the number of measurements to be performed + ancilla (Union[Wires, int, str]): the wire to be used for the estimation. This argument + is deprecated, and the ``aux_wire`` argument should be used instead. If both arguments + are provided, ``aux_wire`` will be used and ``ancilla`` will be ignored. Returns: list[MidMeasureMP]: the list of measurements performed @@ -49,7 +54,7 @@ def circuit(): qml.X(0) # Iterative QPE - measurements = qml.iterative_qpe(qml.RZ(2.0, wires=[0]), ancilla=1, iters=3) + measurements = qml.iterative_qpe(qml.RZ(2.0, wires=[0]), aux_wire=1, iters=3) return qml.sample(measurements) @@ -74,17 +79,37 @@ def circuit(): ╚══════════════════════╩═════════════════════════║═══════╡ ├Sample[MCM] ╚═══════╡ ╰Sample[MCM] """ + missing = [] + if aux_wire == "unset" and ancilla == "unset": + missing.append("'aux_wire'") + if iters == "unset": + missing.append("'iters'") + + if missing: + missing_args = " and ".join(missing) + raise TypeError( + f"iterative_qpe() missing {len(missing)} required positional argument(s): {missing_args}" + ) + + if ancilla != "unset": + warn( + "The 'ancilla' argument for qml.iterative_qpe has been deprecated. Please use the " + "'aux_wire' argument instead.", + qml.PennyLaneDeprecationWarning, + ) + if aux_wire == "unset": + aux_wire = ancilla measurements = [] for i in range(iters): - qml.Hadamard(wires=ancilla) - qml.ctrl(qml.pow(base, z=2 ** (iters - i - 1)), control=ancilla) + qml.Hadamard(wires=aux_wire) + qml.ctrl(qml.pow(base, z=2 ** (iters - i - 1)), control=aux_wire) for ind, meas in enumerate(measurements): - qml.cond(meas, qml.PhaseShift)(-2.0 * np.pi / 2 ** (ind + 2), wires=ancilla) + qml.cond(meas, qml.PhaseShift)(-2.0 * np.pi / 2 ** (ind + 2), wires=aux_wire) - qml.Hadamard(wires=ancilla) - measurements.insert(0, qml.measure(wires=ancilla, reset=True)) + qml.Hadamard(wires=aux_wire) + measurements.insert(0, qml.measure(wires=aux_wire, reset=True)) return measurements diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py index 325b80c1b7e..e09ea4b007d 100644 --- a/pennylane/ops/op_math/adjoint.py +++ b/pennylane/ops/op_math/adjoint.py @@ -367,6 +367,11 @@ def matrix(self, wire_order=None): return moveaxis(conj(base_matrix), -2, -1) + # pylint: disable=arguments-renamed, invalid-overridden-method + @property + def has_sparse_matrix(self) -> bool: + return self.base.has_sparse_matrix + # pylint: disable=arguments-differ def sparse_matrix(self, wire_order=None, format="csr"): base_matrix = self.base.sparse_matrix(wire_order=wire_order) diff --git a/pennylane/ops/op_math/controlled_ops.py b/pennylane/ops/op_math/controlled_ops.py index 4d2a8475f6b..3bcf4d98a0c 100644 --- a/pennylane/ops/op_math/controlled_ops.py +++ b/pennylane/ops/op_math/controlled_ops.py @@ -1016,7 +1016,7 @@ def _check_and_convert_control_values(control_values, control_wires): class MultiControlledX(ControlledOp): - r"""MultiControlledX(control_wires, wires, control_values) + r"""MultiControlledX(control_wires, wires, control_values, work_wires) Apply a Pauli X gate controlled on an arbitrary computational basis state. **Details:** diff --git a/pennylane/ops/op_math/linear_combination.py b/pennylane/ops/op_math/linear_combination.py index 473a0253b60..c14975d1c3d 100644 --- a/pennylane/ops/op_math/linear_combination.py +++ b/pennylane/ops/op_math/linear_combination.py @@ -37,9 +37,6 @@ class LinearCombination(Sum): Args: coeffs (tensor_like): coefficients of the ``LinearCombination`` expression observables (Iterable[Observable]): observables in the ``LinearCombination`` expression, of same length as ``coeffs`` - simplify (bool): Specifies whether the ``LinearCombination`` is simplified upon initialization - (like-terms are combined). The default value is `False`. Note that ``coeffs`` cannot - be differentiated when using the ``'torch'`` interface and ``simplify=True``. Use of this argument is deprecated. grouping_type (str): If not ``None``, compute and store information on how to group commuting observables upon initialization. This information may be accessed when a :class:`~.QNode` containing this ``LinearCombination`` is executed on devices. The string refers to the type of binary relation between Pauli words. @@ -52,10 +49,6 @@ class LinearCombination(Sum): .. seealso:: `rustworkx.ColoringStrategy `_ for more information on the ``('lf', 'dsatur', 'gis')`` strategies. - .. warning:: - The ``simplify`` argument is deprecated and will be removed in a future release. - Instead, you can call ``qml.simplify`` on the constructed operator. - **Example:** A ``LinearCombination`` can be created by simply passing the list of coefficients @@ -124,7 +117,6 @@ def __init__( self, coeffs, observables: list[Operator], - simplify=False, grouping_type=None, method="lf", _grouping_indices=None, @@ -143,23 +135,6 @@ def __init__( if _pauli_rep is None: _pauli_rep = self._build_pauli_rep_static(coeffs, observables) - if simplify: - - warnings.warn( - "The simplify argument in qml.Hamiltonian and qml.ops.LinearCombination is deprecated. " - "Instead, you can call qml.simplify on the constructed operator.", - qml.PennyLaneDeprecationWarning, - ) - - # simplify upon initialization changes ops such that they wouldnt be removed in self.queue() anymore - if qml.QueuingManager.recording(): - for o in observables: - qml.QueuingManager.remove(o) - - coeffs, observables, _pauli_rep = self._simplify_coeffs_ops( - coeffs, observables, _pauli_rep - ) - self._coeffs = coeffs self._ops = [convert_to_opmath(op) for op in observables] diff --git a/pennylane/ops/op_math/pow.py b/pennylane/ops/op_math/pow.py index 6251fb6fdbb..f8ad370d81f 100644 --- a/pennylane/ops/op_math/pow.py +++ b/pennylane/ops/op_math/pow.py @@ -246,6 +246,11 @@ def _matrix(scalar, mat): return fractional_matrix_power(mat, scalar) + # pylint: disable=arguments-renamed, invalid-overridden-method + @property + def has_sparse_matrix(self) -> bool: + return self.base.has_sparse_matrix and isinstance(self.z, int) + # pylint: disable=arguments-differ @staticmethod def compute_sparse_matrix(*params, base=None, z=0): diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py index 1bbe30e29f2..46ab54a531d 100644 --- a/pennylane/ops/op_math/prod.py +++ b/pennylane/ops/op_math/prod.py @@ -186,7 +186,7 @@ class Prod(CompositeOp): 0. +0.j , 0. +0.j ]]) The Prod operation can be used inside a `qnode` as an operation which, - if parameterized, can be differentiated. + if parametrized, can be differentiated. .. code-block:: python @@ -204,7 +204,7 @@ def circuit(theta): tensor(-0.9424888, requires_grad=True) The Prod operation can also be measured as an observable. - If the circuit is parameterized, then we can also differentiate through the + If the circuit is parametrized, then we can also differentiate through the product observable. .. code-block:: python @@ -324,6 +324,10 @@ def sparse_matrix(self, wire_order=None): full_mat = reduce(sparse_kron, mats) return math.expand_matrix(full_mat, self.wires, wire_order=wire_order) + @property + def has_sparse_matrix(self): + return self.pauli_rep is not None or all(op.has_sparse_matrix for op in self) + # pylint: disable=protected-access @property def _queue_category(self): diff --git a/pennylane/ops/op_math/sprod.py b/pennylane/ops/op_math/sprod.py index b3b2a9cc96f..64fcdbdfd72 100644 --- a/pennylane/ops/op_math/sprod.py +++ b/pennylane/ops/op_math/sprod.py @@ -112,7 +112,7 @@ class SProd(ScalarSymbolicOp): :title: Usage Details The SProd operation can also be measured inside a qnode as an observable. - If the circuit is parameterized, then we can also differentiate through the observable. + If the circuit is parametrized, then we can also differentiate through the observable. .. code-block:: python @@ -263,6 +263,10 @@ def sparse_matrix(self, wire_order=None): mat.eliminate_zeros() return mat + @property + def has_sparse_matrix(self): + return self.pauli_rep is not None or self.base.has_sparse_matrix + @property def has_matrix(self): """Bool: Whether or not the Operator returns a defined matrix.""" diff --git a/pennylane/ops/op_math/sum.py b/pennylane/ops/op_math/sum.py index f585607da20..40ed5f229c3 100644 --- a/pennylane/ops/op_math/sum.py +++ b/pennylane/ops/op_math/sum.py @@ -171,7 +171,7 @@ class Sum(CompositeOp): .. details:: :title: Usage Details - We can combine parameterized operators, and support sums between operators acting on + We can combine parametrized operators, and support sums between operators acting on different wires. >>> summed_op = Sum(qml.RZ(1.23, wires=0), qml.I(wires=1)) @@ -186,7 +186,7 @@ class Sum(CompositeOp): 0. +0.j , 1.81677345+0.57695852j]]) The Sum operation can also be measured inside a qnode as an observable. - If the circuit is parameterized, then we can also differentiate through the + If the circuit is parametrized, then we can also differentiate through the sum observable. .. code-block:: python @@ -342,6 +342,11 @@ def matrix(self, wire_order=None): return math.expand_matrix(reduced_mat, sum_wires, wire_order=wire_order) + # pylint: disable=arguments-renamed, invalid-overridden-method + @property + def has_sparse_matrix(self) -> bool: + return self.pauli_rep is not None or all(op.has_sparse_matrix for op in self) + def sparse_matrix(self, wire_order=None): if self.pauli_rep: # Get the sparse matrix from the PauliSentence representation return self.pauli_rep.to_mat(wire_order=wire_order or self.wires, format="csr") diff --git a/pennylane/ops/op_math/symbolicop.py b/pennylane/ops/op_math/symbolicop.py index 3053e58c8c8..24f81e52e93 100644 --- a/pennylane/ops/op_math/symbolicop.py +++ b/pennylane/ops/op_math/symbolicop.py @@ -28,7 +28,7 @@ class SymbolicOp(Operator): """Developer-facing base class for single-operator symbolic operators. Args: - base (~.operation.Operator): the base operation that is modified symbolicly + base (~.operation.Operator): the base operation that is modified symbolically id (str): custom label given to an operator instance, can be useful for some applications where the instance has to be identified @@ -155,7 +155,7 @@ class ScalarSymbolicOp(SymbolicOp): scalar coefficient. Args: - base (~.operation.Operator): the base operation that is modified symbolicly + base (~.operation.Operator): the base operation that is modified symbolically scalar (float): the scalar coefficient id (str): custom label given to an operator instance, can be useful for some applications where the instance has to be identified diff --git a/pennylane/ops/qubit/hamiltonian.py b/pennylane/ops/qubit/hamiltonian.py index e369d5001e4..73e75bf76d8 100644 --- a/pennylane/ops/qubit/hamiltonian.py +++ b/pennylane/ops/qubit/hamiltonian.py @@ -81,8 +81,6 @@ class Hamiltonian(Observable): Args: coeffs (tensor_like): coefficients of the Hamiltonian expression observables (Iterable[Observable]): observables in the Hamiltonian expression, of same length as coeffs - simplify (bool): Specifies whether the Hamiltonian is simplified upon initialization - (like-terms are combined). The default value is `False`. Use of this argument is deprecated. grouping_type (str): If not None, compute and store information on how to group commuting observables upon initialization. This information may be accessed when QNodes containing this Hamiltonian are executed on devices. The string refers to the type of binary relation between Pauli words. @@ -91,10 +89,6 @@ class Hamiltonian(Observable): can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive Largest First). Ignored if ``grouping_type=None``. id (str): name to be assigned to this Hamiltonian instance - .. warning:: - The ``simplify`` argument is deprecated and will be removed in a future release. - Instead, you can call ``qml.simplify`` on the constructed operator. - **Example:** .. note:: @@ -254,7 +248,6 @@ def __init__( self, coeffs: TensorLike, observables: Iterable[Observable], - simplify: bool = False, grouping_type: Literal[None, "qwc", "commuting", "anticommuting"] = None, _grouping_indices: Optional[list[list[int]]] = None, method: Literal["lf", "rlf"] = "rlf", @@ -293,23 +286,6 @@ def __init__( # commuting observables, since recomputation is costly self._grouping_indices = _grouping_indices - if simplify: - - warn( - "The simplify argument in qml.Hamiltonian and qml.ops.LinearCombination is deprecated. " - "Instead, you can call qml.simplify on the constructed operator.", - qml.PennyLaneDeprecationWarning, - ) - - # simplify upon initialization changes ops such that they wouldnt be - # removed in self.queue() anymore, removing them here manually. - if qml.QueuingManager.recording(): - for o in observables: - qml.QueuingManager.remove(o) - - with qml.QueuingManager.stop_recording(): - self.simplify() - if grouping_type is not None: with qml.QueuingManager.stop_recording(): self._grouping_indices = _compute_grouping_indices( diff --git a/pennylane/ops/qubit/non_parametric_ops.py b/pennylane/ops/qubit/non_parametric_ops.py index 973fa44d83a..86715feedf9 100644 --- a/pennylane/ops/qubit/non_parametric_ops.py +++ b/pennylane/ops/qubit/non_parametric_ops.py @@ -1188,7 +1188,7 @@ def is_hermitian(self) -> bool: class ECR(Operation): r""" ECR(wires) - An echoed RZX(pi/2) gate. + An echoed RZX(:math:`\pi/2`) gate. .. math:: ECR = {\frac{1}{\sqrt{2}}} \begin{bmatrix} 0 & 0 & 1 & i \\ diff --git a/pennylane/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/ops/qubit/parametric_ops_multi_qubit.py index fcbd8f2ad4f..f53af311124 100644 --- a/pennylane/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/ops/qubit/parametric_ops_multi_qubit.py @@ -14,7 +14,7 @@ # pylint: disable=too-many-arguments """ This submodule contains the discrete-variable quantum operations that are the -core parameterized gates. +core parametrized gates. """ # pylint:disable=abstract-method,arguments-differ,protected-access,invalid-overridden-method import functools diff --git a/pennylane/ops/qubit/parametric_ops_single_qubit.py b/pennylane/ops/qubit/parametric_ops_single_qubit.py index ce829e24153..fe02238bafd 100644 --- a/pennylane/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/ops/qubit/parametric_ops_single_qubit.py @@ -14,7 +14,7 @@ # pylint: disable=too-many-arguments """ This submodule contains the discrete-variable quantum operations that are the -core parameterized gates. +core parametrized gates. """ # pylint:disable=abstract-method,arguments-differ,protected-access,invalid-overridden-method import functools diff --git a/pennylane/ops/qubit/state_preparation.py b/pennylane/ops/qubit/state_preparation.py index 7ef84be70fd..6bfb4179e00 100644 --- a/pennylane/ops/qubit/state_preparation.py +++ b/pennylane/ops/qubit/state_preparation.py @@ -15,6 +15,8 @@ This submodule contains the discrete-variable quantum operations concerned with preparing a certain state on the device. """ +import warnings + # pylint:disable=too-many-branches,abstract-method,arguments-differ,protected-access,no-member from typing import Optional @@ -442,9 +444,19 @@ def _preprocess(state, wires, pad_with, normalize, validate_norm): return state -# pylint: disable=missing-class-docstring class QubitStateVector(StatePrep): - pass # QSV is still available + r""" + ``QubitStateVector`` is deprecated and will be removed in version 0.40. Instead, please use ``StatePrep``. + """ + + # pylint: disable=too-many-arguments + def __init__(self, state, wires, pad_with=None, normalize=False, validate_norm=True): + warnings.warn( + "QubitStateVector is deprecated and will be removed in version 0.40. " + "Instead, please use StatePrep.", + qml.PennyLaneDeprecationWarning, + ) + super().__init__(state, wires, pad_with, normalize, validate_norm) class QubitDensityMatrix(Operation): diff --git a/pennylane/ops/qutrit/parametric_ops.py b/pennylane/ops/qutrit/parametric_ops.py index 074b6f14d57..119ebde5aab 100644 --- a/pennylane/ops/qutrit/parametric_ops.py +++ b/pennylane/ops/qutrit/parametric_ops.py @@ -14,7 +14,7 @@ # pylint: disable=too-many-arguments """ This submodule contains the discrete-variable quantum operations that are the -core parameterized gates for qutrits. +core parametrized gates for qutrits. """ import functools diff --git a/pennylane/optimize/adaptive.py b/pennylane/optimize/adaptive.py index 0c6dd38af71..cf9da9f2d68 100644 --- a/pennylane/optimize/adaptive.py +++ b/pennylane/optimize/adaptive.py @@ -24,7 +24,7 @@ @transform def append_gate(tape: QuantumScript, params, gates) -> tuple[QuantumScriptBatch, PostprocessingFn]: - """Append parameterized gates to an existing tape. + """Append parametrized gates to an existing tape. Args: tape (QuantumTape or QNode or Callable): quantum circuit to transform by adding gates @@ -156,7 +156,7 @@ def __init__(self, param_steps=10, stepsize=0.5): @staticmethod def _circuit(params, gates, initial_circuit): - """Append parameterized gates to an existing circuit. + """Append parametrized gates to an existing circuit. Args: params (array[float]): parameters of the gates to be added diff --git a/pennylane/optimize/qng.py b/pennylane/optimize/qng.py index 9952caaff23..fb3af80b90d 100644 --- a/pennylane/optimize/qng.py +++ b/pennylane/optimize/qng.py @@ -71,7 +71,7 @@ class QNGOptimizer(GradientDescentOptimizer): where :math:`|\psi_\ell\rangle = V(\theta_1, \dots, \theta_{i-1})|0\rangle` (that is, :math:`|\psi_\ell\rangle` is the quantum state prior to the application - of parameterized layer :math:`\ell`). + of parametrized layer :math:`\ell`). Combining the quantum natural gradient optimizer with the analytic parameter-shift rule to optimize a variational circuit with :math:`d` parameters and :math:`L` layers, diff --git a/pennylane/optimize/qnspsa.py b/pennylane/optimize/qnspsa.py index 75b773e5f9b..7a34b766550 100644 --- a/pennylane/optimize/qnspsa.py +++ b/pennylane/optimize/qnspsa.py @@ -61,7 +61,7 @@ class QNSPSAOptimizer: where :math:`F(\mathbf{x}', \mathbf{x}) = \bigr\rvert\langle \phi(\mathbf{x}') | \phi(\mathbf{x}) \rangle \bigr\rvert ^ 2` measures the state overlap between :math:`\phi(\mathbf{x}')` and :math:`\phi(\mathbf{x})`, - where :math:`\phi` is the parameterized ansatz. The finite difference :math:`\delta F` is + where :math:`\phi` is the parametrized ansatz. The finite difference :math:`\delta F` is computed from the two perturbations: .. math:: @@ -110,7 +110,7 @@ class QNSPSAOptimizer: Keyword Args: stepsize (float): the user-defined hyperparameter :math:`\eta` for learning rate (default: 1e-3) - regularization (float): regularitzation term :math:`\beta` to the Fubini-Study metric tensor + regularization (float): regularization term :math:`\beta` to the Fubini-Study metric tensor for numerical stability (default: 1e-3) finite_diff_step (float): step size :math:`\epsilon` to compute the finite difference gradient and the Fubini-Study metric tensor (default: 1e-2) diff --git a/pennylane/optimize/rms_prop.py b/pennylane/optimize/rms_prop.py index 70990aa59fd..ddf7a35af4c 100644 --- a/pennylane/optimize/rms_prop.py +++ b/pennylane/optimize/rms_prop.py @@ -20,8 +20,8 @@ class RMSPropOptimizer(AdagradOptimizer): r"""Root mean squared propagation optimizer. - The root mean square progation optimizer is a modified - :class:`Adagrad optimizer `, + The root mean square propagation optimizer is a modified + :class:`Adagrad optimizer `, with a decay of learning rate adaptation. Extensions of the Adagrad optimization method generally @@ -36,10 +36,10 @@ class RMSPropOptimizer(AdagradOptimizer): Args: stepsize (float): the user-defined hyperparameter :math:`\eta` - used in the Adagrad optmization + used in the Adagrad optimization decay (float): the learning rate decay :math:`\gamma` eps (float): offset :math:`\epsilon` added for numerical stability - (see :class:`Adagrad `) + (see :class:`Adagrad `) """ diff --git a/pennylane/pauli/grouping/graph_colouring.py b/pennylane/pauli/grouping/graph_colouring.py index d7e528eb5ce..787271c2d51 100644 --- a/pennylane/pauli/grouping/graph_colouring.py +++ b/pennylane/pauli/grouping/graph_colouring.py @@ -38,7 +38,7 @@ def largest_first(binary_observables, adj): Args: binary_observables (array[int]): the set of Pauli words represented by a column matrix - of the Pauli words in binary vector represenation + of the Pauli words in binary vector representation adj (array[int]): the adjacency matrix of the Pauli graph Returns: @@ -87,7 +87,7 @@ def recursive_largest_first(binary_observables, adj): # pylint:disable=too-many Args: binary_observables (array[int]): the set of Pauli words represented by a column matrix of - the Pauli words in binary vector represenation + the Pauli words in binary vector representation adj (array[int]): the adjacency matrix of the Pauli graph Returns: diff --git a/pennylane/pauli/grouping/group_observables.py b/pennylane/pauli/grouping/group_observables.py index 27ff9df5939..948d35e50f7 100644 --- a/pennylane/pauli/grouping/group_observables.py +++ b/pennylane/pauli/grouping/group_observables.py @@ -173,7 +173,7 @@ def complement_graph(self) -> rx.PyGraph: Edge ``(i,j)`` is present in the graph if ``observable[i]`` and ``observable[j]`` do **not** satisfy the ``grouping_type`` strategy. - The nodes are the observables (can only be accesssed through their integer index). + The nodes are the observables (can only be accessed through their integer index). """ # Use upper triangle since adjacency matrix is symmetric and we have an undirected graph edges = list(zip(*np.where(np.triu(self.adj_matrix, k=1)))) diff --git a/pennylane/pulse/__init__.py b/pennylane/pulse/__init__.py index 8bbafb5af6d..b1e6fd53e09 100644 --- a/pennylane/pulse/__init__.py +++ b/pennylane/pulse/__init__.py @@ -85,7 +85,7 @@ parameters :math:`p` and time :math:`t`. Defining a :class:`~.ParametrizedHamiltonian` requires coefficients and operators, where some of the coefficients -are callables. The callables defining the parameterized coefficients must have the call signature ``(p, t)``, where ``p`` can be a ``float``, +are callables. The callables defining the parametrized coefficients must have the call signature ``(p, t)``, where ``p`` can be a ``float``, ``list`` or ``jnp.array``. These functions should be defined using ``jax.numpy`` rather than ``numpy`` where relevant. .. code-block:: python diff --git a/pennylane/pulse/parametrized_hamiltonian.py b/pennylane/pulse/parametrized_hamiltonian.py index f241ac38e76..88e31b567ad 100644 --- a/pennylane/pulse/parametrized_hamiltonian.py +++ b/pennylane/pulse/parametrized_hamiltonian.py @@ -138,7 +138,7 @@ def f2(p, t): Internally we are computing ``f1([4.6, 2.3], 0.5)`` and ``f2(1.2, 0.5)``. Parametrized coefficients can be any callable that takes ``(p, t)`` and returns a scalar. It is not a - requirement that both ``p`` and ``t`` be used in the callable: for example, the convenince function + requirement that both ``p`` and ``t`` be used in the callable: for example, the convenience function :func:`~pulse.constant` takes ``(p, t)`` and returns ``p``. .. warning:: diff --git a/pennylane/qaoa/cost.py b/pennylane/qaoa/cost.py index 7ffc65be5e7..a30bcc421e7 100644 --- a/pennylane/qaoa/cost.py +++ b/pennylane/qaoa/cost.py @@ -560,7 +560,7 @@ def max_weight_cycle(graph: Union[nx.Graph, rx.PyGraph, rx.PyDiGraph], constrain where :math:`E` are the edges of the graph, :math:`x_{ij}` is a binary number that selects whether to include the edge :math:`(i, j)` and :math:`c_{ij}` is the corresponding edge weight. - Our objective is to maximimize :math:`P`, subject to selecting the :math:`x_{ij}` so that + Our objective is to maximize :math:`P`, subject to selecting the :math:`x_{ij}` so that our subset of edges composes a `cycle `__. Args: diff --git a/pennylane/qaoa/cycle.py b/pennylane/qaoa/cycle.py index 0edfb4b9146..05310a44916 100644 --- a/pennylane/qaoa/cycle.py +++ b/pennylane/qaoa/cycle.py @@ -20,7 +20,7 @@ from typing import Union import networkx as nx -import numpy as np +import numpy as np # pylint: disable=wrong-import-order import rustworkx as rx import pennylane as qml @@ -302,7 +302,7 @@ def loss_hamiltonian(graph: Union[nx.Graph, rx.PyGraph, rx.PyDiGraph]) -> qml.op where :math:`E` are the edges of the graph, :math:`x_{ij}` is a binary number that selects whether to include the edge :math:`(i, j)` and :math:`c_{ij}` is the corresponding edge weight. - Our objective is to maximimize :math:`P`, subject to selecting the :math:`x_{ij}` so that + Our objective is to maximize :math:`P`, subject to selecting the :math:`x_{ij}` so that our subset of edges composes a cycle. The product of edge weights is maximized by equivalently considering diff --git a/pennylane/qchem/tapering.py b/pennylane/qchem/tapering.py index 8dccd068f0a..4c7c9b8fd7c 100644 --- a/pennylane/qchem/tapering.py +++ b/pennylane/qchem/tapering.py @@ -575,7 +575,7 @@ def _build_generator(operation, wire_order, op_gen=None): + (0.25) [X0 Y1] """ if op_gen is None: - if operation.num_params < 1: # Non-parameterized gates + if operation.num_params < 1: # Non-parametrized gates gen_mat = 1j * scipy.linalg.logm(qml.matrix(operation, wire_order=wire_order)) op_gen = qml.pauli_decompose( gen_mat, wire_order=wire_order, hide_identity=True, pauli=True diff --git a/pennylane/qcut/cutstrategy.py b/pennylane/qcut/cutstrategy.py index 2e92a8cdb87..f5986c8e09b 100644 --- a/pennylane/qcut/cutstrategy.py +++ b/pennylane/qcut/cutstrategy.py @@ -366,7 +366,7 @@ def _infer_probed_cuts( ks = list(range(k_lower, k_upper + 1)) if len(ks) > self.HIGH_PARTITION_ATTEMPTS: - warnings.warn(f"The numer of partition attempts seems high ({len(ks)}).") + warnings.warn(f"The number of partition attempts seems high ({len(ks)}).") else: # When the by-fragment wire and/or gate limits are supplied, derive k and imbalance and # return a single partition config. diff --git a/pennylane/qinfo/__init__.py b/pennylane/qinfo/__init__.py index 819c52d989f..691e357c0d0 100644 --- a/pennylane/qinfo/__init__.py +++ b/pennylane/qinfo/__init__.py @@ -18,8 +18,6 @@ vn_entropy, purity, mutual_info, - classical_fisher, - quantum_fisher, fidelity, relative_entropy, trace_distance, diff --git a/pennylane/qinfo/transforms.py b/pennylane/qinfo/transforms.py index 07e58cfe33f..d9fceccda04 100644 --- a/pennylane/qinfo/transforms.py +++ b/pennylane/qinfo/transforms.py @@ -19,8 +19,7 @@ import pennylane as qml from pennylane import transform -from pennylane.devices import DefaultMixed, DefaultQubit, DefaultQubitLegacy -from pennylane.gradients import adjoint_metric_tensor, metric_tensor +from pennylane.devices import DefaultMixed from pennylane.measurements import DensityMatrixMP, StateMP from pennylane.tape import QuantumScript, QuantumScriptBatch from pennylane.typing import PostprocessingFn @@ -31,6 +30,11 @@ def reduced_dm(tape: QuantumScript, wires, **kwargs) -> tuple[QuantumScriptBatch """Compute the reduced density matrix from a :class:`~.QNode` returning :func:`~pennylane.state`. + .. warning:: + + The ``qml.qinfo.reduced_dm`` transform is deprecated and will be removed in v0.40. Instead include + the :func:`pennylane.density_matrix` measurement process in the return line of your QNode. + Args: tape (QuantumTape or QNode or Callable)): A quantum circuit returning :func:`~pennylane.state`. wires (Sequence(int)): List of wires in the considered subsystem. @@ -78,6 +82,14 @@ def measured_circuit(x): .. seealso:: :func:`pennylane.density_matrix` and :func:`pennylane.math.reduce_dm` """ + + warnings.warn( + "The qml.qinfo.reduced_dm transform is deprecated and will be removed " + "in v0.40. Instead include the qml.density_matrix measurement process in the " + "return line of your QNode.", + qml.PennyLaneDeprecationWarning, + ) + # device_wires is provided by the custom QNode transform all_wires = kwargs.get("device_wires", tape.wires) wire_map = {w: i for i, w in enumerate(all_wires)} @@ -137,6 +149,11 @@ def purity(tape: QuantumScript, wires, **kwargs) -> tuple[QuantumScriptBatch, Po It is possible to compute the purity of a sub-system from a given state. To find the purity of the overall state, include all wires in the ``wires`` argument. + .. warning:: + + The ``qml.qinfo.purity transform`` is deprecated and will be removed in v0.40. Instead include + the :func:`pennylane.purity` measurement process in the return line of your QNode. + Args: tape (QNode or QuantumTape or Callable): A quantum circuit object returning a :func:`~pennylane.state`. wires (Sequence(int)): List of wires in the considered subsystem. @@ -175,6 +192,14 @@ def circuit(x): .. seealso:: :func:`pennylane.math.purity` """ + + warnings.warn( + "The qml.qinfo.purity transform is deprecated and will be removed " + "in v0.40. Instead include the qml.purity measurement process in the " + "return line of your QNode.", + qml.PennyLaneDeprecationWarning, + ) + # device_wires is provided by the custom QNode transform all_wires = kwargs.get("device_wires", tape.wires) wire_map = {w: i for i, w in enumerate(all_wires)} @@ -229,6 +254,11 @@ def vn_entropy( .. math:: S( \rho ) = -\text{Tr}( \rho \log ( \rho )) + .. warning:: + + The ``qml.qinfo.vn_entropy`` transform is deprecated and will be removed in v0.40. Instead include + the :func:`pennylane.vn_entropy` measurement process in the return line of your QNode. + Args: tape (QNode or QuantumTape or Callable): A quantum circuit returning a :func:`~pennylane.state`. wires (Sequence(int)): List of wires in the considered subsystem. @@ -263,6 +293,14 @@ def circuit(x): .. seealso:: :func:`pennylane.math.vn_entropy` and :func:`pennylane.vn_entropy` """ + + warnings.warn( + "The qml.qinfo.vn_entropy transform is deprecated and will be removed " + "in v0.40. Instead include the qml.vn_entropy measurement process in the " + "return line of your QNode.", + qml.PennyLaneDeprecationWarning, + ) + # device_wires is provided by the custom QNode transform all_wires = kwargs.get("device_wires", tape.wires) wire_map = {w: i for i, w in enumerate(all_wires)} @@ -366,6 +404,11 @@ def mutual_info( More specifically, it quantifies the amount of information obtained about one system by measuring the other system. + .. warning:: + + The ``qml.qinfo.mutual_info`` transform is deprecated and will be removed in v0.40. Instead include + the :func:`pennylane.mutual_info` measurement process in the return line of your QNode. + Args: qnode (QNode or QuantumTape or Callable): A quantum circuit returning a :func:`~pennylane.state`. wires0 (Sequence(int)): List of wires in the first subsystem. @@ -403,6 +446,14 @@ def circuit(x): .. seealso:: :func:`~.qinfo.vn_entropy`, :func:`pennylane.math.mutual_info` and :func:`pennylane.mutual_info` """ + + warnings.warn( + "The qml.qinfo.mutual_info transform is deprecated and will be removed " + "in v0.40. Instead include the qml.mutual_info measurement process in the " + "return line of your QNode.", + qml.PennyLaneDeprecationWarning, + ) + return _bipartite_qinfo_transform(qml.math.mutual_info, tape, wires0, wires1, base, **kwargs) @@ -437,6 +488,10 @@ def vn_entanglement_entropy( where :math:`S` is the von Neumann entropy; :math:`\rho_A = \text{Tr}_B [\rho_{AB}]` and :math:`\rho_B = \text{Tr}_A [\rho_{AB}]` are the reduced density matrices for each partition. + .. warning:: + The ``qml.qinfo.vn_entanglement_entropy`` transform is deprecated and will be removed in v0.40. Instead include + the :func:`pennylane.vn_entanglement_entropy` measurement process in the return line of your QNode. + The Von Neumann entanglement entropy is a measure of the degree of quantum entanglement between two subsystems constituting a pure bipartite quantum state. The entropy of entanglement is the Von Neumann entropy of the reduced density matrix for any of the subsystems. If it is non-zero, @@ -455,297 +510,18 @@ def vn_entanglement_entropy( will provide the entanglement entropy in the form of a tensor. """ - return _bipartite_qinfo_transform( - qml.math.vn_entanglement_entropy, tape, wires0, wires1, base, **kwargs - ) - - -def classical_fisher(qnode, argnums=0): - r"""Returns a function that computes the classical fisher information matrix (CFIM) of a given :class:`.QNode` or - quantum tape. - - Given a parametrized (classical) probability distribution :math:`p(\bm{\theta})`, the classical fisher information - matrix quantifies how changes to the parameters :math:`\bm{\theta}` are reflected in the probability distribution. - For a parametrized quantum state, we apply the concept of classical fisher information to the computational - basis measurement. - More explicitly, this function implements eq. (15) in `arxiv:2103.15191 `_: - - .. math:: - - \text{CFIM}_{i, j} = \sum_{\ell=0}^{2^N-1} \frac{1}{p_\ell(\bm{\theta})} \frac{\partial p_\ell(\bm{\theta})}{ - \partial \theta_i} \frac{\partial p_\ell(\bm{\theta})}{\partial \theta_j} - - for :math:`N` qubits. - - Args: - tape (:class:`.QNode` or qml.QuantumTape): A :class:`.QNode` or quantum tape that may have arbitrary return types. - argnums (Optional[int or List[int]]): Arguments to be differentiated in case interface ``jax`` is used. - - Returns: - func: The function that computes the classical fisher information matrix. This function accepts the same - signature as the :class:`.QNode`. If the signature contains one differentiable variable ``params``, the function - returns a matrix of size ``(len(params), len(params))``. For multiple differentiable arguments ``x, y, z``, - it returns a list of sizes ``[(len(x), len(x)), (len(y), len(y)), (len(z), len(z))]``. - - .. warning:: - ``pennylane.qinfo.classical_fisher`` is being migrated to a different module and will - removed in version 0.39. Instead, use :func:`pennylane.gradients.classical_fisher`. - - .. seealso:: :func:`~.pennylane.metric_tensor`, :func:`~.pennylane.qinfo.transforms.quantum_fisher` - - **Example** - - First, let us define a parametrized quantum state and return its (classical) probability distribution for all - computational basis elements: - - .. code-block:: python - - import pennylane.numpy as pnp - - dev = qml.device("default.qubit") - - @qml.qnode(dev) - def circ(params): - qml.RX(params[0], wires=0) - qml.CNOT([0, 1]) - qml.CRY(params[1], wires=[1, 0]) - qml.Hadamard(1) - return qml.probs(wires=[0, 1]) - - Executing this circuit yields the ``2**2=4`` elements of :math:`p_\ell(\bm{\theta})` - - >>> pnp.random.seed(25) - >>> params = pnp.random.random(2) - >>> circ(params) - [0.41850088 0.41850088 0.08149912 0.08149912] - - We can obtain its ``(2, 2)`` classical fisher information matrix (CFIM) by simply calling the function returned - by ``classical_fisher()``: - - >>> cfim_func = qml.qinfo.classical_fisher(circ) - >>> cfim_func(params) - [[ 0.901561 -0.125558] - [-0.125558 0.017486]] - - This function has the same signature as the :class:`.QNode`. Here is a small example with multiple arguments: - - .. code-block:: python - - @qml.qnode(dev) - def circ(x, y): - qml.RX(x, wires=0) - qml.RY(y, wires=0) - return qml.probs(wires=range(n_wires)) - - >>> x, y = pnp.array([0.5, 0.6], requires_grad=True) - >>> circ(x, y) - [0.86215007 0. 0.13784993 0. ] - >>> qml.qinfo.classical_fisher(circ)(x, y) - [array([[0.32934729]]), array([[0.51650396]])] - - Note how in the case of multiple variables we get a list of matrices with sizes - ``[(n_params0, n_params0), (n_params1, n_params1)]``, which in this case is simply two ``(1, 1)`` matrices. - - - A typical setting where the classical fisher information matrix is used is in variational quantum algorithms. - Closely related to the `quantum natural gradient `_, which employs the - `quantum` fisher information matrix, we can compute a rescaled gradient using the CFIM. In this scenario, - typically a Hamiltonian objective function :math:`\langle H \rangle` is minimized: - - .. code-block:: python - - H = qml.Hamiltonian(coeffs=[0.5, 0.5], observables=[qml.Z(0), qml.Z(1)]) - - @qml.qnode(dev) - def circ(params): - qml.RX(params[0], wires=0) - qml.RY(params[1], wires=0) - qml.RX(params[2], wires=1) - qml.RY(params[3], wires=1) - qml.CNOT(wires=(0,1)) - return qml.expval(H) - - params = pnp.random.random(4) - - We can compute both the gradient of :math:`\langle H \rangle` and the CFIM with the same :class:`.QNode` ``circ`` - in this example since ``classical_fisher()`` ignores the return types and assumes ``qml.probs()`` for all wires. - >>> grad = qml.grad(circ)(params) - >>> cfim = qml.qinfo.classical_fisher(circ)(params) - >>> print(grad.shape, cfim.shape) - (4,) (4, 4) - - Combined together, we can get a rescaled gradient to be employed for optimization schemes like natural gradient - descent. - - >>> rescaled_grad = cfim @ grad - >>> print(rescaled_grad) - [-0.66772533 -0.16618756 -0.05865127 -0.06696078] - - The ``classical_fisher`` matrix itself is again differentiable: - - .. code-block:: python - - @qml.qnode(dev) - def circ(params): - qml.RX(qml.math.cos(params[0]), wires=0) - qml.RX(qml.math.cos(params[0]), wires=1) - qml.RX(qml.math.cos(params[1]), wires=0) - qml.RX(qml.math.cos(params[1]), wires=1) - return qml.probs(wires=range(2)) - - params = pnp.random.random(2) - - >>> qml.qinfo.classical_fisher(circ)(params) - [[4.18575068e-06 2.34443943e-03] - [2.34443943e-03 1.31312079e+00]] - >>> qml.jacobian(qml.qinfo.classical_fisher(circ))(params) - array([[[9.98030491e-01, 3.46944695e-18], - [1.36541817e-01, 5.15248592e-01]], - [[1.36541817e-01, 5.15248592e-01], - [2.16840434e-18, 2.81967252e-01]]])) - - """ warnings.warn( - "pennylane.qinfo.classical_fisher is being migrated to a different module and will " - "removed in version 0.39. Instead, use pennylane.gradients.classical_fisher.", + "The qml.qinfo.vn_entanglement_entropy transform is deprecated and will be removed " + "in v0.40. Instead include the qml.vn_entanglement_entropy measurement process in the " + "return line of your QNode.", qml.PennyLaneDeprecationWarning, ) - return qml.gradients.classical_fisher(qnode, argnums=argnums) - - -@partial(transform, is_informative=True) -def quantum_fisher( - tape: QuantumScript, device, *args, **kwargs -) -> tuple[QuantumScriptBatch, PostprocessingFn]: - r"""Returns a function that computes the quantum fisher information matrix (QFIM) of a given :class:`.QNode`. - - Given a parametrized quantum state :math:`|\psi(\bm{\theta})\rangle`, the quantum fisher information matrix (QFIM) quantifies how changes to the parameters :math:`\bm{\theta}` - are reflected in the quantum state. The metric used to induce the QFIM is the fidelity :math:`f = |\langle \psi | \psi' \rangle|^2` between two (pure) quantum states. - This leads to the following definition of the QFIM (see eq. (27) in `arxiv:2103.15191 `_): - - .. math:: - - \text{QFIM}_{i, j} = 4 \text{Re}\left[ \langle \partial_i \psi(\bm{\theta}) | \partial_j \psi(\bm{\theta}) \rangle - - \langle \partial_i \psi(\bm{\theta}) | \psi(\bm{\theta}) \rangle \langle \psi(\bm{\theta}) | \partial_j \psi(\bm{\theta}) \rangle \right] - - with short notation :math:`| \partial_j \psi(\bm{\theta}) \rangle := \frac{\partial}{\partial \theta_j}| \psi(\bm{\theta}) \rangle`. - - .. seealso:: - :func:`~.pennylane.metric_tensor`, :func:`~.pennylane.adjoint_metric_tensor`, :func:`~.pennylane.qinfo.transforms.classical_fisher` - - Args: - tape (QNode or QuantumTape or Callable): A quantum circuit that may have arbitrary return types. - *args: In case finite shots are used, further arguments according to :func:`~.pennylane.metric_tensor` may be passed. - - Returns: - qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: - - The transformed circuit as described in :func:`qml.transform `. Executing this circuit - will provide the quantum Fisher information in the form of a tensor. - - .. warning:: - ``pennylane.qinfo.quantum_fisher`` is being migrated to a different module and will - removed in version 0.39. Instead, use :func:`pennylane.gradients.quantum_fisher`. - - .. note:: - - ``quantum_fisher`` coincides with the ``metric_tensor`` with a prefactor of :math:`4`. - Internally, :func:`~.pennylane.adjoint_metric_tensor` is used when executing on ``"default.qubit"`` - with exact expectations (``shots=None``). In all other cases, e.g. if a device with finite shots - is used, the hardware-compatible transform :func:`~.pennylane.metric_tensor` is used, which - may require an additional wire on the device. - Please refer to the respective documentations for details. - - **Example** - - The quantum Fisher information matrix (QIFM) can be used to compute the `natural` gradient for `Quantum Natural Gradient Descent `_. - A typical scenario is optimizing the expectation value of a Hamiltonian: - - .. code-block:: python - - n_wires = 2 - - dev = qml.device("default.qubit", wires=n_wires) - - H = 1.*qml.X(0) @ qml.X(1) - 0.5 * qml.Z(1) - - @qml.qnode(dev) - def circ(params): - qml.RY(params[0], wires=1) - qml.CNOT(wires=(1,0)) - qml.RY(params[1], wires=1) - qml.RZ(params[2], wires=1) - return qml.expval(H) - - params = pnp.array([0.5, 1., 0.2], requires_grad=True) - - The natural gradient is then simply the QFIM multiplied by the gradient: - - >>> grad = qml.grad(circ)(params) - >>> grad - [ 0.59422561 -0.02615095 -0.05146226] - >>> qfim = qml.qinfo.quantum_fisher(circ)(params) - >>> qfim - [[1. 0. 0. ] - [0. 1. 0. ] - [0. 0. 0.77517241]] - >>> qfim @ grad - tensor([ 0.59422561, -0.02615095, -0.03989212], requires_grad=True) - - When using real hardware or finite shots, ``quantum_fisher`` is internally calling :func:`~.pennylane.metric_tensor`. - To obtain the full QFIM, we need an auxilary wire to perform the Hadamard test. - - >>> dev = qml.device("default.qubit", wires=n_wires+1, shots=1000) - >>> @qml.qnode(dev) - ... def circ(params): - ... qml.RY(params[0], wires=1) - ... qml.CNOT(wires=(1,0)) - ... qml.RY(params[1], wires=1) - ... qml.RZ(params[2], wires=1) - ... return qml.expval(H) - >>> qfim = qml.qinfo.quantum_fisher(circ)(params) - - Alternatively, we can fall back on the block-diagonal QFIM without the additional wire. - - >>> qfim = qml.qinfo.quantum_fisher(circ, approx="block-diag")(params) - - """ - warnings.warn( - "pennylane.qinfo.quantum_fisher is being migrated to a different module and will " - "removed in version 0.39. Instead, use pennylane.gradients.quantum_fisher.", - qml.PennyLaneDeprecationWarning, + return _bipartite_qinfo_transform( + qml.math.vn_entanglement_entropy, tape, wires0, wires1, base, **kwargs ) - if device.shots or not isinstance(device, (DefaultQubitLegacy, DefaultQubit)): - tapes, processing_fn = metric_tensor(tape, *args, **kwargs) - - def processing_fn_multiply(res): - res = qml.execute(res, device=device) - return 4 * processing_fn(res) - - return tapes, processing_fn_multiply - - res = adjoint_metric_tensor(tape, *args, **kwargs) - - def processing_fn_multiply(r): # pylint: disable=function-redefined - r = qml.math.stack(r) - return 4 * r - - return res, processing_fn_multiply - - -@quantum_fisher.custom_qnode_transform -def qnode_execution_wrapper(self, qnode, targs, tkwargs): - """Here, we overwrite the QNode execution wrapper in order - to take into account that classical processing may be present - inside the QNode.""" - - tkwargs["device"] = qnode.device - - return self.default_qnode_transform(qnode, targs, tkwargs) - def fidelity(qnode0, qnode1, wires0, wires1): r"""Compute the fidelity for two :class:`.QNode` returning a :func:`~pennylane.state` (a state can be a state vector @@ -773,6 +549,11 @@ def fidelity(qnode0, qnode1, wires0, wires1): The second state is coerced to the type and dtype of the first state. The fidelity is returned in the type of the interface of the first state. + .. warning:: + + ``qml.qinfo.fidelity`` is deprecated and will be removed in v0.40. Instead, use + :func:`pennylane.math.fidelity`. + Args: state0 (QNode): A :class:`.QNode` returning a :func:`~pennylane.state`. state1 (QNode): A :class:`.QNode` returning a :func:`~pennylane.state`. @@ -877,9 +658,21 @@ def circuit_ry(y, use_ry): .. seealso:: :func:`pennylane.math.fidelity` """ + warnings.warn( + "qml.qinfo.fidelity is deprecated and will be removed in v0.40. Instead, use " + "qml.math.fidelity.", + qml.PennyLaneDeprecationWarning, + ) + if len(wires0) != len(wires1): raise qml.QuantumFunctionError("The two states must have the same number of wires.") + # with warnings.catch_warnings(): + warnings.filterwarnings( + action="ignore", + message="The qml.qinfo.reduced_dm transform", + category=qml.PennyLaneDeprecationWarning, + ) state_qnode0 = qml.qinfo.reduced_dm(qnode0, wires=wires0) state_qnode1 = qml.qinfo.reduced_dm(qnode1, wires=wires1) @@ -947,6 +740,11 @@ def relative_entropy(qnode0, qnode1, wires0, wires1): Roughly speaking, quantum relative entropy is a measure of distinguishability between two quantum states. It is the quantum mechanical analog of relative entropy. + .. warning:: + + ``qml.qinfo.relative_entropy`` is deprecated and will be removed in v0.40. Instead, use + :func:`~pennylane.math.relative_entropy`. + Args: qnode0 (QNode): A :class:`.QNode` returning a :func:`~pennylane.state`. qnode1 (QNode): A :class:`.QNode` returning a :func:`~pennylane.state`. @@ -997,9 +795,21 @@ def wrapper(x, y): tensor(0.16953273, requires_grad=True)) """ + warnings.warn( + "qml.qinfo.relative_entropy is deprecated and will be removed in v0.40. Instead, use " + "qml.math.relative_entropy.", + qml.PennyLaneDeprecationWarning, + ) + if len(wires0) != len(wires1): raise qml.QuantumFunctionError("The two states must have the same number of wires.") + # with warnings.catch_warnings(): + warnings.filterwarnings( + action="ignore", + message="The qml.qinfo.reduced_dm transform", + category=qml.PennyLaneDeprecationWarning, + ) state_qnode0 = qml.qinfo.reduced_dm(qnode0, wires=wires0) state_qnode1 = qml.qinfo.reduced_dm(qnode1, wires=wires1) @@ -1068,6 +878,11 @@ def trace_distance(qnode0, qnode1, wires0, wires1): The trace distance measures how close two quantum states are. In particular, it upper-bounds the probability of distinguishing two quantum states. + .. warning:: + + ``qml.qinfo.trace_distance`` is deprecated and will be removed in v0.40. Instead, use + :func:`~pennylane.math.trace_distance`. + Args: qnode0 (QNode): A :class:`.QNode` returning a :func:`~pennylane.state`. qnode1 (QNode): A :class:`.QNode` returning a :func:`~pennylane.state`. @@ -1118,9 +933,21 @@ def wrapper(x, y): tensor(0.28232124, requires_grad=True)) """ + warnings.warn( + "qml.qinfo.trace_distance is deprecated and will be removed in v0.40. Instead, use " + "qml.math.trace_distance.", + qml.PennyLaneDeprecationWarning, + ) + if len(wires0) != len(wires1): raise qml.QuantumFunctionError("The two states must have the same number of wires.") + # with warnings.catch_warnings(): + warnings.filterwarnings( + action="ignore", + message="The qml.qinfo.reduced_dm transform", + category=qml.PennyLaneDeprecationWarning, + ) state_qnode0 = qml.qinfo.reduced_dm(qnode0, wires=wires0) state_qnode1 = qml.qinfo.reduced_dm(qnode1, wires=wires1) diff --git a/pennylane/shadows/transforms.py b/pennylane/shadows/transforms.py index feb37a19a06..95007d50d52 100644 --- a/pennylane/shadows/transforms.py +++ b/pennylane/shadows/transforms.py @@ -62,6 +62,11 @@ def shadow_expval(tape: QuantumScript, H, k=1) -> tuple[QuantumScriptBatch, Post See :func:`~.pennylane.shadow_expval` for more usage details. + .. warning:: + + ``qml.shadows.shadow_expval`` is deprecated. Please use the :func:`~pennylane.shadow_expval` + measurement process in your circuits instead. + Args: tape (QNode or QuantumTape or Callable): A quantum circuit. H (:class:`~.pennylane.Observable` or list[:class:`~.pennylane.Observable`]): Observables @@ -96,6 +101,13 @@ def circuit(x): >>> qml.grad(circuit)(x) -0.9323999999999998 """ + + warnings.warn( + "qml.shadows.shadow_expval is deprecated. Instead, use the qml.shadow_expval " + "measurement process in your circuit.", + qml.PennyLaneDeprecationWarning, + ) + tapes, _ = _replace_obs(tape, qml.shadow_expval, H, k=k) def post_processing_fn(res): diff --git a/pennylane/tape/qscript.py b/pennylane/tape/qscript.py index 7cc6809ff82..6e3e46fc1b0 100644 --- a/pennylane/tape/qscript.py +++ b/pennylane/tape/qscript.py @@ -542,7 +542,7 @@ def data(self) -> list[TensorLike]: @property def trainable_params(self) -> list[int]: r"""Store or return a list containing the indices of parameters that support - differentiability. The indices provided match the order of appearence in the + differentiability. The indices provided match the order of appearance in the quantum circuit. Setting this property can help reduce the number of quantum evaluations needed @@ -942,7 +942,7 @@ def expand( The ``stop_at`` callable allows the specification of terminal operations that should no longer be decomposed. In this example, the ``X`` - operator is not decomposed becasue ``stop_at(qml.X(0)) == True``. + operator is not decomposed because ``stop_at(qml.X(0)) == True``. >>> def stop_at(obj): ... return isinstance(obj, qml.X) @@ -1281,7 +1281,7 @@ def make_qscript( Returns: function: The returned function takes the same arguments as the quantum function. When called, it returns the generated quantum script - without any queueing occuring. + without any queueing occurring. **Example** diff --git a/pennylane/tape/tape.py b/pennylane/tape/tape.py index 2a247ecdf58..20c8e3bd9c1 100644 --- a/pennylane/tape/tape.py +++ b/pennylane/tape/tape.py @@ -188,7 +188,7 @@ def expand_tape(tape, depth=1, stop_at=None, expand_measurements=False): The ``stop_at`` callable allows the specification of terminal operations that should no longer be decomposed. In this example, the ``X`` - operator is not decomposed becasue ``stop_at(qml.X(0)) == True``. + operator is not decomposed because ``stop_at(qml.X(0)) == True``. >>> def stop_at(obj): ... return isinstance(obj, qml.X) diff --git a/pennylane/templates/broadcast.py b/pennylane/templates/broadcast.py index 8f770f6a55d..8c78b294927 100644 --- a/pennylane/templates/broadcast.py +++ b/pennylane/templates/broadcast.py @@ -19,6 +19,8 @@ ``details`` section, * add tests to parametrizations in :func:`test_templates_broadcast`. """ +from warnings import warn + # pylint: disable-msg=too-many-branches,too-many-arguments,protected-access import pennylane as qml from pennylane.wires import Wires @@ -212,6 +214,10 @@ def broadcast(unitary, wires, pattern, parameters=None, kwargs=None): For more details, see *Usage Details* below. + .. warning:: + + ``qml.broadcast`` has been deprecated. Please use ``for`` loops instead. + Args: unitary (func): quantum gate or template pattern (str): specifies the wire pattern of the broadcast @@ -553,6 +559,12 @@ def circuit(pars): # We deliberately disable iterating using enumerate here, since # it causes a slowdown when iterating over TensorFlow variables. # pylint: disable=consider-using-enumerate + + warn( + "qml.broadcast is deprecated. Please use a for loop instead", + qml.PennyLaneDeprecationWarning, + ) + wires = Wires(wires) if kwargs is None: kwargs = {} diff --git a/pennylane/templates/layers/cv_neural_net.py b/pennylane/templates/layers/cv_neural_net.py index 5978211f90b..f5b9517d314 100644 --- a/pennylane/templates/layers/cv_neural_net.py +++ b/pennylane/templates/layers/cv_neural_net.py @@ -26,7 +26,7 @@ class CVNeuralNetLayers(Operation): The layer consists of interferometers, displacement and squeezing gates mimicking the linear transformation of a neural network in the x-basis of the quantum system, and uses a Kerr gate - to introduce a 'quantum' nonlinearity. + to introduce a 'quantum' non-linearity. The layers act on the :math:`M` modes given in ``wires``, and include interferometers of :math:`K=M(M-1)/2` beamsplitters. The different weight parameters diff --git a/pennylane/templates/state_preparations/basis.py b/pennylane/templates/state_preparations/basis.py index 48211a6d1e8..d63fa0f1b32 100644 --- a/pennylane/templates/state_preparations/basis.py +++ b/pennylane/templates/state_preparations/basis.py @@ -15,6 +15,8 @@ Contains the BasisStatePreparation template. """ +import warnings + import numpy as np import pennylane as qml @@ -30,6 +32,8 @@ class BasisStatePreparation(Operation): ``basis_state`` influences the circuit architecture and is therefore incompatible with gradient computations. + ``BasisStatePreparation`` is deprecated and will be removed in version 0.40. Instead, please use ``BasisState``. + Args: basis_state (array): Input array of shape ``(n,)``, where n is the number of wires the state preparation acts on. @@ -59,6 +63,13 @@ def circuit(basis_state): ndim_params = (1,) def __init__(self, basis_state, wires, id=None): + + warnings.warn( + "BasisStatePreparation is deprecated and will be removed in version 0.40. " + "Instead, please use BasisState.", + qml.PennyLaneDeprecationWarning, + ) + basis_state = qml.math.stack(basis_state) # check if the `basis_state` param is batched diff --git a/pennylane/templates/state_preparations/basis_qutrit.py b/pennylane/templates/state_preparations/basis_qutrit.py index 8568d0141d4..fb79eddb0f4 100644 --- a/pennylane/templates/state_preparations/basis_qutrit.py +++ b/pennylane/templates/state_preparations/basis_qutrit.py @@ -94,7 +94,7 @@ def compute_decomposition(basis_state, wires): # pylint: disable=arguments-diff .. math:: O = O_1 O_2 \dots O_n. - .. seealso:: :meth:`~.BasisState.decomposition`. + .. seealso:: :meth:`~.BasisState.decomposition`. Args: basis_state (array): Input array of shape ``(len(wires),)`` diff --git a/pennylane/templates/subroutines/arbitrary_unitary.py b/pennylane/templates/subroutines/arbitrary_unitary.py index 5c15dfdfb60..3527bc0145b 100644 --- a/pennylane/templates/subroutines/arbitrary_unitary.py +++ b/pennylane/templates/subroutines/arbitrary_unitary.py @@ -83,7 +83,8 @@ class ArbitraryUnitary(Operation): .. code-block:: python def arbitrary_nearest_neighbour_interaction(weights, wires): - qml.broadcast(unitary=ArbitraryUnitary, pattern="double", wires=wires, parameters=weights) + for i, w in enumerate(range(0, len(wires) - 1, 2)): + ArbitraryUnitary(weights[i], wires=[w, w + 1]) Args: weights (tensor_like): The angles of the Pauli word rotations, needs to have length :math:`4^n - 1` diff --git a/pennylane/templates/subroutines/controlled_sequence.py b/pennylane/templates/subroutines/controlled_sequence.py index 517129556cc..2fb9afc0c2e 100644 --- a/pennylane/templates/subroutines/controlled_sequence.py +++ b/pennylane/templates/subroutines/controlled_sequence.py @@ -165,8 +165,8 @@ def circuit(): 2: ─│────────────│────────────╭●───────────┤ State 3: ─╰(RX(1.00))──╰(RX(0.50))──╰(RX(0.25))──┤ State - To display the operators as powers of the base operator without further simplifcation, - the `compute_decompostion` method can be used with `lazy=True`. + To display the operators as powers of the base operator without further simplification, + the `compute_decomposition` method can be used with `lazy=True`. .. code-block:: python diff --git a/pennylane/templates/subroutines/qrom.py b/pennylane/templates/subroutines/qrom.py index 8396fc9c6e0..7816cbf0e1d 100644 --- a/pennylane/templates/subroutines/qrom.py +++ b/pennylane/templates/subroutines/qrom.py @@ -96,7 +96,7 @@ def circuit(): Then, :math:`k = l \cdot (\lambda-1)` work wires are needed, where :math:`l` is the length of the bitstrings. - The QROM template has two variants. The first one (``clean = False``) is based on [`arXiv:1812.00954 `__] that alterates the state in the ``work_wires``. + The QROM template has two variants. The first one (``clean = False``) is based on [`arXiv:1812.00954 `__] that alternates the state in the ``work_wires``. The second one (``clean = True``), based on [`arXiv:1902.02134 `__], solves that issue by returning ``work_wires`` to their initial state. This technique can be applied when the ``work_wires`` are not initialized to zero. diff --git a/pennylane/templates/subroutines/qubitization.py b/pennylane/templates/subroutines/qubitization.py index f79bfe3d152..e043463f9c0 100644 --- a/pennylane/templates/subroutines/qubitization.py +++ b/pennylane/templates/subroutines/qubitization.py @@ -55,7 +55,7 @@ def circuit(): # apply QPE measurements = qml.iterative_qpe( - qml.Qubitization(H, control = [3,4]), ancilla = 5, iters = 3 + qml.Qubitization(H, control = [3,4]), aux_wire = 5, iters = 3 ) return qml.probs(op = measurements) diff --git a/pennylane/templates/swapnetworks/ccl2.py b/pennylane/templates/swapnetworks/ccl2.py index 9595e577e0c..6e2e45fc6a4 100644 --- a/pennylane/templates/swapnetworks/ccl2.py +++ b/pennylane/templates/swapnetworks/ccl2.py @@ -34,7 +34,7 @@ class TwoLocalSwapNetwork(Operation): stored in physical wires provided by `wires` before they are swapped apart. Parameters for the operation are specified using `param`, and any additional keyword arguments for the callable should be provided using the ``kwargs`` separately - weights (tensor): weight tensor for the parameterized acquaintances of length + weights (tensor): weight tensor for the parametrized acquaintances of length :math:`N \times (N - 1) / 2`, where `N` is the length of `wires` fermionic (bool): If ``True``, qubits are realized as fermionic modes and :class:`~.pennylane.FermionicSWAP` with :math:`\phi=\pi` is used instead of :class:`~.pennylane.SWAP` shift (bool): If ``True``, odd-numbered layers begins from the second qubit instead of first one @@ -162,7 +162,7 @@ def compute_decomposition( .. seealso:: :meth:`~.TwoLocalSwapNetwork.decomposition`. Args: - weights (tensor): weight tensor for the parameterized acquaintances of length :math:`N \times (N - 1) / 2`, where `N` is the length of `wires` + weights (tensor): weight tensor for the parametrized acquaintances of length :math:`N \times (N - 1) / 2`, where `N` is the length of `wires` wires (Iterable or Wires): ordered sequence of wires on which the swap network acts acquaintances (Callable): callable `func(index, wires, param=None, **kwargs)` that returns a two-local operation, which is applied on a pair of logical wires specified by `index`. This corresponds to applying the operation on physical wires provided by `wires` before any SWAP gates occurred. Parameters for the operation are specified using `param`, and any additional keyword arguments for the callable should be provided using the ``kwargs`` separately fermionic (bool): If ``True``, qubits are realized as fermionic modes and :class:`~.pennylane.FermionicSWAP` with :math:`\phi=\pi` is used instead of :class:`~.pennylane.SWAP` @@ -224,7 +224,7 @@ def compute_decomposition( @staticmethod def shape(n_wires): - r"""Returns the shape of the weight tensor required for using parameterized acquaintances in the template. + r"""Returns the shape of the weight tensor required for using parametrized acquaintances in the template. Args: n_wires (int): Number of qubits Returns: diff --git a/pennylane/transforms/decompositions/clifford_t_transform.py b/pennylane/transforms/decompositions/clifford_t_transform.py index ff43181eacd..9473a5c115a 100644 --- a/pennylane/transforms/decompositions/clifford_t_transform.py +++ b/pennylane/transforms/decompositions/clifford_t_transform.py @@ -263,7 +263,7 @@ def _two_qubit_decompose(op): def _merge_param_gates(operations, merge_ops=None): - """Merge the provided parameterized gates on the same wires that are adjacent to each other""" + """Merge the provided parametrized gates on the same wires that are adjacent to each other""" copied_ops = operations.copy() merged_ops, number_ops = [], 0 @@ -472,7 +472,7 @@ def circuit(x, y): else: raise NotImplementedError( - f"Currently we only support Solovay-Kitaev ('sk') decompostion, got {method}" + f"Currently we only support Solovay-Kitaev ('sk') decomposition, got {method}" ) decomp_ops = [] diff --git a/pennylane/transforms/defer_measurements.py b/pennylane/transforms/defer_measurements.py index 8a9aeed9624..60f1446b111 100644 --- a/pennylane/transforms/defer_measurements.py +++ b/pennylane/transforms/defer_measurements.py @@ -199,7 +199,7 @@ def qfunc(par): >>> qml.grad(qnode)(par) tensor(-0.49622252, requires_grad=True) - Reusing and reseting measured wires will work as expected with the + Reusing and resetting measured wires will work as expected with the ``defer_measurements`` transform: .. code-block:: python3 diff --git a/pennylane/transforms/split_non_commuting.py b/pennylane/transforms/split_non_commuting.py index 7082602ecf4..3f2775bba18 100644 --- a/pennylane/transforms/split_non_commuting.py +++ b/pennylane/transforms/split_non_commuting.py @@ -109,7 +109,7 @@ def circuit(x): 1: ──RX(0.79)─┤ ╰ Note that the observable ``Y(1)`` occurs twice in the original QNode, but only once in the - transformed circuits. When there are multiple expecatation value measurements that rely on + transformed circuits. When there are multiple expectation value measurements that rely on the same observable, this observable is measured only once, and the result is copied to each original measurement. diff --git a/pennylane/workflow/construct_batch.py b/pennylane/workflow/construct_batch.py index 1701e459f37..99b674e5080 100644 --- a/pennylane/workflow/construct_batch.py +++ b/pennylane/workflow/construct_batch.py @@ -43,7 +43,7 @@ def expand_fn_transform(expand_fn: Callable) -> "qml.transforms.core.TransformDi .TransformDispatcher: Returns a transform dispatcher object that that can transform any circuit-like object in PennyLane. - >>> device = qml.device('default.qubit.legacy', wires=2) + >>> device = qml.device('default.mixed', wires=2) >>> my_transform = qml.transforms.core.expand_fn_transform(device.expand_fn) >>> my_transform diff --git a/pennylane/workflow/interfaces/autograd.py b/pennylane/workflow/interfaces/autograd.py index cb5731ddc8b..f38132f7862 100644 --- a/pennylane/workflow/interfaces/autograd.py +++ b/pennylane/workflow/interfaces/autograd.py @@ -67,7 +67,7 @@ def grad_fn(dy): is called, we are forced to reproduce the calculation for ``exponent * x ** (exponent-1)``, only to multiply it by a different vector. When executing quantum circuits, that quantity can potentially be quite expensive. Autograd would naively -request indepedent vjps for each entry in the output, even though the internal circuits will be +request independent vjps for each entry in the output, even though the internal circuits will be exactly the same. When caching is enabled, the expensive part (re-executing identical circuits) is diff --git a/pennylane/workflow/interfaces/jax.py b/pennylane/workflow/interfaces/jax.py index 398124c7682..f74d230c213 100644 --- a/pennylane/workflow/interfaces/jax.py +++ b/pennylane/workflow/interfaces/jax.py @@ -120,7 +120,7 @@ def f_and_jvp(primals, tangents): Currently, :class:`~.QuantumScript` is a valid pytree *most* of the time. Once it is a valid pytree *all* of the time and can store tangents in place of the variables, we can use a batch of tapes as our trainable argument. Until then, the tapes -must be a non-pytree non-differenatible argument that accompanies the tree leaves. +must be a non-pytree non-differentiable argument that accompanies the tree leaves. """ import dataclasses diff --git a/pennylane/workflow/return_types_spec.rst b/pennylane/workflow/return_types_spec.rst index 6e9ba4c915e..90233e8bedd 100644 --- a/pennylane/workflow/return_types_spec.rst +++ b/pennylane/workflow/return_types_spec.rst @@ -134,7 +134,7 @@ tuple where each entry corresponds to a different shot value. ({'0': 1}, {'0': 10}, {'0': 100}) Let's look at an example with all forms of nesting. Here, we have a tape with a batch size of ``3``, three -diferent measurements with different fundamental shapes, and a shot vector with three different values. +different measurements with different fundamental shapes, and a shot vector with three different values. >>> op = qml.RX((1.2, 2.3, 3.4), 0) >>> ms = (qml.expval(qml.Z(0)), qml.probs(wires=0), qml.counts()) diff --git a/pennylane/workflow/set_shots.py b/pennylane/workflow/set_shots.py index aaaed1a1a44..532603b4cbf 100644 --- a/pennylane/workflow/set_shots.py +++ b/pennylane/workflow/set_shots.py @@ -45,7 +45,6 @@ def set_shots(device, shots): As a standard context manager: - >>> dev = qml.device("default.qubit.legacy", wires=2, shots=None) >>> with qml.workflow.set_shots(dev, shots=100): ... print(dev.shots) 100 diff --git a/setup.py b/setup.py index 41ae9775027..604c7001917 100644 --- a/setup.py +++ b/setup.py @@ -49,9 +49,9 @@ # This requires a rename in the setup file of all devices, and is best done during another refactor "pennylane.plugins": [ "default.qubit = pennylane.devices:DefaultQubit", - "default.qubit.legacy = pennylane.devices:DefaultQubitLegacy", "default.gaussian = pennylane.devices:DefaultGaussian", "default.mixed = pennylane.devices.default_mixed:DefaultMixed", + "reference.qubit = pennylane.devices.reference_qubit:ReferenceQubit", "null.qubit = pennylane.devices.null_qubit:NullQubit", "default.qutrit = pennylane.devices.default_qutrit:DefaultQutrit", "default.clifford = pennylane.devices.default_clifford:DefaultClifford", diff --git a/tests/capture/test_capture_qnode.py b/tests/capture/test_capture_qnode.py index 4d82d310e61..191df685388 100644 --- a/tests/capture/test_capture_qnode.py +++ b/tests/capture/test_capture_qnode.py @@ -37,11 +37,10 @@ def enable_disable_plxpr(): qml.capture.disable() -@pytest.mark.parametrize("dev_name", ("default.qubit", "default.qubit.legacy")) -def test_error_if_shot_vector(dev_name): +def test_error_if_shot_vector(): """Test that a NotImplementedError is raised if a shot vector is provided.""" - dev = qml.device(dev_name, wires=1, shots=(50, 50)) + dev = qml.device("default.qubit", wires=1, shots=(50, 50)) @qml.qnode(dev) def circuit(): @@ -58,11 +57,10 @@ def circuit(): assert qml.math.allclose(res, jax.numpy.zeros((50,))) -@pytest.mark.parametrize("dev_name", ("default.qubit", "default.qubit.legacy")) -def test_error_if_overridden_shot_vector(dev_name): +def test_error_if_overridden_shot_vector(): """Test that a NotImplementedError is raised if a shot vector is provided on call.""" - dev = qml.device(dev_name, wires=1) + dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def circuit(): @@ -141,14 +139,13 @@ def circuit(x): jax.config.update("jax_enable_x64", initial_mode) -@pytest.mark.parametrize("dev_name", ("default.qubit", "default.qubit.legacy")) @pytest.mark.parametrize("x64_mode", (True, False)) -def test_overriding_shots(dev_name, x64_mode): +def test_overriding_shots(x64_mode): """Test that the number of shots can be overridden on call.""" initial_mode = jax.config.jax_enable_x64 jax.config.update("jax_enable_x64", x64_mode) - dev = qml.device(dev_name, wires=1) + dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def circuit(): diff --git a/tests/capture/test_measurements_capture.py b/tests/capture/test_measurements_capture.py index 7f39e7a83e9..b252a3da642 100644 --- a/tests/capture/test_measurements_capture.py +++ b/tests/capture/test_measurements_capture.py @@ -32,6 +32,7 @@ ShadowExpvalMP, StateMP, VarianceMP, + VnEntanglementEntropyMP, VnEntropyMP, ) @@ -149,6 +150,7 @@ def f(): lambda: ProbabilityMP(wires=qml.wires.Wires((0, 1)), eigvals=np.array([-1.0, -0.5, 0.5, 1.0])), lambda: qml.sample(wires=(3, 4)), lambda: qml.shadow_expval(np.array(2) * qml.X(0)), + lambda: qml.vn_entanglement_entropy(wires0=(1, 3), wires1=(2, 4), log_base=2), lambda: qml.vn_entropy(wires=(1, 2)), lambda: qml.purity(wires=(0, 1)), lambda: qml.mutual_info(wires0=(1, 3), wires1=(2, 4), log_base=2), @@ -578,7 +580,7 @@ def f(): @pytest.mark.parametrize("x64_mode", (True, False)) @pytest.mark.parametrize("mtype, kwargs", [(VnEntropyMP, {"log_base": 2}), (PurityMP, {})]) -def test_qinfo_measurements(mtype, kwargs, x64_mode): +def test_vn_entropy_purity(mtype, kwargs, x64_mode): """Test the capture of a vn entropy and purity measurement.""" initial_mode = jax.config.jax_enable_x64 @@ -609,24 +611,25 @@ def f(w1, w2): @pytest.mark.parametrize("x64_mode", (True, False)) -def test_MutualInfo(x64_mode): - """Test the capture of a vn entropy and purity measurement.""" +@pytest.mark.parametrize("mtype", [MutualInfoMP, VnEntanglementEntropyMP]) +def test_mutual_info_vn_entanglement_entropy(mtype, x64_mode): + """Test the capture of a mutual info and vn entanglement entropy measurement.""" initial_mode = jax.config.jax_enable_x64 jax.config.update("jax_enable_x64", x64_mode) def f(w1, w2): - return qml.mutual_info(wires0=[w1, 1], wires1=[w2, 3], log_base=2) + return mtype(wires=(qml.wires.Wires([w1, 1]), qml.wires.Wires([w2, 3])), log_base=2) jaxpr = jax.make_jaxpr(f)(0, 2) assert len(jaxpr.eqns) == 1 - assert jaxpr.eqns[0].primitive == MutualInfoMP._wires_primitive + assert jaxpr.eqns[0].primitive == mtype._wires_primitive assert jaxpr.eqns[0].params == {"log_base": 2, "n_wires0": 2} assert len(jaxpr.eqns[0].invars) == 4 mp = jaxpr.eqns[0].outvars[0].aval assert isinstance(mp, AbstractMeasurement) - assert mp._abstract_eval == MutualInfoMP._abstract_eval + assert mp._abstract_eval == mtype._abstract_eval shapes = _get_shapes_for( *jaxpr.out_avals, num_device_wires=4, shots=qml.measurements.Shots(None) diff --git a/tests/capture/test_templates.py b/tests/capture/test_templates.py index d04990b2971..619723a9b02 100644 --- a/tests/capture/test_templates.py +++ b/tests/capture/test_templates.py @@ -27,8 +27,12 @@ jax = pytest.importorskip("jax") jnp = jax.numpy -pytestmark = pytest.mark.jax - +pytestmark = [ + pytest.mark.jax, + pytest.mark.filterwarnings( + "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning" + ), +] original_op_bind_code = qml.operation.Operator._primitive_bind_call.__code__ diff --git a/tests/circuit_graph/test_circuit_graph.py b/tests/circuit_graph/test_circuit_graph.py index 10886f559f2..f307cbf8735 100644 --- a/tests/circuit_graph/test_circuit_graph.py +++ b/tests/circuit_graph/test_circuit_graph.py @@ -58,8 +58,8 @@ def circuit_fixture(ops, obs): return CircuitGraph(ops, obs, Wires([0, 1, 2])) -@pytest.fixture(name="parameterized_circuit_gaussian") -def parameterized_circuit_gaussian_fixture(wires): +@pytest.fixture(name="parametrized_circuit_gaussian") +def parametrized_circuit_gaussian_fixture(wires): def qfunc(a, b, c, d, e, f): qml.Rotation(a, wires=wires[0]) qml.Rotation(b, wires=wires[1]) @@ -248,11 +248,11 @@ def test_op_indices(self, circuit): assert circuit.wire_indices(2) == op_indices_for_wire_2 @pytest.mark.parametrize("wires", [["a", "q1", 3]]) - def test_layers(self, parameterized_circuit_gaussian, wires): + def test_layers(self, parametrized_circuit_gaussian, wires): """A test of a simple circuit with 3 layers and 6 trainable parameters""" dev = qml.device("default.gaussian", wires=wires) - qnode = qml.QNode(parameterized_circuit_gaussian, dev) + qnode = qml.QNode(parametrized_circuit_gaussian, dev) qnode(*pnp.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6], requires_grad=True)) circuit = qnode.qtape.graph layers = circuit.parametrized_layers @@ -267,11 +267,11 @@ def test_layers(self, parameterized_circuit_gaussian, wires): assert layers[2].param_inds == [6, 7] @pytest.mark.parametrize("wires", [["a", "q1", 3]]) - def test_iterate_layers(self, parameterized_circuit_gaussian, wires): + def test_iterate_layers(self, parametrized_circuit_gaussian, wires): """A test of the different layers, their successors and ancestors using a simple circuit""" dev = qml.device("default.gaussian", wires=wires) - qnode = qml.QNode(parameterized_circuit_gaussian, dev) + qnode = qml.QNode(parametrized_circuit_gaussian, dev) qnode(*pnp.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6], requires_grad=True)) circuit = qnode.qtape.graph result = list(circuit.iterate_parametrized_layers()) diff --git a/tests/circuit_graph/test_qasm.py b/tests/circuit_graph/test_qasm.py index 9521b1c8bb9..11787ee1c98 100644 --- a/tests/circuit_graph/test_qasm.py +++ b/tests/circuit_graph/test_qasm.py @@ -711,7 +711,7 @@ def qnode(): @pytest.mark.tf def test_tf_interface_information_removed(self): """Test that interface information from tensorflow is not included in the - parameter string for parameterized operators""" + parameter string for parametrized operators""" import tensorflow as tf dev = qml.device("default.qubit") diff --git a/tests/conftest.py b/tests/conftest.py index d478b6f0998..2c6772e6bdb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,7 +19,6 @@ import os import pathlib import sys -import warnings import numpy as np import pytest @@ -36,23 +35,6 @@ TOL_STOCHASTIC = 0.05 -@pytest.fixture(scope="function", autouse=True) -def capture_legacy_device_deprecation_warnings(): - with warnings.catch_warnings(record=True) as recwarn: - warnings.simplefilter("always") - yield - - for w in recwarn: - if isinstance(w, qml.PennyLaneDeprecationWarning): - assert "Use of 'default.qubit." in str(w.message) - assert "is deprecated" in str(w.message) - assert "use 'default.qubit'" in str(w.message) - - for w in recwarn: - if "Use of 'default.qubit." not in str(w.message): - warnings.warn(message=w.message, category=w.category) - - # pylint: disable=too-few-public-methods class DummyDevice(DefaultGaussian): """Dummy device to allow Kerr operations""" @@ -99,29 +81,7 @@ def n_subsystems_fixture(request): @pytest.fixture(scope="session") def qubit_device(n_subsystems): - with pytest.warns(qml.PennyLaneDeprecationWarning, match="Use of 'default.qubit.legacy'"): - return qml.device("default.qubit.legacy", wires=n_subsystems) - - -@pytest.fixture(scope="function", params=[(np.float32, np.complex64), (np.float64, np.complex128)]) -def qubit_device_1_wire(request): - return qml.device( - "default.qubit.legacy", wires=1, r_dtype=request.param[0], c_dtype=request.param[1] - ) - - -@pytest.fixture(scope="function", params=[(np.float32, np.complex64), (np.float64, np.complex128)]) -def qubit_device_2_wires(request): - return qml.device( - "default.qubit.legacy", wires=2, r_dtype=request.param[0], c_dtype=request.param[1] - ) - - -@pytest.fixture(scope="function", params=[(np.float32, np.complex64), (np.float64, np.complex128)]) -def qubit_device_3_wires(request): - return qml.device( - "default.qubit.legacy", wires=3, r_dtype=request.param[0], c_dtype=request.param[1] - ) + return qml.device("default.qubit", wires=n_subsystems) # The following 3 fixtures are for default.qutrit devices to be used @@ -143,30 +103,6 @@ def qutrit_device_3_wires(request): return qml.device("default.qutrit", wires=3, r_dtype=request.param[0], c_dtype=request.param[1]) -@pytest.fixture(scope="session") -def gaussian_device(n_subsystems): - """Number of qubits or modes.""" - return DummyDevice(wires=n_subsystems) - - -@pytest.fixture(scope="session") -def gaussian_dummy(): - """Gaussian device with dummy Kerr gate.""" - return DummyDevice - - -@pytest.fixture(scope="session") -def gaussian_device_2_wires(): - """A 2-mode Gaussian device.""" - return DummyDevice(wires=2) - - -@pytest.fixture(scope="session") -def gaussian_device_4modes(): - """A 4 mode Gaussian device.""" - return DummyDevice(wires=4) - - ####################################################################### diff --git a/pennylane/devices/default_qubit_legacy.py b/tests/default_qubit_legacy.py similarity index 98% rename from pennylane/devices/default_qubit_legacy.py rename to tests/default_qubit_legacy.py index 868c426d47f..66212c2b7a9 100644 --- a/pennylane/devices/default_qubit_legacy.py +++ b/tests/default_qubit_legacy.py @@ -20,7 +20,6 @@ """ import functools import itertools -import warnings from string import ascii_letters as ABC import numpy as np @@ -28,6 +27,8 @@ import pennylane as qml from pennylane import BasisState, Snapshot, StatePrep +from pennylane._version import __version__ +from pennylane.devices._qubit_device import QubitDevice from pennylane.devices.qubit import measure from pennylane.measurements import ExpectationMP from pennylane.operation import Operation @@ -37,9 +38,6 @@ from pennylane.typing import TensorLike from pennylane.wires import WireError -from .._version import __version__ -from ._qubit_device import QubitDevice - ABC_ARRAY = np.array(list(ABC)) # tolerance for numerical errors @@ -208,14 +206,6 @@ class DefaultQubitLegacy(QubitDevice): def __init__( self, wires, *, r_dtype=np.float64, c_dtype=np.complex128, shots=None, analytic=None ): - warnings.warn( - f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit', " - "which supports backpropagation. " - "If you experience issues, reach out to the PennyLane team on " - "the discussion forum: https://discuss.pennylane.ai/", - qml.PennyLaneDeprecationWarning, - ) - super().__init__(wires, shots, r_dtype=r_dtype, c_dtype=c_dtype, analytic=analytic) self._debugger = None diff --git a/tests/devices/default_qubit/test_default_qubit.py b/tests/devices/default_qubit/test_default_qubit.py index d3049d90eae..ff17a6600d7 100644 --- a/tests/devices/default_qubit/test_default_qubit.py +++ b/tests/devices/default_qubit/test_default_qubit.py @@ -83,7 +83,7 @@ def circuit(): # pylint: disable=protected-access def test_applied_modifiers(): - """Test that defualt qubit has the `single_tape_support` and `simulator_tracking` + """Test that default qubit has the `single_tape_support` and `simulator_tracking` modifiers applied. """ dev = DefaultQubit() @@ -1864,6 +1864,7 @@ def circ_expected(): if use_jit: import jax + pytest.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") circ_postselect = jax.jit(circ_postselect, static_argnames=["shots"]) res = circ_postselect(param, shots=shots) @@ -2051,6 +2052,7 @@ def circ(): if use_jit: import jax + pytest.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") circ = jax.jit(circ, static_argnames=["shots"]) res = circ(shots=shots) diff --git a/tests/devices/default_qubit/test_default_qubit_native_mcm.py b/tests/devices/default_qubit/test_default_qubit_native_mcm.py index 5f63ec7fe2d..80467c66d3b 100644 --- a/tests/devices/default_qubit/test_default_qubit_native_mcm.py +++ b/tests/devices/default_qubit/test_default_qubit_native_mcm.py @@ -20,7 +20,7 @@ import pytest import pennylane as qml -from pennylane.devices.qubit.apply_operation import MidMeasureMP, apply_mid_measure +from pennylane.devices.qubit.apply_operation import MidMeasureMP from pennylane.devices.qubit.simulate import combine_measurements_core, measurement_with_no_shots from pennylane.transforms.dynamic_one_shot import fill_in_value @@ -53,16 +53,9 @@ def test_measurement_with_no_shots(): assert all(np.isnan(probs).tolist()) -def test_apply_mid_measure(): - """Test that apply_mid_measure raises if applied to a batched state.""" - with pytest.raises(ValueError, match="MidMeasureMP cannot be applied to batched states."): - _ = apply_mid_measure( - MidMeasureMP(0), np.zeros((2, 2)), is_state_batched=True, mid_measurements={} - ) - - +@pytest.mark.parametrize("obs", ["mid-meas", "pauli"]) @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -def test_all_invalid_shots_circuit(mcm_method): +def test_all_invalid_shots_circuit(obs, mcm_method): """Test that circuits in which all shots mismatch with post-selection conditions return the same answer as ``defer_measurements``.""" dev = get_device() dev_shots = get_device(shots=10) @@ -71,9 +64,13 @@ def circuit_op(): m = qml.measure(0, postselect=1) qml.cond(m, qml.PauliX)(1) return ( - qml.expval(op=qml.PauliZ(1)), - qml.probs(op=qml.PauliY(0) @ qml.PauliZ(1)), - qml.var(op=qml.PauliZ(1)), + ( + qml.expval(op=qml.PauliZ(1)), + qml.probs(op=qml.PauliY(0) @ qml.PauliZ(1)), + qml.var(op=qml.PauliZ(1)), + ) + if obs == "pauli" + else (qml.expval(op=m), qml.probs(op=m), qml.var(op=m)) ) res1 = qml.QNode(circuit_op, dev, mcm_method="deferred")() @@ -84,19 +81,6 @@ def circuit_op(): assert np.all(np.isnan(r1)) assert np.all(np.isnan(r2)) - def circuit_mcm(): - m = qml.measure(0, postselect=1) - qml.cond(m, qml.PauliX)(1) - return qml.expval(op=m), qml.probs(op=m), qml.var(op=m) - - res1 = qml.QNode(circuit_mcm, dev, mcm_method="deferred")() - res2 = qml.QNode(circuit_mcm, dev_shots, mcm_method=mcm_method)() - for r1, r2 in zip(res1, res2): - if isinstance(r1, Sequence): - assert len(r1) == len(r2) - assert np.all(np.isnan(r1)) - assert np.all(np.isnan(r2)) - @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) def test_unsupported_measurement(mcm_method): @@ -118,41 +102,6 @@ def func(x, y): func(*params) -@pytest.mark.parametrize("postselect_mode", ["hw-like", "fill-shots"]) -def test_tree_traversal_postselect_mode(postselect_mode): - """Test that invalid shots are discarded if requested""" - shots = 100 - dev = qml.device("default.qubit", shots=shots) - - @qml.qnode(dev, mcm_method="tree-traversal", postselect_mode=postselect_mode) - def f(x): - qml.RX(x, 0) - _ = qml.measure(0, postselect=1) - return qml.sample(wires=[0, 1]) - - res = f(np.pi / 2) - - if postselect_mode == "hw-like": - assert len(res) < shots - else: - assert len(res) == shots - assert np.all(res != np.iinfo(np.int32).min) - - -def test_deep_circuit(): - """Tests that DefaultQubit handles a circuit with more than 1000 mid-circuit measurements.""" - - dev = qml.device("default.qubit", shots=10) - - def func(x): - for _ in range(600): - qml.RX(x, wires=0) - m0 = qml.measure(0) - return qml.expval(qml.PauliY(0)), qml.expval(m0) - - _ = qml.QNode(func, dev, mcm_method="tree-traversal")(0.1234) - - # pylint: disable=unused-argument def obs_tape(x, y, z, reset=False, postselect=None): qml.RX(x, 0) @@ -174,69 +123,16 @@ def obs_tape(x, y, z, reset=False, postselect=None): @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) @pytest.mark.parametrize("shots", [None, 5500, [5500, 5501]]) -@pytest.mark.parametrize("postselect", [None, 0, 1]) -@pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs, qml.sample, qml.var]) @pytest.mark.parametrize( - "meas_obj", - [qml.PauliZ(0), qml.PauliY(1), [0], [0, 1], [1, 0], "mcm", "composite_mcm", "mcm_list"], + "params", + [ + [np.pi / 2.5, np.pi / 3, -np.pi / 3.5], + [[np.pi / 2.5, -np.pi / 3.5], [np.pi / 4.5, np.pi / 3.2], [np.pi, -np.pi / 0.5]], + ], ) -def test_simple_dynamic_circuit(mcm_method, shots, measure_f, postselect, meas_obj): - """Tests that DefaultQubit handles a simple dynamic circuit with the following measurements: - - * qml.counts with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list - * qml.expval with obs (comp basis or not), MCM, f(MCM), MCM list - * qml.probs with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list - * qml.sample with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list - * qml.var with obs (comp basis or not), MCM, f(MCM), MCM list - - The above combinations should work for finite shots, shot vectors and post-selecting of either the 0 or 1 branch. - """ - - if ( - isinstance(meas_obj, (qml.Z, qml.Y)) - and measure_f == qml.var - and mcm_method == "tree-traversal" - and not qml.operation.active_new_opmath() - ): - pytest.xfail( - "The tree-traversal method does not work with legacy opmath with " - "`qml.var` of pauli observables in the circuit." - ) - - if mcm_method == "one-shot" and shots is None: - pytest.skip("`mcm_method='one-shot'` is incompatible with analytic mode (`shots=None`)") - - if measure_f in (qml.expval, qml.var) and ( - isinstance(meas_obj, list) or meas_obj == "mcm_list" - ): - pytest.skip("Can't use wires/mcm lists with var or expval") - - if measure_f in (qml.counts, qml.sample) and shots is None: - pytest.skip("Can't measure counts/sample in analytic mode (`shots=None`)") - - dev = get_device(shots=shots) - params = [np.pi / 2.5, np.pi / 3, -np.pi / 3.5] - - def func(x, y, z): - m0, m1 = obs_tape(x, y, z, postselect=postselect) - mid_measure = ( - m0 if meas_obj == "mcm" else (0.5 * m0 if meas_obj == "composite_mcm" else [m0, m1]) - ) - measurement_key = "wires" if isinstance(meas_obj, list) else "op" - measurement_value = mid_measure if isinstance(meas_obj, str) else meas_obj - return measure_f(**{measurement_key: measurement_value}) - - results0 = qml.QNode(func, dev, mcm_method=mcm_method)(*params) - results1 = qml.QNode(func, dev, mcm_method="deferred")(*params) - - mcm_utils.validate_measurements(measure_f, shots, results1, results0) - - -@pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -@pytest.mark.parametrize("shots", [None, 5000]) @pytest.mark.parametrize("postselect", [None, 0, 1]) @pytest.mark.parametrize("reset", [False, True]) -def test_multiple_measurements_and_reset(mcm_method, shots, postselect, reset): +def test_multiple_measurements_and_reset(mcm_method, shots, params, postselect, reset): """Tests that DefaultQubit handles a circuit with a single mid-circuit measurement with reset and a conditional gate. Multiple measurements of the mid-circuit measurement value are performed. This function also tests `reset` parametrizing over the parameter.""" @@ -250,8 +146,11 @@ def test_multiple_measurements_and_reset(mcm_method, shots, postselect, reset): if mcm_method == "one-shot" and shots is None: pytest.skip("`mcm_method='one-shot'` is incompatible with analytic mode (`shots=None`)") + batch_size = len(params[0]) if isinstance(params[0], list) else None + if batch_size is not None and shots is not None and postselect is not None: + pytest.skip("Postselection with samples doesn't work with broadcasting") + dev = get_device(shots=shots) - params = [np.pi / 2.5, np.pi / 3, -np.pi / 3.5] obs = qml.PauliY(1) state = qml.math.zeros((4,)) state[0] = 1.0 @@ -286,27 +185,34 @@ def func(x, y, z): if shots is None else [qml.counts, qml.expval, qml.probs, qml.sample, qml.var] ) - for measure_f, r1, r0 in zip(measurements, results1, results0): - mcm_utils.validate_measurements(measure_f, shots, r1, r0) + + if not isinstance(shots, list): + shots, results0, results1 = [shots], [results0], [results1] + + for shot, res1, res0 in zip(shots, results1, results0): + for measure_f, r1, r0 in zip(measurements, res1, res0): + if shots is None and measure_f in [qml.expval, qml.probs] and batch_size is not None: + r0 = qml.math.squeeze(r0) + mcm_utils.validate_measurements(measure_f, shot, r1, r0, batch_size=batch_size) @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -@pytest.mark.parametrize("shots", [None, 3000]) +@pytest.mark.parametrize("shots", [None, 5000, [5000, 5001]]) @pytest.mark.parametrize( - "mcm_f", + "mcm_name, mcm_func", [ - lambda x: x * -1, - lambda x: x * 1, - lambda x: x * 2, - lambda x: 1 - x, - lambda x: x + 1, - lambda x: x & 3, - "mix", - "list", + ("single", lambda x: x * -1), + ("single", lambda x: x * 2), + ("single", lambda x: 1 - x), + ("single", lambda x: x & 3), + ("mix", lambda x, y: x == y), + ("mix", lambda x, y: 4 * x + 2 * y), + ("all", lambda x, y, z: [x, y, z]), + ("all", lambda x, y, z: (x - 2 * y) * z + 7), ], ) @pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs, qml.sample, qml.var]) -def test_composite_mcms(mcm_method, shots, mcm_f, measure_f): +def test_composite_mcms(mcm_method, shots, mcm_name, mcm_func, measure_f): """Tests that DefaultQubit handles a circuit with a composite mid-circuit measurement and a conditional gate. A single measurement of a composite mid-circuit measurement is performed at the end.""" @@ -317,20 +223,20 @@ def test_composite_mcms(mcm_method, shots, mcm_f, measure_f): if measure_f in (qml.counts, qml.sample) and shots is None: pytest.skip("Can't measure counts/sample in analytic mode (`shots=None`)") - if measure_f in (qml.expval, qml.var) and (mcm_f in ("list", "mix")): + if measure_f in (qml.expval, qml.var) and mcm_name in ["mix", "all"]: pytest.skip( "expval/var does not support measuring sequences of measurements or observables." ) - if measure_f == qml.probs and mcm_f == "mix": + if measure_f in (qml.probs,) and mcm_name in ["mix", "all"]: pytest.skip( "Cannot use qml.probs() when measuring multiple mid-circuit measurements collected using arithmetic operators." ) dev = get_device(shots=shots) - param = np.pi / 3 + param = qml.numpy.array([np.pi / 3, np.pi / 6]) - def func(x): + def func(x, y): qml.RX(x, 0) m0 = qml.measure(0) qml.RX(0.5 * x, 1) @@ -338,52 +244,18 @@ def func(x): qml.cond((m0 + m1) == 2, qml.RY)(2.0 * x, 0) m2 = qml.measure(0) obs = ( - (m0 - 2 * m1) * m2 + 7 - if mcm_f == "mix" - else ([m0, m1, m2] if mcm_f == "list" else mcm_f(m2)) + mcm_func(m2) + if mcm_name == "single" + else (mcm_func(m0, m1) if mcm_name == "mix" else mcm_func(m0, m1, m2)) ) return measure_f(op=obs) - results0 = qml.QNode(func, dev, mcm_method=mcm_method)(param) - results1 = qml.QNode(func, dev, mcm_method="deferred")(param) + results0 = qml.QNode(func, dev, mcm_method=mcm_method)(*param) + results1 = qml.QNode(func, dev, mcm_method="deferred")(*param) mcm_utils.validate_measurements(measure_f, shots, results1, results0) -@pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -@pytest.mark.parametrize( - "mcm_f", - [ - lambda x, y: x + y, - lambda x, y: x - 7 * y, - lambda x, y: x & y, - lambda x, y: x == y, - lambda x, y: 4.0 * x + 2.0 * y, - ], -) -def test_counts_return_type(mcm_method, mcm_f): - """Tests that DefaultQubit returns the same keys for ``qml.counts`` measurements with ``dynamic_one_shot`` and ``defer_measurements``.""" - - shots = 500 - - dev = get_device(shots=shots) - param = np.pi / 3 - - def func(x): - qml.RX(x, 0) - m0 = qml.measure(0) - qml.RX(0.5 * x, 1) - m1 = qml.measure(1) - qml.cond((m0 + m1) == 2, qml.RY)(2.0 * x, 0) - return qml.counts(op=mcm_f(m0, m1)) - - results0 = qml.QNode(func, dev, mcm_method=mcm_method)(param) - results1 = qml.QNode(func, dev, mcm_method="deferred")(param) - - for r1, r0 in zip(results1.keys(), results0.keys()): - assert r1 == r0 - - @pytest.mark.parametrize("shots", [5000]) @pytest.mark.parametrize("postselect", [None, 0, 1]) @pytest.mark.parametrize("reset", [False, True]) @@ -421,44 +293,6 @@ def func(x, y): assert np.allclose(grad1, grad2, atol=0.01, rtol=0.3) -@pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) -@pytest.mark.parametrize("shots", [None, 5500, [5500, 5501]]) -@pytest.mark.parametrize("postselect", [None, 0]) -@pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs, qml.sample]) -def test_broadcasting_qnode(mcm_method, shots, postselect, measure_f): - """Test that executing qnodes with broadcasting works as expected""" - - if mcm_method == "one-shot" and shots is None: - pytest.skip("`mcm_method='one-shot'` is incompatible with analytic mode (`shots=None`)") - - if measure_f in (qml.counts, qml.sample) and shots is None: - pytest.skip("Can't measure counts/sample in analytic mode (`shots=None`)") - - if measure_f is qml.sample and postselect is not None: - pytest.skip("Postselection with samples doesn't work with broadcasting") - - dev = get_device(shots=shots) - param = [[np.pi / 3, np.pi / 4], [np.pi / 6, 2 * np.pi / 3]] - obs = qml.PauliZ(0) @ qml.PauliZ(1) - - def func(x, y): - obs_tape(x, y, None, postselect=postselect) - return measure_f(op=obs) - - results0 = qml.QNode(func, dev, mcm_method=mcm_method)(*param) - results1 = qml.QNode(func, dev, mcm_method="deferred")(*param) - - mcm_utils.validate_measurements(measure_f, shots, results1, results0, batch_size=2) - - if measure_f is qml.sample and postselect is None: - for i in range(2): # batch_size - if isinstance(shots, list): - for s, r1, r2 in zip(shots, results1, results0): - assert len(r1[i]) == len(r2[i]) == s - else: - assert len(results1[i]) == len(results0[i]) == shots - - @pytest.mark.parametrize("mcm_method", ["one-shot", "tree-traversal"]) def test_sample_with_broadcasting_and_postselection_error(mcm_method): """Test that an error is raised if returning qml.sample if postselecting with broadcasting""" diff --git a/tests/devices/default_qubit/test_default_qubit_preprocessing.py b/tests/devices/default_qubit/test_default_qubit_preprocessing.py index 74c30f8661d..89691125852 100644 --- a/tests/devices/default_qubit/test_default_qubit_preprocessing.py +++ b/tests/devices/default_qubit/test_default_qubit_preprocessing.py @@ -977,7 +977,7 @@ def test_valid_tape_with_expansion(self): assert qs.shots == qs_valid.shots def test_untrainable_operations(self): - """Tests that a parameterized QubitUnitary that is not trainable is not expanded""" + """Tests that a parametrized QubitUnitary that is not trainable is not expanded""" @qml.qnode(qml.device("default.qubit", wires=3), diff_method="adjoint") def circuit(x): diff --git a/tests/devices/modifiers/test_all_modifiers.py b/tests/devices/modifiers/test_all_modifiers.py index a62854c974a..e9235962519 100644 --- a/tests/devices/modifiers/test_all_modifiers.py +++ b/tests/devices/modifiers/test_all_modifiers.py @@ -60,7 +60,7 @@ def test_error_on_old_interface(self, modifier): """Test that a ValueError is raised is called on something that is not a subclass of Device.""" with pytest.raises(ValueError, match=f"{modifier.__name__} only accepts"): - modifier(qml.devices.DefaultQubitLegacy) + modifier(qml.devices.DefaultMixed) def test_adds_to_applied_modifiers_private_property(self, modifier): """Test that the modifier is added to the `_applied_modifiers` property.""" diff --git a/tests/devices/qubit/test_adjoint_jacobian.py b/tests/devices/qubit/test_adjoint_jacobian.py index 72e4379155e..16d80ad3ce5 100644 --- a/tests/devices/qubit/test_adjoint_jacobian.py +++ b/tests/devices/qubit/test_adjoint_jacobian.py @@ -66,7 +66,7 @@ def test_pauli_rotation_gradient(self, G, theta, tol): @pytest.mark.autograd @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) def test_Rot_gradient(self, theta, tol): - """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is + """Tests that the device gradient of an arbitrary Euler-angle-parametrized gate is correct.""" params = np.array([theta, theta**3, np.sqrt(2) * theta]) prep_op = qml.StatePrep(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) diff --git a/tests/devices/qubit/test_apply_operation.py b/tests/devices/qubit/test_apply_operation.py index 49abf3e462b..ec4a31804bb 100644 --- a/tests/devices/qubit/test_apply_operation.py +++ b/tests/devices/qubit/test_apply_operation.py @@ -301,7 +301,7 @@ class TestApplyParametrizedEvolution: """Test that apply_operation works with ParametrizedEvolution""" @pytest.mark.parametrize("method", methods) - def test_parameterized_evolution_time_independent(self, method): + def test_parametrized_evolution_time_independent(self, method): """Test that applying a ParametrizedEvolution gives the expected state for a time-independent hamiltonian""" @@ -329,7 +329,7 @@ def test_parameterized_evolution_time_independent(self, method): assert np.allclose(new_state, new_state_expected, atol=0.002) @pytest.mark.parametrize("method", methods) - def test_parameterized_evolution_time_dependent(self, method): + def test_parametrized_evolution_time_dependent(self, method): """Test that applying a ParametrizedEvolution gives the expected state for a time dependent Hamiltonian""" diff --git a/tests/devices/qubit/test_measure.py b/tests/devices/qubit/test_measure.py index 47e4d8c2a31..e764ede4616 100644 --- a/tests/devices/qubit/test_measure.py +++ b/tests/devices/qubit/test_measure.py @@ -39,6 +39,7 @@ def test_sample_based_observable(self): _ = measure(qml.sample(wires=0), state) +@pytest.mark.unit class TestMeasurementDispatch: """Test that get_measurement_function dispatchs to the correct place.""" @@ -96,6 +97,49 @@ def test_sum_sum_of_terms_when_backprop(self): state = qml.numpy.zeros(2) assert get_measurement_function(qml.expval(S), state) is sum_of_terms_method + @pytest.mark.usefixtures("use_legacy_opmath") + def test_hamiltonian_with_multi_wire_obs(self): + """Check that a Hamiltonian with a multi-wire observable uses the sum of terms method.""" + + S = qml.Hamiltonian( + [0.5, 0.5], + [ + qml.X(0), + qml.Hermitian( + np.array( + [ + [0.5, 1.0j, 0.0, -3j], + [-1.0j, -1.1, 0.0, -0.1], + [0.0, 0.0, -0.9, 12.0], + [3j, -0.1, 12.0, 0.0], + ] + ), + wires=[0, 1], + ), + ], + ) + state = np.zeros(2) + assert get_measurement_function(qml.expval(S), state) is sum_of_terms_method + + def test_no_sparse_matrix(self): + """Tests Hamiltonians/Sums containing observables that do not have a sparse matrix.""" + + class DummyOp(qml.operation.Operator): # pylint: disable=too-few-public-methods + num_wires = 1 + + S1 = qml.Hamiltonian([0.5, 0.5], [qml.X(0), DummyOp(wires=1)]) + state = np.zeros(2) + assert get_measurement_function(qml.expval(S1), state) is sum_of_terms_method + + S2 = qml.X(0) + DummyOp(wires=1) + assert get_measurement_function(qml.expval(S2), state) is sum_of_terms_method + + S3 = 0.5 * qml.X(0) + 0.5 * DummyOp(wires=1) + assert get_measurement_function(qml.expval(S3), state) is sum_of_terms_method + + S4 = qml.Y(0) + qml.X(0) @ DummyOp(wires=1) + assert get_measurement_function(qml.expval(S4), state) is sum_of_terms_method + class TestMeasurements: @pytest.mark.parametrize( diff --git a/tests/devices/qubit/test_simulate.py b/tests/devices/qubit/test_simulate.py index 4dce5afd4c5..85c97acfa9d 100644 --- a/tests/devices/qubit/test_simulate.py +++ b/tests/devices/qubit/test_simulate.py @@ -13,13 +13,36 @@ # limitations under the License. """Unit tests for simulate in devices/qubit.""" +import mcm_utils import numpy as np import pytest +import scipy as sp from dummy_debugger import Debugger +from flaky import flaky +from stat_utils import fisher_exact_test import pennylane as qml from pennylane.devices.qubit import get_final_state, measure_final_state, simulate -from pennylane.devices.qubit.simulate import _FlexShots +from pennylane.devices.qubit.simulate import ( + TreeTraversalStack, + _FlexShots, + branch_state, + combine_measurements_core, + counts_to_probs, + find_post_processed_mcms, + samples_to_counts, + simulate_one_shot_native_mcm, + simulate_tree_mcm, + split_circuit_at_mcms, +) + +ml_frameworks_list = [ + "numpy", + pytest.param("autograd", marks=pytest.mark.autograd), + pytest.param("jax", marks=pytest.mark.jax), + pytest.param("torch", marks=pytest.mark.torch), + pytest.param("tensorflow", marks=pytest.mark.tf), +] class TestCurrentlyUnsupportedCases: @@ -1178,3 +1201,415 @@ def test_qinfo_tf(self): grad5 = grad_tape.jacobian(results[5], phi) assert qml.math.allclose(grad5, expected_grads[5]) + + +@pytest.mark.unit +class TestTreeTraversalStack: + """Unit tests for TreeTraversalStack""" + + @pytest.mark.parametrize( + "max_depth", + [0, 1, 10, 100], + ) + def test_init_with_depth(self, max_depth): + """Test that TreeTraversalStack is initialized correctly with given ``max_depth``""" + tree_stack = TreeTraversalStack(max_depth) + + assert tree_stack.counts.count(None) == max_depth + assert tree_stack.probs.count(None) == max_depth + assert tree_stack.results_0.count(None) == max_depth + assert tree_stack.results_1.count(None) == max_depth + assert tree_stack.states.count(None) == max_depth + + def test_full_prune_empty_methods(self): + """Test that TreeTraversalStack object's class methods work correctly.""" + + max_depth = 10 + tree_stack = TreeTraversalStack(max_depth) + + np.random.shuffle(r_depths := list(range(max_depth))) + for depth in r_depths: + counts_0 = np.random.randint(1, 9) + counts_1 = 10 - counts_0 + tree_stack.counts[depth] = [counts_0, counts_1] + tree_stack.probs[depth] = [counts_0 / 10, counts_1 / 10] + tree_stack.results_0[depth] = [0] * counts_0 + tree_stack.results_1[depth] = [1] * counts_1 + tree_stack.states[depth] = [np.sqrt(counts_0), np.sqrt(counts_1)] + assert tree_stack.is_full(depth) + + assert tree_stack.counts[depth] == list( + samples_to_counts( + np.array(tree_stack.results_0[depth] + tree_stack.results_1[depth]) + ).values() + ) + assert tree_stack.probs[depth] == list( + counts_to_probs(dict(zip([0, 1], tree_stack.counts[depth]))).values() + ) + + state_vec = np.array(tree_stack.states[depth]).T + state_vec /= np.linalg.norm(tree_stack.states[depth]) + meas, meas_r = qml.measure(0), qml.measure(0, reset=True) + assert np.allclose(branch_state(state_vec, 0, meas.measurements[0]), [1.0, 0.0]) + assert np.allclose(branch_state(state_vec, 1, meas_r.measurements[0]), [1.0, 0.0]) + + tree_stack.prune(depth) + assert tree_stack.any_is_empty(depth) + + +@pytest.mark.unit +class TestMidMeasurements: + """Tests for simulating scripts with mid-circuit measurements using the ``simulate_tree_mcm``.""" + + @pytest.mark.parametrize("val", [0, 1]) + def test_basic_mid_meas_circuit(self, val): + """Test execution with a basic circuit with mid-circuit measurements.""" + qs = qml.tape.QuantumScript( + [qml.Hadamard(0), qml.CNOT([0, 1]), qml.measurements.MidMeasureMP(0, postselect=val)], + [qml.expval(qml.X(0)), qml.expval(qml.Z(0))], + ) + result = simulate_tree_mcm(qs) + assert result == (0, (-1.0) ** val) + + def test_basic_mid_meas_circuit_with_reset(self): + """Test execution with a basic circuit with mid-circuit measurements.""" + qs = qml.tape.QuantumScript( + [ + qml.Hadamard(0), + qml.Hadamard(1), + qml.CNOT([0, 1]), + (m0 := qml.measure(0, reset=True)).measurements[0], + qml.Hadamard(0), + qml.CNOT([1, 0]), + ], # equivalent to a circuit that gives equiprobable basis states + [qml.probs(op=m0), qml.probs(op=qml.Z(0)), qml.probs(op=qml.Z(1))], + ) + result = simulate_tree_mcm(qs) + assert qml.math.allclose(result, qml.math.array([0.5, 0.5])) + + # pylint: disable=too-many-arguments + @pytest.mark.parametrize("shots", [None, 5500]) + @pytest.mark.parametrize("postselect", [None, 0]) + @pytest.mark.parametrize("reset", [False, True]) + @pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs, qml.sample, qml.var]) + @pytest.mark.parametrize( + "meas_obj", [qml.Y(0), [1], [1, 0], "mcm", "composite_mcm", "mcm_list"] + ) + def test_simple_dynamic_circuit(self, shots, measure_f, postselect, reset, meas_obj): + """Tests that `simulate` can handles a simple dynamic circuit with the following measurements: + + * qml.counts with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list + * qml.expval with obs (comp basis or not), MCM, f(MCM), MCM list + * qml.probs with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list + * qml.sample with obs (comp basis or not), single wire, multiple wires (ordered/unordered), MCM, f(MCM), MCM list + * qml.var with obs (comp basis or not), MCM, f(MCM), MCM list + + The above combinations should work for finite shots, shot vectors and post-selecting of either the 0 or 1 branch. + """ + + if ( + isinstance(meas_obj, (qml.X, qml.Z, qml.Y)) + and measure_f in (qml.var,) + and not qml.operation.active_new_opmath() + ): + pytest.xfail( + "The tree-traversal method does not work with legacy opmath with " + "`qml.var` of pauli observables in the circuit." + ) + + if measure_f in (qml.expval, qml.var) and ( + isinstance(meas_obj, list) or meas_obj == "mcm_list" + ): + pytest.skip("Can't use wires/mcm lists with var or expval") + + if measure_f in (qml.counts, qml.sample) and shots is None: + pytest.skip("Can't measure counts/sample in analytic mode (`shots=None`)") + + if measure_f in (qml.probs,) and meas_obj in ["composite_mcm"]: + pytest.skip( + "Cannot use qml.probs() when measuring multiple mid-circuit measurements collected using arithmetic operators." + ) + + qscript = qml.tape.QuantumScript( + [ + qml.RX(np.pi / 2.5, 0), + qml.RZ(np.pi / 4, 0), + (m0 := qml.measure(0, reset=reset)).measurements[0], + qml.ops.op_math.Conditional(m0 == 0, qml.RX(np.pi / 4, 0)), + qml.ops.op_math.Conditional(m0 == 1, qml.RX(-np.pi / 4, 0)), + qml.RX(np.pi / 3, 1), + qml.RZ(np.pi / 4, 1), + (m1 := qml.measure(1, postselect=postselect)).measurements[0], + qml.ops.op_math.Conditional(m1 == 0, qml.RY(np.pi / 4, 1)), + qml.ops.op_math.Conditional(m1 == 1, qml.RY(-np.pi / 4, 1)), + ], + [ + measure_f( + **{ + "wires" if isinstance(meas_obj, list) else "op": ( + ( + m0 + if meas_obj == "mcm" + else (0.5 * m0 + m1 if meas_obj == "composite_mcm" else [m0, m1]) + ) + if isinstance(meas_obj, str) + else meas_obj + ) + } + ) + ], + shots=shots, + ) + + results0 = simulate(qscript, mcm_method="tree-traversal") + + deferred_tapes, deferred_func = qml.defer_measurements(qscript) + results1 = deferred_func([simulate(tape, mcm_method="deferred") for tape in deferred_tapes]) + mcm_utils.validate_measurements(measure_f, shots, results1, results0) + + if shots is not None: + one_shot_tapes, one_shot_func = qml.dynamic_one_shot(qscript) + results2 = one_shot_func( + [simulate(tape, mcm_method="one-shot") for tape in one_shot_tapes] + ) + mcm_utils.validate_measurements(measure_f, shots, results2, results0) + + @pytest.mark.parametrize("shots", [None, 5500, [5500, 5500]]) + @pytest.mark.parametrize("rng", [None, 42, np.array([37])]) + @pytest.mark.parametrize("angles", [(0.123, 0.015), (0.543, 0.057)]) + @pytest.mark.parametrize("measure_f", [qml.probs, qml.sample]) + def test_approx_dynamic_mid_meas_circuit(self, shots, rng, angles, measure_f): + """Test execution of a dynamic circuit with an equivalent static one.""" + + if measure_f in (qml.sample,) and shots is None: + pytest.skip("Can't measure samples in analytic mode (`shots=None`)") + + qs_with_mid_meas = qml.tape.QuantumScript( + [ + qml.Hadamard(0), + qml.Hadamard(1), + qml.CZ([0, 1]), + qml.CNOT([0, 2]), + qml.CNOT([2, 3]), + qml.CZ([1, 3]), + qml.Toffoli([3, 2, 0]), + (m0 := qml.measure(0)).measurements[0], + qml.ops.op_math.Conditional(m0, qml.RZ(angles[0], 1)), + qml.Hadamard(1), + qml.Z(1), + (m1 := qml.measure(1)).measurements[0], + qml.ops.op_math.Conditional(m1, qml.RX(angles[1], 3)), + ], + [measure_f(wires=[0, 1, 2, 3])], + shots=shots, + ) + qs_without_mid_meas = qml.tape.QuantumScript( + [ + qml.Hadamard(0), + qml.Hadamard(1), + qml.CZ([0, 1]), + qml.CNOT([0, 2]), + qml.CNOT([2, 3]), + qml.CZ([1, 3]), + qml.Toffoli([3, 2, 0]), + qml.Hadamard(1), + qml.Z(1), + qml.RX(angles[1], 3), + ], + [measure_f(wires=[0, 1, 2, 3])], + shots=shots, + ) # approximate compiled circuit of the above + res1 = simulate_tree_mcm(qs_with_mid_meas, rng=rng) + res2 = simulate(qs_without_mid_meas, rng=rng) + + if not isinstance(shots, list): + res1, res2 = (res1,), (res2,) + + for rs1, rs2 in zip(res1, res2): + prob_dist1, prob_dist2 = rs1, rs2 + if measure_f in (qml.sample,): + n_wires = rs1.shape[1] + prob_dist1, prob_dist2 = np.zeros(2**n_wires), np.zeros(2**n_wires) + for prob, rs in zip([prob_dist1, prob_dist2], [rs1, rs2]): + index, count = np.unique( + np.packbits(rs, axis=1, bitorder="little").squeeze(), return_counts=True + ) + prob[index] = count + + assert qml.math.allclose( + sp.stats.entropy(prob_dist1 + 1e-12, prob_dist2 + 1e-12), 0.0, atol=5e-2 + ) + + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) + @pytest.mark.parametrize( + "postselect_mode", [None, "hw-like", "pad-invalid-samples", "fill-shots"] + ) + def test_tree_traversal_interface_mcm(self, ml_framework, postselect_mode): + """Test that tree traversal works numerically with different interfaces""" + # pylint:disable = singleton-comparison, import-outside-toplevel + + qscript = qml.tape.QuantumScript( + [ + qml.RX(np.pi / 4, wires=0), + (m0 := qml.measure(0, reset=True)).measurements[0], + qml.RX(np.pi / 4, wires=0), + ], + [qml.sample(qml.Z(0)), qml.sample(m0)], + shots=5500, + ) + + res1, res2 = simulate_tree_mcm(qscript, interface=ml_framework) + + p1 = [qml.math.mean(res1 == -1), qml.math.mean(res1 == 1)] + p2 = [qml.math.mean(res2 == True), qml.math.mean(res2 == False)] + assert qml.math.allclose(qml.math.sum(sp.special.rel_entr(p1, p2)), 0.0, atol=0.05) + + qscript2 = qml.tape.QuantumScript( + [ + qml.RX(np.pi / 4, wires=0), + (m0 := qml.measure(0, postselect=0)).measurements[0], + qml.RX(np.pi / 4, wires=0), + ], + [qml.sample(qml.Z(0))], + shots=5500, + ) + qscript3 = qml.tape.QuantumScript( + [qml.RX(np.pi / 4, wires=0)], [qml.sample(qml.Z(0))], shots=5500 + ) + + res3 = simulate_tree_mcm(qscript2, postselect_mode=postselect_mode) + res4 = simulate(qscript3) + + p3 = [qml.math.mean(res3 == -1), qml.math.mean(res3 == 1)] + p4 = [qml.math.mean(res4 == -1), qml.math.mean(res4 == 1)] + assert qml.math.allclose(qml.math.sum(sp.special.rel_entr(p3, p4)), 0.0, atol=0.05) + + @pytest.mark.parametrize("postselect_mode", ["hw-like", "fill-shots"]) + def test_tree_traversal_postselect_mode(self, postselect_mode): + """Test that invalid shots are discarded if requested""" + + shots = 100 + qscript = qml.tape.QuantumScript( + [ + qml.RX(np.pi / 2, 0), + (m0 := qml.measure(0, postselect=1)).measurements[0], + qml.ops.op_math.Conditional(m0, qml.RZ(1.57, 1)), + ], + [qml.sample(wires=[0, 1])], + shots=shots, + ) + + res = simulate_tree_mcm(qscript, postselect_mode=postselect_mode) + + assert (len(res) < shots) if postselect_mode == "hw-like" else (len(res) == shots) + assert np.all(res != np.iinfo(np.int32).min) + + def test_tree_traversal_deep_circuit(self): + """Test that `simulate_tree_mcm` works with circuits with many mid-circuit measurements""" + + n_circs = 500 + operations = [] + for _ in range(n_circs): + operations.extend( + [ + qml.RX(1.234, 0), + (m0 := qml.measure(0, postselect=1)).measurements[0], + qml.CNOT([0, 1]), + qml.ops.op_math.Conditional(m0, qml.RZ(1.786, 1)), + ] + ) + + qscript = qml.tape.QuantumScript( + operations, + [qml.sample(wires=[0, 1]), qml.counts(wires=[0, 1])], + shots=20, + ) + + mcms = find_post_processed_mcms(qscript) + assert len(mcms) == n_circs + + split_circs = split_circuit_at_mcms(qscript) + assert len(split_circs) == n_circs + 1 + for circ in split_circs: + assert not [o for o in circ.operations if isinstance(o, qml.measurements.MidMeasureMP)] + + res = simulate_tree_mcm(qscript, postselect_mode="fill-shots") + assert len(res[0]) == 20 + assert isinstance(res[1], dict) and sum(list(res[1].values())) == 20 + + @pytest.mark.parametrize( + "measurements, expected", + [ + [(qml.counts(0), {"a": (1, {0: 42}), "b": (2, {1: 58})}), {0: 42, 1: 58}], + [ + (qml.expval(qml.Z(0)), {"a": (1, (0.42, -1)), "b": (2, (0.58, 1))}), + [1.58 / 3, 1 / 3], + ], + [(qml.probs(wires=0), {"a": (1, (0.42, -1)), "b": (2, (0.58, 1))}), [1.58 / 3, 1 / 3]], + [(qml.sample(wires=0), {"a": (1, (0, 1, 0)), "b": (2, (1, 0, 1))}), [0, 1, 0, 1, 0, 1]], + ], + ) + def test_tree_traversal_combine_measurements(self, measurements, expected): + """Test that the measurement value of a given type can be combined""" + print(combine_measurements_core(*measurements)) + combined_measurement = combine_measurements_core(*measurements) + if isinstance(combined_measurement, dict): + assert combined_measurement == expected + else: + assert qml.math.allclose(combined_measurement, expected) + + @flaky(max_runs=3, min_passes=1) + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) + @pytest.mark.parametrize( + "postselect_mode", [None, "hw-like", "pad-invalid-samples", "fill-shots"] + ) + def test_simulate_one_shot_native_mcm(self, ml_framework, postselect_mode): + """Unit tests for simulate_one_shot_native_mcm""" + + with qml.queuing.AnnotatedQueue() as q: + qml.RX(np.pi / 4, wires=0) + m = qml.measure(wires=0, postselect=0) + qml.RX(np.pi / 4, wires=0) + + circuit = qml.tape.QuantumScript(q.queue, [qml.expval(qml.Z(0)), qml.sample(m)], shots=[1]) + + n_shots = 500 + results = [ + simulate_one_shot_native_mcm( + circuit, + n_shots, + interface=ml_framework, + postselect_mode=postselect_mode, + ) + for _ in range(n_shots) + ] + terminal_results, mcm_results = zip(*results) + + if postselect_mode == "fill-shots": + assert all(ms == 0 for ms in mcm_results) + equivalent_tape = qml.tape.QuantumScript( + [qml.RX(np.pi / 4, wires=0)], [qml.expval(qml.Z(0))], shots=n_shots + ) + expected_sample = simulate(equivalent_tape) + fisher_exact_test(terminal_results, expected_sample, outcomes=(-1, 1)) + + else: + equivalent_tape = qml.tape.QuantumScript( + [qml.RX(np.pi / 4, wires=0)], [qml.sample(wires=0)], shots=n_shots + ) + expected_result = simulate(equivalent_tape) + fisher_exact_test(mcm_results, expected_result) + + subset = [ts for ms, ts in zip(mcm_results, terminal_results) if ms == 0] + equivalent_tape = qml.tape.QuantumScript( + [qml.RX(np.pi / 4, wires=0)], [qml.expval(qml.Z(0))], shots=n_shots + ) + expected_sample = simulate(equivalent_tape) + fisher_exact_test(subset, expected_sample, outcomes=(-1, 1)) + + subset = [ts for ms, ts in zip(mcm_results, terminal_results) if ms == 1] + equivalent_tape = qml.tape.QuantumScript( + [qml.X(0), qml.RX(np.pi / 4, wires=0)], [qml.expval(qml.Z(0))], shots=n_shots + ) + expected_sample = simulate(equivalent_tape) + fisher_exact_test(subset, expected_sample, outcomes=(-1, 1)) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_measure.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_measure.py index 8a8de382f57..56d61fa339b 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_measure.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_measure.py @@ -499,7 +499,7 @@ def test_jax_backprop(self, use_jit): x = jax.numpy.array(self.x, dtype=jax.numpy.float64) coeffs = (5.2, 6.7) - f = jax.jit(self.f, static_argnums=(1, 2, 3, 4)) if use_jit else self.f + f = jax.jit(self.f, static_argnums=(1, 2, 3)) if use_jit else self.f out = f(x, coeffs) expected_out = self.expected(x, coeffs) diff --git a/tests/devices/test_default_clifford.py b/tests/devices/test_default_clifford.py index 8c5ebecef7b..29294dc4a2c 100644 --- a/tests/devices/test_default_clifford.py +++ b/tests/devices/test_default_clifford.py @@ -31,7 +31,7 @@ # pylint: disable=protected-access def test_applied_modifiers(): - """Test that defualt qubit has the `single_tape_support` and `simulator_tracking` + """Test that default qubit has the `single_tape_support` and `simulator_tracking` modifiers applied. """ dev = qml.device("default.clifford") diff --git a/tests/devices/test_default_gaussian.py b/tests/devices/test_default_gaussian.py index 46a8b3f9af2..cb5181cc63e 100644 --- a/tests/devices/test_default_gaussian.py +++ b/tests/devices/test_default_gaussian.py @@ -77,6 +77,7 @@ ] ) + H = np.array([[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]]) hbar = 2 diff --git a/tests/devices/test_default_mixed.py b/tests/devices/test_default_mixed.py index a6bf45665a4..bed20e39d3d 100644 --- a/tests/devices/test_default_mixed.py +++ b/tests/devices/test_default_mixed.py @@ -911,6 +911,7 @@ def test_identity_skipped(self, mocker): qml.probs(op=qml.Y(0)), qml.probs(op=qml.X(0) @ qml.Y(1)), qml.vn_entropy(wires=[0]), + qml.vn_entanglement_entropy(wires0=[1], wires1=[0]), qml.mutual_info(wires0=[1], wires1=[0]), qml.purity(wires=[1]), ], @@ -1202,18 +1203,21 @@ def circuit(): @pytest.mark.parametrize("prob", [0, 0.5, 1]) @pytest.mark.parametrize("nr_wires", [2, 3]) - def test_readout_vnentropy_and_mutualinfo(self, nr_wires, prob): - """Tests the output of qml.vn_entropy and qml.mutual_info is not affected by readout error""" + def test_readout_vnentropy_and_vnentanglemententropy_and_mutualinfo(self, nr_wires, prob): + """Tests the output of qml.vn_entropy, qml.vn_entanglement_entropy, and qml.mutual_info + are not affected by readout error""" dev = qml.device("default.mixed", wires=nr_wires, readout_prob=prob) @qml.qnode(dev) def circuit(): - return qml.vn_entropy(wires=0, log_base=2), qml.mutual_info( - wires0=[0], wires1=[1], log_base=2 + return ( + qml.vn_entropy(wires=0, log_base=2), + qml.vn_entanglement_entropy(wires0=[0], wires1=[1], log_base=2), + qml.mutual_info(wires0=[0], wires1=[1], log_base=2), ) res = circuit() - expected = np.array([0, 0]) + expected = np.array([0, 0, 0]) assert np.allclose(res, expected) @pytest.mark.parametrize("nr_wires", [2, 3]) diff --git a/tests/devices/test_default_qubit_legacy.py b/tests/devices/test_default_qubit_legacy.py deleted file mode 100644 index 9b67d18b5c6..00000000000 --- a/tests/devices/test_default_qubit_legacy.py +++ /dev/null @@ -1,2433 +0,0 @@ -# Copyright 2018-2020 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Unit tests for the :mod:`pennylane.devices.DefaultQubitLegacy` device. -""" -# pylint: disable=too-many-arguments,too-few-public-methods -# pylint: disable=protected-access,cell-var-from-loop -import cmath -import math -from importlib.metadata import version - -import pytest - -import pennylane as qml -from pennylane import numpy as np -from pennylane.devices.default_qubit_legacy import DefaultQubitLegacy, _get_slice -from pennylane.wires import WireError, Wires - -U = np.array( - [ - [0.83645892 - 0.40533293j, -0.20215326 + 0.30850569j], - [-0.23889780 - 0.28101519j, -0.88031770 - 0.29832709j], - ] -) - -U2 = np.array( - [ - [ - -0.07843244 - 3.57825948e-01j, - 0.71447295 - 5.38069384e-02j, - 0.20949966 + 6.59100734e-05j, - -0.50297381 + 2.35731613e-01j, - ], - [ - -0.26626692 + 4.53837083e-01j, - 0.27771991 - 2.40717436e-01j, - 0.41228017 - 1.30198687e-01j, - 0.01384490 - 6.33200028e-01j, - ], - [ - -0.69254712 - 2.56963068e-02j, - -0.15484858 + 6.57298384e-02j, - -0.53082141 + 7.18073414e-02j, - -0.41060450 - 1.89462315e-01j, - ], - [ - -0.09686189 - 3.15085273e-01j, - -0.53241387 - 1.99491763e-01j, - 0.56928622 + 3.97704398e-01j, - -0.28671074 - 6.01574497e-02j, - ], - ] -) - -U_toffoli = np.diag([1 for i in range(8)]) -U_toffoli[6:8, 6:8] = np.array([[0, 1], [1, 0]]) - -U_swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) - -U_cswap = np.array( - [ - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1], - ] -) - -THETA = np.linspace(0.11, 1, 3) -PHI = np.linspace(0.32, 1, 3) -VARPHI = np.linspace(0.02, 1, 3) - - -def test_analytic_deprecation(): - """Tests if the kwarg `analytic` is used and displays error message.""" - msg = "The analytic argument has been replaced by shots=None. " - msg += "Please use shots=None instead of analytic=True." - - with pytest.raises(qml.DeviceError, match=msg): - qml.device("default.qubit.legacy", wires=1, shots=1, analytic=True) - - -def test_dtype_errors(): - """Test that if an incorrect dtype is provided to the device then an error is raised.""" - with pytest.raises(qml.DeviceError, match="Real datatype must be a floating point type."): - qml.device("default.qubit.legacy", wires=1, r_dtype=np.complex128) - with pytest.raises( - qml.DeviceError, match="Complex datatype must be a complex floating point type." - ): - qml.device("default.qubit.legacy", wires=1, c_dtype=np.float64) - - -def test_custom_op_with_matrix(): - """Test that a dummy op with a matrix is supported.""" - - class DummyOp(qml.operation.Operation): - num_wires = 1 - - def compute_matrix(self): # pylint:disable=arguments-differ - return np.eye(2) - - with qml.queuing.AnnotatedQueue() as q: - DummyOp(0) - qml.state() - - tape = qml.tape.QuantumScript.from_queue(q) - dev = qml.device("default.qubit.legacy", wires=1) - assert qml.math.allclose(dev.execute(tape), np.array([1, 0])) - - -class TestApply: - """Tests that operations and inverses of certain operations are applied correctly or that the proper - errors are raised. - """ - - test_data_no_parameters = [ - (qml.PauliX, [1, 0], np.array([0, 1])), - (qml.PauliX, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), - (qml.PauliY, [1, 0], [0, 1j]), - (qml.PauliY, [1 / math.sqrt(2), 1 / math.sqrt(2)], [-1j / math.sqrt(2), 1j / math.sqrt(2)]), - (qml.PauliZ, [1, 0], [1, 0]), - (qml.PauliZ, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), -1 / math.sqrt(2)]), - (qml.S, [1, 0], [1, 0]), - (qml.S, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1j / math.sqrt(2)]), - (qml.T, [1, 0], [1, 0]), - ( - qml.T, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / math.sqrt(2), np.exp(1j * np.pi / 4) / math.sqrt(2)], - ), - (qml.Hadamard, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)]), - (qml.Hadamard, [1 / math.sqrt(2), -1 / math.sqrt(2)], [0, 1]), - (qml.Identity, [1, 0], [1, 0]), - (qml.Identity, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), - ] - - @pytest.mark.parametrize("operation,input,expected_output", test_data_no_parameters) - def test_apply_operation_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output - ): - """Tests that applying an operation yields the expected output state for single wire - operations that have no parameters.""" - - qubit_device_1_wire.target_device._state = np.array( - input, dtype=qubit_device_1_wire.C_DTYPE - ) - qubit_device_1_wire.apply([operation(wires=[0])]) - - assert np.allclose(qubit_device_1_wire._state, np.array(expected_output), atol=tol, rtol=0) - assert qubit_device_1_wire._state.dtype == qubit_device_1_wire.C_DTYPE - - test_data_two_wires_no_parameters = [ - (qml.CNOT, [1, 0, 0, 0], [1, 0, 0, 0]), - (qml.CNOT, [0, 0, 1, 0], [0, 0, 0, 1]), - ( - qml.CNOT, - [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], - [1 / math.sqrt(2), 0, 1 / math.sqrt(2), 0], - ), - (qml.SWAP, [1, 0, 0, 0], [1, 0, 0, 0]), - (qml.SWAP, [0, 0, 1, 0], [0, 1, 0, 0]), - ( - qml.SWAP, - [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0], - [1 / math.sqrt(2), -1 / math.sqrt(2), 0, 0], - ), - (qml.CZ, [1, 0, 0, 0], [1, 0, 0, 0]), - (qml.CZ, [0, 0, 0, 1], [0, 0, 0, -1]), - ( - qml.CZ, - [1 / math.sqrt(2), 0, 0, -1 / math.sqrt(2)], - [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], - ), - ] - - test_data_iswap = [ - (qml.ISWAP, [1, 0, 0, 0], [1, 0, 0, 0]), - (qml.ISWAP, [0, 0, 1, 0], [0, 1j, 0, 0]), - ( - qml.ISWAP, - [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0], - [1 / math.sqrt(2), -1j / math.sqrt(2), 0, 0], - ), - ] - - test_data_siswap = [ - (qml.SISWAP, [1, 0, 0, 0], [1, 0, 0, 0]), - (qml.SISWAP, [0, 1, 0, 0], [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0]), - ( - qml.SISWAP, - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [1 / math.sqrt(2), 0.5, 0.5 * 1j, 0], - ), - ] - - test_data_sqisw = [ - (qml.SQISW, [1, 0, 0, 0], [1, 0, 0, 0]), - (qml.SQISW, [0, 1, 0, 0], [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0]), - ( - qml.SQISW, - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [1 / math.sqrt(2), 0.5, 0.5 * 1j, 0], - ), - ] - - all_two_wires_no_parameters = ( - test_data_two_wires_no_parameters + test_data_iswap + test_data_siswap + test_data_sqisw - ) - - @pytest.mark.parametrize("operation,input,expected_output", all_two_wires_no_parameters) - def test_apply_operation_two_wires_no_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output - ): - """Tests that applying an operation yields the expected output state for two wire - operations that have no parameters.""" - - qubit_device_2_wires.target_device._state = np.array( - input, dtype=qubit_device_2_wires.C_DTYPE - ).reshape((2, 2)) - qubit_device_2_wires.apply([operation(wires=[0, 1])]) - - assert np.allclose( - qubit_device_2_wires._state.flatten(), np.array(expected_output), atol=tol, rtol=0 - ) - assert qubit_device_2_wires._state.dtype == qubit_device_2_wires.C_DTYPE - - test_data_three_wires_no_parameters = [ - (qml.CSWAP, [1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0]), - (qml.CSWAP, [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0]), - (qml.CSWAP, [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0]), - ] - - @pytest.mark.parametrize("operation,input,expected_output", test_data_three_wires_no_parameters) - def test_apply_operation_three_wires_no_parameters( - self, qubit_device_3_wires, tol, operation, input, expected_output - ): - """Tests that applying an operation yields the expected output state for three wire - operations that have no parameters.""" - - qubit_device_3_wires.target_device._state = np.array( - input, dtype=qubit_device_3_wires.C_DTYPE - ).reshape((2, 2, 2)) - qubit_device_3_wires.apply([operation(wires=[0, 1, 2])]) - - assert np.allclose( - qubit_device_3_wires._state.flatten(), np.array(expected_output), atol=tol, rtol=0 - ) - assert qubit_device_3_wires._state.dtype == qubit_device_3_wires.C_DTYPE - - @pytest.mark.parametrize( - "operation,expected_output,par", - [ - (qml.BasisState, [0, 0, 1, 0], [1, 0]), - (qml.BasisState, [0, 0, 1, 0], [1, 0]), - (qml.BasisState, [0, 0, 0, 1], [1, 1]), - (qml.StatePrep, [0, 0, 1, 0], [0, 0, 1, 0]), - (qml.StatePrep, [0, 0, 1, 0], [0, 0, 1, 0]), - (qml.StatePrep, [0, 0, 0, 1], [0, 0, 0, 1]), - ( - qml.StatePrep, - [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], - [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], - ), - ( - qml.StatePrep, - [1 / math.sqrt(3), 0, -1 / math.sqrt(3), 1 / math.sqrt(3)], - [1 / math.sqrt(3), 0, -1 / math.sqrt(3), 1 / math.sqrt(3)], - ), - ], - ) - def test_apply_operation_state_preparation( - self, qubit_device_2_wires, tol, operation, expected_output, par - ): - """Tests that applying an operation yields the expected output state for single wire - operations that have no parameters.""" - - par = np.array(par) - qubit_device_2_wires.reset() - qubit_device_2_wires.apply([operation(par, wires=[0, 1])]) - - assert np.allclose( - qubit_device_2_wires._state.flatten(), np.array(expected_output), atol=tol, rtol=0 - ) - - test_data_single_wire_with_parameters = [ - (qml.PhaseShift, [1, 0], [1, 0], [math.pi / 2]), - (qml.PhaseShift, [0, 1], [0, 1j], [math.pi / 2]), - ( - qml.PhaseShift, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / math.sqrt(2), 1 / 2 + 1j / 2], - [math.pi / 4], - ), - (qml.RX, [1, 0], [1 / math.sqrt(2), -1j * 1 / math.sqrt(2)], [math.pi / 2]), - (qml.RX, [1, 0], [0, -1j], [math.pi]), - ( - qml.RX, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / 2 - 1j / 2, 1 / 2 - 1j / 2], - [math.pi / 2], - ), - (qml.RY, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)], [math.pi / 2]), - (qml.RY, [1, 0], [0, 1], [math.pi]), - (qml.RY, [1 / math.sqrt(2), 1 / math.sqrt(2)], [0, 1], [math.pi / 2]), - (qml.RZ, [1, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0], [math.pi / 2]), - (qml.RZ, [0, 1], [0, 1j], [math.pi]), - ( - qml.RZ, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / 2 - 1j / 2, 1 / 2 + 1j / 2], - [math.pi / 2], - ), - (qml.MultiRZ, [1, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0], [math.pi / 2]), - (qml.MultiRZ, [0, 1], [0, 1j], [math.pi]), - ( - qml.MultiRZ, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / 2 - 1j / 2, 1 / 2 + 1j / 2], - [math.pi / 2], - ), - (qml.Rot, [1, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0], [math.pi / 2, 0, 0]), - (qml.Rot, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)], [0, math.pi / 2, 0]), - ( - qml.Rot, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / 2 - 1j / 2, 1 / 2 + 1j / 2], - [0, 0, math.pi / 2], - ), - ( - qml.Rot, - [1, 0], - [-1j / math.sqrt(2), -1 / math.sqrt(2)], - [math.pi / 2, -math.pi / 2, math.pi / 2], - ), - ( - qml.Rot, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / 2 + 1j / 2, -1 / 2 + 1j / 2], - [-math.pi / 2, math.pi, math.pi], - ), - ( - qml.QubitUnitary, - [1, 0], - [1j / math.sqrt(2), 1j / math.sqrt(2)], - [ - np.array( - [ - [1j / math.sqrt(2), 1j / math.sqrt(2)], - [1j / math.sqrt(2), -1j / math.sqrt(2)], - ] - ) - ], - ), - ( - qml.QubitUnitary, - [0, 1], - [1j / math.sqrt(2), -1j / math.sqrt(2)], - [ - np.array( - [ - [1j / math.sqrt(2), 1j / math.sqrt(2)], - [1j / math.sqrt(2), -1j / math.sqrt(2)], - ] - ) - ], - ), - ( - qml.QubitUnitary, - [1 / math.sqrt(2), -1 / math.sqrt(2)], - [0, 1j], - [ - np.array( - [ - [1j / math.sqrt(2), 1j / math.sqrt(2)], - [1j / math.sqrt(2), -1j / math.sqrt(2)], - ] - ) - ], - ), - (qml.DiagonalQubitUnitary, [1, 0], [-1, 0], [np.array([-1, 1])]), - ( - qml.DiagonalQubitUnitary, - [1 / math.sqrt(2), 1 / math.sqrt(2)], - [1 / math.sqrt(2), 1j / math.sqrt(2)], - [np.array([1, 1j])], - ), - ( - qml.DiagonalQubitUnitary, - [1 / 2, math.sqrt(3) / 4], - [cmath.exp(1j * 0.4) / 2, cmath.exp(1j * -0.4) * math.sqrt(3) / 4], - [np.array([cmath.exp(1j * 0.4), cmath.exp(1j * -0.4)])], - ), - (qml.SpecialUnitary, [1, 0], [0, 1j], [np.array([np.pi / 2, 0, 0])]), - (qml.SpecialUnitary, [1, 0], [0, -1], [np.array([0, np.pi / 2, 0])]), - (qml.SpecialUnitary, [1, 0], [1j, 0], [np.array([0, 0, np.pi / 2])]), - (qml.SpecialUnitary, [0.6, 0.8], [0.573 + 0.236j, 0.764 + 0.177j], [np.array([0.3, 0, 0])]), - ( - qml.SpecialUnitary, - [0.8j, -0.6], - [-0.808 + 0.049j, -0.411 + 0.419j], - [np.array([0.4, 0.2, 1.2])], - ), - ] - - @pytest.mark.parametrize( - "operation,input,expected_output,par", test_data_single_wire_with_parameters - ) - def test_apply_operation_single_wire_with_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output, par - ): - """Tests that applying an operation yields the expected output state for single wire - operations that have parameters.""" - - qubit_device_1_wire.target_device._state = np.array( - input, dtype=qubit_device_1_wire.C_DTYPE - ) - - qubit_device_1_wire.apply([operation(*par, wires=[0])]) - - assert np.allclose(qubit_device_1_wire._state, np.array(expected_output), atol=tol, rtol=0) - assert qubit_device_1_wire._state.dtype == qubit_device_1_wire.C_DTYPE - - test_data_two_wires_with_parameters = [ - (qml.CRX, [0, 1, 0, 0], [0, 1, 0, 0], [math.pi / 2]), - (qml.CRX, [0, 0, 0, 1], [0, 0, -1j, 0], [math.pi]), - ( - qml.CRX, - [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], - [0, 1 / math.sqrt(2), 1 / 2, -1j / 2], - [math.pi / 2], - ), - (qml.CRY, [0, 0, 0, 1], [0, 0, -1 / math.sqrt(2), 1 / math.sqrt(2)], [math.pi / 2]), - (qml.CRY, [0, 0, 0, 1], [0, 0, -1, 0], [math.pi]), - ( - qml.CRY, - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [math.pi / 2], - ), - (qml.CRZ, [0, 0, 0, 1], [0, 0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2)], [math.pi / 2]), - (qml.CRZ, [0, 0, 0, 1], [0, 0, 0, 1j], [math.pi]), - ( - qml.CRZ, - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [math.pi / 2], - ), - (qml.MultiRZ, [0, 0, 0, 1], [0, 0, 0, 1 / math.sqrt(2) - 1j / math.sqrt(2)], [math.pi / 2]), - (qml.MultiRZ, [0, 0, 1, 0], [0, 0, 1j, 0], [math.pi]), - ( - qml.MultiRZ, - [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], - [1 / 2 - 1j / 2, 1 / 2 + 1j / 2, 0, 0], - [math.pi / 2], - ), - ( - qml.CRot, - [0, 0, 0, 1], - [0, 0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2)], - [math.pi / 2, 0, 0], - ), - (qml.CRot, [0, 0, 0, 1], [0, 0, -1 / math.sqrt(2), 1 / math.sqrt(2)], [0, math.pi / 2, 0]), - ( - qml.CRot, - [0, 0, 1 / math.sqrt(2), 1 / math.sqrt(2)], - [0, 0, 1 / 2 - 1j / 2, 1 / 2 + 1j / 2], - [0, 0, math.pi / 2], - ), - ( - qml.CRot, - [0, 0, 0, 1], - [0, 0, 1 / math.sqrt(2), 1j / math.sqrt(2)], - [math.pi / 2, -math.pi / 2, math.pi / 2], - ), - ( - qml.CRot, - [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], - [0, 1 / math.sqrt(2), 0, -1 / 2 + 1j / 2], - [-math.pi / 2, math.pi, math.pi], - ), - ( - qml.QubitUnitary, - [1, 0, 0, 0], - [1, 0, 0, 0], - [ - np.array( - [ - [1, 0, 0, 0], - [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], - [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], - [0, 0, 0, 1], - ] - ) - ], - ), - ( - qml.QubitUnitary, - [0, 1, 0, 0], - [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], - [ - np.array( - [ - [1, 0, 0, 0], - [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], - [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], - [0, 0, 0, 1], - ] - ) - ], - ), - ( - qml.QubitUnitary, - [1 / 2, 1 / 2, -1 / 2, 1 / 2], - [1 / 2, 0, 1 / math.sqrt(2), 1 / 2], - [ - np.array( - [ - [1, 0, 0, 0], - [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], - [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], - [0, 0, 0, 1], - ] - ) - ], - ), - (qml.DiagonalQubitUnitary, [1, 0, 0, 0], [-1, 0, 0, 0], [np.array([-1, 1, 1, -1])]), - ( - qml.DiagonalQubitUnitary, - [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], - [1 / math.sqrt(2), 0, 0, -1 / math.sqrt(2)], - [np.array([1, 1, 1, -1])], - ), - (qml.DiagonalQubitUnitary, [0, 0, 1, 0], [0, 0, 1j, 0], [np.array([-1, 1j, 1j, -1])]), - ( - qml.SpecialUnitary, - [0.5, -0.5j, 0.5j, -0.5], - [0.382 - 0.322j, -0.322 - 0.382j, 0.322 + 0.382j, -0.382 + 0.322j], - [np.eye(15)[4] * 0.7], - ), - ( - qml.SpecialUnitary, - [0.6, 0, 0, -0.8], - [0.553, 0.312, -0.234, -0.737], - [np.eye(15)[10] * 0.4], - ), - ( - qml.SpecialUnitary, - [0, 0, 1, 0], - [0, -1j / math.sqrt(2), 1 / math.sqrt(2), 0], - [-np.eye(15)[4] * np.pi / 4], - ), # Like Ising XX - ( - qml.SpecialUnitary, - [0, 0, 1, 0], - [0, -1j / math.sqrt(2), 1 / math.sqrt(2), 0], - [-np.eye(15)[9] * np.pi / 4], - ), # Like Ising YY - ( - qml.SpecialUnitary, - [0, 0, 0, 1], - [0, 0, 0, 1 / math.sqrt(2) - 1j / math.sqrt(2)], - [-np.eye(15)[14] * np.pi / 4], - ), # Like Ising ZZ - ( - qml.SpecialUnitary, - [0.5, -0.5j, -0.5, -0.5], - [-0.616 - 0.018j, 0.316 + 0.243j, 0.427 + 0.437j, 0.294 + 0.043j], - [np.linspace(0.1, 3, 15)], - ), - (qml.IsingXX, [0, 0, 1, 0], [0, -1j / math.sqrt(2), 1 / math.sqrt(2), 0], [math.pi / 2]), - (qml.IsingXX, [0, 0, 0, 1], [-1j / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], [math.pi / 2]), - (qml.IsingXX, [1, 0, 0, 0], [1 / math.sqrt(2), 0, 0, -1j / math.sqrt(2)], [math.pi / 2]), - (qml.IsingYY, [0, 0, 1, 0], [0, -1j / math.sqrt(2), 1 / math.sqrt(2), 0], [math.pi / 2]), - (qml.IsingYY, [0, 0, 0, 1], [1j / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], [math.pi / 2]), - (qml.IsingYY, [1, 0, 0, 0], [1 / math.sqrt(2), 0, 0, 1j / math.sqrt(2)], [math.pi / 2]), - (qml.IsingZZ, [0, 0, 1, 0], [0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2), 0], [math.pi / 2]), - (qml.IsingZZ, [0, 0, 0, 1], [0, 0, 0, 1 / math.sqrt(2) - 1j / math.sqrt(2)], [math.pi / 2]), - (qml.IsingZZ, [1, 0, 0, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0, 0, 0], [math.pi / 2]), - ] - - @pytest.mark.parametrize( - "operation,input,expected_output,par", test_data_two_wires_with_parameters - ) - def test_apply_operation_two_wires_with_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output, par - ): - """Tests that applying an operation yields the expected output state for two wire - operations that have parameters.""" - - qubit_device_2_wires.target_device._state = np.array( - input, dtype=qubit_device_2_wires.C_DTYPE - ).reshape((2, 2)) - qubit_device_2_wires.apply([operation(*par, wires=[0, 1])]) - - assert np.allclose( - qubit_device_2_wires._state.flatten(), np.array(expected_output), atol=tol, rtol=0 - ) - assert qubit_device_2_wires._state.dtype == qubit_device_2_wires.C_DTYPE - - @pytest.mark.parametrize("wire", [None, [0], [1], [2]]) - @pytest.mark.parametrize( - "input_state", ([1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]) - ) - def test_apply_global_phase(self, qubit_device_3_wires, tol, wire, input_state): - """Tests that applying an operation yields the expected output state for single wire - operations that have parameters.""" - - qubit_device_3_wires.target_device._state = np.array( - input_state, dtype=qubit_device_3_wires.C_DTYPE - ) - phase = 0.234 - - qubit_device_3_wires.apply([qml.GlobalPhase(phase, wires=wire)]) - expected_output = np.array(input_state) * np.exp(-1j * phase) - - assert np.allclose(qubit_device_3_wires._state, np.array(expected_output), atol=tol, rtol=0) - if version("numpy") < "2.0.0": - assert qubit_device_3_wires._state.dtype == qubit_device_3_wires.C_DTYPE - - def test_apply_errors_qubit_state_vector(self, qubit_device_2_wires): - """Test that apply fails for incorrect state preparation, and > 2 qubit gates""" - with pytest.raises(ValueError, match="The state must be a vector of norm 1.0"): - qubit_device_2_wires.apply([qml.StatePrep(np.array([1, -1]), wires=[0])]) - - with pytest.raises(ValueError, match=r"State must be of length 4"): - p = np.array([1, 0, 1, 1, 0]) / np.sqrt(3) - qubit_device_2_wires.apply([qml.StatePrep(p, wires=[0, 1])]) - - with pytest.raises( - qml.DeviceError, - match="Operation StatePrep cannot be used after other Operations have already been applied " - "on a default.qubit.legacy device.", - ): - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( - [qml.RZ(0.5, wires=[0]), qml.StatePrep(np.array([0, 1, 0, 0]), wires=[0, 1])] - ) - - def test_apply_errors_basis_state(self, qubit_device_2_wires): - with np.printoptions(legacy="1.21"): - with pytest.raises( - ValueError, match=r"Basis state must only consist of 0s and 1s; got \[-0\.2, 4\.2\]" - ): - qubit_device_2_wires.apply([qml.BasisState(np.array([-0.2, 4.2]), wires=[0, 1])]) - - with pytest.raises( - ValueError, match=r"State must be of length 1; got length 2 \(state=\[0 1\]\)\." - ): - qubit_device_2_wires.apply([qml.BasisState(np.array([0, 1]), wires=[0])]) - - with pytest.raises( - qml.DeviceError, - match="Operation BasisState cannot be used after other Operations have already been applied " - "on a default.qubit.legacy device.", - ): - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( - [qml.RZ(0.5, wires=[0]), qml.BasisState(np.array([1, 1]), wires=[0, 1])] - ) - - -class TestExpval: - """Tests that expectation values are properly calculated or that the proper errors are raised.""" - - @pytest.mark.parametrize( - "operation,input,expected_output", - [ - (qml.PauliX, [1 / math.sqrt(2), 1 / math.sqrt(2)], 1), - (qml.PauliX, [1 / math.sqrt(2), -1 / math.sqrt(2)], -1), - (qml.PauliX, [1, 0], 0), - (qml.PauliY, [1 / math.sqrt(2), 1j / math.sqrt(2)], 1), - (qml.PauliY, [1 / math.sqrt(2), -1j / math.sqrt(2)], -1), - (qml.PauliY, [1, 0], 0), - (qml.PauliZ, [1, 0], 1), - (qml.PauliZ, [0, 1], -1), - (qml.PauliZ, [1 / math.sqrt(2), 1 / math.sqrt(2)], 0), - (qml.Hadamard, [1, 0], 1 / math.sqrt(2)), - (qml.Hadamard, [0, 1], -1 / math.sqrt(2)), - (qml.Hadamard, [1 / math.sqrt(2), 1 / math.sqrt(2)], 1 / math.sqrt(2)), - (qml.Identity, [1, 0], 1), - (qml.Identity, [0, 1], 1), - (qml.Identity, [1 / math.sqrt(2), -1 / math.sqrt(2)], 1), - ], - ) - def test_expval_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output - ): - """Tests that expectation values are properly calculated for single-wire observables without parameters.""" - - obs = operation(wires=[0]) - - qubit_device_1_wire.reset() - qubit_device_1_wire.apply( - [qml.StatePrep(np.array(input), wires=[0])], obs.diagonalizing_gates() - ) - res = qubit_device_1_wire.expval(obs) - - assert np.isclose(res, expected_output, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "operation,input,expected_output,par", - [ - (qml.Hermitian, [1, 0], 1, [[1, 1j], [-1j, 1]]), - (qml.Hermitian, [0, 1], 1, [[1, 1j], [-1j, 1]]), - (qml.Hermitian, [1 / math.sqrt(2), -1 / math.sqrt(2)], 1, [[1, 1j], [-1j, 1]]), - ], - ) - def test_expval_single_wire_with_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output, par - ): - """Tests that expectation values are properly calculated for single-wire observables with parameters.""" - - obs = operation(np.array(par), wires=[0]) - - qubit_device_1_wire.reset() - qubit_device_1_wire.apply( - [qml.StatePrep(np.array(input), wires=[0])], obs.diagonalizing_gates() - ) - res = qubit_device_1_wire.expval(obs) - - assert np.isclose(res, expected_output, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "operation,input,expected_output,par", - [ - ( - qml.Hermitian, - [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], - 5 / 3, - [[1, 1j, 0, 1], [-1j, 1, 0, 0], [0, 0, 1, -1j], [1, 0, 1j, 1]], - ), - ( - qml.Hermitian, - [0, 0, 0, 1], - 0, - [[0, 1j, 0, 0], [-1j, 0, 0, 0], [0, 0, 0, -1j], [0, 0, 1j, 0]], - ), - ( - qml.Hermitian, - [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0], - 1, - [[1, 1j, 0, 0], [-1j, 1, 0, 0], [0, 0, 1, -1j], [0, 0, 1j, 1]], - ), - ( - qml.Hermitian, - [1 / math.sqrt(3), -1 / math.sqrt(3), 1 / math.sqrt(6), 1 / math.sqrt(6)], - 1, - [[1, 1j, 0, 0.5j], [-1j, 1, 0, 0], [0, 0, 1, -1j], [-0.5j, 0, 1j, 1]], - ), - ( - qml.Hermitian, - [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], - 1, - [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], - ), - ( - qml.Hermitian, - [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], - -1, - [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], - ), - ], - ) - def test_expval_two_wires_with_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output, par - ): - """Tests that expectation values are properly calculated for two-wire observables with parameters.""" - - obs = operation(np.array(par), wires=[0, 1]) - - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( - [qml.StatePrep(np.array(input), wires=[0, 1])], obs.diagonalizing_gates() - ) - res = qubit_device_2_wires.expval(obs) - - assert np.isclose(res, expected_output, atol=tol, rtol=0) - - def test_expval_estimate(self): - """Test that the expectation value is not analytically calculated""" - - dev = qml.device("default.qubit.legacy", wires=1, shots=3) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(): - return qml.expval(qml.PauliX(0)) - - expval = circuit() - - # With 3 samples we are guaranteed to see a difference between - # an estimated variance an an analytically calculated one - assert expval != 0.0 - - -class TestVar: - """Tests that variances are properly calculated.""" - - @pytest.mark.parametrize( - "operation,input,expected_output", - [ - (qml.PauliX, [1 / math.sqrt(2), 1 / math.sqrt(2)], 0), - (qml.PauliX, [1 / math.sqrt(2), -1 / math.sqrt(2)], 0), - (qml.PauliX, [1, 0], 1), - (qml.PauliY, [1 / math.sqrt(2), 1j / math.sqrt(2)], 0), - (qml.PauliY, [1 / math.sqrt(2), -1j / math.sqrt(2)], 0), - (qml.PauliY, [1, 0], 1), - (qml.PauliZ, [1, 0], 0), - (qml.PauliZ, [0, 1], 0), - (qml.PauliZ, [1 / math.sqrt(2), 1 / math.sqrt(2)], 1), - (qml.Hadamard, [1, 0], 1 / 2), - (qml.Hadamard, [0, 1], 1 / 2), - (qml.Hadamard, [1 / math.sqrt(2), 1 / math.sqrt(2)], 1 / 2), - (qml.Identity, [1, 0], 0), - (qml.Identity, [0, 1], 0), - (qml.Identity, [1 / math.sqrt(2), -1 / math.sqrt(2)], 0), - ], - ) - def test_var_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output - ): - """Tests that variances are properly calculated for single-wire observables without parameters.""" - - obs = operation(wires=[0]) - - qubit_device_1_wire.reset() - qubit_device_1_wire.apply( - [qml.StatePrep(np.array(input), wires=[0])], obs.diagonalizing_gates() - ) - res = qubit_device_1_wire.var(obs) - - assert np.isclose(res, expected_output, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "operation,input,expected_output,par", - [ - (qml.Hermitian, [1, 0], 1, [[1, 1j], [-1j, 1]]), - (qml.Hermitian, [0, 1], 1, [[1, 1j], [-1j, 1]]), - (qml.Hermitian, [1 / math.sqrt(2), -1 / math.sqrt(2)], 1, [[1, 1j], [-1j, 1]]), - ], - ) - def test_var_single_wire_with_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output, par - ): - """Tests that variances are properly calculated for single-wire observables with parameters.""" - - obs = operation(np.array(par), wires=[0]) - - qubit_device_1_wire.reset() - qubit_device_1_wire.apply( - [qml.StatePrep(np.array(input), wires=[0])], obs.diagonalizing_gates() - ) - res = qubit_device_1_wire.var(obs) - - assert np.isclose(res, expected_output, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "operation,input,expected_output,par", - [ - ( - qml.Hermitian, - [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], - 11 / 9, - [[1, 1j, 0, 1], [-1j, 1, 0, 0], [0, 0, 1, -1j], [1, 0, 1j, 1]], - ), - ( - qml.Hermitian, - [0, 0, 0, 1], - 1, - [[0, 1j, 0, 0], [-1j, 0, 0, 0], [0, 0, 0, -1j], [0, 0, 1j, 0]], - ), - ( - qml.Hermitian, - [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0], - 1, - [[1, 1j, 0, 0], [-1j, 1, 0, 0], [0, 0, 1, -1j], [0, 0, 1j, 1]], - ), - ( - qml.Hermitian, - [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], - 0, - [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], - ), - ( - qml.Hermitian, - [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], - 0, - [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], - ), - ], - ) - def test_var_two_wires_with_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output, par - ): - """Tests that variances are properly calculated for two-wire observables with parameters.""" - - obs = operation(np.array(par), wires=[0, 1]) - - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( - [qml.StatePrep(np.array(input), wires=[0, 1])], obs.diagonalizing_gates() - ) - res = qubit_device_2_wires.var(obs) - - assert np.isclose(res, expected_output, atol=tol, rtol=0) - - def test_var_estimate(self): - """Test that the variance is not analytically calculated""" - - dev = qml.device("default.qubit.legacy", wires=1, shots=3) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(): - return qml.var(qml.PauliX(0)) - - var = circuit() - - # With 3 samples we are guaranteed to see a difference between - # an estimated variance and an analytically calculated one - assert var != 1.0 - - -class TestSample: - """Tests that samples are properly calculated.""" - - def test_sample_dimensions(self): - """Tests if the samples returned by the sample function have - the correct dimensions - """ - - # Explicitly resetting is necessary as the internal - # state is set to None in __init__ and only properly - # initialized during reset - dev = qml.device("default.qubit.legacy", wires=2, shots=1000) - - dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) - - dev.target_device.shots = 10 - dev.target_device._wires_measured = {0} - dev.target_device._samples = dev.generate_samples() - s1 = dev.sample(qml.PauliZ(wires=[0])) - assert np.array_equal(s1.shape, (10,)) - - dev.reset() - dev.target_device.shots = 12 - dev.target_device._wires_measured = {1} - dev.target_device._samples = dev.generate_samples() - s2 = dev.sample(qml.PauliZ(wires=[1])) - assert np.array_equal(s2.shape, (12,)) - - dev.reset() - dev.target_device.shots = 17 - dev.target_device._wires_measured = {0, 1} - dev.target_device._samples = dev.generate_samples() - s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) - assert np.array_equal(s3.shape, (17,)) - - def test_sample_values(self, tol): - """Tests if the samples returned by sample have - the correct values - """ - - # Explicitly resetting is necessary as the internal - # state is set to None in __init__ and only properly - # initialized during reset - dev = qml.device("default.qubit.legacy", wires=2, shots=1000) - - dev.apply([qml.RX(1.5708, wires=[0])]) - dev.target_device._wires_measured = {0} - dev.target_device._samples = dev.generate_samples() - - s1 = dev.sample(qml.PauliZ(0)) - - # s1 should only contain 1 and -1, which is guaranteed if - # they square to 1 - assert np.allclose(s1**2, 1, atol=tol, rtol=0) - - -class TestDefaultQubitLegacyIntegration: - """Integration tests for default.qubit. This test ensures it integrates - properly with the PennyLane interface, in particular QNode.""" - - def test_defines_correct_capabilities(self): - """Test that the device defines the right capabilities""" - - dev = qml.device("default.qubit.legacy", wires=1) - cap = dev.capabilities() - capabilities = { - "model": "qubit", - "supports_finite_shots": True, - "supports_tensor_observables": True, - "returns_probs": True, - "returns_state": True, - "supports_inverse_operations": True, - "supports_analytic_computation": True, - "supports_broadcasting": True, - "passthru_devices": {}, - } - assert cap == capabilities - - @pytest.mark.parametrize("r_dtype", [np.float32, np.float64]) - def test_qubit_circuit(self, qubit_device_1_wire, r_dtype, tol): - """Test that the default qubit plugin provides correct result for a simple circuit""" - - p = 0.543 - - dev = qubit_device_1_wire - dev.target_device.R_DTYPE = r_dtype - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(x): - qml.RX(x, wires=0) - return qml.expval(qml.PauliY(0)) - - expected = -np.sin(p) - - res = circuit(p) - assert np.isclose(res, expected, atol=tol, rtol=0) - assert res.dtype == r_dtype # pylint:disable=no-member - - def test_qubit_identity(self, qubit_device_1_wire, tol): - """Test that the default qubit plugin provides correct result for the Identity expectation""" - - p = 0.543 - - @qml.qnode(qubit_device_1_wire) - def circuit(x): - """Test quantum function""" - qml.RX(x, wires=0) - return qml.expval(qml.Identity(0)) - - assert np.isclose(circuit(p), 1, atol=tol, rtol=0) - - def test_nonzero_shots(self, tol): - """Test that the default qubit plugin provides correct result for high shot number""" - - shots = 10**5 - dev = qml.device("default.qubit.legacy", wires=1, shots=shots) - - p = 0.543 - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(x): - """Test quantum function""" - qml.RX(x, wires=0) - return qml.expval(qml.PauliY(0)) - - runs = [] - for _ in range(100): - runs.append(circuit(p)) - - assert np.isclose(np.mean(runs), -np.sin(p), atol=tol, rtol=0) - - @pytest.mark.parametrize( - "name,state,expected_output", - [ - ("PauliX", [1 / math.sqrt(2), 1 / math.sqrt(2)], 1), - ("PauliX", [1 / math.sqrt(2), -1 / math.sqrt(2)], -1), - ("PauliX", [1, 0], 0), - ("PauliY", [1 / math.sqrt(2), 1j / math.sqrt(2)], 1), - ("PauliY", [1 / math.sqrt(2), -1j / math.sqrt(2)], -1), - ("PauliY", [1, 0], 0), - ("PauliZ", [1, 0], 1), - ("PauliZ", [0, 1], -1), - ("PauliZ", [1 / math.sqrt(2), 1 / math.sqrt(2)], 0), - ("Hadamard", [1, 0], 1 / math.sqrt(2)), - ("Hadamard", [0, 1], -1 / math.sqrt(2)), - ("Hadamard", [1 / math.sqrt(2), 1 / math.sqrt(2)], 1 / math.sqrt(2)), - ], - ) - def test_supported_observable_single_wire_no_parameters( - self, qubit_device_1_wire, tol, name, state, expected_output - ): - """Tests supported observables on single wires without parameters.""" - - obs = getattr(qml.ops, name) - - assert qubit_device_1_wire.supports_observable(name) - - @qml.qnode(qubit_device_1_wire) - def circuit(): - qml.StatePrep(np.array(state), wires=[0]) - return qml.expval(obs(wires=[0])) - - assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "name,state,expected_output,par", - [ - ("Identity", [1, 0], 1, []), - ("Identity", [0, 1], 1, []), - ("Identity", [1 / math.sqrt(2), -1 / math.sqrt(2)], 1, []), - ("Hermitian", [1, 0], 1, [np.array([[1, 1j], [-1j, 1]])]), - ("Hermitian", [0, 1], 1, [np.array([[1, 1j], [-1j, 1]])]), - ( - "Hermitian", - [1 / math.sqrt(2), -1 / math.sqrt(2)], - 1, - [np.array([[1, 1j], [-1j, 1]])], - ), - ], - ) - def test_supported_observable_single_wire_with_parameters( - self, qubit_device_1_wire, tol, name, state, expected_output, par - ): - """Tests supported observables on single wires with parameters.""" - - obs = getattr(qml.ops, name) - - assert qubit_device_1_wire.supports_observable(name) - - @qml.qnode(qubit_device_1_wire) - def circuit(): - qml.StatePrep(np.array(state), wires=[0]) - return qml.expval(obs(*par, wires=[0])) - - assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "name,state,expected_output,par", - [ - ( - "Hermitian", - [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], - 5 / 3, - [np.array([[1, 1j, 0, 1], [-1j, 1, 0, 0], [0, 0, 1, -1j], [1, 0, 1j, 1]])], - ), - ( - "Hermitian", - [0, 0, 0, 1], - 0, - [np.array([[0, 1j, 0, 0], [-1j, 0, 0, 0], [0, 0, 0, -1j], [0, 0, 1j, 0]])], - ), - ( - "Hermitian", - [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0], - 1, - [np.array([[1, 1j, 0, 0], [-1j, 1, 0, 0], [0, 0, 1, -1j], [0, 0, 1j, 1]])], - ), - ( - "Hermitian", - [1 / math.sqrt(3), -1 / math.sqrt(3), 1 / math.sqrt(6), 1 / math.sqrt(6)], - 1, - [np.array([[1, 1j, 0, 0.5j], [-1j, 1, 0, 0], [0, 0, 1, -1j], [-0.5j, 0, 1j, 1]])], - ), - ( - "Hermitian", - [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], - 1, - [np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])], - ), - ( - "Hermitian", - [0, 1 / math.sqrt(2), -1 / math.sqrt(2), 0], - -1, - [np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])], - ), - ], - ) - def test_supported_observable_two_wires_with_parameters( - self, qubit_device_2_wires, tol, name, state, expected_output, par - ): - """Tests supported observables on two wires with parameters.""" - - obs = getattr(qml.ops, name) - - assert qubit_device_2_wires.supports_observable(name) - - @qml.qnode(qubit_device_2_wires) - def circuit(): - qml.StatePrep(np.array(state), wires=[0, 1]) - return qml.expval(obs(*par, wires=[0, 1])) - - assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - - def test_multi_samples_return_correlated_results(self): - """Tests if the samples returned by the sample function have - the correct dimensions - """ - - dev = qml.device("default.qubit.legacy", wires=2, shots=1000) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(): - qml.Hadamard(0) - qml.CNOT(wires=[0, 1]) - return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) - - outcomes = circuit() - - assert np.array_equal(outcomes[0], outcomes[1]) - - @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) - def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( - self, num_wires - ): - """Tests if the samples returned by the sample function have - the correct dimensions - """ - - dev = qml.device("default.qubit.legacy", wires=num_wires, shots=1000) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(): - qml.Hadamard(0) - qml.CNOT(wires=[0, 1]) - return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) - - outcomes = circuit() - - assert np.array_equal(outcomes[0], outcomes[1]) - - -# pylint: disable=unused-argument -@pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) -class TestTensorExpval: - """Test tensor expectation values""" - - def test_paulix_pauliy(self, theta, phi, varphi, tol): - """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - dev.reset() - - obs = qml.PauliX(0) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) - - expected = np.sin(theta) * np.sin(phi) * np.sin(varphi) - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_pauliz_identity(self, theta, phi, varphi, tol): - """Test that a tensor product involving PauliZ and Identity works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - dev.reset() - - obs = qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) - - expected = np.cos(varphi) * np.cos(phi) - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_pauliz_hadamard(self, theta, phi, varphi, tol): - """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) - - dev.reset() - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) - - expected = -(np.cos(varphi) * np.sin(phi) + np.sin(varphi) * np.cos(theta)) / np.sqrt(2) - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_hermitian(self, theta, phi, varphi, tol): - """Test that a tensor product involving qml.Hermitian works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - dev.reset() - - A = np.array( - [ - [-6, 2 + 1j, -3, -5 + 2j], - [2 - 1j, 0, 2 - 1j, -5 + 4j], - [-3, 2 + 1j, 0, -4 + 3j], - [-5 - 2j, -5 - 4j, -4 - 3j, -6], - ] - ) - - obs = qml.PauliZ(0) @ qml.Hermitian(A, wires=[1, 2]) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) - - expected = 0.5 * ( - -6 * np.cos(theta) * (np.cos(varphi) + 1) - - 2 * np.sin(varphi) * (np.cos(theta) + np.sin(phi) - 2 * np.cos(phi)) - + 3 * np.cos(varphi) * np.sin(phi) - + np.sin(phi) - ) - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_hermitian_hermitian(self, theta, phi, varphi, tol): - """Test that a tensor product involving two Hermitian matrices works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - - A1 = np.array([[1, 2], [2, 4]]) - - A2 = np.array( - [ - [-6, 2 + 1j, -3, -5 + 2j], - [2 - 1j, 0, 2 - 1j, -5 + 4j], - [-3, 2 + 1j, 0, -4 + 3j], - [-5 - 2j, -5 - 4j, -4 - 3j, -6], - ] - ) - - obs = qml.Hermitian(A1, wires=[0]) @ qml.Hermitian(A2, wires=[1, 2]) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) - - expected = 0.25 * ( - -30 - + 4 * np.cos(phi) * np.sin(theta) - + 3 * np.cos(varphi) * (-10 + 4 * np.cos(phi) * np.sin(theta) - 3 * np.sin(phi)) - - 3 * np.sin(phi) - - 2 - * (5 + np.cos(phi) * (6 + 4 * np.sin(theta)) + (-3 + 8 * np.sin(theta)) * np.sin(phi)) - * np.sin(varphi) - + np.cos(theta) - * ( - 18 - + 5 * np.sin(phi) - + 3 * np.cos(varphi) * (6 + 5 * np.sin(phi)) - + 2 * (3 + 10 * np.cos(phi) - 5 * np.sin(phi)) * np.sin(varphi) - ) - ) - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_hermitian_identity_expectation(self, theta, phi, varphi, tol): - """Test that a tensor product involving an Hermitian matrix and the identity works correctly""" - dev = qml.device("default.qubit.legacy", wires=2) - - A = np.array( - [[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]] - ) - - obs = qml.Hermitian(A, wires=[0]) @ qml.Identity(wires=[1]) - - dev.apply( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) - - a = A[0, 0] - re_b = A[0, 1].real - d = A[1, 1] - expected = ((a - d) * np.cos(theta) + 2 * re_b * np.sin(theta) * np.sin(phi) + a + d) / 2 - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_hermitian_two_wires_identity_expectation(self, theta, phi, varphi, tol): - """Test that a tensor product involving an Hermitian matrix for two wires and the identity works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - - A = np.array( - [[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]] - ) - Identity = np.array([[1, 0], [0, 1]]) - H = np.kron(np.kron(Identity, Identity), A) - obs = qml.Hermitian(H, wires=[2, 1, 0]) - - dev.apply( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - obs.diagonalizing_gates(), - ) - res = dev.expval(obs) - - a = A[0, 0] - re_b = A[0, 1].real - d = A[1, 1] - - expected = ((a - d) * np.cos(theta) + 2 * re_b * np.sin(theta) * np.sin(phi) + a + d) / 2 - assert np.allclose(res, expected, atol=tol, rtol=0) - - -@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) -class TestTensorVar: - """Tests for variance of tensor observables""" - - def test_paulix_pauliy(self, theta, phi, varphi, tol): - """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - - obs = qml.PauliX(0) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.var(obs) - - expected = ( - 8 * np.sin(theta) ** 2 * np.cos(2 * varphi) * np.sin(phi) ** 2 - - np.cos(2 * (theta - phi)) - - np.cos(2 * (theta + phi)) - + 2 * np.cos(2 * theta) - + 2 * np.cos(2 * phi) - + 14 - ) / 16 - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_pauliz_hadamard(self, theta, phi, varphi, tol): - """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) - - dev.reset() - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.var(obs) - - expected = ( - 3 - + np.cos(2 * phi) * np.cos(varphi) ** 2 - - np.cos(2 * theta) * np.sin(varphi) ** 2 - - 2 * np.cos(theta) * np.sin(phi) * np.sin(2 * varphi) - ) / 4 - - assert np.allclose(res, expected, atol=tol, rtol=0) - - def test_hermitian(self, theta, phi, varphi, tol): - """Test that a tensor product involving qml.Hermitian works correctly""" - dev = qml.device("default.qubit.legacy", wires=3) - - A = np.array( - [ - [-6, 2 + 1j, -3, -5 + 2j], - [2 - 1j, 0, 2 - 1j, -5 + 4j], - [-3, 2 + 1j, 0, -4 + 3j], - [-5 - 2j, -5 - 4j, -4 - 3j, -6], - ] - ) - - obs = qml.PauliZ(0) @ qml.Hermitian(A, wires=[1, 2]) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - res = dev.var(obs) - - expected = ( - 1057 - - np.cos(2 * phi) - + 12 * (27 + np.cos(2 * phi)) * np.cos(varphi) - - 2 * np.cos(2 * varphi) * np.sin(phi) * (16 * np.cos(phi) + 21 * np.sin(phi)) - + 16 * np.sin(2 * phi) - - 8 * (-17 + np.cos(2 * phi) + 2 * np.sin(2 * phi)) * np.sin(varphi) - - 8 * np.cos(2 * theta) * (3 + 3 * np.cos(varphi) + np.sin(varphi)) ** 2 - - 24 * np.cos(phi) * (np.cos(phi) + 2 * np.sin(phi)) * np.sin(2 * varphi) - - 8 - * np.cos(theta) - * ( - 4 - * np.cos(phi) - * ( - 4 - + 8 * np.cos(varphi) - + np.cos(2 * varphi) - - (1 + 6 * np.cos(varphi)) * np.sin(varphi) - ) - + np.sin(phi) - * ( - 15 - + 8 * np.cos(varphi) - - 11 * np.cos(2 * varphi) - + 42 * np.sin(varphi) - + 3 * np.sin(2 * varphi) - ) - ) - ) / 16 - - assert np.allclose(res, expected, atol=tol, rtol=0) - - -@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) -class TestTensorSample: - """Test tensor expectation values""" - - def test_paulix_pauliy(self, theta, phi, varphi, tol_stochastic): - """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qml.device("default.qubit.legacy", wires=3, shots=int(1e6)) - - obs = qml.PauliX(0) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - dev.target_device._wires_measured = {0, 1, 2} - dev.target_device._samples = dev.generate_samples() - dev.sample(obs) - - s1 = obs.eigvals() - p = dev.probability(wires=dev.map_wires(obs.wires)) - - # s1 should only contain 1 and -1 - assert np.allclose(s1**2, 1, atol=tol_stochastic, rtol=0) - - mean = s1 @ p - expected = np.sin(theta) * np.sin(phi) * np.sin(varphi) - assert np.allclose(mean, expected, atol=tol_stochastic, rtol=0) - - var = (s1**2) @ p - (s1 @ p).real ** 2 - expected = ( - 8 * np.sin(theta) ** 2 * np.cos(2 * varphi) * np.sin(phi) ** 2 - - np.cos(2 * (theta - phi)) - - np.cos(2 * (theta + phi)) - + 2 * np.cos(2 * theta) - + 2 * np.cos(2 * phi) - + 14 - ) / 16 - assert np.allclose(var, expected, atol=tol_stochastic, rtol=0) - - def test_pauliz_hadamard(self, theta, phi, varphi, tol_stochastic): - """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" - dev = qml.device("default.qubit.legacy", wires=3, shots=int(1e6)) - obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - dev.target_device._wires_measured = {0, 1, 2} - dev.target_device._samples = dev.generate_samples() - dev.sample(obs) - - s1 = obs.eigvals() - p = dev.marginal_prob(dev.probability(), wires=obs.wires) - - # s1 should only contain 1 and -1 - assert np.allclose(s1**2, 1, atol=tol_stochastic, rtol=0) - - mean = s1 @ p - expected = -(np.cos(varphi) * np.sin(phi) + np.sin(varphi) * np.cos(theta)) / np.sqrt(2) - assert np.allclose(mean, expected, atol=tol_stochastic, rtol=0) - - var = (s1**2) @ p - (s1 @ p).real ** 2 - expected = ( - 3 - + np.cos(2 * phi) * np.cos(varphi) ** 2 - - np.cos(2 * theta) * np.sin(varphi) ** 2 - - 2 * np.cos(theta) * np.sin(phi) * np.sin(2 * varphi) - ) / 4 - assert np.allclose(var, expected, atol=tol_stochastic, rtol=0) - - def test_hermitian(self, theta, phi, varphi, tol_stochastic): - """Test that a tensor product involving qml.Hermitian works correctly""" - dev = qml.device("default.qubit.legacy", wires=3, shots=int(1e6)) - - A = 0.1 * np.array( - [ - [-6, 2 + 1j, -3, -5 + 2j], - [2 - 1j, 0, 2 - 1j, -5 + 4j], - [-3, 2 + 1j, 0, -4 + 3j], - [-5 - 2j, -5 - 4j, -4 - 3j, -6], - ] - ) - - obs = qml.PauliZ(0) @ qml.Hermitian(A, wires=[1, 2]) - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - obs.diagonalizing_gates(), - ) - - dev.target_device._wires_measured = {0, 1, 2} - dev.target_device._samples = dev.generate_samples() - dev.sample(obs) - - s1 = obs.eigvals() - p = dev.marginal_prob(dev.probability(), wires=obs.wires) - - # s1 should only contain the eigenvalues of - # the hermitian matrix tensor product Z - Z = np.diag([1, -1]) - eigvals = np.linalg.eigvalsh(np.kron(Z, A)) - assert set(np.round(s1, 8).tolist()).issubset(set(np.round(eigvals, 8).tolist())) - - mean = s1 @ p - expected = ( - 0.1 - * 0.5 - * ( - -6 * np.cos(theta) * (np.cos(varphi) + 1) - - 2 * np.sin(varphi) * (np.cos(theta) + np.sin(phi) - 2 * np.cos(phi)) - + 3 * np.cos(varphi) * np.sin(phi) - + np.sin(phi) - ) - ) - assert np.allclose(mean, expected, atol=tol_stochastic, rtol=0) - - var = (s1**2) @ p - (s1 @ p).real ** 2 - expected = ( - 0.01 - * ( - 1057 - - np.cos(2 * phi) - + 12 * (27 + np.cos(2 * phi)) * np.cos(varphi) - - 2 * np.cos(2 * varphi) * np.sin(phi) * (16 * np.cos(phi) + 21 * np.sin(phi)) - + 16 * np.sin(2 * phi) - - 8 * (-17 + np.cos(2 * phi) + 2 * np.sin(2 * phi)) * np.sin(varphi) - - 8 * np.cos(2 * theta) * (3 + 3 * np.cos(varphi) + np.sin(varphi)) ** 2 - - 24 * np.cos(phi) * (np.cos(phi) + 2 * np.sin(phi)) * np.sin(2 * varphi) - - 8 - * np.cos(theta) - * ( - 4 - * np.cos(phi) - * ( - 4 - + 8 * np.cos(varphi) - + np.cos(2 * varphi) - - (1 + 6 * np.cos(varphi)) * np.sin(varphi) - ) - + np.sin(phi) - * ( - 15 - + 8 * np.cos(varphi) - - 11 * np.cos(2 * varphi) - + 42 * np.sin(varphi) - + 3 * np.sin(2 * varphi) - ) - ) - ) - / 16 - ) - assert np.allclose(var, expected, atol=tol_stochastic, rtol=0) - - -@pytest.mark.parametrize( - "r_dtype,c_dtype", [(np.float32, np.complex64), (np.float64, np.complex128)] -) -class TestDtypePreserved: - """Test that the user-defined dtype of the device is preserved for QNode - evaluation""" - - @pytest.mark.parametrize( - "op", - [ - qml.SingleExcitation, - qml.SingleExcitationPlus, - qml.SingleExcitationMinus, - qml.DoubleExcitation, - qml.DoubleExcitationPlus, - qml.DoubleExcitationMinus, - qml.OrbitalRotation, - qml.FermionicSWAP, - qml.QubitSum, - qml.QubitCarry, - ], - ) - def test_state_dtype_after_op(self, r_dtype, c_dtype, op): - """Test that the default qubit plugin preserves data types of states when an operation is - applied. As TestApply class check most of operators, we here only check some subtle - examples. - """ - - dev = qml.device("default.qubit.legacy", wires=4, r_dtype=r_dtype, c_dtype=c_dtype) - - n_wires = op.num_wires - n_params = op.num_params - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(): - if n_params == 0: - op(wires=range(n_wires)) - elif n_params == 1: - op(0.543, wires=range(n_wires)) - else: - op([0.543] * n_params, wires=range(n_wires)) - return qml.state() - - res = circuit() - assert res.dtype == c_dtype # pylint:disable=no-member - - @pytest.mark.parametrize( - "measurement", - [ - qml.expval(qml.PauliY(0)), - qml.var(qml.PauliY(0)), - qml.probs(wires=[1]), - qml.probs(wires=[2, 0]), - ], - ) - def test_measurement_real_dtype(self, r_dtype, c_dtype, measurement): - """Test that the default qubit plugin provides correct result for a simple circuit""" - p = 0.543 - - dev = qml.device("default.qubit.legacy", wires=3, r_dtype=r_dtype, c_dtype=c_dtype) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(x): - qml.RX(x, wires=0) - return qml.apply(measurement) - - res = circuit(p) - assert res.dtype == r_dtype - - @pytest.mark.parametrize( - "measurement", - [qml.state(), qml.density_matrix(wires=[1]), qml.density_matrix(wires=[2, 0])], - ) - def test_measurement_complex_dtype(self, r_dtype, c_dtype, measurement): - """Test that the default qubit plugin provides correct result for a simple circuit""" - p = 0.543 - - dev = qml.device("default.qubit.legacy", wires=3, r_dtype=r_dtype, c_dtype=c_dtype) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(x): - qml.RX(x, wires=0) - return qml.apply(measurement) - - res = circuit(p) - assert res.dtype == c_dtype - - -class TestProbabilityIntegration: - """Test probability method for when analytic is True/False""" - - # pylint: disable=unused-argument - def mock_analytic_counter(self, wires=None): - self.analytic_counter += 1 - return np.array([1, 0, 0, 0], dtype=float) - - @pytest.mark.parametrize("x", [[0.2, 0.5], [0.4, 0.9], [0.8, 0.3]]) - def test_probability(self, x, tol): - """Test that the probability function works for finite and infinite shots""" - dev = qml.device("default.qubit.legacy", wires=2, shots=1000) - dev_analytic = qml.device("default.qubit.legacy", wires=2, shots=None) - - def circuit(x): - qml.RX(x[0], wires=0) - qml.RY(x[1], wires=0) - qml.CNOT(wires=[0, 1]) - return qml.probs(wires=[0, 1]) - - prob = qml.QNode(circuit, dev) - prob_analytic = qml.QNode(circuit, dev_analytic) - - assert np.isclose(prob(x).sum(), 1, atol=tol, rtol=0) - assert np.allclose(prob_analytic(x), prob(x), atol=0.1, rtol=0) - assert not np.array_equal(prob_analytic(x), prob(x)) - - # pylint: disable=attribute-defined-outside-init - def test_call_generate_samples(self, monkeypatch): - """Test analytic_probability call when generating samples""" - self.analytic_counter = False - - dev = qml.device("default.qubit.legacy", wires=2, shots=1000) - monkeypatch.setattr(dev.target_device, "analytic_probability", self.mock_analytic_counter) - - # generate samples through `generate_samples` (using 'analytic_probability') - dev.generate_samples() - - # should call `analytic_probability` once through `generate_samples` - assert self.analytic_counter == 1 - - def test_stateless_analytic_return(self): - """Test that analytic_probability returns None if device is stateless""" - dev = qml.device("default.qubit.legacy", wires=2) - dev.target_device._state = None - - assert dev.analytic_probability() is None - - -class TestWiresIntegration: - """Test that the device integrates with PennyLane's wire management.""" - - def make_circuit_probs(self, wires): - """Factory for a qnode returning probabilities using arbitrary wire labels.""" - dev = qml.device("default.qubit.legacy", wires=wires) - n_wires = len(wires) - - @qml.qnode(dev, diff_method="parameter-shift") - def circuit(): - qml.RX(0.5, wires=wires[0 % n_wires]) - qml.RY(2.0, wires=wires[1 % n_wires]) - if n_wires > 1: - qml.CNOT(wires=[wires[0], wires[1]]) - return qml.probs(wires=wires) - - return circuit - - @pytest.mark.parametrize( - "wires1, wires2", - [ - (["a", "c", "d"], [2, 3, 0]), - ([-1, -2, -3], ["q1", "ancilla", 2]), - (["a", "c"], [3, 0]), - ([-1, -2], ["ancilla", 2]), - (["a"], ["nothing"]), - ], - ) - def test_wires_probs(self, wires1, wires2, tol): - """Test that the probability vector of a circuit is independent from the wire labels used.""" - - circuit1 = self.make_circuit_probs(wires1) - circuit2 = self.make_circuit_probs(wires2) - - assert np.allclose(circuit1(), circuit2(), tol) - - def test_wires_not_found_exception(self): - """Tests that an exception is raised when wires not present on the device are adressed.""" - dev = qml.device("default.qubit.legacy", wires=["a", "b"]) - - with qml.queuing.AnnotatedQueue() as q: - qml.RX(0.5, wires="c") - - tape = qml.tape.QuantumScript.from_queue(q) - with pytest.raises(WireError, match="Did not find some of the wires"): - dev.execute(tape) - - wires_to_try = [ - (1, Wires([0])), - (4, Wires([1, 3])), - (["a", 2], Wires([2])), - (["a", 2], Wires([2, "a"])), - ] - - @pytest.mark.parametrize("dev_wires, wires_to_map", wires_to_try) - def test_map_wires_caches(self, dev_wires, wires_to_map): - """Test that multiple calls to map_wires will use caching.""" - dev = qml.device("default.qubit.legacy", wires=dev_wires) - - original_hits = dev.map_wires.cache_info().hits - original_misses = dev.map_wires.cache_info().misses - - # The first call is computed: it's a miss as it didn't come from the cache - dev.map_wires(wires_to_map) - - # The number of misses increased - assert dev.map_wires.cache_info().misses > original_misses - - # The second call comes from the cache: it's a hit - dev.map_wires(wires_to_map) - - # The number of hits increased - assert dev.map_wires.cache_info().hits > original_hits - - -class TestGetSlice: - """Tests for the _get_slice function.""" - - def test_get_slice(self): - """Test that the _get_slice function returns the expected slice and allows us to slice - correctly into an array.""" - - sl = _get_slice(1, 1, 3) - array = np.arange(27).reshape((3, 3, 3)) - target = array[:, 1, :] - - assert sl == (slice(None, None, None), 1, slice(None, None, None)) - assert np.allclose(array[sl], target) - - def test_get_slice_first(self): - """Test that the _get_slice function returns the expected slice when accessing the first - axis of an array.""" - - sl = _get_slice(2, 0, 3) - array = np.arange(27).reshape((3, 3, 3)) - target = array[2] - - assert sl == (2, slice(None, None, None), slice(None, None, None)) - assert np.allclose(array[sl], target) - - def test_get_slice_last(self): - """Test that the _get_slice function returns the expected slice when accessing the last - axis of an array.""" - - sl = _get_slice(0, 2, 3) - array = np.arange(27).reshape((3, 3, 3)) - target = array[:, :, 0] - - assert sl == (slice(None, None, None), slice(None, None, None), 0) - assert np.allclose(array[sl], target) - - def test_get_slice_1d(self): - """Test that the _get_slice function returns the expected slice when accessing a - 1-dimensional array.""" - - sl = _get_slice(2, 0, 1) - array = np.arange(27) - target = array[2] - - assert sl == (2,) - assert np.allclose(array[sl], target) - - -class TestApplyOps: - """Tests for special methods listed in _apply_ops that use array manipulation tricks to apply - gates in DefaultQubitLegacy.""" - - state = np.arange(2**4, dtype=np.complex128).reshape((2, 2, 2, 2)) - with pytest.warns(qml.PennyLaneDeprecationWarning): - dev = qml.device("default.qubit.legacy", wires=4) - - single_qubit_ops = [ - (qml.PauliX, dev._apply_x), - (qml.PauliY, dev._apply_y), - (qml.PauliZ, dev._apply_z), - (qml.Hadamard, dev._apply_hadamard), - (qml.S, dev._apply_s), - (qml.T, dev._apply_t), - (qml.SX, dev._apply_sx), - ] - two_qubit_ops = [ - (qml.CNOT, dev._apply_cnot), - (qml.SWAP, dev._apply_swap), - (qml.CZ, dev._apply_cz), - ] - three_qubit_ops = [ - (qml.Toffoli, dev._apply_toffoli), - ] - - @pytest.mark.parametrize("op, method", single_qubit_ops) - def test_apply_single_qubit_op(self, op, method): - """Test if the application of single qubit operations is correct.""" - state_out = method(self.state, axes=[1]) - op = op(wires=[1]) - matrix = op.matrix() - state_out_einsum = np.einsum("ab,ibjk->iajk", matrix, self.state) - assert np.allclose(state_out, state_out_einsum) - - @pytest.mark.parametrize("op, method", two_qubit_ops) - def test_apply_two_qubit_op(self, op, method): - """Test if the application of two qubit operations is correct.""" - state_out = method(self.state, axes=[0, 1]) - op = op(wires=[0, 1]) - matrix = op.matrix() - matrix = matrix.reshape((2, 2, 2, 2)) - state_out_einsum = np.einsum("abcd,cdjk->abjk", matrix, self.state) - assert np.allclose(state_out, state_out_einsum) - - @pytest.mark.parametrize("op, method", two_qubit_ops) - def test_apply_two_qubit_op_reverse(self, op, method): - """Test if the application of two qubit operations is correct when the applied wires are - reversed.""" - state_out = method(self.state, axes=[2, 1]) - op = op(wires=[2, 1]) - matrix = op.matrix() - matrix = matrix.reshape((2, 2, 2, 2)) - state_out_einsum = np.einsum("abcd,idck->ibak", matrix, self.state) - assert np.allclose(state_out, state_out_einsum) - - @pytest.mark.parametrize("op, method", three_qubit_ops) - def test_apply_three_qubit_op_controls_smaller(self, op, method): - """Test if the application of three qubit operations is correct when both control wires are - smaller than the target wire.""" - state_out = method(self.state, axes=[0, 2, 3]) - op = op(wires=[0, 2, 3]) - matrix = op.matrix() - matrix = matrix.reshape((2, 2) * 3) - state_out_einsum = np.einsum("abcdef,dkef->akbc", matrix, self.state) - assert np.allclose(state_out, state_out_einsum) - - @pytest.mark.parametrize("op, method", three_qubit_ops) - def test_apply_three_qubit_op_controls_greater(self, op, method): - """Test if the application of three qubit operations is correct when both control wires are - greater than the target wire.""" - state_out = method(self.state, axes=[2, 1, 0]) - op = op(wires=[2, 1, 0]) - matrix = op.matrix() - matrix = matrix.reshape((2, 2) * 3) - state_out_einsum = np.einsum("abcdef,fedk->cbak", matrix, self.state) - assert np.allclose(state_out, state_out_einsum) - - @pytest.mark.parametrize("op, method", three_qubit_ops) - def test_apply_three_qubit_op_controls_split(self, op, method): - """Test if the application of three qubit operations is correct when one control wire is smaller - and one control wire is greater than the target wire.""" - state_out = method(self.state, axes=[3, 1, 2]) - op = op(wires=[3, 1, 2]) - matrix = op.matrix() - matrix = matrix.reshape((2, 2) * 3) - state_out_einsum = np.einsum("abcdef,kdfe->kacb", matrix, self.state) - assert np.allclose(state_out, state_out_einsum) - - -class TestStateVector: - """Unit tests for the _apply_state_vector method""" - - def test_full_subsystem(self, mocker): - """Test applying a state vector to the full subsystem""" - dev = DefaultQubitLegacy(wires=["a", "b", "c"]) - state = np.array([1, 0, 0, 0, 1, 0, 1, 1]) / 2.0 - state_wires = qml.wires.Wires(["a", "b", "c"]) - - spy = mocker.spy(dev, "_scatter") - dev._apply_state_vector(state=state, device_wires=state_wires) - - assert np.all(dev._state.flatten() == state) - spy.assert_not_called() - - def test_partial_subsystem(self, mocker): - """Test applying a state vector to a subset of wires of the full subsystem""" - - dev = DefaultQubitLegacy(wires=["a", "b", "c"]) - state = np.array([1, 0, 1, 0]) / np.sqrt(2.0) - state_wires = qml.wires.Wires(["a", "c"]) - - spy = mocker.spy(dev, "_scatter") - dev._apply_state_vector(state=state, device_wires=state_wires) - res = np.sum(dev._state, axis=(1,)).flatten() - - assert np.all(res == state) - spy.assert_called() - - -# pylint:disable = unnecessary-lambda-assignment -class TestApplyOperationUnit: - """Unit tests for the internal _apply_operation method.""" - - def test_internal_apply_ops_case(self, monkeypatch): - """Tests that if we provide an operation that has an internal - implementation, then we use that specific implementation. - - This test provides a new internal function that `default.qubit` uses to - apply `PauliX` (rather than redefining the gate itself). - """ - dev = qml.device("default.qubit.legacy", wires=1) - - # Create a dummy operation - expected_test_output = np.ones(1) - supported_gate_application = lambda *args, **kwargs: expected_test_output - - with monkeypatch.context() as m: - # Set the internal ops implementations dict - m.setattr(dev.target_device, "_apply_ops", {"PauliX": supported_gate_application}) - - test_state = np.array([1, 0]) - op = qml.PauliX(0) - - res = dev._apply_operation(test_state, op) - assert np.allclose(res, expected_test_output) - - def test_diagonal_operation_case(self, monkeypatch): - """Tests the case when the operation to be applied is - diagonal in the computational basis and the _apply_diagonal_unitary method is used.""" - dev = qml.device("default.qubit.legacy", wires=1) - par = 0.3 - - test_state = np.array([1, 0]) - wires = 0 - op = qml.PhaseShift(par, wires=wires) - assert op.name not in dev._apply_ops - - # Set the internal _apply_diagonal_unitary - history = [] - mock_apply_diag = lambda state, matrix, wires: history.append((state, matrix, wires)) - with monkeypatch.context() as m: - m.setattr(dev.target_device, "_apply_diagonal_unitary", mock_apply_diag) - assert dev._apply_diagonal_unitary == mock_apply_diag - - dev._apply_operation(test_state, op) - - res_state, res_mat, res_wires = history[0] - - assert np.allclose(res_state, test_state) - assert np.allclose(res_mat, np.diag(op.matrix())) - assert np.allclose(res_wires, wires) - - def test_apply_einsum_case(self, monkeypatch): - """Tests the case when np.einsum is used to apply an operation in - default.qubit.""" - dev = qml.device("default.qubit.legacy", wires=1) - - test_state = np.array([1, 0]) - wires = 0 - - # Redefine the S gate so that it is an example for a one-qubit gate - # that is not registered in the diagonal_in_z_basis attribute - class TestSGate(qml.operation.Operation): - num_wires = 1 - - # pylint: disable=unused-argument - @staticmethod - def compute_matrix(*params, **hyperparams): - return np.array([[1, 0], [0, 1j]]) - - dev.operations.add("TestSGate") - op = TestSGate(wires=wires) - - assert op.name in dev.operations - assert op.name not in dev._apply_ops - - # Set the internal _apply_unitary_einsum - history = [] - mock_apply_einsum = lambda state, matrix, wires: history.append((state, matrix, wires)) - with monkeypatch.context() as m: - m.setattr(dev.target_device, "_apply_unitary_einsum", mock_apply_einsum) - - dev._apply_operation(test_state, op) - - res_state, res_mat, res_wires = history[0] - - assert np.allclose(res_state, test_state) - assert np.allclose(res_mat, op.matrix()) - assert np.allclose(res_wires, wires) - - def test_apply_tensordot_case(self, monkeypatch): - """Tests the case when np.tensordot is used to apply an operation in - default.qubit.""" - dev = qml.device("default.qubit.legacy", wires=3) - - test_state = np.array([1, 0]) - wires = [0, 1, 2] - - # Redefine the Toffoli gate so that it is an example for a gate with - # more than two wires - class TestToffoli(qml.operation.Operation): - num_wires = 3 - - # pylint: disable=unused-argument - @staticmethod - def compute_matrix(*params, **hyperparams): - return U_toffoli - - dev.operations.add("TestToffoli") - op = TestToffoli(wires=wires) - - assert op.name in dev.operations - assert op.name not in dev._apply_ops - - # Set the internal _apply_unitary_tensordot - history = [] - mock_apply_tensordot = lambda state, matrix, wires: history.append((state, matrix, wires)) - - with monkeypatch.context() as m: - m.setattr(dev.target_device, "_apply_unitary", mock_apply_tensordot) - - dev._apply_operation(test_state, op) - - res_state, res_mat, res_wires = history[0] - - assert np.allclose(res_state, test_state) - assert np.allclose(res_mat, op.matrix()) - assert np.allclose(res_wires, wires) - - def test_apply_unitary_tensordot_double_broadcasting_error(self): - """Tests that an error is raised if attempting to use _apply_unitary - with a broadcasted matrix and a broadcasted state simultaneously.""" - dev = qml.device("default.qubit.legacy", wires=3) - - class BroadcastedToffoli(qml.operation.Operation): - num_wires = 3 - batch_size = 3 - num_params = 0 - - # pylint: disable=unused-argument - @staticmethod - def compute_matrix(*params, **hyperparams): - return np.array([U_toffoli] * 3) - - state = np.eye(8)[:3] - wires = qml.wires.Wires([0, 1, 2]) - - mat = BroadcastedToffoli(wires=wires).matrix() - with pytest.raises(NotImplementedError, match="broadcasted unitary to an already"): - dev._apply_unitary(state, mat, wires=wires) - - def test_identity_skipped(self, mocker): - """Test that applying the identity operation does not perform any additional computations.""" - dev = qml.device("default.qubit.legacy", wires=1) - - starting_state = np.array([1, 0]) - op = qml.Identity(0) - - spy_diagonal = mocker.spy(dev.target_device, "_apply_diagonal_unitary") - spy_einsum = mocker.spy(dev.target_device, "_apply_unitary_einsum") - spy_unitary = mocker.spy(dev.target_device, "_apply_unitary") - - res = dev._apply_operation(starting_state, op) - assert res is starting_state - - spy_diagonal.assert_not_called() - spy_einsum.assert_not_called() - spy_unitary.assert_not_called() - - -class TestHamiltonianSupport: - """Tests the devices' native support for Hamiltonian observables.""" - - def test_do_not_split_analytic(self, mocker): - """Tests that the Hamiltonian is not split for shots=None.""" - dev = qml.device("default.qubit.legacy", wires=2) - H = qml.Hamiltonian(np.array([0.1, 0.2]), [qml.PauliX(0), qml.PauliZ(1)]) - - @qml.qnode(dev, diff_method="parameter-shift", interface=None) - def circuit(): - return qml.expval(H) - - spy = mocker.spy(dev.target_device, "expval") - - circuit() - # evaluated one expval altogether - assert spy.call_count == 1 - - def test_split_finite_shots(self, mocker): - """Tests that the Hamiltonian is split for finite shots.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=10) - spy = mocker.spy(dev.target_device, "expval") - - H = qml.Hamiltonian(np.array([0.1, 0.2]), [qml.PauliX(0), qml.PauliZ(1)]) - - @qml.qnode(dev) - def circuit(): - return qml.expval(H) - - circuit() - - # evaluated one expval per Pauli observable - assert spy.call_count == 2 - - @pytest.mark.usefixtures("use_legacy_opmath") # only a problem for legacy opmath - def test_error_hamiltonian_expval_finite_shots_legacy_opmath(self): - """Tests that the Hamiltonian is split for finite shots.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=10) - H = qml.Hamiltonian([0.1, 0.2], [qml.PauliX(0), qml.PauliZ(1)]) - - with pytest.raises(AssertionError, match="Hamiltonian must be used with shots=None"): - dev.expval(H) - - @pytest.mark.usefixtures("use_legacy_opmath") - def test_error_hamiltonian_expval_wrong_wires_legacy_opmath(self): - """Tests that expval fails if Hamiltonian uses non-device wires.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=None) - H = qml.Hamiltonian([0.1, 0.2, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliY(2)]) - - with pytest.raises( - WireError, - match=r"Did not find some of the wires \(0, 1, 2\) on device with wires \(0, 1\).", - ): - dev.expval(H) - - @pytest.mark.usefixtures("use_legacy_and_new_opmath") - def test_Hamiltonian_filtered_from_rotations(self, mocker): - """Tests that the device does not attempt to get rotations for Hamiltonians.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=10) - H = qml.Hamiltonian([0.1, 0.2], [qml.PauliX(0), qml.PauliZ(1)]) - - spy = mocker.spy(qml.devices.QubitDevice, "_get_diagonalizing_gates") - qs = qml.tape.QuantumScript([qml.RX(1, 0)], [qml.expval(qml.PauliX(0)), qml.expval(H)]) - rotations = dev._get_diagonalizing_gates(qs) - - assert len(rotations) == 1 - qml.assert_equal(rotations[0], qml.Hadamard(0)) - - call_args = spy.call_args.args[1] # 0 is self (the device) - assert isinstance(call_args, qml.tape.QuantumScript) - assert len(call_args.operations) == 0 - assert len(call_args.measurements) == 1 - qml.assert_equal(call_args.measurements[0], qml.expval(qml.PauliX(0))) - - -@pytest.mark.parametrize("is_state_batched", [False, True]) -class TestSumSupport: - """Tests for custom Sum support in DefaultQubitLegacy.""" - - @staticmethod - def expected_grad(is_state_batched): - if is_state_batched: - return [[-np.sin(1.3), -np.sin(0.4)], [np.cos(1.3), np.cos(0.4)]] - return [-np.sin(1.3), np.cos(1.3)] - - @staticmethod - def circuit(y, z, is_state_batched): - rx_param = [1.3, 0.4] if is_state_batched else 1.3 - qml.RX(rx_param, 0) - return qml.expval( - qml.sum( - qml.s_prod(y, qml.PauliY(0)), - qml.s_prod(z, qml.PauliZ(0)), - ) - ) - - def test_super_expval_not_called(self, is_state_batched, mocker): - """Tests basic expval result, and ensures QubitDevice.expval is not called.""" - dev = qml.device("default.qubit.legacy", wires=1) - spy = mocker.spy(qml.devices.QubitDevice, "expval") - obs = qml.sum(qml.s_prod(0.1, qml.PauliX(0)), qml.s_prod(0.2, qml.PauliZ(0))) - assert np.isclose(dev.expval(obs), 0.2) - spy.assert_not_called() - - -class TestGetBatchSize: - """Tests for the helper method ``_get_batch_size`` of ``QubitDevice``.""" - - @pytest.mark.parametrize("shape", [(4, 4), (1, 8), (4,)]) - def test_batch_size_None(self, shape): - """Test that a ``batch_size=None`` is reported correctly.""" - dev = qml.device("default.qubit.legacy", wires=1) - tensor0 = np.ones(shape, dtype=complex) - assert dev._get_batch_size(tensor0, shape, qml.math.prod(shape)) is None - tensor1 = np.arange(np.prod(shape)).reshape(shape) - assert dev._get_batch_size(tensor1, shape, qml.math.prod(shape)) is None - - @pytest.mark.parametrize("shape", [(4, 4), (1, 8), (4,)]) - @pytest.mark.parametrize("batch_size", [1, 3]) - def test_batch_size_int(self, shape, batch_size): - """Test that an integral ``batch_size`` is reported correctly.""" - dev = qml.device("default.qubit.legacy", wires=1) - full_shape = (batch_size,) + shape - tensor0 = np.ones(full_shape, dtype=complex) - assert dev._get_batch_size(tensor0, shape, qml.math.prod(shape)) == batch_size - tensor1 = np.arange(np.prod(full_shape)).reshape(full_shape) - assert dev._get_batch_size(tensor1, shape, qml.math.prod(shape)) == batch_size - - -class TestDenseMatrixDecompositionThreshold: - """Tests for QFT and Grover operators the automatic transition from dense matrix to decomposition - on calculations.""" - - input = [ - (qml.QFT, 4, True), - (qml.QFT, 6, False), - (qml.GroverOperator, 4, True), - (qml.GroverOperator, 13, False), - ] - - @pytest.mark.parametrize("op, n_wires, condition", input) - def test_threshold(self, op, n_wires, condition): - wires = np.linspace(0, n_wires - 1, n_wires, dtype=int) - op = op(wires=wires) - # pylint:disable=no-member - assert DefaultQubitLegacy.stopping_condition.__get__(op)(op) == condition diff --git a/tests/devices/test_default_qutrit_mixed.py b/tests/devices/test_default_qutrit_mixed.py index 5178e1c800a..13f3d744bb1 100644 --- a/tests/devices/test_default_qutrit_mixed.py +++ b/tests/devices/test_default_qutrit_mixed.py @@ -823,7 +823,7 @@ def test_jax_backprop(self, use_jit): x = jax.numpy.array(self.x, dtype=jax.numpy.float64) coeffs = (5.2, 6.7) - f = jax.jit(self.f, static_argnums=(1, 2, 3, 4)) if use_jit else self.f + f = jax.jit(self.f, static_argnums=(1, 2, 3)) if use_jit else self.f out = f(x, coeffs) expected_out = self.expected(x, coeffs) diff --git a/tests/devices/test_legacy_device.py b/tests/devices/test_legacy_device.py index c08d9411dca..037f1da076e 100644 --- a/tests/devices/test_legacy_device.py +++ b/tests/devices/test_legacy_device.py @@ -20,6 +20,7 @@ import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane.devices import LegacyDevice as Device @@ -203,7 +204,7 @@ def test_invalid_attribute_in_devices_raises_error(): def test_gradients_record(): """Test that execute_and_gradients and gradient both track the number of gradients requested.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = DefaultQubitLegacy(wires=1) tape = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.PauliZ(0))]) @@ -1074,15 +1075,6 @@ def test_shot_vector_property(self): assert dev.shots.total_shots == 22 - def test_decomp_depth_is_deprecated(self): - """Test that a warning is raised when using the deprecated decomp_depth argument""" - - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="The decomp_depth argument is deprecated", - ): - qml.device("default.qubit", decomp_depth=1) - class TestBatchExecution: """Tests for the batch_execute method.""" diff --git a/tests/devices/test_legacy_facade.py b/tests/devices/test_legacy_facade.py index 4af1ae1e77a..9dcd55ab2f4 100644 --- a/tests/devices/test_legacy_facade.py +++ b/tests/devices/test_legacy_facade.py @@ -19,6 +19,7 @@ import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane.devices.execution_config import ExecutionConfig @@ -105,7 +106,7 @@ def test_tracker(): def test_debugger(): """Test that snapshots still work with legacy devices.""" - dev = LegacyDeviceFacade(qml.devices.DefaultQubitLegacy(wires=1)) + dev = LegacyDeviceFacade(DefaultQubitLegacy(wires=1)) @qml.qnode(dev) def circuit(): diff --git a/tests/devices/test_qubit_device.py b/tests/devices/test_qubit_device.py index 8f50bb65329..83d66ba59d0 100644 --- a/tests/devices/test_qubit_device.py +++ b/tests/devices/test_qubit_device.py @@ -19,6 +19,7 @@ import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as pnp @@ -45,7 +46,7 @@ mock_qubit_device_rotations = ["RX", "RY", "RZ"] -# pylint: disable=abstract-class-instantiated, no-self-use, redefined-outer-name, invalid-name +# pylint: disable=abstract-class-instantiated, no-self-use, redefined-outer-name, invalid-name,abstract-method @pytest.fixture(scope="function") @@ -424,6 +425,17 @@ def test_no_entropy_with_shot_vectors(self, mock_qubit_device_extract_stats): with pytest.raises(NotImplementedError, match="Returning the Von Neumann entropy"): dev.statistics(tape) + def test_vn_entanglement_entropy_with_shot_vectors(self, mock_qubit_device_extract_stats): + + dev = mock_qubit_device_extract_stats() + dev.shots = (10, 10) + tape = qml.tape.QuantumScript([], [qml.vn_entanglement_entropy(wires0=0, wires1=1)]) + + with pytest.raises( + NotImplementedError, match="Returning the Von Neumann entanglement entropy" + ): + dev.statistics(tape) + def test_mutual_info_with_shot_vectors(self, mock_qubit_device_extract_stats): dev = mock_qubit_device_extract_stats() @@ -1211,7 +1223,7 @@ def test_defines_correct_capabilities(self): class TestNativeMidCircuitMeasurements: """Unit tests for mid-circuit measurements related functionality""" - class MCMDevice(qml.devices.DefaultQubitLegacy): + class MCMDevice(DefaultQubitLegacy): def apply(self, *args, **kwargs): for op in args[0]: if isinstance(op, qml.measurements.MidMeasureMP): @@ -1219,7 +1231,7 @@ def apply(self, *args, **kwargs): @classmethod def capabilities(cls): - default_capabilities = copy.copy(qml.devices.DefaultQubitLegacy.capabilities()) + default_capabilities = copy.copy(DefaultQubitLegacy.capabilities()) default_capabilities["supports_mid_measure"] = True return default_capabilities @@ -1514,20 +1526,17 @@ class TestResourcesTracker: Resources(2, 6, {"Hadamard": 3, "RX": 2, "CNOT": 1}, {1: 5, 2: 1}, 4, Shots((10, 10, 50))), ) # Resources(wires, gates, gate_types, gate_sizes, depth, shots) - devices = ("default.qubit.legacy",) - @pytest.mark.all_interfaces - @pytest.mark.parametrize("dev_name", devices) @pytest.mark.parametrize( "qs_shots_wires, expected_resource", zip(qs_shots_wires_data, expected_resources) ) - def test_tracker_single_execution(self, dev_name, qs_shots_wires, expected_resource): + def test_tracker_single_execution(self, qs_shots_wires, expected_resource): """Test that the tracker accurately tracks resources in a single execution""" qs, shots, wires = qs_shots_wires qs._shots = qml.measurements.Shots(shots) - dev = qml.device(dev_name, shots=shots, wires=wires) + dev = DefaultQubitLegacy(shots=shots, wires=wires) with qml.Tracker(dev) as tracker: dev.execute(qs) @@ -1536,8 +1545,7 @@ def test_tracker_single_execution(self, dev_name, qs_shots_wires, expected_resou assert tracker.history["resources"][0] == expected_resource @pytest.mark.all_interfaces - @pytest.mark.parametrize("dev_name", devices) - def test_tracker_multi_execution(self, dev_name): + def test_tracker_multi_execution(self): """Test that the tracker accurately tracks resources for multi executions""" qs1 = qml.tape.QuantumScript([qml.Hadamard(0), qml.CNOT([0, 1])]) qs2 = qml.tape.QuantumScript([qml.PauliZ(0), qml.CNOT([0, 1]), qml.RX(1.23, 2)]) @@ -1545,7 +1553,7 @@ def test_tracker_multi_execution(self, dev_name): exp_res1 = Resources(2, 2, {"Hadamard": 1, "CNOT": 1}, {1: 1, 2: 1}, 2, Shots(10)) exp_res2 = Resources(3, 3, {"PauliZ": 1, "CNOT": 1, "RX": 1}, {1: 2, 2: 1}, 2, Shots(10)) - dev = qml.device(dev_name, shots=10, wires=[0, 1, 2]) + dev = DefaultQubitLegacy(shots=10, wires=[0, 1, 2]) with qml.Tracker(dev) as tracker: dev.batch_execute([qs1]) dev.batch_execute([qs1, qs2]) @@ -1561,7 +1569,7 @@ def test_tracker_multi_execution(self, dev_name): @pytest.mark.autograd def test_tracker_grad(self): """Test that the tracker can track resources through a gradient computation""" - dev = qml.device("default.qubit", wires=1, shots=100) + dev = DefaultQubitLegacy(wires=1, shots=100) @qml.qnode(dev, diff_method="parameter-shift") def circuit(x): diff --git a/tests/devices/test_qutrit_device.py b/tests/devices/test_qutrit_device.py index 8799d75234b..27ef3398a36 100644 --- a/tests/devices/test_qutrit_device.py +++ b/tests/devices/test_qutrit_device.py @@ -1216,6 +1216,13 @@ def test_state(self, mock_qutrit_device): with pytest.raises(NotImplementedError): dev.state() + def test_density_matrix(self, mock_qutrit_device): + """Test that density_matrix is unimplemented""" + dev = mock_qutrit_device() + + with pytest.raises(qml.QuantumFunctionError, match="Unsupported return type"): + dev.density_matrix(wires=0) + def test_vn_entropy(self, mock_qutrit_device): """Test that vn_entropy is unimplemented""" dev = mock_qutrit_device() @@ -1223,12 +1230,12 @@ def test_vn_entropy(self, mock_qutrit_device): with pytest.raises(qml.QuantumFunctionError, match="Unsupported return type"): dev.vn_entropy(wires=0, log_base=3) - def test_density_matrix(self, mock_qutrit_device): - """Test that vn_entropy is unimplemented""" + def test_vn_entanglement_entropy(self, mock_qutrit_device): + """Test that vn_entanglement_entropy is unimplemented""" dev = mock_qutrit_device() with pytest.raises(qml.QuantumFunctionError, match="Unsupported return type"): - dev.density_matrix(wires=0) + dev.vn_entanglement_entropy(0, 1, log_base=3) def test_mutual_info(self, mock_qutrit_device): """Test that mutual_info is unimplemented""" diff --git a/tests/drawer/test_drawable_layers.py b/tests/drawer/test_drawable_layers.py index 7e1bdb1d0bd..729e0f74609 100644 --- a/tests/drawer/test_drawable_layers.py +++ b/tests/drawer/test_drawable_layers.py @@ -196,7 +196,7 @@ def test_mid_measure_custom_wires(self): m1 = qml.measurements.MeasurementValue([mp1], lambda v: v) def teleport(state): - qml.QubitStateVector(state, wires=["A"]) + qml.StatePrep(state, wires=["A"]) qml.Hadamard(wires="a") qml.CNOT(wires=["a", "B"]) qml.CNOT(wires=["A", "a"]) diff --git a/tests/fermi/test_fermionic.py b/tests/fermi/test_fermionic.py index dc61295115a..4e028ad9875 100644 --- a/tests/fermi/test_fermionic.py +++ b/tests/fermi/test_fermionic.py @@ -53,6 +53,62 @@ fw7 = FermiWord({(0, 10): "-", (1, 30): "+", (2, 0): "-", (3, 400): "+"}) fw7_dag = FermiWord({(0, 400): "-", (1, 0): "+", (2, 30): "-", (3, 10): "+"}) +fw8 = FermiWord({(0, 0): "-", (1, 1): "+"}) +fw8c = FermiWord({(0, 1): "+", (1, 0): "-"}) +fw8cs = FermiSentence({fw8c: -1}) + +fw9 = FermiWord({(0, 0): "-", (1, 1): "-"}) +fw9c = FermiWord({(0, 1): "-", (1, 0): "-"}) +fw9cs = FermiSentence({fw9c: -1}) + +fw10 = FermiWord({(0, 0): "+", (1, 1): "+"}) +fw10c = FermiWord({(0, 1): "+", (1, 0): "+"}) +fw10cs = FermiSentence({fw10c: -1}) + +fw11 = FermiWord({(0, 0): "-", (1, 0): "+"}) +fw11c = FermiWord({(0, 0): "+", (1, 0): "-"}) +fw11cs = 1 + FermiSentence({fw11c: -1}) + +fw12 = FermiWord({(0, 0): "+", (1, 0): "+"}) +fw12c = FermiWord({(0, 0): "+", (1, 0): "+"}) +fw12cs = FermiSentence({fw12c: 1}) + +fw13 = FermiWord({(0, 0): "-", (1, 0): "-"}) +fw13c = FermiWord({(0, 0): "-", (1, 0): "-"}) +fw13cs = FermiSentence({fw13c: 1}) + +fw14 = FermiWord({(0, 0): "+", (1, 0): "-"}) +fw14c = FermiWord({(0, 0): "-", (1, 0): "+"}) +fw14cs = 1 + FermiSentence({fw14c: -1}) + +fw15 = FermiWord({(0, 0): "-", (1, 1): "+", (2, 2): "+"}) +fw15c = FermiWord({(0, 1): "+", (1, 0): "-", (2, 2): "+"}) +fw15cs = FermiSentence({fw15c: -1}) + +fw16 = FermiWord({(0, 0): "-", (1, 1): "+", (2, 2): "-"}) +fw16c = FermiWord({(0, 0): "-", (1, 2): "-", (2, 1): "+"}) +fw16cs = FermiSentence({fw16c: -1}) + +fw17 = FermiWord({(0, 0): "-", (1, 0): "+", (2, 2): "-"}) +fw17c1 = FermiWord({(0, 2): "-"}) +fw17c2 = FermiWord({(0, 0): "+", (1, 0): "-", (2, 2): "-"}) +fw17cs = fw17c1 - fw17c2 + +fw18 = FermiWord({(0, 0): "+", (1, 1): "+", (2, 2): "-", (3, 3): "-"}) +fw18c = FermiWord({(0, 0): "+", (1, 3): "-", (2, 1): "+", (3, 2): "-"}) +fw18cs = FermiSentence({fw18c: 1}) + +fw19 = FermiWord({(0, 0): "+", (1, 1): "+", (2, 2): "-", (3, 2): "+"}) +fw19c1 = FermiWord({(0, 0): "+", (1, 1): "+"}) +fw19c2 = FermiWord({(0, 2): "+", (1, 0): "+", (2, 1): "+", (3, 2): "-"}) +fw19cs = FermiSentence({fw19c1: 1, fw19c2: -1}) + +fw20 = FermiWord({(0, 0): "-", (1, 0): "+", (2, 1): "-", (3, 0): "-", (4, 0): "+"}) +fw20c1 = FermiWord({(0, 0): "-", (1, 0): "+", (2, 1): "-"}) +fw20c2 = FermiWord({(0, 0): "+", (1, 1): "-", (2, 0): "-"}) +fw20c3 = FermiWord({(0, 0): "+", (1, 0): "-", (2, 0): "+", (3, 1): "-", (4, 0): "-"}) +fw20cs = fw20c1 + fw20c2 - fw20c3 + class TestFermiWord: def test_missing(self): @@ -167,6 +223,41 @@ def test_to_mat_error(self): with pytest.raises(ValueError, match="n_orbitals cannot be smaller than 2"): fw1.to_mat(n_orbitals=1) + tup_fw_shift = ( + (fw8, 0, 1, fw8cs), + (fw9, 0, 1, fw9cs), + (fw10, 0, 1, fw10cs), + (fw11, 0, 1, fw11cs), + (fw12, 0, 1, fw12cs), + (fw13, 0, 1, fw13cs), + (fw14, 0, 1, fw14cs), + (fw15, 0, 1, fw15cs), + (fw16, 1, 2, fw16cs), + (fw17, 0, 1, fw17cs), + (fw8, 0, 0, FermiSentence({fw8: 1})), + (fw8, 1, 0, fw8cs), + (fw11, 1, 0, fw11cs), + (fw18, 3, 1, fw18cs), + (fw19, 3, 0, fw19cs), + (fw20, 4, 0, fw20cs), + ) + + @pytest.mark.parametrize("fw, i, j, fs", tup_fw_shift) + def test_shift_operator(self, fw, i, j, fs): + """Test that the shift_operator method correctly applies the anti-commutator relations.""" + assert fw.shift_operator(i, j) == fs + + def test_shift_operator_errors(self): + """Test that the shift_operator method correctly raises exceptions.""" + with pytest.raises(TypeError, match="Positions must be integers."): + fw8.shift_operator(0.5, 1) + + with pytest.raises(ValueError, match="Positions must be positive integers."): + fw8.shift_operator(-1, 0) + + with pytest.raises(ValueError, match="Positions are out of range."): + fw8.shift_operator(1, 2) + tup_fw_dag = ( (fw1, fw1_dag), (fw2, fw2_dag), @@ -588,6 +679,15 @@ def test_array_must_not_exceed_length_1(self, method_name): } ) +fs8 = fw8 + fw9 +fs8c = fw8 + fw9cs + +fs9 = 1.3 * fw8 + (1.4 + 3.8j) * fw9 +fs9c = 1.3 * fw8 + (1.4 + 3.8j) * fw9cs + +fs10 = -1.3 * fw11 + 2.3 * fw9 +fs10c = -1.3 * fw11cs + 2.3 * fw9 + class TestFermiSentence: def test_missing(self): diff --git a/tests/gradients/core/test_adjoint_diff.py b/tests/gradients/core/test_adjoint_diff.py index 2cbc0c63216..6d9e585b1a8 100644 --- a/tests/gradients/core/test_adjoint_diff.py +++ b/tests/gradients/core/test_adjoint_diff.py @@ -15,6 +15,7 @@ Tests for the ``adjoint_jacobian`` method of the :mod:`pennylane` :class:`QubitDevice` class. """ import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as np @@ -26,7 +27,7 @@ class TestAdjointJacobian: @pytest.fixture def dev(self): """Fixture that creates a device with two wires.""" - return qml.device("default.qubit.legacy", wires=2) + return DefaultQubitLegacy(wires=2) def test_not_expval(self, dev): """Test if a QuantumFunctionError is raised for a tape with measurements that are not @@ -43,7 +44,7 @@ def test_not_expval(self, dev): def test_finite_shots_warns(self): """Tests warning raised when finite shots specified""" - dev = qml.device("default.qubit.legacy", wires=1, shots=10) + dev = DefaultQubitLegacy(wires=2, shots=10) with qml.queuing.AnnotatedQueue() as q: qml.expval(qml.PauliZ(0)) @@ -107,7 +108,7 @@ def test_trainable_hermitian_warns(self): """Test attempting to compute the gradient of a tape that obtains the expectation value of a Hermitian operator emits a warning if the parameters to Hermitian are trainable.""" - dev = qml.device("default.qubit.legacy", wires=3) + dev = DefaultQubitLegacy(wires=3) mx = qml.matrix(qml.PauliX(0) @ qml.PauliY(2)) with qml.queuing.AnnotatedQueue() as q: @@ -147,7 +148,7 @@ def test_pauli_rotation_gradient(self, G, theta, tol, dev): @pytest.mark.autograd @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) def test_Rot_gradient(self, theta, tol, dev): - """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is + """Tests that the device gradient of an arbitrary Euler-angle-parametrized gate is correct.""" params = np.array([theta, theta**3, np.sqrt(2) * theta]) @@ -212,7 +213,7 @@ def test_rx_gradient(self, tol, dev): def test_multiple_rx_gradient(self, tol): """Tests that the gradient of multiple RX gates in a circuit yields the correct result.""" - dev = qml.device("default.qubit.legacy", wires=3) + dev = DefaultQubitLegacy(wires=3) params = np.array([np.pi, np.pi / 2, np.pi / 3]) with qml.queuing.AnnotatedQueue() as q: @@ -350,7 +351,7 @@ def test_provide_starting_state(self, tol, dev): def test_gradient_of_tape_with_hermitian(self, tol): """Test that computing the gradient of a tape that obtains the expectation value of a Hermitian operator works correctly.""" - dev = qml.device("default.qubit.legacy", wires=3) + dev = DefaultQubitLegacy(wires=3) a, b, c = [0.5, 0.3, -0.7] @@ -443,7 +444,7 @@ def test_with_nontrainable_parametrized(self): """Test that a parametrized `QubitUnitary` is accounted for correctly when it is not trainable.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = DefaultQubitLegacy(wires=1) par = np.array(0.6) def circuit(x): diff --git a/tests/gradients/core/test_hadamard_gradient.py b/tests/gradients/core/test_hadamard_gradient.py index ab225109fa9..9b0e01c5d2e 100644 --- a/tests/gradients/core/test_hadamard_gradient.py +++ b/tests/gradients/core/test_hadamard_gradient.py @@ -174,7 +174,7 @@ def test_pauli_rotation_gradient(self, G, theta, tol): @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) def test_rot_gradient(self, theta, tol): - """Tests that the automatic gradient of an arbitrary Euler-angle-parameterized gate is correct.""" + """Tests that the automatic gradient of an arbitrary Euler-angle-parametrized gate is correct.""" dev = qml.device("default.qubit", wires=2) params = np.array([theta, theta**3, np.sqrt(2) * theta]) @@ -283,7 +283,7 @@ def test_ising_gradient(self, G, theta, tol): @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, np.pi, 7)) def test_CRot_gradient_with_expansion(self, theta, tol): - """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parameterized + """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parametrized gate is correct.""" dev = qml.device("default.qubit", wires=3) a, b, c = np.array([theta, theta**3, np.sqrt(2) * theta]) diff --git a/tests/gradients/finite_diff/test_finite_difference.py b/tests/gradients/finite_diff/test_finite_difference.py index 29045701566..03bf4f3ad81 100644 --- a/tests/gradients/finite_diff/test_finite_difference.py +++ b/tests/gradients/finite_diff/test_finite_difference.py @@ -14,13 +14,13 @@ """ Tests for the gradients.finite_difference module. """ -# pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison,abstract-method import numpy import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as np -from pennylane.devices import DefaultQubitLegacy from pennylane.gradients import finite_diff, finite_diff_coeffs from pennylane.operation import AnyWires, Observable diff --git a/tests/gradients/finite_diff/test_finite_difference_shot_vec.py b/tests/gradients/finite_diff/test_finite_difference_shot_vec.py index b86e829703b..14bece2585e 100644 --- a/tests/gradients/finite_diff/test_finite_difference_shot_vec.py +++ b/tests/gradients/finite_diff/test_finite_difference_shot_vec.py @@ -16,15 +16,15 @@ """ import numpy import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as np -from pennylane.devices import DefaultQubitLegacy from pennylane.gradients import finite_diff from pennylane.measurements import Shots from pennylane.operation import AnyWires, Observable -# pylint:disable = use-implicit-booleaness-not-comparison +# pylint:disable = use-implicit-booleaness-not-comparison,abstract-method h_val = 0.1 diff --git a/tests/gradients/finite_diff/test_spsa_gradient.py b/tests/gradients/finite_diff/test_spsa_gradient.py index 2730cd53d00..fb543381335 100644 --- a/tests/gradients/finite_diff/test_spsa_gradient.py +++ b/tests/gradients/finite_diff/test_spsa_gradient.py @@ -16,15 +16,15 @@ """ import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as pnp -from pennylane.devices import DefaultQubitLegacy from pennylane.gradients import spsa_grad from pennylane.gradients.spsa_gradient import _rademacher_sampler from pennylane.operation import AnyWires, Observable -# pylint:disable = use-implicit-booleaness-not-comparison +# pylint:disable = use-implicit-booleaness-not-comparison,abstract-method def coordinate_sampler(indices, num_params, idx, rng=None): diff --git a/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py b/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py index 2c771dc2832..69646131de2 100644 --- a/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py +++ b/tests/gradients/finite_diff/test_spsa_gradient_shot_vec.py @@ -14,12 +14,15 @@ """ Tests for the gradients.spsa_gradient module using shot vectors. """ + +# pylint: disable=abstract-method + import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as pnp -from pennylane.devices import DefaultQubitLegacy from pennylane.gradients import spsa_grad from pennylane.measurements import Shots from pennylane.operation import AnyWires, Observable diff --git a/tests/gradients/parameter_shift/test_parameter_shift.py b/tests/gradients/parameter_shift/test_parameter_shift.py index e5a00b2cdf0..8d8d17bfaa7 100644 --- a/tests/gradients/parameter_shift/test_parameter_shift.py +++ b/tests/gradients/parameter_shift/test_parameter_shift.py @@ -12,12 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. """Tests for the gradients.parameter_shift module using the new return types.""" -# pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison,abstract-method import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane import numpy as np -from pennylane.devices import DefaultQubitLegacy from pennylane.gradients import param_shift from pennylane.gradients.parameter_shift import ( _evaluate_gradient, @@ -1373,7 +1373,7 @@ def test_pauli_rotation_gradient(self, mocker, G, theta, shift, tol): @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) @pytest.mark.parametrize("shift", [np.pi / 2, 0.3, np.sqrt(2)]) def test_Rot_gradient(self, mocker, theta, shift, tol): - """Tests that the automatic gradient of an arbitrary Euler-angle-parameterized gate is correct.""" + """Tests that the automatic gradient of an arbitrary Euler-angle-parametrized gate is correct.""" spy = mocker.spy(qml.gradients.parameter_shift, "_get_operation_recipe") dev = qml.device("default.qubit", wires=1) params = np.array([theta, theta**3, np.sqrt(2) * theta]) @@ -1447,7 +1447,7 @@ def test_controlled_rotation_gradient(self, G, tol): @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, np.pi, 7)) def test_CRot_gradient(self, theta, tol): - """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parameterized + """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parametrized gate is correct.""" dev = qml.device("default.qubit", wires=2) a, b, c = np.array([theta, theta**3, np.sqrt(2) * theta]) @@ -2714,7 +2714,7 @@ def test_pauli_rotation_gradient(self, mocker, G, theta, shift, tol): @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) @pytest.mark.parametrize("shift", [np.pi / 2, 0.3, np.sqrt(2)]) def test_Rot_gradient(self, mocker, theta, shift, tol): - """Tests that the automatic gradient of an arbitrary Euler-angle-parameterized gate is correct.""" + """Tests that the automatic gradient of an arbitrary Euler-angle-parametrized gate is correct.""" spy = mocker.spy(qml.gradients.parameter_shift, "_get_operation_recipe") dev = qml.device("default.qubit", wires=1) params = np.array([theta, theta**3, np.sqrt(2) * theta]) @@ -2784,7 +2784,7 @@ def test_controlled_rotation_gradient(self, G, tol): @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, np.pi, 7)) def test_CRot_gradient(self, theta, tol): - """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parameterized + """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parametrized gate is correct.""" dev = qml.device("default.qubit", wires=2) a, b, c = np.array([theta, theta**3, np.sqrt(2) * theta]) diff --git a/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py b/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py index 6d9212dceb9..4ad2b84007f 100644 --- a/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py +++ b/tests/gradients/parameter_shift/test_parameter_shift_shot_vec.py @@ -12,15 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. """Tests for the gradients.parameter_shift module using the new return types and devices that define a shot vector.""" -# pylint:disable=use-implicit-booleaness-not-comparison +# pylint:disable=use-implicit-booleaness-not-comparison,abstract-method from functools import partial import pytest +from default_qubit_legacy import DefaultQubitLegacy from flaky import flaky import pennylane as qml from pennylane import numpy as np -from pennylane.devices import DefaultQubitLegacy from pennylane.gradients import param_shift from pennylane.measurements import Shots from pennylane.operation import AnyWires, Observable @@ -556,7 +556,7 @@ def test_pauli_rotation_gradient(self, mocker, G, theta, shift, broadcast): @pytest.mark.parametrize("theta", angles) @pytest.mark.parametrize("shift", [np.pi / 2, 0.3]) def test_Rot_gradient(self, mocker, theta, shift, broadcast): - """Tests that the automatic gradient of an arbitrary Euler-angle-parameterized gate is correct.""" + """Tests that the automatic gradient of an arbitrary Euler-angle-parametrized gate is correct.""" spy = mocker.spy(qml.gradients.parameter_shift, "_get_operation_recipe") shot_vec = tuple([1000000] * 2) @@ -646,7 +646,7 @@ def test_controlled_rotation_gradient(self, G, broadcast): @pytest.mark.parametrize("theta", angles) def test_CRot_gradient(self, theta, broadcast): - """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parameterized + """Tests that the automatic gradient of an arbitrary controlled Euler-angle-parametrized gate is correct.""" shot_vec = tuple([1000000] * 2) dev = qml.device("default.qubit", wires=2, shots=shot_vec) diff --git a/tests/helpers/stat_utils.py b/tests/helpers/stat_utils.py new file mode 100644 index 00000000000..e0699df6eca --- /dev/null +++ b/tests/helpers/stat_utils.py @@ -0,0 +1,32 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Helper functions for testing stochastic processes. +""" +import numpy as np +from scipy.stats import fisher_exact + + +def fisher_exact_test(actual, expected, outcomes=(0, 1), threshold=0.1): + """Checks that a binary sample matches the expected distribution using the Fisher exact test.""" + + actual, expected = np.asarray(actual), np.asarray(expected) + contingency_table = np.array( + [ + [np.sum(actual == outcomes[0]), np.sum(actual == outcomes[1])], + [np.sum(expected == outcomes[0]), np.sum(expected == outcomes[1])], + ] + ) + _, p_value = fisher_exact(contingency_table) + assert p_value > threshold, "The sample does not match the expected distribution." diff --git a/tests/interfaces/legacy_devices_integration/test_execute_legacy.py b/tests/interfaces/legacy_devices_integration/test_execute_legacy.py index e12c5d8ff55..12903a7ba12 100644 --- a/tests/interfaces/legacy_devices_integration/test_execute_legacy.py +++ b/tests/interfaces/legacy_devices_integration/test_execute_legacy.py @@ -22,7 +22,7 @@ def test_old_interface_no_device_jacobian_products(): """Test that an error is always raised for the old device interface if device jacobian products are requested.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) tape = qml.tape.QuantumScript([qml.RX(1.0, wires=0)], [qml.expval(qml.PauliZ(0))]) with pytest.raises(qml.QuantumFunctionError): qml.execute((tape,), dev, device_vjp=True) diff --git a/tests/interfaces/test_autograd.py b/tests/interfaces/test_autograd.py index 2a6ee306508..d206f1758d3 100644 --- a/tests/interfaces/test_autograd.py +++ b/tests/interfaces/test_autograd.py @@ -13,12 +13,13 @@ # limitations under the License. """Autograd specific tests for execute and default qubit 2.""" import autograd +import numpy as np import pytest from param_shift_dev import ParamShiftDerivativesDevice import pennylane as qml from pennylane import execute -from pennylane import numpy as np +from pennylane import numpy as pnp from pennylane.devices import DefaultQubit from pennylane.gradients import param_shift from pennylane.measurements import Shots @@ -36,7 +37,7 @@ def test_caching_param_shift_hessian(self, num_params): caching reduces the number of evaluations to their optimum when computing Hessians.""" dev = DefaultQubit() - params = np.arange(1, num_params + 1) / 10 + params = pnp.arange(1, num_params + 1) / 10 N = len(params) @@ -125,8 +126,8 @@ def f(x): # add tests for lightning 2 when possible # set rng for device when possible test_matrix = [ - ({"gradient_fn": param_shift}, Shots(100000), DefaultQubit(seed=42)), - ({"gradient_fn": param_shift}, Shots((100000, 100000)), DefaultQubit(seed=42)), + ({"gradient_fn": param_shift}, Shots(50000), DefaultQubit(seed=42)), + ({"gradient_fn": param_shift}, Shots((50000, 50000)), DefaultQubit(seed=42)), ({"gradient_fn": param_shift}, Shots(None), DefaultQubit()), ({"gradient_fn": "backprop"}, Shots(None), DefaultQubit()), ( @@ -146,7 +147,7 @@ def f(x): ({"gradient_fn": "adjoint", "device_vjp": True}, Shots(None), DefaultQubit()), ( {"gradient_fn": "device", "device_vjp": False}, - Shots((100000, 100000)), + Shots((50000, 50000)), ParamShiftDerivativesDevice(seed=904747894), ), ( @@ -154,12 +155,27 @@ def f(x): Shots((100000, 100000)), ParamShiftDerivativesDevice(seed=10490244), ), + ( + {"gradient_fn": param_shift}, + Shots(None), + qml.device("reference.qubit"), + ), + ( + {"gradient_fn": param_shift}, + Shots(50000), + qml.device("reference.qubit", seed=8743274), + ), + ( + {"gradient_fn": param_shift}, + Shots((50000, 50000)), + qml.device("reference.qubit", seed=8743274), + ), ] def atol_for_shots(shots): """Return higher tolerance if finite shots.""" - return 1e-2 if shots else 1e-6 + return 5e-2 if shots else 1e-6 @pytest.mark.parametrize("execute_kwargs, shots, device", test_matrix) @@ -179,8 +195,8 @@ def cost(a, b): return execute([tape1, tape2], device, **execute_kwargs) - a = np.array(0.1, requires_grad=True) - b = np.array(0.2, requires_grad=False) + a = pnp.array(0.1, requires_grad=True) + b = pnp.array(0.2, requires_grad=False) with device.tracker: res = cost(a, b) @@ -200,7 +216,7 @@ def cost(a, b): def test_scalar_jacobian(self, execute_kwargs, shots, device): """Test scalar jacobian calculation""" - a = np.array(0.1, requires_grad=True) + a = pnp.array(0.1, requires_grad=True) def cost(a): tape = qml.tape.QuantumScript([qml.RY(a, 0)], [qml.expval(qml.PauliZ(0))], shots=shots) @@ -224,8 +240,8 @@ def cost(a): def test_jacobian(self, execute_kwargs, shots, device): """Test jacobian calculation""" - a = np.array(0.1, requires_grad=True) - b = np.array(0.2, requires_grad=True) + a = pnp.array(0.1, requires_grad=True) + b = pnp.array(0.2, requires_grad=True) def cost(a, b): ops = [qml.RY(a, wires=0), qml.RX(b, wires=1), qml.CNOT(wires=[0, 1])] @@ -270,7 +286,7 @@ def cost(params): ) tape2 = qml.tape.QuantumScript( - [qml.RY(np.array(0.5, requires_grad=False), wires=0)], + [qml.RY(pnp.array(0.5, requires_grad=False), wires=0)], [qml.expval(qml.PauliZ(0))], shots=shots, ) @@ -282,7 +298,7 @@ def cost(params): ) tape4 = qml.tape.QuantumScript( - [qml.RY(np.array(0.5, requires_grad=False), 0)], + [qml.RY(pnp.array(0.5, requires_grad=False), 0)], [qml.probs(wires=[0, 1])], shots=shots, ) @@ -291,7 +307,7 @@ def cost(params): res = tuple(i for r in res for i in r) return sum(autograd.numpy.hstack(res)) - params = np.array([0.1, 0.2], requires_grad=True) + params = pnp.array([0.1, 0.2], requires_grad=True) x, y = params res = cost(params) @@ -321,7 +337,7 @@ def cost(params): ) tape2 = qml.tape.QuantumScript( - [qml.RY(np.array(0.5, requires_grad=False), 0)], + [qml.RY(pnp.array(0.5, requires_grad=False), 0)], [qml.expval(qml.PauliZ(0))], shots=shots, ) @@ -336,7 +352,7 @@ def cost(params): res = tuple(i for r in res for i in r) return autograd.numpy.hstack(res) - params = np.array([0.1, 0.2], requires_grad=True) + params = pnp.array([0.1, 0.2], requires_grad=True) x, y = params res = cost(params) @@ -392,8 +408,8 @@ def cost(params): def test_reusing_quantum_tape(self, execute_kwargs, shots, device): """Test re-using a quantum tape by passing new parameters""" - a = np.array(0.1, requires_grad=True) - b = np.array(0.2, requires_grad=True) + a = pnp.array(0.1, requires_grad=True) + b = pnp.array(0.2, requires_grad=True) tape = qml.tape.QuantumScript( [qml.RY(a, 0), qml.RX(b, 1), qml.CNOT((0, 1))], @@ -408,8 +424,8 @@ def cost(a, b): jac_fn = qml.jacobian(cost) jac = jac_fn(a, b) - a = np.array(0.54, requires_grad=True) - b = np.array(0.8, requires_grad=True) + a = pnp.array(0.54, requires_grad=True) + b = pnp.array(0.8, requires_grad=True) # check that the cost function continues to depend on the # values of the parameters for subsequent calls @@ -429,15 +445,15 @@ def cost(a, b): def test_classical_processing(self, execute_kwargs, device, shots): """Test classical processing within the quantum tape""" - a = np.array(0.1, requires_grad=True) - b = np.array(0.2, requires_grad=False) - c = np.array(0.3, requires_grad=True) + a = pnp.array(0.1, requires_grad=True) + b = pnp.array(0.2, requires_grad=False) + c = pnp.array(0.3, requires_grad=True) def cost(a, b, c): ops = [ qml.RY(a * c, wires=0), qml.RZ(b, wires=0), - qml.RX(c + c**2 + np.sin(a), wires=0), + qml.RX(c + c**2 + pnp.sin(a), wires=0), ] tape = qml.tape.QuantumScript(ops, [qml.expval(qml.PauliZ(0))], shots=shots) @@ -457,8 +473,8 @@ def cost(a, b, c): def test_no_trainable_parameters(self, execute_kwargs, shots, device): """Test evaluation and Jacobian if there are no trainable parameters""" - a = np.array(0.1, requires_grad=False) - b = np.array(0.2, requires_grad=False) + a = pnp.array(0.1, requires_grad=False) + b = pnp.array(0.2, requires_grad=False) def cost(a, b): ops = [qml.RY(a, 0), qml.RX(b, 0), qml.CNOT((0, 1))] @@ -484,8 +500,8 @@ def loss(a, b): def test_matrix_parameter(self, execute_kwargs, device, shots): """Test that the autograd interface works correctly with a matrix parameter""" - U = np.array([[0, 1], [1, 0]], requires_grad=False) - a = np.array(0.1, requires_grad=True) + U = pnp.array([[0, 1], [1, 0]], requires_grad=False) + a = pnp.array(0.1, requires_grad=True) def cost(a, U): ops = [qml.QubitUnitary(U, wires=0), qml.RY(a, wires=0)] @@ -535,8 +551,8 @@ def cost_fn(a, p): program, _ = device.preprocess(execution_config=config) return execute([tape], device, **execute_kwargs, transform_program=program)[0] - a = np.array(0.1, requires_grad=False) - p = np.array([0.1, 0.2, 0.3], requires_grad=True) + a = pnp.array(0.1, requires_grad=False) + p = pnp.array([0.1, 0.2, 0.3], requires_grad=True) res = cost_fn(a, p) expected = np.cos(a) * np.cos(p[1]) * np.sin(p[0]) + np.sin(a) * ( @@ -568,8 +584,8 @@ def cost(x, y): tape = qml.tape.QuantumScript(ops, m) return autograd.numpy.hstack(execute([tape], device, **execute_kwargs)[0]) - x = np.array(0.543, requires_grad=True) - y = np.array(-0.654, requires_grad=True) + x = pnp.array(0.543, requires_grad=True) + y = pnp.array(-0.654, requires_grad=True) res = cost(x, y) expected = np.array( @@ -621,8 +637,8 @@ def cost(x, y): tape = qml.tape.QuantumScript(ops, m) return autograd.numpy.hstack(execute([tape], device, **execute_kwargs)[0]) - x = np.array(0.543, requires_grad=True) - y = np.array(-0.654, requires_grad=True) + x = pnp.array(0.543, requires_grad=True) + y = pnp.array(-0.654, requires_grad=True) res = cost(x, y) expected = np.array( @@ -650,9 +666,9 @@ class TestHigherOrderDerivatives: @pytest.mark.parametrize( "params", [ - np.array([0.543, -0.654], requires_grad=True), - np.array([0, -0.654], requires_grad=True), - np.array([-2.0, 0], requires_grad=True), + pnp.array([0.543, -0.654], requires_grad=True), + pnp.array([0, -0.654], requires_grad=True), + pnp.array([-2.0, 0], requires_grad=True), ], ) def test_parameter_shift_hessian(self, params, tol): @@ -693,7 +709,7 @@ def test_max_diff(self, tol): """Test that setting the max_diff parameter blocks higher-order derivatives""" dev = DefaultQubit() - params = np.array([0.543, -0.654], requires_grad=True) + params = pnp.array([0.543, -0.654], requires_grad=True) def cost_fn(x): ops = [qml.RX(x[0], 0), qml.RY(x[1], 1), qml.CNOT((0, 1))] @@ -788,11 +804,11 @@ def test_multiple_hamiltonians_not_trainable(self, execute_kwargs, cost_fn, shot """Test hamiltonian with no trainable parameters.""" if execute_kwargs["gradient_fn"] == "adjoint" and not qml.operation.active_new_opmath(): - pytest.skip("adjoint differentiation does not suppport hamiltonians.") + pytest.skip("adjoint differentiation does not support hamiltonians.") - coeffs1 = np.array([0.1, 0.2, 0.3], requires_grad=False) - coeffs2 = np.array([0.7], requires_grad=False) - weights = np.array([0.4, 0.5], requires_grad=True) + coeffs1 = pnp.array([0.1, 0.2, 0.3], requires_grad=False) + coeffs2 = pnp.array([0.7], requires_grad=False) + weights = pnp.array([0.4, 0.5], requires_grad=True) res = cost_fn(weights, coeffs1, coeffs2) expected = self.cost_fn_expected(weights, coeffs1, coeffs2) @@ -817,9 +833,9 @@ def test_multiple_hamiltonians_trainable(self, execute_kwargs, cost_fn, shots): if qml.operation.active_new_opmath(): pytest.skip("parameter shift derivatives do not yet support sums.") - coeffs1 = np.array([0.1, 0.2, 0.3], requires_grad=True) - coeffs2 = np.array([0.7], requires_grad=True) - weights = np.array([0.4, 0.5], requires_grad=True) + coeffs1 = pnp.array([0.1, 0.2, 0.3], requires_grad=True) + coeffs2 = pnp.array([0.7], requires_grad=True) + weights = pnp.array([0.4, 0.5], requires_grad=True) res = cost_fn(weights, coeffs1, coeffs2) expected = self.cost_fn_expected(weights, coeffs1, coeffs2) @@ -829,11 +845,11 @@ def test_multiple_hamiltonians_trainable(self, execute_kwargs, cost_fn, shots): else: assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) - res = np.hstack(qml.jacobian(cost_fn)(weights, coeffs1, coeffs2)) + res = pnp.hstack(qml.jacobian(cost_fn)(weights, coeffs1, coeffs2)) expected = self.cost_fn_jacobian(weights, coeffs1, coeffs2) if shots.has_partitioned_shots: pytest.xfail( "multiple hamiltonians with shot vectors does not seem to be differentiable." ) else: - assert np.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) + assert qml.math.allclose(res, expected, atol=atol_for_shots(shots), rtol=0) diff --git a/tests/interfaces/test_autograd_qnode.py b/tests/interfaces/test_autograd_qnode.py index 129ab56dfe8..1d6dcfe397b 100644 --- a/tests/interfaces/test_autograd_qnode.py +++ b/tests/interfaces/test_autograd_qnode.py @@ -37,6 +37,7 @@ [ParamShiftDerivativesDevice(), "best", False, False], [ParamShiftDerivativesDevice(), "parameter-shift", True, False], [ParamShiftDerivativesDevice(), "parameter-shift", False, True], + [qml.device("reference.qubit"), "parameter-shift", False, False], ] interface_qubit_device_and_diff_method = [ @@ -62,6 +63,7 @@ ["auto", DefaultQubit(), "hadamard", False, False], ["auto", qml.device("lightning.qubit", wires=5), "adjoint", False, False], ["auto", qml.device("lightning.qubit", wires=5), "adjoint", True, False], + ["auto", qml.device("reference.qubit"), "parameter-shift", False, False], ] pytestmark = pytest.mark.autograd @@ -1378,6 +1380,8 @@ def test_projector( """Test that the variance of a projector is correctly returned""" if diff_method == "adjoint": pytest.skip("adjoint supports either expvals or diagonal measurements.") + if dev.name == "reference.qubit": + pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") kwargs = dict( diff_method=diff_method, interface=interface, @@ -1435,6 +1439,8 @@ def test_postselection_differentiation( if diff_method in ["adjoint", "spsa", "hadamard"]: pytest.skip("Diff method does not support postselection.") + if dev.name == "reference.qubit": + pytest.skip("reference.qubit does not support postselection.") @qml.qnode( dev, diff --git a/tests/interfaces/test_jacobian_products.py b/tests/interfaces/test_jacobian_products.py index 3d758c38642..6967aad515e 100644 --- a/tests/interfaces/test_jacobian_products.py +++ b/tests/interfaces/test_jacobian_products.py @@ -31,8 +31,6 @@ ) dev = qml.device("default.qubit") -with pytest.warns(qml.PennyLaneDeprecationWarning): - dev_old = qml.device("default.qubit.legacy", wires=5) dev_lightning = qml.device("lightning.qubit", wires=5) adjoint_config = qml.devices.ExecutionConfig(gradient_method="adjoint") dev_ps = ParamShiftDerivativesDevice() @@ -52,20 +50,18 @@ def inner_execute_numpy(tapes): inner_execute_numpy, qml.gradients.hadamard_grad, {"aux_wire": "aux"} ) device_jacs = DeviceDerivatives(dev, adjoint_config) -legacy_device_jacs = DeviceDerivatives(dev_old, execution_config=aj_config) device_ps_jacs = DeviceDerivatives(dev_ps, ps_config) device_native_jps = DeviceJacobianProducts(dev, adjoint_config) device_ps_native_jps = DeviceJacobianProducts(dev_ps, ps_config) lightning_vjps = DeviceJacobianProducts(dev_lightning, execution_config=adjoint_config) transform_jpc_matrix = [param_shift_jpc, param_shift_cached_jpc, hadamard_grad_jpc] -dev_jpc_matrix = [device_jacs, legacy_device_jacs, device_ps_jacs] +dev_jpc_matrix = [device_jacs, device_ps_jacs] jpc_matrix = [ param_shift_jpc, param_shift_cached_jpc, hadamard_grad_jpc, device_jacs, - legacy_device_jacs, device_ps_jacs, device_native_jps, device_ps_native_jps, diff --git a/tests/interfaces/test_jax.py b/tests/interfaces/test_jax.py index 519c0daa028..1c12ba0b524 100644 --- a/tests/interfaces/test_jax.py +++ b/tests/interfaces/test_jax.py @@ -122,16 +122,22 @@ def cost(x, cache): # add tests for lightning 2 when possible # set rng for device when possible no_shots = Shots(None) +shots_10k = Shots(10000) shots_2_10k = Shots((10000, 10000)) -dev_def = DefaultQubit() +dev_def = DefaultQubit(seed=42) dev_ps = ParamShiftDerivativesDevice(seed=54353453) +dev_ref = qml.device("reference.qubit") test_matrix = [ - ({"gradient_fn": param_shift}, Shots(100000), DefaultQubit(seed=42)), # 0 - ({"gradient_fn": param_shift}, no_shots, dev_def), # 1 - ({"gradient_fn": "backprop"}, no_shots, dev_def), # 2 - ({"gradient_fn": "adjoint"}, no_shots, dev_def), # 3 - ({"gradient_fn": "adjoint", "device_vjp": True}, no_shots, dev_def), # 4 - ({"gradient_fn": "device"}, shots_2_10k, dev_ps), # 5 + ({"gradient_fn": param_shift}, shots_10k, dev_def), # 0 + ({"gradient_fn": param_shift}, shots_2_10k, dev_def), # 1 + ({"gradient_fn": param_shift}, no_shots, dev_def), # 2 + ({"gradient_fn": "backprop"}, no_shots, dev_def), # 3 + ({"gradient_fn": "adjoint"}, no_shots, dev_def), # 4 + ({"gradient_fn": "adjoint", "device_vjp": True}, no_shots, dev_def), # 5 + ({"gradient_fn": "device"}, shots_2_10k, dev_ps), # 6 + ({"gradient_fn": param_shift}, no_shots, dev_ref), # 7 + ({"gradient_fn": param_shift}, shots_10k, dev_ref), # 8 + ({"gradient_fn": param_shift}, shots_2_10k, dev_ref), # 9 ] diff --git a/tests/interfaces/test_jax_jit_qnode.py b/tests/interfaces/test_jax_jit_qnode.py index cce76a83b9e..99ac281ef8f 100644 --- a/tests/interfaces/test_jax_jit_qnode.py +++ b/tests/interfaces/test_jax_jit_qnode.py @@ -41,6 +41,7 @@ [qml.device("lightning.qubit", wires=5), "adjoint", False, False], [qml.device("lightning.qubit", wires=5), "adjoint", True, True], [qml.device("lightning.qubit", wires=5), "parameter-shift", False, False], + [qml.device("reference.qubit"), "parameter-shift", False, False], ] interface_and_qubit_device_and_diff_method = [ ["auto"] + inner_list for inner_list in qubit_device_and_diff_method @@ -812,6 +813,7 @@ def circuit(a, b): res = circuit(a, b, shots=100) # pylint: disable=unexpected-keyword-arg assert res.shape == (100, 2) # pylint:disable=comparison-with-callable + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_gradient_integration(self, interface): """Test that temporarily setting the shots works for gradient computations""" @@ -911,6 +913,7 @@ def circuit(x): class TestQubitIntegration: """Tests that ensure various qubit circuits integrate correctly""" + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_sampling(self, dev, diff_method, grad_on_execution, device_vjp, interface): """Test sampling works as expected""" if grad_on_execution: @@ -940,6 +943,7 @@ def circuit(): assert isinstance(res[1], jax.Array) assert res[1].shape == (10,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_counts(self, dev, diff_method, grad_on_execution, device_vjp, interface): """Test counts works as expected""" if grad_on_execution: @@ -1040,6 +1044,8 @@ def test_postselection_differentiation( pytest.xfail("gradient transforms have a different vjp shape convention") elif dev.name == "lightning.qubit": pytest.xfail("lightning qubit does not support postselection.") + if dev.name == "reference.qubit": + pytest.skip("reference.qubit does not support postselection.") @qml.qnode( dev, diff_method=diff_method, interface=interface, grad_on_execution=grad_on_execution @@ -1431,6 +1437,8 @@ def test_projector( elif diff_method == "spsa": gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} tol = TOL_FOR_SPSA + if dev.name == "reference.qubit": + pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") P = jax.numpy.array(state) x, y = 0.765, -0.654 @@ -1514,6 +1522,11 @@ def test_hamiltonian_expansion_analytic( are non-commuting groups and the number of shots is None and the first and second order gradients are correctly evaluated""" gradient_kwargs = {} + if dev.name == "reference.qubit": + pytest.skip( + "Cannot add transform to the transform program in preprocessing" + "when using mocker.spy on it." + ) if dev.name == "param_shift.qubit": pytest.xfail("gradients transforms have a different vjp shape convention.") if diff_method == "adjoint": @@ -1840,6 +1853,9 @@ def test_hermitian( to different reasons, hence the parametrization in the test. """ # pylint: disable=unused-argument + if dev.name == "reference.qubit": + pytest.xfail("diagonalize_measurements do not support Hermitians (sc-72911)") + if diff_method == "backprop": pytest.skip("Backpropagation is unsupported if shots > 0.") @@ -2028,6 +2044,7 @@ def circ(p, U): class TestReturn: """Class to test the shape of the Grad/Jacobian with different return types.""" + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_grad_single_measurement_param( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2060,6 +2077,7 @@ def circuit(a): assert isinstance(grad, jax.numpy.ndarray) assert grad.shape == () + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_grad_single_measurement_multiple_param( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2097,6 +2115,7 @@ def circuit(a, b): assert grad[0].shape == () assert grad[1].shape == () + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_grad_single_measurement_multiple_param_array( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2129,6 +2148,7 @@ def circuit(a): assert isinstance(grad, jax.numpy.ndarray) assert grad.shape == (2,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_single_measurement_param_probs( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2162,6 +2182,7 @@ def circuit(a): assert isinstance(jac, jax.numpy.ndarray) assert jac.shape == (4,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_single_measurement_probs_multiple_param( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2201,6 +2222,7 @@ def circuit(a, b): assert isinstance(jac[1], jax.numpy.ndarray) assert jac[1].shape == (4,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_single_measurement_probs_multiple_param_single_array( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2233,6 +2255,7 @@ def circuit(a): assert isinstance(jac, jax.numpy.ndarray) assert jac.shape == (4, 2) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_expval_expval_multiple_params( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2282,6 +2305,7 @@ def circuit(x, y): assert isinstance(jac[1][1], jax.numpy.ndarray) assert jac[1][1].shape == () + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_expval_expval_multiple_params_array( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2320,6 +2344,7 @@ def circuit(a): assert isinstance(jac[1], jax.numpy.ndarray) assert jac[1].shape == (2,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_var_var_multiple_params( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2372,6 +2397,7 @@ def circuit(x, y): assert isinstance(jac[1][1], jax.numpy.ndarray) assert jac[1][1].shape == () + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_var_var_multiple_params_array( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2412,6 +2438,7 @@ def circuit(a): assert isinstance(jac[1], jax.numpy.ndarray) assert jac[1].shape == (2,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_multiple_measurement_single_param( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2450,6 +2477,7 @@ def circuit(a): assert isinstance(jac[1], jax.numpy.ndarray) assert jac[1].shape == (4,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_multiple_measurement_multiple_param( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2497,6 +2525,7 @@ def circuit(a, b): assert isinstance(jac[1][1], jax.numpy.ndarray) assert jac[1][1].shape == (4,) + @pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") def test_jacobian_multiple_measurement_multiple_param_array( self, dev, diff_method, grad_on_execution, device_vjp, jacobian, shots, interface ): @@ -2858,6 +2887,7 @@ def circuit(x): assert hess[1].shape == (2, 2, 2) +@pytest.mark.xfail(reason="'shots' cannot be a static_argname for 'jit' in JAX 0.4.28") @pytest.mark.parametrize("hessian", hessian_fn) @pytest.mark.parametrize("diff_method", ["parameter-shift", "hadamard"]) def test_jax_device_hessian_shots(hessian, diff_method): diff --git a/tests/interfaces/test_jax_qnode.py b/tests/interfaces/test_jax_qnode.py index d24dec3383d..d536288fa4b 100644 --- a/tests/interfaces/test_jax_qnode.py +++ b/tests/interfaces/test_jax_qnode.py @@ -40,6 +40,7 @@ [qml.device("lightning.qubit", wires=5), "adjoint", True, True], [qml.device("lightning.qubit", wires=5), "adjoint", False, False], [qml.device("lightning.qubit", wires=5), "adjoint", True, False], + [qml.device("reference.qubit"), "parameter-shift", False, False], ] interface_and_device_and_diff_method = [ @@ -845,7 +846,10 @@ def test_counts(self, dev, diff_method, grad_on_execution, device_vjp): def circuit(): qml.Hadamard(wires=[0]) qml.CNOT(wires=[0, 1]) - return qml.counts(qml.PauliZ(0)), qml.counts(qml.PauliX(1)) + return ( + qml.counts(qml.PauliZ(0), all_outcomes=True), + qml.counts(qml.PauliX(1), all_outcomes=True), + ) res = circuit(shots=10) @@ -911,6 +915,8 @@ def test_postselection_differentiation(self, dev, diff_method, grad_on_execution if diff_method in ["adjoint", "spsa", "hadamard"]: pytest.skip("Diff method does not support postselection.") + if dev.name == "reference.qubit": + pytest.xfail("reference.qubit does not support postselection.") @qml.qnode( dev, @@ -1298,6 +1304,8 @@ def test_projector( elif diff_method == "spsa": gradient_kwargs = {"h": H_FOR_SPSA, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} tol = TOL_FOR_SPSA + if dev.name == "reference.qubit": + pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") P = jax.numpy.array(state) x, y = 0.765, -0.654 @@ -1373,7 +1381,7 @@ def circuit(x, y): jax.grad(circuit, argnums=[0])(x, y) @pytest.mark.parametrize("max_diff", [1, 2]) - def test_hamiltonian_expansion_analytic( + def test_split_non_commuting_analytic( self, dev, diff_method, grad_on_execution, max_diff, interface, device_vjp, mocker, tol ): """Test that the Hamiltonian is not expanded if there @@ -1391,6 +1399,11 @@ def test_hamiltonian_expansion_analytic( "sampler_rng": np.random.default_rng(SEED_FOR_SPSA), } tol = TOL_FOR_SPSA + if dev.name == "reference.qubit": + pytest.skip( + "Cannot add transform to the transform program in preprocessing" + "when using mocker.spy on it." + ) spy = mocker.spy(qml.transforms, "split_non_commuting") obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] @@ -1451,6 +1464,13 @@ def test_hamiltonian_finite_shots( """Test that the Hamiltonian is correctly measured (and not expanded) if there are non-commuting groups and the number of shots is finite and the first and second order gradients are correctly evaluated""" + + if dev.name == "reference.qubit": + pytest.skip( + "Cannot added to a transform to the transform program in " + "preprocessing when using mocker.spy on it." + ) + gradient_kwargs = {} tol = 0.3 if diff_method in ("adjoint", "backprop", "finite-diff"): diff --git a/tests/interfaces/test_tensorflow.py b/tests/interfaces/test_tensorflow.py index b2329cd27c1..0abe82c1942 100644 --- a/tests/interfaces/test_tensorflow.py +++ b/tests/interfaces/test_tensorflow.py @@ -118,6 +118,16 @@ def cost(x, cache): ({"gradient_fn": "backprop", "interface": "tf-autograph"}, None, DefaultQubit()), # 6 ({"gradient_fn": "adjoint", "interface": "tf-autograph"}, None, DefaultQubit()), # 7 ({"gradient_fn": "adjoint", "interface": "tf", "device_vjp": True}, None, DefaultQubit()), # 8 + ( + {"gradient_fn": param_shift, "interface": "tensorflow"}, + None, + qml.device("reference.qubit"), + ), # 9 + ( + {"gradient_fn": param_shift, "interface": "tensorflow"}, + 100000, + qml.device("reference.qubit"), + ), # 10 ] diff --git a/tests/interfaces/test_tensorflow_qnode.py b/tests/interfaces/test_tensorflow_qnode.py index c09f1632202..c01d32091c6 100644 --- a/tests/interfaces/test_tensorflow_qnode.py +++ b/tests/interfaces/test_tensorflow_qnode.py @@ -38,6 +38,7 @@ [qml.device("lightning.qubit", wires=4), "adjoint", False, False], [qml.device("lightning.qubit", wires=4), "adjoint", True, True], [qml.device("lightning.qubit", wires=4), "adjoint", True, False], + [qml.device("reference.qubit"), "parameter-shift", False, False], ] TOL_FOR_SPSA = 1.0 @@ -980,6 +981,8 @@ def test_projector( kwargs["sampler_rng"] = np.random.default_rng(SEED_FOR_SPSA) kwargs["num_directions"] = 20 tol = TOL_FOR_SPSA + if dev.name == "reference.qubit": + pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") P = tf.constant(state, dtype=dtype) @@ -1014,6 +1017,9 @@ def test_postselection_differentiation( if diff_method in ["adjoint", "spsa", "hadamard"]: pytest.skip("Diff method does not support postselection.") + if dev.name == "reference.qubit": + pytest.skip("reference.qubit does not support postselection.") + @qml.qnode( dev, diff_method=diff_method, diff --git a/tests/interfaces/test_torch.py b/tests/interfaces/test_torch.py index 3cdcf5eae30..3640d31de9c 100644 --- a/tests/interfaces/test_torch.py +++ b/tests/interfaces/test_torch.py @@ -159,6 +159,21 @@ def cost_cache(x): Shots((100000, 100000)), ParamShiftDerivativesDevice(), ), + ( + {"gradient_fn": param_shift}, + Shots(None), + qml.device("reference.qubit"), + ), + ( + {"gradient_fn": param_shift}, + Shots(100000), + qml.device("reference.qubit"), + ), + ( + {"gradient_fn": param_shift}, + Shots((100000, 100000)), + qml.device("reference.qubit"), + ), ] diff --git a/tests/interfaces/test_torch_qnode.py b/tests/interfaces/test_torch_qnode.py index 82dbda669d4..5ecf181d343 100644 --- a/tests/interfaces/test_torch_qnode.py +++ b/tests/interfaces/test_torch_qnode.py @@ -47,6 +47,7 @@ [ParamShiftDerivativesDevice(), "best", False, False], [ParamShiftDerivativesDevice(), "parameter-shift", True, False], [ParamShiftDerivativesDevice(), "parameter-shift", False, True], + [qml.device("reference.qubit"), "parameter-shift", False, False], ] interface_and_qubit_device_and_diff_method = [ @@ -1131,6 +1132,8 @@ def test_projector( tol = TOL_FOR_SPSA elif diff_method == "hadamard": pytest.skip("Hadamard does not support variances.") + if dev.name == "reference.qubit": + pytest.xfail("diagonalize_measurements do not support projectors (sc-72911)") P = torch.tensor(state, requires_grad=False) @@ -1167,6 +1170,9 @@ def test_postselection_differentiation( if diff_method in ["adjoint", "spsa", "hadamard"]: pytest.skip("Diff method does not support postselection.") + if dev.name == "reference.qubit": + pytest.skip("reference.qubit does not support postselection.") + @qml.qnode( dev, diff_method=diff_method, diff --git a/tests/interfaces/test_transform_program_integration.py b/tests/interfaces/test_transform_program_integration.py index 6a3a58d3735..c346ea86810 100644 --- a/tests/interfaces/test_transform_program_integration.py +++ b/tests/interfaces/test_transform_program_integration.py @@ -25,12 +25,10 @@ from pennylane.tape import QuantumScript, QuantumScriptBatch from pennylane.typing import PostprocessingFn -with pytest.warns(qml.PennyLaneDeprecationWarning): - device_suite = ( - qml.device("default.qubit.legacy", wires=5), - qml.devices.DefaultQubit(), - qml.device("lightning.qubit", wires=5), - ) +device_suite = ( + qml.device("default.qubit"), + qml.device("lightning.qubit", wires=5), +) @pytest.mark.all_interfaces diff --git a/tests/logging/test_logging_autograd.py b/tests/logging/test_logging_autograd.py index 8ae7a9baaa2..f1468358935 100644 --- a/tests/logging/test_logging_autograd.py +++ b/tests/logging/test_logging_autograd.py @@ -106,7 +106,7 @@ def circuit(params): "diff_method,num_records", [("parameter-shift", 23), ("backprop", 14), ("adjoint", 18)] ) def test_dq_qnode_execution_grad(self, caplog, diff_method, num_records): - "Test logging of QNode with parameterised gradients" + "Test logging of QNode with parametrized gradients" dev = qml.device("default.qubit", wires=2) params = qml.numpy.array(0.1234) diff --git a/tests/measurements/test_classical_shadow.py b/tests/measurements/test_classical_shadow.py index de44c4e8ad7..0bb9443f895 100644 --- a/tests/measurements/test_classical_shadow.py +++ b/tests/measurements/test_classical_shadow.py @@ -719,12 +719,12 @@ def test_backward_torch(self, obs=obs_strongly_entangled): assert qml.math.allclose(actual, qml.math.stack(expected), atol=1e-1) -def get_basis_circuit(wires, shots, basis, interface="autograd", device="default.qubit.legacy"): +def get_basis_circuit(wires, shots, basis, interface="autograd", device="default.mixed"): """ Return a QNode that prepares a state in a given computational basis and performs a classical shadow measurement """ - dev = qml.device(device or "default.qubit.legacy", wires=wires, shots=shots) + dev = qml.device(device or "default.mixed", wires=wires, shots=shots) @qml.qnode(dev, interface=interface) def circuit(): diff --git a/tests/measurements/test_measurements.py b/tests/measurements/test_measurements.py index 3019da7ddba..3e86a61de32 100644 --- a/tests/measurements/test_measurements.py +++ b/tests/measurements/test_measurements.py @@ -41,6 +41,7 @@ StateMP, Variance, VarianceMP, + VnEntanglementEntropyMP, VnEntropyMP, expval, sample, @@ -184,6 +185,7 @@ class DummyMP(MeasurementProcess): VarianceMP(eigvals=[0.6, 0.7], wires=Wires(0)), VarianceMP(obs=mv), VnEntropyMP(wires=Wires("a"), log_base=3), + VnEntanglementEntropyMP(wires=(Wires("a"), Wires("b")), log_base=3), ] @@ -511,6 +513,7 @@ def test_has_decomposition_false_no_observable(self): CountsMP(wires=["a", 1]), StateMP(), VnEntropyMP(wires=["a", 1]), + VnEntanglementEntropyMP(wires=[["a", 1], ["b", 2]]), MutualInfoMP(wires=[["a", 1], ["b", 2]]), ProbabilityMP(wires=["a", 1]), ], @@ -699,6 +702,7 @@ class TestMeasurementProcess: (qml.state(), (8,)), (qml.density_matrix(wires=[0, 1]), (4, 4)), (qml.mutual_info(wires0=[0], wires1=[1]), ()), + (qml.vn_entanglement_entropy(wires0=[0], wires1=[1]), ()), (qml.vn_entropy(wires=[0, 1]), ()), ] @@ -711,6 +715,7 @@ class TestMeasurementProcess: (qml.sample(qml.PauliZ(0)), (10,)), (qml.sample(), (10, 3)), (qml.mutual_info(wires0=0, wires1=1), ()), + (qml.vn_entanglement_entropy(wires0=[0], wires1=[1]), ()), (qml.vn_entropy(wires=[0, 1]), ()), ] diff --git a/tests/measurements/test_mutual_info.py b/tests/measurements/test_mutual_info.py index a47284f744b..fa0ab30b83d 100644 --- a/tests/measurements/test_mutual_info.py +++ b/tests/measurements/test_mutual_info.py @@ -22,6 +22,12 @@ from pennylane.measurements.mutual_info import MutualInfoMP from pennylane.wires import Wires +DEP_WARNING_MESSAGE_MUTUAL_INFO = ( + "The qml.qinfo.mutual_info transform is deprecated and will be removed " + "in v0.40. Instead include the qml.mutual_info measurement process in the " + "return line of your QNode." +) + class TestMutualInfoUnitTests: """Tests for the mutual_info function""" @@ -143,7 +149,11 @@ def circuit(params): qml.CNOT(wires=[0, 1]) return qml.state() - actual = qml.qinfo.mutual_info(circuit, wires0=[0], wires1=[1])(params) + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match=DEP_WARNING_MESSAGE_MUTUAL_INFO, + ): + actual = qml.qinfo.mutual_info(circuit, wires0=[0], wires1=[1])(params) # compare transform results with analytic values expected = -2 * np.cos(params / 2) ** 2 * np.log( @@ -175,12 +185,13 @@ def circuit_state(params): qml.RY(params[0], wires=0) qml.RY(params[1], wires=1) qml.CNOT(wires=[0, 1]) - return qml.state() + return qml.density_matrix(wires=[0, 1]) actual = circuit_mutual_info(params) # compare measurement results with transform results - expected = qml.qinfo.mutual_info(circuit_state, wires0=[0], wires1=[1])(params) + dm = circuit_state(params) + expected = qml.math.mutual_info(dm, indices0=[0], indices1=[1]) assert np.allclose(actual, expected) @@ -197,7 +208,11 @@ def circuit(param): qml.CNOT(wires=wires) return qml.state() - actual = qml.qinfo.mutual_info(circuit, wires0=[wires[0]], wires1=[wires[1]])(param) + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match=DEP_WARNING_MESSAGE_MUTUAL_INFO, + ): + actual = qml.qinfo.mutual_info(circuit, wires0=[wires[0]], wires1=[wires[1]])(param) # compare transform results with analytic values expected = -2 * np.cos(param / 2) ** 2 * np.log(np.cos(param / 2) ** 2) - 2 * np.sin( @@ -236,7 +251,11 @@ def circuit(params): transformed_circuit = qml.qinfo.mutual_info(circuit, wires0=[0], wires1=[1]) with pytest.raises(ValueError, match="The qfunc return type needs to be a state"): - _ = transformed_circuit(0.1) + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match=DEP_WARNING_MESSAGE_MUTUAL_INFO, + ): + _ = transformed_circuit(0.1) @pytest.mark.jax @pytest.mark.parametrize("params", np.linspace(0, 2 * np.pi, 8)) @@ -256,7 +275,11 @@ def circuit(params): qml.CNOT(wires=[0, 1]) return qml.state() - actual = jax.jit(qml.qinfo.mutual_info(circuit, wires0=[0], wires1=[1]))(params) + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match=DEP_WARNING_MESSAGE_MUTUAL_INFO, + ): + actual = jax.jit(qml.qinfo.mutual_info(circuit, wires0=[0], wires1=[1]))(params) # compare transform results with analytic values expected = -2 * jnp.cos(params / 2) ** 2 * jnp.log( @@ -290,12 +313,13 @@ def circuit_state(params): qml.RY(params[0], wires=0) qml.RY(params[1], wires=1) qml.CNOT(wires=[0, 1]) - return qml.state() + return qml.density_matrix(wires=[0, 1]) actual = jax.jit(circuit_mutual_info)(params) # compare measurement results with transform results - expected = jax.jit(qml.qinfo.mutual_info(circuit_state, wires0=[0], wires1=[1]))(params) + dm = circuit_state(params) + expected = qml.math.mutual_info(dm, indices0=[0], indices1=[1]) assert np.allclose(actual, expected) @@ -513,8 +537,11 @@ def circuit_expected(params): qml.RY(params[0], wires="a") qml.RY(params[1], wires="b") qml.CNOT(wires=["a", "b"]) - return qml.state() + return qml.density_matrix(wires=["a", "b"]) actual = circuit(params) - expected = qml.qinfo.mutual_info(circuit_expected, wires0=["a"], wires1=["b"])(params) + + dm = circuit_expected(params) + expected = qml.math.mutual_info(dm, indices0=[0], indices1=[1]) + assert np.allclose(actual, expected) diff --git a/tests/measurements/test_sample.py b/tests/measurements/test_sample.py index d31ce97d4a5..9bf45028e46 100644 --- a/tests/measurements/test_sample.py +++ b/tests/measurements/test_sample.py @@ -458,7 +458,7 @@ class TestJAXCompatibility: @pytest.mark.parametrize("samples", (1, 10)) def test_jitting_with_sampling_on_subset_of_wires(self, samples): """Test case covering bug in Issue #3904. Sampling should be jit-able - when sampling occurs on a subset of wires. The bug was occuring due an improperly + when sampling occurs on a subset of wires. The bug was occurring due an improperly set shape method.""" import jax diff --git a/tests/measurements/test_vn_entanglement_entropy.py b/tests/measurements/test_vn_entanglement_entropy.py new file mode 100644 index 00000000000..c48e79c983c --- /dev/null +++ b/tests/measurements/test_vn_entanglement_entropy.py @@ -0,0 +1,413 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Unit tests for the vn_entanglement_entropy module""" +import copy + +import numpy as np +import pytest + +import pennylane as qml +from pennylane.measurements import VnEntanglementEntropy, VnEntanglementEntropyMP +from pennylane.wires import Wires + +# pylint: disable=too-many-arguments, no-member + + +def expected_entropy_ising_xx(param): + """ + Return the analytical entropy for the IsingXX. + """ + eig_1 = (1 + np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eig_2 = (1 - np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eigs = [eig_1, eig_2] + eigs = [eig for eig in eigs if eig > 0] + + expected_entropy = eigs * np.log(eigs) + + expected_entropy = -np.sum(expected_entropy) + return expected_entropy + + +def expected_entropy_grad_ising_xx(param): + """ + Return the analytical gradient entropy for the IsingXX. + """ + eig_1 = (1 + np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eig_2 = (1 - np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eigs = [eig_1, eig_2] + eigs = np.maximum(eigs, 1e-08) + + return -( + (np.log(eigs[0]) + 1) + * (np.sin(param / 2) ** 3 * np.cos(param / 2) - np.sin(param / 2) * np.cos(param / 2) ** 3) + / np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2) + ) - ( + (np.log(eigs[1]) + 1) + * ( + np.sin(param / 2) + * np.cos(param / 2) + * (np.cos(param / 2) ** 2 - np.sin(param / 2) ** 2) + ) + / np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2) + ) + + +class TestInitialization: + """Unit tests for the ``qml.vn_entanglement_entropy`` function.""" + + @pytest.mark.all_interfaces + @pytest.mark.parametrize( + "state_vector,expected", + [([1.0, 0.0, 0.0, 1.0] / qml.math.sqrt(2), qml.math.log(2)), ([1.0, 0.0, 0.0, 0.0], 0)], + ) + @pytest.mark.parametrize("interface", ["autograd", "jax", "tf", "torch"]) + def test_vn_entanglement_entropy(self, interface, state_vector, expected): + """Tests the output of qml.vn_entanglement_entropy""" + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev, interface=interface) + def circuit(): + qml.StatePrep(state_vector, wires=[0, 1]) + return qml.vn_entanglement_entropy(wires0=0, wires1=1) + + assert qml.math.allclose(circuit(), expected) + + def test_queue(self): + """Test that the right measurement class is queued.""" + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.vn_entanglement_entropy(wires0=0, wires1=1, log_base=2) + + circuit() + + assert isinstance(circuit.tape[0], VnEntanglementEntropyMP) + + def test_copy(self): + """Test that the ``__copy__`` method also copies the ``log_base`` information.""" + meas = qml.vn_entanglement_entropy(wires0=0, wires1=1, log_base=2) + meas_copy = copy.copy(meas) + assert meas_copy.log_base == 2 + assert meas_copy.wires == Wires([0, 1]) + + def test_properties(self): + """Test that the properties are correct.""" + meas = qml.vn_entanglement_entropy(wires0=0, wires1=1) + assert meas.numeric_type == float + assert meas.return_type == VnEntanglementEntropy + + @pytest.mark.parametrize("shots, shape", [(None, ()), (10, ())]) + def test_shape(self, shots, shape): + """Test the ``shape`` method.""" + meas = qml.vn_entanglement_entropy(wires0=0, wires1=1) + + assert meas.shape(shots, 1) == shape + + def test_overlapping_wires_error(self): + """Test that an error is raised if wires0 and wires1 have overlap""" + with pytest.raises( + qml.QuantumFunctionError, + match="Subsystems for computing entanglement entropy must not overlap", + ): + _ = qml.vn_entanglement_entropy(wires0=[0, 1], wires1=[1, 2]) + + +class TestIntegration: + """Integration tests for the vn_entanglement_entropy measurement function.""" + + parameters = np.linspace(0, 2 * np.pi, 10) + + devices = ["default.qubit", "default.mixed", "lightning.qubit"] + + wires_list = [ + [0, 1], + [1, 0], + ] + + base = [2, np.exp(1), 10] + + check_state = [True, False] + + diff_methods = ["backprop", "finite-diff"] + + @pytest.mark.parametrize("shots", [1000, [1, 10, 10, 1000]]) + def test_finite_shots_error(self, shots): + """Test an error is raised when using shot vectors with vn_entanglement_entropy.""" + dev = qml.device("default.qubit", wires=2, shots=shots) + + @qml.qnode(device=dev) + def circuit(x): + qml.Hadamard(wires=[0]) + qml.CRX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(wires0=[0], wires1=[1]) + + with pytest.raises( + qml.DeviceError, match="not accepted with finite shots on default.qubit" + ): + circuit(0.5) + + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("device", devices) + @pytest.mark.parametrize("base", base) + def test_IsingXX_qnode_entropy(self, param, wires, device, base): + """Test entropy for a QNode numpy.""" + + dev = qml.device(device, wires=2) + + @qml.qnode(dev) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + entropy = circuit_entropy(param) + + expected_entropy = expected_entropy_ising_xx(param) / np.log(base) + assert qml.math.allclose(entropy, expected_entropy) + + @pytest.mark.autograd + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("diff_method", diff_methods) + def test_IsingXX_qnode_entropy_grad(self, param, wires, base, diff_method): + """Test entropy for a QNode gradient with autograd.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev, diff_method=diff_method) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + grad_entropy = qml.grad(circuit_entropy)(param) + + # higher tolerance for finite-diff method + tol = 1e-8 if diff_method == "backprop" else 1e-5 + + grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) + assert qml.math.allclose(grad_entropy, grad_expected_entropy, atol=tol) + + @pytest.mark.torch + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("device", devices) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("interface", ["torch"]) + def test_IsingXX_qnode_torch_entropy(self, param, wires, device, base, interface): + """Test entropy for a QNode with torch interface.""" + import torch + + dev = qml.device(device, wires=2) + + @qml.qnode(dev, interface=interface) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + entropy = circuit_entropy(torch.tensor(param)) + + expected_entropy = expected_entropy_ising_xx(param) / np.log(base) + + assert qml.math.allclose(entropy, expected_entropy) + + @pytest.mark.torch + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("diff_method", diff_methods) + def test_IsingXX_qnode_entropy_grad_torch(self, param, wires, base, diff_method): + """Test entropy for a QNode gradient with torch.""" + import torch + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev, interface="torch", diff_method=diff_method) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) + + param = torch.tensor(param, dtype=torch.float64, requires_grad=True) + entropy = circuit_entropy(param) + entropy.backward() + grad_entropy = param.grad + + # higher tolerance for finite-diff method + tol = 1e-8 if diff_method == "backprop" else 1e-5 + + assert qml.math.allclose(grad_entropy, grad_expected_entropy, atol=tol) + + @pytest.mark.tf + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("device", devices) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("interface", ["tf"]) + def test_IsingXX_qnode_tf_entropy(self, param, wires, device, base, interface): + """Test entropy for a QNode with tf interface.""" + import tensorflow as tf + + dev = qml.device(device, wires=2) + + @qml.qnode(dev, interface=interface) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + entropy = circuit_entropy(tf.Variable(param)) + + expected_entropy = expected_entropy_ising_xx(param) / np.log(base) + + assert qml.math.allclose(entropy, expected_entropy) + + @pytest.mark.tf + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("diff_method", diff_methods) + @pytest.mark.parametrize("interface", ["tf"]) + def test_IsingXX_qnode_entropy_grad_tf(self, param, wires, base, diff_method, interface): + """Test entropy for a QNode gradient with tf.""" + import tensorflow as tf + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev, interface=interface, diff_method=diff_method) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + param = tf.Variable(param) + with tf.GradientTape() as tape: + entropy = circuit_entropy(param) + + grad_entropy = tape.gradient(entropy, param) + + grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) + + # higher tolerance for finite-diff method + tol = 1e-8 if diff_method == "backprop" else 1e-5 + + assert qml.math.allclose(grad_entropy, grad_expected_entropy, atol=tol) + + @pytest.mark.jax + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("device", devices) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("interface", ["jax"]) + def test_IsingXX_qnode_jax_entropy(self, param, wires, device, base, interface): + """Test entropy for a QNode with jax interface.""" + import jax.numpy as jnp + + dev = qml.device(device, wires=2) + + @qml.qnode(dev, interface=interface) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + entropy = circuit_entropy(jnp.array(param)) + + expected_entropy = expected_entropy_ising_xx(param) / np.log(base) + + assert qml.math.allclose(entropy, expected_entropy) + + @pytest.mark.jax + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("diff_method", diff_methods) + @pytest.mark.parametrize("interface", ["jax"]) + def test_IsingXX_qnode_entropy_grad_jax(self, param, wires, base, diff_method, interface): + """Test entropy for a QNode gradient with Jax.""" + import jax + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev, interface=interface, diff_method=diff_method) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + grad_entropy = jax.grad(circuit_entropy)(jax.numpy.array(param)) + grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) + + # higher tolerance for finite-diff method + tol = 1e-8 if diff_method == "backprop" else 1e-5 + + assert qml.math.allclose(grad_entropy, grad_expected_entropy, rtol=1e-04, atol=tol) + + @pytest.mark.jax + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("device", devices) + @pytest.mark.parametrize("interface", ["jax"]) + def test_IsingXX_qnode_jax_jit_entropy(self, param, wires, base, device, interface): + """Test entropy for a QNode with jax-jit interface.""" + import jax + import jax.numpy as jnp + + dev = qml.device(device, wires=2) + + @qml.qnode(dev, interface=interface) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + entropy = jax.jit(circuit_entropy)(jnp.array(param)) + + expected_entropy = expected_entropy_ising_xx(param) / np.log(base) + + assert qml.math.allclose(entropy, expected_entropy) + + @pytest.mark.jax + @pytest.mark.parametrize("wires", wires_list) + @pytest.mark.parametrize("param", parameters) + @pytest.mark.parametrize("base", base) + @pytest.mark.parametrize("diff_method", diff_methods) + @pytest.mark.parametrize("interface", ["jax-jit"]) + def test_IsingXX_qnode_entropy_grad_jax_jit(self, param, wires, base, diff_method, interface): + """Test entropy for a QNode gradient with Jax-jit.""" + import jax + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev, interface=interface, diff_method=diff_method) + def circuit_entropy(x): + qml.IsingXX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(*wires, log_base=base) + + grad_entropy = jax.jit(jax.grad(circuit_entropy))(jax.numpy.array(param)) + + grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) + + assert qml.math.allclose(grad_entropy, grad_expected_entropy, rtol=1e-04, atol=1e-05) + + def test_qnode_entropy_custom_wires(self): + """Test that entropy can be returned with custom wires.""" + # Note that this test will only work with devices that map custom wires to standard labels + # before execution. + dev = qml.device("default.qubit", wires=["a", 1]) + + @qml.qnode(dev) + def circuit_entropy(x): + qml.IsingXX(x, wires=["a", 1]) + return qml.vn_entanglement_entropy("a", 1) + + assert np.isclose(circuit_entropy(0.1), expected_entropy_ising_xx(0.1)) diff --git a/tests/ops/functions/conftest.py b/tests/ops/functions/conftest.py index 92863eb7ab1..692745b48b6 100644 --- a/tests/ops/functions/conftest.py +++ b/tests/ops/functions/conftest.py @@ -38,7 +38,6 @@ qml.sum(qml.X(0), qml.X(0), qml.Z(0), qml.Z(0)), qml.BasisState([1], wires=[0]), qml.ControlledQubitUnitary(np.eye(2), control_wires=1, wires=0), - qml.QubitStateVector([0, 1], wires=0), qml.QubitChannel([np.array([[1, 0], [0, 0.8]]), np.array([[0, 0.6], [0, 0]])], wires=0), qml.MultiControlledX(wires=[0, 1]), qml.Projector([1], 0), # the state-vector version is already tested @@ -137,6 +136,7 @@ PowOpObs, PowOperation, PowObs, + qml.QubitStateVector, } """Types that should not have actual instances created.""" diff --git a/tests/ops/functions/test_iterative_qpe.py b/tests/ops/functions/test_iterative_qpe.py index e06bdb9a4b9..e3507c60f8b 100644 --- a/tests/ops/functions/test_iterative_qpe.py +++ b/tests/ops/functions/test_iterative_qpe.py @@ -23,6 +23,36 @@ class TestIQPE: """Test to check that the iterative quantum phase estimation function works as expected.""" + def test_ancilla_deprecation(self): + """Test that the ancilla argument is deprecated and superceded by the aux_wire argument + if provided.""" + aux_wire = 1 + ancilla = 2 + + with pytest.warns(qml.PennyLaneDeprecationWarning, match="The 'ancilla' argument"): + meas1 = qml.iterative_qpe(qml.RZ(2.0, wires=0), ancilla=ancilla, iters=3) + meas2 = qml.iterative_qpe( + qml.RZ(2.0, wires=0), aux_wire=aux_wire, iters=3, ancilla=ancilla + ) + + assert all(m.wires == qml.wires.Wires(ancilla) for m in meas1) + assert all(m.wires == qml.wires.Wires(aux_wire) for m in meas2) + + @pytest.mark.parametrize( + "args, n_missing, missing_args", + [ + ({"aux_wire": 1}, 1, "'iters'"), + ({"ancilla": 1}, 1, "'iters'"), + ({"iters": 1}, 1, "'aux_wire'"), + ({}, 2, "'aux_wire' and 'iters'"), + ], + ) + def test_args_not_provided(self, args, n_missing, missing_args): + """Test that the correct error is raised if there are missing arguments""" + err_msg = rf"iterative_qpe\(\) missing {n_missing} required positional argument\(s\): {missing_args}" + with pytest.raises(TypeError, match=err_msg): + _ = qml.iterative_qpe(qml.RZ(1.5, 0), **args) + @pytest.mark.parametrize("mcm_method", ["deferred", "tree-traversal"]) @pytest.mark.parametrize("phi", (1.0, 2.0, 3.0)) def test_compare_qpe(self, mcm_method, phi): @@ -37,7 +67,7 @@ def circuit_iterative(): qml.PauliX(wires=[0]) # Iterative QPE - measurements = qml.iterative_qpe(qml.RZ(phi, wires=[0]), ancilla=[1], iters=3) + measurements = qml.iterative_qpe(qml.RZ(phi, wires=[0]), aux_wire=[1], iters=3) return [qml.sample(op=meas) for meas in measurements] @@ -206,7 +236,7 @@ def circuit_iterative(): qml.PauliX(wires=[0]) # Iterative QPE - measurements = qml.iterative_qpe(qml.RZ(phi, wires=[0]), ancilla=[1], iters=3) + measurements = qml.iterative_qpe(qml.RZ(phi, wires=[0]), aux_wire=[1], iters=3) return [qml.probs(op=i) for i in measurements] @@ -235,7 +265,7 @@ def circuit_iterative(): qml.PauliX(wires=[0]) # Iterative QPE - measurements = qml.iterative_qpe(qml.RZ(phi, wires=[0]), ancilla=[1], iters=3) + measurements = qml.iterative_qpe(qml.RZ(phi, wires=[0]), aux_wire=[1], iters=3) return [qml.expval(op=i) for i in measurements] diff --git a/tests/ops/op_math/test_adjoint.py b/tests/ops/op_math/test_adjoint.py index c8d20ead0b7..725537e8bcf 100644 --- a/tests/ops/op_math/test_adjoint.py +++ b/tests/ops/op_math/test_adjoint.py @@ -844,7 +844,7 @@ def test_error_adjoint_on_noncallable(obj): class TestAdjointConstructorPreconstructedOp: - """Test providing an already initalized operator to the transform.""" + """Test providing an already initialized operator to the transform.""" @pytest.mark.parametrize( "base", (qml.IsingXX(1.23, wires=("c", "d")), qml.QFT(wires=(0, 1, 2))) diff --git a/tests/ops/op_math/test_controlled_decompositions.py b/tests/ops/op_math/test_controlled_decompositions.py index 6c1adda9f92..03290fca0d5 100644 --- a/tests/ops/op_math/test_controlled_decompositions.py +++ b/tests/ops/op_math/test_controlled_decompositions.py @@ -120,13 +120,15 @@ def test_decomposition_circuit_general_ops(self, op, control_wires, tol): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) ctrl_decomp_zyz(op, Wires(control_wires)) return qml.probs() @qml.qnode(dev) def expected_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) qml.ctrl(op, control_wires) return qml.probs() @@ -144,7 +146,8 @@ def test_decomposition_circuit_general_ops_error(self, op, control_wires): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) ctrl_decomp_zyz(op, Wires(control_wires)) return qml.probs() @@ -230,14 +233,16 @@ def test_decomp_queues_correctly(self, op, control_wires, tol): @qml.qnode(dev) def queue_from_list(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) for o in decomp: qml.apply(o) return qml.state() @qml.qnode(dev) def queue_from_qnode(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) ctrl_decomp_zyz(op, control_wires=Wires(control_wires)) return qml.state() @@ -452,7 +457,8 @@ def test_decomposition_circuit(self, op, control_wires, tol): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) record_from_list(_ctrl_decomp_bisect_od)( _convert_to_su2(op.matrix()), op.wires, Wires(control_wires) ) @@ -460,7 +466,8 @@ def decomp_circuit(): @qml.qnode(dev) def expected_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) qml.ctrl(op, control_wires) return qml.probs() @@ -604,7 +611,8 @@ def test_decomposition_circuit(self, op, control_wires, tol): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) record_from_list(_ctrl_decomp_bisect_md)( _convert_to_su2(op.matrix()), op.wires, Wires(control_wires) ) @@ -612,7 +620,8 @@ def decomp_circuit(): @qml.qnode(dev) def expected_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) qml.ctrl(op, control_wires) return qml.probs() @@ -733,7 +742,8 @@ def test_decomposition_circuit(self, op, control_wires, auto, tol): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) if auto: ctrl_decomp_bisect(op, Wires(control_wires)) else: @@ -744,7 +754,8 @@ def decomp_circuit(): @qml.qnode(dev) def expected_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) qml.ctrl(op, control_wires) return qml.probs() @@ -877,13 +888,15 @@ def test_decomposition_circuit(self, op, control_wires, tol): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) _decompose_multicontrolled_unitary(op, Wires(control_wires)) return qml.probs() @qml.qnode(dev) def expected_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) qml.ctrl(op, control_wires) return qml.probs() @@ -982,7 +995,8 @@ def test_decomposition_circuit(self, op, control_wires, tol): @qml.qnode(dev) def decomp_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) record_from_list(_decompose_recursive)( op, 1.0, Wires(control_wires), op.wires, Wires([]) ) @@ -990,7 +1004,8 @@ def decomp_circuit(): @qml.qnode(dev) def expected_circuit(): - qml.broadcast(unitary=qml.Hadamard, pattern="single", wires=control_wires) + for wire in control_wires: + qml.Hadamard(wire) qml.ctrl(op, control_wires) return qml.probs() diff --git a/tests/ops/op_math/test_linear_combination.py b/tests/ops/op_math/test_linear_combination.py index 553427ce799..748f2bfb48a 100644 --- a/tests/ops/op_math/test_linear_combination.py +++ b/tests/ops/op_math/test_linear_combination.py @@ -580,14 +580,6 @@ def circuit2(param): class TestLinearCombination: """Test the LinearCombination class""" - def test_deprecation_simplify_argument(self): - """Test that a deprecation warning is raised if the simplify argument is True.""" - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - _ = qml.ops.LinearCombination([1.0], [qml.X(0)], simplify=True) - def test_error_if_observables_operator(self): """Test thatt an error is raised if an operator is provided to observables.""" @@ -613,13 +605,9 @@ def test_error_if_observables_operator(self): def test_pauli_rep(self, coeffs, ops, true_pauli, simplify): """Test the pauli rep is correctly constructed""" if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - H = qml.ops.LinearCombination(coeffs, ops, simplify=simplify) + H = qml.ops.LinearCombination(coeffs, ops).simplify() else: - H = qml.ops.LinearCombination(coeffs, ops, simplify=simplify) + H = qml.ops.LinearCombination(coeffs, ops) pr = H.pauli_rep if simplify: pr.simplify() @@ -1659,31 +1647,13 @@ def test_simplify_reduces_tape_parameters(self): @qml.qnode(device) def circuit(): qml.RY(0.1, wires=0) - return qml.expval(qml.ops.LinearCombination([1.0, 2.0], [X(1), X(1)], simplify=True)) + return qml.expval(qml.simplify(qml.ops.LinearCombination([1.0, 2.0], [X(1), X(1)]))) - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - circuit() + circuit() pars = circuit.qtape.get_parameters(trainable_only=False) # simplify worked and added 1. and 2. assert pars == [0.1, 3.0] - @pytest.mark.usefixtures("use_legacy_and_new_opmath") - def test_queuing_behaviour(self): - """Tests that the base observables are correctly dequeued with simplify=True""" - - with qml.queuing.AnnotatedQueue() as q: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - obs = qml.Hamiltonian([1, 1, 1], [qml.X(0), qml.X(0), qml.Z(0)], simplify=True) - - assert len(q) == 1 - assert q.queue[0] == obs - class TestLinearCombinationDifferentiation: """Test that the LinearCombination coefficients are differentiable""" @@ -1702,23 +1672,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.ops.LinearCombination( - coeffs, - [X(0), Z(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group)) + if simplify + else qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group) ) grad_fn = qml.grad(circuit) - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - grad = grad_fn(coeffs, param) - else: - grad = grad_fn(coeffs, param) + grad = grad_fn(coeffs, param) # differentiating a cost that combines circuits with # measurements expval(Pauli) @@ -1783,23 +1743,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.ops.LinearCombination( - coeffs, - [X(0), Z(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group)) + if simplify + else qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group) ) grad_fn = qml.grad(circuit) - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - grad = grad_fn(coeffs, param) - else: - grad = grad_fn(coeffs, param) + grad = grad_fn(coeffs, param) # differentiating a cost that combines circuits with # measurements expval(Pauli) @@ -1860,24 +1810,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.ops.LinearCombination( - coeffs, - [X(0), Z(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group)) + if simplify + else qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group) ) grad_fn = jax.grad(circuit) - - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - grad = grad_fn(coeffs, param) - else: - grad = grad_fn(coeffs, param) + grad = grad_fn(coeffs, param) # differentiating a cost that combines circuits with # measurements expval(Pauli) @@ -1938,22 +1877,12 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.ops.LinearCombination( - coeffs, - [X(0), Z(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group)) + if simplify + else qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group) ) - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - res = circuit(coeffs, param) - else: - res = circuit(coeffs, param) + res = circuit(coeffs, param) res.backward() # pylint:disable=no-member grad = (coeffs.grad, param.grad) @@ -2032,23 +1961,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.ops.LinearCombination( - coeffs, - [X(0), Z(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group)) + if simplify + else qml.ops.LinearCombination(coeffs, [X(0), Z(0)], grouping_type=group) ) with tf.GradientTape() as tape: - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - res = circuit(coeffs, param) - else: - res = circuit(coeffs, param) + res = circuit(coeffs, param) grad = tape.gradient(res, [coeffs, param]) # differentiating a cost that combines circuits with diff --git a/tests/ops/op_math/test_prod.py b/tests/ops/op_math/test_prod.py index 7963ce3fb6c..bebc7112f93 100644 --- a/tests/ops/op_math/test_prod.py +++ b/tests/ops/op_math/test_prod.py @@ -966,7 +966,7 @@ class DummyOp(Operator): # pylint:disable=too-few-public-methods prod_op = prod(qml.Identity(wires=0), DummyOp(wires=0)) assert prod_op._queue_category is None - def test_eigendecompostion(self): + def test_eigendecomposition(self): """Test that the computed Eigenvalues and Eigenvectors are correct.""" diag_prod_op = Prod(qml.PauliZ(wires=0), qml.PauliZ(wires=1)) eig_decomp = diag_prod_op.eigendecomposition diff --git a/tests/ops/op_math/test_sum.py b/tests/ops/op_math/test_sum.py index 85e6fd2bba3..c63fb0a4753 100644 --- a/tests/ops/op_math/test_sum.py +++ b/tests/ops/op_math/test_sum.py @@ -689,7 +689,7 @@ def test_eigvals_Identity_no_wires(self): op2 = qml.X(0) + 2 * qml.I(0) assert qml.math.allclose(sorted(op1.eigvals()), sorted(op2.eigvals())) - def test_eigendecompostion(self): + def test_eigendecomposition(self): """Test that the computed Eigenvalues and Eigenvectors are correct.""" diag_sum_op = Sum(qml.PauliZ(wires=0), qml.Identity(wires=1)) eig_decomp = diag_sum_op.eigendecomposition diff --git a/tests/ops/qubit/test_hamiltonian.py b/tests/ops/qubit/test_hamiltonian.py index 169988f8c80..b977a2f9f7a 100644 --- a/tests/ops/qubit/test_hamiltonian.py +++ b/tests/ops/qubit/test_hamiltonian.py @@ -703,15 +703,6 @@ def test_deprecation_with_new_opmath(recwarn): assert len(recwarn) == 0 -def test_deprecation_simplify_argument(): - """Test that a deprecation warning is raised if the simplify argument is True.""" - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - _ = qml.ops.Hamiltonian([1.0], [qml.X(0)], simplify=True) - - @pytest.mark.usefixtures("use_legacy_opmath") class TestHamiltonian: """Test the Hamiltonian class""" @@ -1103,7 +1094,7 @@ def test_hermitian_tensor_prod(self): assert isinstance(ham, qml.Hamiltonian) def test_hamiltonian_pauli_rep(self): - """Test that the pauli rep is set for a hamiltonain that is a linear combination of paulis.""" + """Test that the pauli rep is set for a hamiltonian that is a linear combination of paulis.""" h = qml.Hamiltonian([1.0, 2.0], [qml.X(0) @ qml.Y(1), qml.Z(0) @ qml.Z(2)]) pw1 = qml.pauli.PauliWord({0: "X", 1: "Y"}) @@ -1750,14 +1741,10 @@ def test_simplify_reduces_tape_parameters(self): def circuit(): qml.RY(0.1, wires=0) return qml.expval( - qml.Hamiltonian([1.0, 2.0], [qml.PauliX(1), qml.PauliX(1)], simplify=True) + qml.simplify(qml.Hamiltonian([1.0, 2.0], [qml.PauliX(1), qml.PauliX(1)])) ) - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - circuit() + circuit() pars = circuit.qtape.get_parameters(trainable_only=False) # simplify worked and added 1. and 2. assert pars == [0.1, 3.0] @@ -1781,24 +1768,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.Hamiltonian( - coeffs, - [qml.PauliX(0), qml.PauliZ(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group)) + if simplify + else qml.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group) ) grad_fn = qml.grad(circuit) - - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - grad = grad_fn(coeffs, param) - else: - grad = grad_fn(coeffs, param) + grad = grad_fn(coeffs, param) # differentiating a cost that combines circuits with # measurements expval(Pauli) @@ -1863,24 +1839,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.Hamiltonian( - coeffs, - [qml.PauliX(0), qml.PauliZ(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group)) + if simplify + else qml.ops.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group) ) grad_fn = qml.grad(circuit) - - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - grad = grad_fn(coeffs, param) - else: - grad = grad_fn(coeffs, param) + grad = grad_fn(coeffs, param) # differentiating a cost that combines circuits with # measurements expval(Pauli) @@ -1941,23 +1906,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.Hamiltonian( - coeffs, - [qml.PauliX(0), qml.PauliZ(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group)) + if simplify + else qml.ops.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group) ) grad_fn = jax.grad(circuit) - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - grad = grad_fn(coeffs, param) - else: - grad = grad_fn(coeffs, param) + grad = grad_fn(coeffs, param) # differentiating a cost that combines circuits with # measurements expval(Pauli) @@ -2017,23 +1972,12 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.Hamiltonian( - coeffs, - [qml.PauliX(0), qml.PauliZ(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group)) + if simplify + else qml.ops.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group) ) - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - res = circuit(coeffs, param) - else: - res = circuit(coeffs, param) - + res = circuit(coeffs, param) res.backward() # pylint:disable=no-member grad = (coeffs.grad, param.grad) @@ -2112,23 +2056,13 @@ def circuit(coeffs, param): qml.RX(param, wires=0) qml.RY(param, wires=0) return qml.expval( - qml.Hamiltonian( - coeffs, - [qml.PauliX(0), qml.PauliZ(0)], - simplify=simplify, - grouping_type=group, - ) + qml.simplify(qml.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group)) + if simplify + else qml.ops.Hamiltonian(coeffs, [qml.X(0), qml.Z(0)], grouping_type=group) ) with tf.GradientTape() as tape: - if simplify: - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="deprecated", - ): - res = circuit(coeffs, param) - else: - res = circuit(coeffs, param) + res = circuit(coeffs, param) grad = tape.gradient(res, [coeffs, param]) diff --git a/tests/ops/qubit/test_observables.py b/tests/ops/qubit/test_observables.py index f7c7cb10dfd..66b4d7dbfa0 100644 --- a/tests/ops/qubit/test_observables.py +++ b/tests/ops/qubit/test_observables.py @@ -813,9 +813,8 @@ def test_matrix_representation(self, basis_state, expected, n_wires, tol): assert np.allclose(res_dynamic, expected, atol=tol) assert np.allclose(res_static, expected, atol=tol) - @pytest.mark.parametrize("dev_name", ("default.qubit", "default.qubit.legacy")) - def test_integration_batched_state(self, dev_name): - dev = qml.device(dev_name, wires=1) + def test_integration_batched_state(self): + dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def circuit(x): diff --git a/tests/ops/qubit/test_sparse.py b/tests/ops/qubit/test_sparse.py index d78b3e8a19d..97b39d34582 100644 --- a/tests/ops/qubit/test_sparse.py +++ b/tests/ops/qubit/test_sparse.py @@ -282,16 +282,13 @@ def circuit(): ), ], ) - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) - def test_sparse_hamiltonian_expval( - self, device_name, qubits, operations, hamiltonian, expected_output, tol - ): + def test_sparse_hamiltonian_expval(self, qubits, operations, hamiltonian, expected_output, tol): """Test that expectation values of sparse hamiltonians are properly calculated.""" # pylint: disable=too-many-arguments hamiltonian = csr_matrix(hamiltonian) - dev = qml.device(device_name, wires=qubits, shots=None) + dev = qml.device("default.qubit", wires=qubits, shots=None) qs = qml.tape.QuantumScript( operations, [qml.expval((qml.SparseHamiltonian(hamiltonian, range(qubits))))] ) diff --git a/tests/ops/qubit/test_state_prep.py b/tests/ops/qubit/test_state_prep.py index 342aaff5df0..e6da832a8eb 100644 --- a/tests/ops/qubit/test_state_prep.py +++ b/tests/ops/qubit/test_state_prep.py @@ -36,6 +36,12 @@ def test_adjoint_error_exception(op): op.adjoint() +def test_QubitStateVector_is_deprecated(): + """Test that QubitStateVector is deprecated.""" + with pytest.warns(qml.PennyLaneDeprecationWarning, match="QubitStateVector is deprecated"): + _ = qml.QubitStateVector([1, 0, 0, 0], wires=[0, 1]) + + @pytest.mark.parametrize( "op, mat, base", [ diff --git a/tests/optimize/test_qnspsa.py b/tests/optimize/test_qnspsa.py index ac21f190503..66fa613cdd6 100644 --- a/tests/optimize/test_qnspsa.py +++ b/tests/optimize/test_qnspsa.py @@ -103,7 +103,7 @@ def get_metric_from_single_input_qnode(params, finite_diff_step, tensor_dirs): perturb2 = dir2 * finite_diff_step def get_state_overlap(params1, params2): - # analytically computed state overlap between two parameterized ansatzes + # analytically computed state overlap between two parametrized ansatzes # with input params1 and params2 return ( np.cos(params1[0][0] / 2) * np.cos(params2[0][0] / 2) @@ -348,11 +348,10 @@ def test_step_and_cost_from_multi_input(self, finite_diff_step, seed): new_params_tensor_expected, ) - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) - def test_step_and_cost_with_non_trainable_input(self, device_name, finite_diff_step, seed): + def test_step_and_cost_with_non_trainable_input(self, finite_diff_step, seed): """ Test step_and_cost() function with the qnode with non-trainable input, - both using the `default.qubit` and `default.qubit.legacy` device. + both using the `default.qubit` device. """ regularization = 1e-3 stepsize = 1e-2 @@ -367,7 +366,7 @@ def test_step_and_cost_with_non_trainable_input(self, device_name, finite_diff_s ) # a deep copy of the same opt, to be applied to qnode_reduced target_opt = deepcopy(opt) - dev = qml.device(device_name, wires=2) + dev = qml.device("default.qubit", wires=2) non_trainable_param = np.random.rand(1) non_trainable_param.requires_grad = False diff --git a/tests/optimize/test_riemannian_gradient_optimizer.py b/tests/optimize/test_riemannian_gradient_optimizer.py index 74af2b24775..d3d3a91f71f 100644 --- a/tests/optimize/test_riemannian_gradient_optimizer.py +++ b/tests/optimize/test_riemannian_gradient_optimizer.py @@ -29,7 +29,7 @@ def circuit_1(): def circuit_2(): - """Simply parameterized circuit.""" + """Simply parametrized circuit.""" qml.RX(0.1, wires=[0]) qml.RY(0.5, wires=[1]) qml.CNOT(wires=[0, 1]) diff --git a/tests/qinfo/test_entropies.py b/tests/qinfo/test_entropies.py index 92b8e48971b..c275036a58e 100644 --- a/tests/qinfo/test_entropies.py +++ b/tests/qinfo/test_entropies.py @@ -19,6 +19,15 @@ import pennylane as qml from pennylane import numpy as np +pytestmark = [ + pytest.mark.filterwarnings( + r"ignore:The qml\.qinfo\.(vn_entropy|mutual_info|reduced_dm) transform:pennylane.PennyLaneDeprecationWarning" + ), + pytest.mark.filterwarnings( + "ignore:qml.qinfo.relative_entropy is deprecated:pennylane.PennyLaneDeprecationWarning" + ), +] + def expected_entropy_ising_xx(param): """ @@ -70,6 +79,21 @@ class TestVonNeumannEntropy: parameters = np.linspace(0, 2 * np.pi, 10) devices = ["default.qubit", "default.mixed", "lightning.qubit"] + def test_qinfo_vn_entropy_deprecated(self): + """Test that qinfo.vn_entropy is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.state() + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="The qml.qinfo.vn_entropy transform is deprecated", + ): + _ = qml.qinfo.vn_entropy(circuit, [0])() + def test_vn_entropy_cannot_specify_device(self): """Test that an error is raised if a device or device wires are given to the vn_entropy transform manually.""" @@ -102,7 +126,6 @@ def circuit_state(x): return qml.state() entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(param) - expected_entropy = expected_entropy_ising_xx(param) / np.log(base) assert qml.math.allclose(entropy, expected_entropy) @@ -124,7 +147,6 @@ def circuit_state(x): return qml.state() grad_entropy = qml.grad(qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base))(param) - grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) assert qml.math.allclose(grad_entropy, grad_expected_entropy) @@ -148,7 +170,6 @@ def circuit_state(x): return qml.state() entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(torch.tensor(param)) - expected_entropy = expected_entropy_ising_xx(param) / np.log(base) assert qml.math.allclose(entropy, expected_entropy) @@ -176,8 +197,8 @@ def circuit_state(x): grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) param = torch.tensor(param, dtype=torch.float64, requires_grad=True) - entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(param) + entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(param) entropy.backward() grad_entropy = param.grad @@ -203,7 +224,6 @@ def circuit_state(x): return qml.state() entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(tf.Variable(param)) - expected_entropy = expected_entropy_ising_xx(param) / np.log(base) assert qml.math.allclose(entropy, expected_entropy) @@ -229,7 +249,6 @@ def circuit_state(x): entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(param) grad_entropy = tape.gradient(entropy, param) - grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) assert qml.math.allclose(grad_entropy, grad_expected_entropy) @@ -254,7 +273,6 @@ def circuit_state(x): return qml.state() entropy = qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)(jnp.array(param)) - expected_entropy = expected_entropy_ising_xx(param) / np.log(base) assert qml.math.allclose(entropy, expected_entropy) @@ -278,7 +296,6 @@ def circuit_state(x): grad_entropy = jax.grad(qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base))( jax.numpy.array(param) ) - grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) assert qml.math.allclose(grad_entropy, grad_expected_entropy, rtol=1e-04, atol=1e-05) @@ -305,7 +322,6 @@ def circuit_state(x): entropy = jax.jit(qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base))( jnp.array(param) ) - expected_entropy = expected_entropy_ising_xx(param) / np.log(base) assert qml.math.allclose(entropy, expected_entropy) @@ -329,7 +345,6 @@ def circuit_state(x): grad_entropy = jax.jit( jax.grad(qml.qinfo.vn_entropy(circuit_state, wires=wires, base=base)) )(jax.numpy.array(param)) - grad_expected_entropy = expected_entropy_grad_ising_xx(param) / np.log(base) assert qml.math.allclose(grad_entropy, grad_expected_entropy, rtol=1e-04, atol=1e-05) @@ -361,7 +376,6 @@ def circuit_state(x): return qml.state() entropy = qml.qinfo.vn_entropy(circuit_state, wires=[0, 1])(param) - expected_entropy = 0.0 assert qml.math.allclose(entropy, expected_entropy) @@ -377,7 +391,6 @@ def circuit_state(x): entropy = qml.qinfo.vn_entropy(circuit_state, wires=[0, 1])(param) expected_entropy = 0.0 - assert qml.math.allclose(entropy, expected_entropy) @pytest.mark.parametrize("device", devices) @@ -394,10 +407,12 @@ def circuit(x): return qml.state() entropy0 = qml.qinfo.vn_entropy(circuit, wires=[wires[0]])(param) + eigs0 = [np.sin(param / 2) ** 2, np.cos(param / 2) ** 2] exp0 = -np.sum(eigs0 * np.log(eigs0)) entropy1 = qml.qinfo.vn_entropy(circuit, wires=[wires[1]])(param) + eigs1 = [np.cos(param / 2) ** 2, np.sin(param / 2) ** 2] exp1 = -np.sum(eigs1 * np.log(eigs1)) @@ -406,7 +421,7 @@ def circuit(x): class TestRelativeEntropy: - """Tests for the mutual information functions""" + """Tests for the relative entropy information functions""" diff_methods = ["backprop", "finite-diff"] @@ -415,6 +430,25 @@ class TestRelativeEntropy: # to avoid nan values in the gradient for relative entropy grad_params = [[0.123, 0.456], [0.789, 1.618]] + def test_qinfo_relative_entropy_deprecated(self): + """Test that qinfo.relative_entropy is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(param): + qml.RY(param, wires=0) + qml.CNOT(wires=[0, 1]) + return qml.state() + + x, y = 0.4, 0.6 + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="qml.qinfo.relative_entropy is deprecated", + ): + _ = qml.qinfo.relative_entropy(circuit, circuit, wires0=[0], wires1=[0])((x,), (y,)) + @pytest.mark.all_interfaces @pytest.mark.parametrize("device", ["default.qubit", "default.mixed", "lightning.qubit"]) @pytest.mark.parametrize("interface", ["autograd", "jax", "tensorflow", "torch"]) @@ -464,7 +498,7 @@ def circuit2(param): @pytest.mark.parametrize("param", params) @pytest.mark.parametrize("interface", interfaces) def test_qnode_relative_entropy_jax_jit(self, param, interface): - """Test that the mutual information transform works for QNodes by comparing + """Test that the relative entropy transform works for QNodes by comparing against analytic values, for the JAX-jit interface""" import jax import jax.numpy as jnp @@ -660,6 +694,7 @@ def circuit2(param): ] param0, param1 = tf.Variable(param[0]), tf.Variable(param[1]) + with tf.GradientTape() as tape: out = qml.qinfo.relative_entropy(circuit1, circuit2, [0], [1])((param0,), (param1,)) @@ -701,9 +736,9 @@ def circuit2(param): param0 = torch.tensor(param[0], requires_grad=True) param1 = torch.tensor(param[1], requires_grad=True) + out = qml.qinfo.relative_entropy(circuit1, circuit2, [0], [1])((param0,), (param1,)) out.backward() - actual = [param0.grad, param1.grad] assert np.allclose(actual, expected, atol=1e-8) @@ -748,7 +783,6 @@ def circuit2(param): return qml.state() rel_ent_circuit = qml.qinfo.relative_entropy(circuit1, circuit2, [0], [0]) - x, y = np.array(0.3), np.array(0.7) # test that the circuit executes @@ -878,6 +912,7 @@ def circuit_state(x): x = np.array([0.4, 0.6, 0.8]) y = np.array([0.6, 0.8, 1.0]) + entropy = qml.qinfo.relative_entropy(circuit_state, circuit_state, wires0=[0], wires1=[1])( x, y ) diff --git a/tests/qinfo/test_fidelity.py b/tests/qinfo/test_fidelity.py index 90c59386d00..5ace7162908 100644 --- a/tests/qinfo/test_fidelity.py +++ b/tests/qinfo/test_fidelity.py @@ -18,6 +18,10 @@ import pennylane as qml from pennylane import numpy as np +pytestmark = pytest.mark.filterwarnings( + "ignore:qml.qinfo.fidelity is deprecated:pennylane.PennyLaneDeprecationWarning" +) + def expected_fidelity_rx_pauliz(param): """Return the analytical fidelity for the RX and PauliZ.""" @@ -34,6 +38,21 @@ class TestFidelityQnode: devices = ["default.qubit", "lightning.qubit", "default.mixed"] + def test_qinfo_transform_deprecated(self): + """Test that qinfo.fidelity is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.state() + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="qml.qinfo.fidelity is deprecated", + ): + _ = qml.qinfo.fidelity(circuit, circuit, wires0=[0], wires1=[1])() + @pytest.mark.parametrize("device", devices) def test_not_same_number_wires(self, device): """Test that wires must have the same length.""" @@ -120,7 +139,7 @@ def circuit1(x): qml.RX(x, wires=0) return qml.state() - fid = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])(all_args1=(np.pi)) + fid = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])(all_args1=np.pi) assert qml.math.allclose(fid, 0.0) @pytest.mark.parametrize("device", devices) @@ -266,6 +285,7 @@ def circuit(x): (qml.numpy.array(param, requires_grad=True)), (qml.numpy.array(2 * param, requires_grad=True)), ) + expected = expected_grad_fidelity_rx_pauliz(param) expected_fid = [-expected, expected] assert qml.math.allclose(fid_grad, expected_fid) @@ -319,6 +339,7 @@ def circuit1(): expected_fid_grad = expected_grad_fidelity_rx_pauliz(param) param = torch.tensor(param, dtype=torch.float64, requires_grad=True) + fid = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])((param)) fid.backward() fid_grad = param.grad @@ -347,6 +368,7 @@ def circuit1(x): expected_fid = expected_grad_fidelity_rx_pauliz(param) param = torch.tensor(param, dtype=torch.float64, requires_grad=True) + fid = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])(None, (param)) fid.backward() fid_grad = param.grad @@ -374,6 +396,7 @@ def circuit(x): torch.tensor(param, dtype=torch.float64, requires_grad=True), torch.tensor(2 * param, dtype=torch.float64, requires_grad=True), ) + fid = qml.qinfo.fidelity(circuit, circuit, wires0=[0], wires1=[0])(*params) fid.backward() fid_grad = [p.grad for p in params] @@ -428,9 +451,9 @@ def circuit1(): expected_grad_fid = expected_grad_fidelity_rx_pauliz(param) param = tf.Variable(param) + with tf.GradientTape() as tape: entropy = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])((param)) - fid_grad = tape.gradient(entropy, param) assert qml.math.allclose(fid_grad, expected_grad_fid) @@ -456,9 +479,9 @@ def circuit1(x): expected_fid = expected_grad_fidelity_rx_pauliz(param) param = tf.Variable(param) + with tf.GradientTape() as tape: entropy = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])(None, (param)) - fid_grad = tape.gradient(entropy, param) assert qml.math.allclose(fid_grad, expected_fid) @@ -480,9 +503,9 @@ def circuit(x): expected = expected_grad_fidelity_rx_pauliz(param) expected_fid = [-expected, expected] params = (tf.Variable(param), tf.Variable(2 * param)) + with tf.GradientTape() as tape: fid = qml.qinfo.fidelity(circuit, circuit, wires0=[0], wires1=[0])(*params) - fid_grad = tape.gradient(fid, params) assert qml.math.allclose(fid_grad, expected_fid) @@ -712,7 +735,8 @@ def circuit1(x): return qml.state() fid_grad = qml.grad(qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0]))( - (qml.numpy.array(param, requires_grad=True)), (qml.numpy.array(2.0, requires_grad=True)) + (qml.numpy.array(param, requires_grad=True)), + (qml.numpy.array(2.0, requires_grad=True)), ) expected_fid_grad = expected_grad_fidelity_rx_pauliz(param) assert qml.math.allclose(fid_grad, (expected_fid_grad, 0.0)) @@ -744,6 +768,7 @@ def circuit1(x): expected_fid_grad = expected_grad_fidelity_rx_pauliz(param) param = torch.tensor(param, dtype=torch.float64, requires_grad=True) param2 = torch.tensor(0, dtype=torch.float64, requires_grad=True) + fid = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])((param), (param2)) fid.backward() fid_grad = (param.grad, param2.grad) @@ -777,11 +802,11 @@ def circuit1(x): param1 = tf.Variable(param) params2 = tf.Variable(0.0) + with tf.GradientTape() as tape: entropy = qml.qinfo.fidelity(circuit0, circuit1, wires0=[0], wires1=[0])( (param1), (params2) ) - fid_grad = tape.gradient(entropy, [param1, params2]) assert qml.math.allclose(fid_grad, (expected_fid_grad, 0.0)) @@ -858,7 +883,7 @@ def circuit_state(x): x = np.array([0.4, 0.6, 0.8]) y = np.array([0.6, 0.8, 1.0]) - fid = qml.qinfo.fidelity(circuit_state, circuit_state, wires0=[0], wires1=[1])(x, y) + fid = qml.qinfo.fidelity(circuit_state, circuit_state, wires0=[0], wires1=[1])(x, y) expected = 0.5 * (np.sin(x) * np.sin(y) + np.cos(x) * np.cos(y) + 1) assert qml.math.allclose(fid, expected) diff --git a/tests/qinfo/test_fisher_deprecation.py b/tests/qinfo/test_fisher_deprecation.py deleted file mode 100644 index b5412f56a82..00000000000 --- a/tests/qinfo/test_fisher_deprecation.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Tests for the deprecation of the classical and quantum fisher information matrix in the pennylane.qinfo -""" -import pytest - -import pennylane as qml -import pennylane.numpy as pnp -from pennylane.qinfo import classical_fisher, quantum_fisher - - -@pytest.mark.parametrize("fn", (classical_fisher, quantum_fisher)) -def test_qinfo_fisher_fns_raises_warning(fn): - n_wires = 3 - n_params = 3 - - dev = qml.device("default.qubit", shots=10000) - - @qml.qnode(dev) - def circ(params): - for i in range(n_wires): - qml.Hadamard(wires=i) - - for x in params: - for j in range(n_wires): - qml.RX(x, wires=j) - qml.RY(x, wires=j) - qml.RZ(x, wires=j) - - return qml.probs(wires=range(n_wires)) - - params = pnp.zeros(n_params, requires_grad=True) - - with pytest.warns(qml.PennyLaneDeprecationWarning, match=f"{fn.__name__} is being migrated"): - fn(circ)(params) diff --git a/tests/qinfo/test_purity.py b/tests/qinfo/test_purity.py index d59f140a89d..8a96815fddf 100644 --- a/tests/qinfo/test_purity.py +++ b/tests/qinfo/test_purity.py @@ -12,12 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for purities.""" + # pylint: disable=too-many-arguments import pytest import pennylane as qml from pennylane import numpy as np +pytestmark = pytest.mark.filterwarnings( + "ignore:The qml.qinfo.purity transform is deprecated:pennylane.PennyLaneDeprecationWarning" +) + def expected_purity_ising_xx(param): """Returns the analytical purity for subsystems of the IsingXX""" @@ -60,6 +65,21 @@ class TestPurity: wires_list = [([0], True), ([1], True), ([0, 1], False)] + def test_qinfo_purity_deprecated(self): + """Test that qinfo.purity is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.state() + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="The qml.qinfo.purity transform is deprecated", + ): + _ = qml.qinfo.purity(circuit, [0])() + def test_purity_cannot_specify_device(self): """Test that an error is raised if a device or device wires are given to the purity transform manually.""" @@ -333,6 +353,7 @@ def circuit_state(x): expected_grad = expected_purity_grad_ising_xx(param) if is_partial else 0 param = torch.tensor(param, dtype=torch.float64, requires_grad=True) + purity = qml.qinfo.purity(circuit_state, wires=wires)(param) purity.backward() grad_purity = param.grad @@ -385,6 +406,7 @@ def circuit_state(x): grad_expected_purity = expected_purity_grad_ising_xx(param) if is_partial else 0 param = tf.Variable(param) + with tf.GradientTape() as tape: purity = qml.qinfo.purity(circuit_state, wires=wires)(param) @@ -407,6 +429,7 @@ def circuit_state(x): purity0 = qml.qinfo.purity(circuit_state, wires=[wires[0]])(param) purity1 = qml.qinfo.purity(circuit_state, wires=[wires[1]])(param) + expected = expected_purity_ising_xx(param) assert qml.math.allclose(purity0, expected, atol=tol) @@ -425,6 +448,5 @@ def circuit_state(x): x = np.array([0.4, 0.6, 0.8]) purity = qml.qinfo.purity(circuit_state, wires=[0])(x) - expected = expected_purity_ising_xx(x) assert qml.math.allclose(purity, expected) diff --git a/tests/qinfo/test_reduced_dm.py b/tests/qinfo/test_reduced_dm.py index 90f2fa271b8..675eb634f59 100644 --- a/tests/qinfo/test_reduced_dm.py +++ b/tests/qinfo/test_reduced_dm.py @@ -18,7 +18,12 @@ import pennylane as qml from pennylane import numpy as np -pytestmark = pytest.mark.all_interfaces +pytestmark = [ + pytest.mark.all_interfaces, + pytest.mark.filterwarnings( + "ignore:The qml.qinfo.reduced_dm transform is deprecated:pennylane.PennyLaneDeprecationWarning" + ), +] tf = pytest.importorskip("tensorflow", minversion="2.1") torch = pytest.importorskip("torch") @@ -42,6 +47,21 @@ class TestDensityMatrixQNode: """Tests for the (reduced) density matrix for QNodes returning states.""" + def test_qinfo_transform_deprecated(self): + """Test that qinfo.reduced_dm is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.state() + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="The qml.qinfo.reduced_dm transform is deprecated", + ): + _ = qml.qinfo.reduced_dm(circuit, [0])() + def test_reduced_dm_cannot_specify_device(self): """Test that an error is raised if a device or device wires are given to the reduced_dm transform manually.""" @@ -165,6 +185,7 @@ def circuit(x): jit_compile=True, input_signature=(tf.TensorSpec(shape=(), dtype=tf.float32),), ) + density_matrix = density_matrix(tf.Variable(0.0, dtype=tf.float32)) assert np.allclose(density_matrix, [[1, 0], [0, 0]]) @@ -175,7 +196,7 @@ def circuit(x): def test_density_matrix_c_dtype(self, wires, c_dtype): """Test different complex dtype.""" - dev = qml.device("default.qubit.legacy", wires=2, c_dtype=c_dtype) + dev = qml.device("default.mixed", wires=2, c_dtype=c_dtype) @qml.qnode(dev, diff_method=None) def circuit(x): diff --git a/tests/qinfo/test_trace_distance.py b/tests/qinfo/test_trace_distance.py index 892000a9737..f90411c51fa 100644 --- a/tests/qinfo/test_trace_distance.py +++ b/tests/qinfo/test_trace_distance.py @@ -18,7 +18,12 @@ import pennylane as qml from pennylane import numpy as np -pytestmark = pytest.mark.all_interfaces +pytestmark = [ + pytest.mark.all_interfaces, + pytest.mark.filterwarnings( + "ignore:qml.qinfo.trace_distance is deprecated:pennylane.PennyLaneDeprecationWarning" + ), +] tf = pytest.importorskip("tensorflow", minversion="2.1") torch = pytest.importorskip("torch") @@ -41,6 +46,21 @@ class TestTraceDistanceQnode: devices = ["default.qubit", "lightning.qubit", "default.mixed"] + def test_qinfo_transform_deprecated(self): + """Test that qinfo.trace_distance is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.state() + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="qml.qinfo.trace_distance is deprecated", + ): + _ = qml.qinfo.trace_distance(circuit, circuit, wires0=[0], wires1=[1])() + @pytest.mark.parametrize("device", devices) def test_not_same_number_wires(self, device): """Test that wires must have the same length.""" diff --git a/tests/qinfo/test_vn_entanglement_entropy.py b/tests/qinfo/test_vn_entanglement_entropy_qinfo.py similarity index 92% rename from tests/qinfo/test_vn_entanglement_entropy.py rename to tests/qinfo/test_vn_entanglement_entropy_qinfo.py index b160c228a8b..ca4927ff4ea 100644 --- a/tests/qinfo/test_vn_entanglement_entropy.py +++ b/tests/qinfo/test_vn_entanglement_entropy_qinfo.py @@ -20,10 +20,29 @@ import pennylane as qml +pytestmark = pytest.mark.filterwarnings( + "ignore:The qml.qinfo.vn_entanglement_entropy transform is deprecated:pennylane.PennyLaneDeprecationWarning" +) + class TestVnEntanglementEntropy: """Tests for the vn entanglement entropy transform""" + def test_qinfo_transform_deprecated(self): + """Test that vn_entanglement_entropy is deprecated.""" + + dev = qml.device("default.qubit", wires=2) + + @qml.qnode(dev) + def circuit(): + return qml.state() + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="The qml.qinfo.vn_entanglement_entropy transform is deprecated", + ): + _ = qml.qinfo.vn_entanglement_entropy(circuit, [0], [1])() + @pytest.mark.all_interfaces @pytest.mark.parametrize("device", ["default.qubit", "lightning.qubit"]) @pytest.mark.parametrize("interface", ["autograd", "jax", "tensorflow", "torch"]) @@ -175,6 +194,7 @@ def circuit(theta): ) params = torch.tensor(params, dtype=torch.float64, requires_grad=True) + entropy = qml.qinfo.vn_entanglement_entropy(circuit, wires0=[0], wires1=[1])(params) entropy.backward() actual = params.grad diff --git a/tests/resource/test_specs.py b/tests/resource/test_specs.py index a79f9fb4d3f..125524ca033 100644 --- a/tests/resource/test_specs.py +++ b/tests/resource/test_specs.py @@ -23,12 +23,10 @@ from pennylane.tape import QuantumScript, QuantumScriptBatch from pennylane.typing import PostprocessingFn -with pytest.warns(qml.PennyLaneDeprecationWarning): - devices_list = [ - (qml.device("default.qubit"), 1), - (qml.device("default.qubit", wires=2), 2), - (qml.device("default.qubit.legacy", wires=2), 2), - ] +devices_list = [ + (qml.device("default.qubit"), 1), + (qml.device("default.qubit", wires=2), 2), +] class TestSpecsTransform: diff --git a/tests/shadow/test_shadow_entropies.py b/tests/shadow/test_shadow_entropies.py index 009607bdf0b..6549cd5e984 100644 --- a/tests/shadow/test_shadow_entropies.py +++ b/tests/shadow/test_shadow_entropies.py @@ -68,9 +68,7 @@ def test_constant_distribution(self, n_wires, base): expected = np.log(2) / np.log(base) assert np.allclose(entropies, expected, atol=2e-2) - def test_non_constant_distribution( - self, - ): + def test_non_constant_distribution(self): """Test entropies match roughly with exact solution for a non-constant distribution using other PennyLane functionalities""" n_wires = 4 # exact solution @@ -108,7 +106,11 @@ def qnode(x): # this is intentionally not done in a parametrize loop because this would re-execute the quantum function # exact solution - rdm = qml.qinfo.reduced_dm(qnode_exact, wires=rdm_wires)(x) + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match=("The qml.qinfo.reduced_dm transform is deprecated"), + ): + rdm = qml.qinfo.reduced_dm(qnode_exact, wires=rdm_wires)(x) evs = qml.math.eigvalsh(rdm) evs = evs[np.where(evs > 0)] diff --git a/tests/shadow/test_shadow_transforms.py b/tests/shadow/test_shadow_transforms.py index 0bce7c2876c..1910293264b 100644 --- a/tests/shadow/test_shadow_transforms.py +++ b/tests/shadow/test_shadow_transforms.py @@ -22,6 +22,10 @@ from pennylane import numpy as np from pennylane.shadows.transforms import _replace_obs +pytestmark = pytest.mark.filterwarnings( + "ignore:qml.shadows.shadow_expval is deprecated:pennylane.PennyLaneDeprecationWarning" +) + def hadamard_circuit(wires, shots=10000, interface="autograd"): """Hadamard circuit to put all qubits in equal superposition (locally)""" @@ -108,7 +112,7 @@ def test_replace_tape(self): new_tapes, _ = _replace_obs(tape, qml.probs, wires=0) assert len(new_tapes) == 1 - assert new_tapes[0].operations == [] + assert len(new_tapes[0].operations) == 0 assert len(new_tapes[0].observables) == 1 assert isinstance(new_tapes[0].observables[0], qml.measurements.ProbabilityMP) @@ -326,6 +330,15 @@ def test_backward_torch(self): class TestExpvalTransform: """Test that the expval transform is applied correctly""" + def test_shadow_expval_deprecation(self): + """Test that the shadow_expval transform is deprecated""" + tape = qml.tape.QuantumScript([], [qml.classical_shadow(wires=[0, 1])]) + + with pytest.warns( + qml.PennyLaneDeprecationWarning, match="qml.shadows.shadow_expval is deprecated" + ): + _, _ = qml.shadows.shadow_expval(tape, [qml.Z(0)]) + def test_hadamard_forward(self): """Test that the expval estimation is correct for a uniform superposition of qubits""" diff --git a/tests/tape/test_tape.py b/tests/tape/test_tape.py index dd9e6f99c86..734df641229 100644 --- a/tests/tape/test_tape.py +++ b/tests/tape/test_tape.py @@ -132,7 +132,7 @@ def test_tensor_observables_matmul(self): def test_tensor_observables_rmatmul(self): """Test that tensor observables are correctly processed from the annotated queue. Here, we test multiple tensor observables constructed via matmul - with the observable occuring on the left hand side.""" + with the observable occurring on the left hand side.""" with QuantumTape() as tape: op_ = qml.RX(1.0, wires=0) diff --git a/tests/templates/test_broadcast.py b/tests/templates/test_broadcast.py index 9b185d82d4f..49d0b77fc60 100644 --- a/tests/templates/test_broadcast.py +++ b/tests/templates/test_broadcast.py @@ -27,6 +27,10 @@ from pennylane.templates.broadcast import wires_all_to_all, wires_pyramid, wires_ring from pennylane.wires import Wires +pytestmark = pytest.mark.filterwarnings( + "ignore:qml.broadcast is deprecated:pennylane.PennyLaneDeprecationWarning" +) + def ConstantTemplate(wires): T(wires=wires) @@ -131,6 +135,15 @@ def KwargTemplateDouble(par, wires, a=True): ] +def test_broadcast_deprecation(): + """Test that a warning is raised when using qml.broadcast""" + op = qml.Hadamard + wires = [0, 1, 2] + + with pytest.warns(qml.PennyLaneDeprecationWarning, match="qml.broadcast is deprecated"): + qml.broadcast(op, wires, "single") + + class TestBuiltinPatterns: """Tests the built-in patterns ("single", "ring", etc) of the broadcast template constructor.""" diff --git a/tests/templates/test_state_preparations/test_basis_state_prep.py b/tests/templates/test_state_preparations/test_basis_state_prep.py index 90b88463a05..441af8e007d 100644 --- a/tests/templates/test_state_preparations/test_basis_state_prep.py +++ b/tests/templates/test_state_preparations/test_basis_state_prep.py @@ -21,6 +21,10 @@ import pennylane as qml +pytestmark = pytest.mark.filterwarnings( + "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning" +) + def test_standard_validity(): """Check the operation using the assert_valid function.""" @@ -33,6 +37,12 @@ def test_standard_validity(): qml.ops.functions.assert_valid(op) +def test_BasisStatePreparation_is_deprecated(): + """Test that BasisStatePreparation is deprecated.""" + with pytest.warns(qml.PennyLaneDeprecationWarning, match="BasisStatePreparation is deprecated"): + _ = qml.BasisStatePreparation([1, 0], wires=[0, 1]) + + class TestDecomposition: """Tests that the template defines the correct decomposition.""" diff --git a/tests/templates/test_subroutines/test_commuting_evolution.py b/tests/templates/test_subroutines/test_commuting_evolution.py index 96950dcc265..629bdadcab5 100644 --- a/tests/templates/test_subroutines/test_commuting_evolution.py +++ b/tests/templates/test_subroutines/test_commuting_evolution.py @@ -202,13 +202,13 @@ def test_differentiable_hamiltonian(self): diff_coeffs = np.array([1.0, -1.0], requires_grad=True) frequencies = (2, 4) - def parameterized_hamiltonian(coeffs): + def parametrized_hamiltonian(coeffs): return qml.Hamiltonian(coeffs, obs) @qml.qnode(dev) def circuit(time, coeffs): qml.PauliX(0) - qml.CommutingEvolution(parameterized_hamiltonian(coeffs), time, frequencies) + qml.CommutingEvolution(parametrized_hamiltonian(coeffs), time, frequencies) return qml.expval(qml.PauliZ(0)) x_vals = [np.array(x, requires_grad=True) for x in np.linspace(-np.pi, np.pi, num=10)] diff --git a/tests/templates/test_subroutines/test_qdrift.py b/tests/templates/test_subroutines/test_qdrift.py index 8cb9a67cd52..83ef2e55ccb 100644 --- a/tests/templates/test_subroutines/test_qdrift.py +++ b/tests/templates/test_subroutines/test_qdrift.py @@ -58,7 +58,7 @@ def test_queuing(self): @pytest.mark.parametrize("seed", (None, 1234, 42)) @pytest.mark.parametrize("coeffs, ops", test_hamiltonians) def test_init_correctly(self, coeffs, ops, time, n, seed): # pylint: disable=too-many-arguments - """Test that all of the attributes are initalized correctly.""" + """Test that all of the attributes are initialized correctly.""" h = qml.dot(coeffs, ops) op = qml.QDrift(h, time, n=n, seed=seed) diff --git a/tests/templates/test_subroutines/test_qubitization.py b/tests/templates/test_subroutines/test_qubitization.py index 6545af9bbd5..50c23080400 100644 --- a/tests/templates/test_subroutines/test_qubitization.py +++ b/tests/templates/test_subroutines/test_qubitization.py @@ -49,7 +49,7 @@ def circuit(theta): # apply QPE (used iterative qpe here) measurements = qml.iterative_qpe( - qml.Qubitization(hamiltonian, control=[3, 4]), ancilla=5, iters=8 + qml.Qubitization(hamiltonian, control=[3, 4]), aux_wire=5, iters=8 ) return qml.probs(op=measurements) diff --git a/tests/templates/test_subroutines/test_trotter.py b/tests/templates/test_subroutines/test_trotter.py index c4f65e31bc1..6ac6e2bd65f 100644 --- a/tests/templates/test_subroutines/test_trotter.py +++ b/tests/templates/test_subroutines/test_trotter.py @@ -415,7 +415,7 @@ def test_error_order(self, order): @pytest.mark.parametrize("hamiltonian", test_hamiltonians) def test_init_correctly(self, hamiltonian): - """Test that all of the attributes are initalized correctly.""" + """Test that all of the attributes are initialized correctly.""" time, n, order = (4.2, 10, 4) op = qml.TrotterProduct(hamiltonian, time, n=n, order=order, check_hermitian=False) diff --git a/tests/test_debugging.py b/tests/test_debugging.py index 4e049fd3e72..e575017f5a1 100644 --- a/tests/test_debugging.py +++ b/tests/test_debugging.py @@ -278,49 +278,6 @@ def circuit(): _compare_numpy_dicts(result, expected) - @pytest.mark.parametrize("diff_method", [None, "backprop", "parameter-shift", "adjoint"]) - def test_default_qubit_legacy_only_supports_state(self, diff_method): - with pytest.warns(qml.PennyLaneDeprecationWarning, match="Use of 'default.qubit"): - dev = qml.device("default.qubit.legacy", wires=2) - - assert qml.debugging.snapshot._is_snapshot_compatible(dev) - - @qml.qnode(dev, diff_method=diff_method) - def circuit_faulty(): - qml.Hadamard(wires=0) - qml.Snapshot("important_expval", measurement=qml.expval(qml.PauliX(0))) - qml.CNOT(wires=[0, 1]) - qml.Snapshot() - return qml.expval(qml.PauliX(0)) - - circuit_faulty() - assert dev._debugger is None - if diff_method is not None: - assert circuit_faulty.interface == "auto" - - with pytest.raises(NotImplementedError, match="only supports `qml.state` measurements"): - qml.snapshots(circuit_faulty)() - - @qml.qnode(dev, diff_method=diff_method) - def circuit(): - qml.Hadamard(wires=0) - qml.CNOT(wires=[0, 1]) - qml.Snapshot() - return qml.expval(qml.PauliX(0)) - - expected = { - 0: np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]), - "execution_results": np.array(0), - } - - result = qml.snapshots(circuit)() - _compare_numpy_dicts(result, expected) - - if diff_method not in ("backprop", "adjoint"): - result_shots = qml.snapshots(circuit)(shots=200) - expected["execution_results"] = np.array(-0.04) - _compare_numpy_dicts(result_shots, expected) - # pylint: disable=protected-access @pytest.mark.parametrize("method", [None, "parameter-shift"]) def test_default_mixed(self, method): diff --git a/tests/test_operation.py b/tests/test_operation.py index a9938b66817..7d14d85a951 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -2948,7 +2948,7 @@ def test_use_legacy_opmath_fixture(): assert not qml.operation.active_new_opmath() -CONVERT_HAMILTONAIN = [ +CONVERT_HAMILTONIAN = [ ( [1.5, 0.5, 1, 1], [ @@ -2986,7 +2986,7 @@ def test_use_legacy_opmath_fixture(): @pytest.mark.usefixtures("use_new_opmath") -@pytest.mark.parametrize("coeffs, obs", CONVERT_HAMILTONAIN) +@pytest.mark.parametrize("coeffs, obs", CONVERT_HAMILTONIAN) def test_convert_to_hamiltonian(coeffs, obs): """Test that arithmetic operators can be converted to Hamiltonian instances""" diff --git a/tests/test_qnode.py b/tests/test_qnode.py index 1322ca62c16..b0b381a1806 100644 --- a/tests/test_qnode.py +++ b/tests/test_qnode.py @@ -229,7 +229,7 @@ def capabilities(cls): return capabilities # finite differences is the fallback when we know nothing about the device - monkeypatch.setattr(qml.devices.DefaultQubitLegacy, "capabilities", capabilities) + monkeypatch.setattr(qml.devices.DefaultMixed, "capabilities", capabilities) res = QNode.get_best_method(dev, "another_interface") assert res == (qml.gradients.finite_diff, {}, dev) @@ -897,7 +897,7 @@ def circuit(x, y): assert np.allclose(res, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("dev_name", ["default.qubit", "default.mixed"]) @pytest.mark.parametrize("first_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize("sec_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize( @@ -945,12 +945,11 @@ def conditional_ry_qnode(x, y): assert np.allclose(r2[1], mv_res(first_par)) assert spy.call_count == 2 - @pytest.mark.parametrize("dev_name", ["default.qubit.legacy", "default.mixed"]) - def test_dynamic_one_shot_if_mcm_unsupported(self, dev_name): + def test_dynamic_one_shot_if_mcm_unsupported(self): """Test an error is raised if the dynamic one shot transform is a applied to a qnode with a device that does not support mid circuit measurements. """ - dev = qml.device(dev_name, wires=2, shots=100) + dev = qml.device("default.mixed", wires=2, shots=100) with pytest.raises( TypeError, @@ -975,7 +974,7 @@ def test_sampling_with_mcm(self, basis_state, mocker): @qml.qnode(dev) def cry_qnode(x): """QNode where we apply a controlled Y-rotation.""" - qml.BasisStatePreparation(basis_state, wires=[0, 1]) + qml.BasisState(basis_state, wires=[0, 1]) qml.CRY(x, wires=[0, 1]) return qml.sample(qml.PauliZ(1)) @@ -983,7 +982,7 @@ def cry_qnode(x): def conditional_ry_qnode(x): """QNode where the defer measurements transform is applied by default under the hood.""" - qml.BasisStatePreparation(basis_state, wires=[0, 1]) + qml.BasisState(basis_state, wires=[0, 1]) m_0 = qml.measure(0) qml.cond(m_0, qml.RY)(x, wires=1) return qml.sample(qml.PauliZ(1)) @@ -1734,10 +1733,9 @@ def circuit(): class TestMCMConfiguration: """Tests for MCM configuration arguments""" - @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) - def test_one_shot_error_without_shots(self, dev_name): + def test_one_shot_error_without_shots(self): """Test that an error is raised if mcm_method="one-shot" with no shots""" - dev = qml.device(dev_name, wires=3) + dev = qml.device("default.qubit", wires=3) param = np.pi / 4 @qml.qnode(dev, mcm_method="one-shot") diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index 73eaf29b302..76a5b5f485d 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -20,6 +20,7 @@ import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy from scipy.sparse import csr_matrix import pennylane as qml @@ -82,7 +83,7 @@ class TestValidation: def test_invalid_interface(self): """Test that an exception is raised for an invalid interface""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) test_interface = "something" expected_error = rf"Unknown interface {test_interface}\. Interface must be one of" @@ -92,7 +93,7 @@ def test_invalid_interface(self): def test_changing_invalid_interface(self): """Test that an exception is raised for an invalid interface on a pre-existing QNode""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) test_interface = "something" @qnode(dev) @@ -112,7 +113,7 @@ def test_invalid_device(self): QNode(dummyfunc, None) def test_best_method_wraps_legacy_device_correctly(self, mocker): - dev_legacy = qml.devices.DefaultQubitLegacy(wires=2) + dev_legacy = DefaultQubitLegacy(wires=2) spy = mocker.spy(qml.devices.LegacyDeviceFacade, "__init__") @@ -126,7 +127,7 @@ def test_best_method_is_device(self, monkeypatch): """Test that the method for determining the best diff method for a given device and interface returns the device""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) monkeypatch.setitem(dev._capabilities, "passthru_interface", "some_interface") monkeypatch.setitem(dev._capabilities, "provides_jacobian", True) @@ -144,7 +145,7 @@ def test_best_method_is_device(self, monkeypatch): def test_best_method_is_backprop(self, interface): """Test that the method for determining the best diff method for a given device and interface returns backpropagation""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) # backprop is returned when the interface is an allowed interface for the device and Jacobian is not provided res = QNode.get_best_method(dev, interface) @@ -154,7 +155,7 @@ def test_best_method_is_backprop(self, interface): def test_best_method_is_param_shift(self): """Test that the method for determining the best diff method for a given device and interface returns the parameter shift rule""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) tape = qml.tape.QuantumScript([], [], shots=50) res = QNode.get_best_method(dev, None, tape=tape) @@ -172,9 +173,9 @@ def capabilities(cls): return capabilities # finite differences is the fallback when we know nothing about the device - monkeypatch.setattr(qml.devices.DefaultQubitLegacy, "capabilities", capabilities) + monkeypatch.setattr(DefaultQubitLegacy, "capabilities", capabilities) - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) monkeypatch.setitem(dev._capabilities, "passthru_interface", "some_interface") monkeypatch.setitem(dev._capabilities, "provides_jacobian", False) @@ -185,7 +186,7 @@ def capabilities(cls): def test_best_method_str_is_device(self, monkeypatch): """Test that the method for determining the best diff method string for a given device and interface returns 'device'""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) monkeypatch.setitem(dev._capabilities, "passthru_interface", "some_interface") monkeypatch.setitem(dev._capabilities, "provides_jacobian", True) @@ -201,7 +202,7 @@ def test_best_method_str_is_device(self, monkeypatch): def test_best_method_str_is_backprop(self, monkeypatch): """Test that the method for determining the best diff method string for a given device and interface returns 'backprop'""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) monkeypatch.setitem(dev._capabilities, "passthru_interface", "some_interface") monkeypatch.setitem(dev._capabilities, "provides_jacobian", False) @@ -210,7 +211,7 @@ def test_best_method_str_is_backprop(self, monkeypatch): assert res == "backprop" def test_best_method_str_wraps_legacy_device_correctly(self, mocker): - dev_legacy = qml.devices.DefaultQubitLegacy(wires=2) + dev_legacy = DefaultQubitLegacy(wires=2) spy = mocker.spy(qml.devices.LegacyDeviceFacade, "__init__") @@ -222,7 +223,7 @@ def test_best_method_str_wraps_legacy_device_correctly(self, mocker): def test_best_method_str_is_param_shift(self): """Test that the method for determining the best diff method string for a given device and interface returns 'parameter-shift'""" - dev = qml.device("default.qubit.legacy", wires=1, shots=50) + dev = qml.device("default.mixed", wires=1, shots=50) # parameter shift is returned when Jacobian is not provided and # the backprop interfaces do not match @@ -233,7 +234,7 @@ def test_best_method_str_is_param_shift(self): def test_best_method_str_is_finite_diff(self, mocker): """Test that the method for determining the best diff method string for a given device and interface returns 'finite-diff'""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) mocker.patch.object(QNode, "get_best_method", return_value=[qml.gradients.finite_diff]) @@ -243,7 +244,7 @@ def test_best_method_str_is_finite_diff(self, mocker): def test_unknown_diff_method_string(self): """Test that an exception is raised for an unknown differentiation method string""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) with pytest.raises( qml.QuantumFunctionError, match="Differentiation method hello not recognized" @@ -252,7 +253,7 @@ def test_unknown_diff_method_string(self): def test_unknown_diff_method_type(self): """Test that an exception is raised for an unknown differentiation method type""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) with pytest.raises( qml.QuantumFunctionError, @@ -265,7 +266,7 @@ def test_adjoint_finite_shots(self): on QNode construction when the device has finite shots """ - dev = qml.device("default.qubit.legacy", wires=1, shots=1) + dev = qml.device("default.mixed", wires=1, shots=1) with pytest.raises( qml.QuantumFunctionError, match="does not support adjoint with requested circuit." @@ -281,7 +282,7 @@ def circ(): def test_sparse_diffmethod_error(self): """Test that an error is raised when the observable is SparseHamiltonian and the differentiation method is not parameter-shift.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=None) + dev = qml.device("default.mixed", wires=2, shots=None) @qnode(dev, diff_method="backprop") def circuit(param): @@ -295,7 +296,7 @@ def circuit(param): def test_qnode_print(self): """Test that printing a QNode object yields the right information.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) def func(x): qml.RX(x, wires=0) @@ -305,14 +306,14 @@ def func(x): assert ( repr(qn) - == "" + == "" ) qn = QNode(func, dev, interface="autograd") assert ( repr(qn) - == "" + == "" ) # QNode can still be executed @@ -326,7 +327,7 @@ def func(x): # pylint: disable=unused-variable def test_unrecognized_kwargs_raise_warning(self): """Test that passing gradient_kwargs not included in qml.gradients.SUPPORTED_GRADIENT_KWARGS raises warning""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) with warnings.catch_warnings(record=True) as w: @@ -342,7 +343,7 @@ def circuit(params): def test_incorrect_diff_method_kwargs_raise_warning(self): """Tests that using one of the incorrect kwargs previously used in some examples in PennyLane (grad_method, gradient_fn) to set the qnode diff_method raises a warning""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) with warnings.catch_warnings(record=True) as w: @@ -362,7 +363,7 @@ def circuit2(params): def test_auto_interface_tracker_device_switched(self): """Test that checks that the tracker is switched to the new device.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) @qml.qnode(dev) def circuit(params): @@ -385,7 +386,7 @@ def circuit(params): def test_autograd_interface_device_switched_no_warnings(self): """Test that checks that no warning is raised for device switch when you define an interface, except for the deprecation warnings which will be caught by the fixture.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) @qml.qnode(dev, interface="autograd") def circuit(params): @@ -395,13 +396,12 @@ def circuit(params): circuit(qml.numpy.array(0.1, requires_grad=True)) def test_not_giving_mode_kwarg_does_not_raise_warning(self): - """Test that not providing a value for mode does not raise a warning - except for the deprecation warning.""" + """Test that not providing a value for mode does not raise a warning.""" + with warnings.catch_warnings(record=True) as record: - qml.QNode(lambda f: f, qml.device("default.qubit.legacy", wires=1)) + qml.QNode(lambda f: f, qml.device("default.mixed", wires=1)) - assert len(record) == 1 - assert record[0].category == qml.PennyLaneDeprecationWarning + assert len(record) == 0 class TestTapeConstruction: @@ -409,7 +409,7 @@ class TestTapeConstruction: def test_basic_tape_construction(self, tol): """Test that a quantum tape is properly constructed""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) def func(x, y): qml.RX(x, wires=0) @@ -443,7 +443,7 @@ def func(x, y): def test_returning_non_measurements(self): """Test that an exception is raised if a non-measurement is returned from the QNode.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) def func0(x, y): qml.RX(x, wires=0) @@ -487,7 +487,7 @@ def func3(x, y): def test_inconsistent_measurement_order(self): """Test that an exception is raised if measurements are returned in an order different to how they were queued on the tape""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) def func(x, y): qml.RX(x, wires=0) @@ -507,7 +507,7 @@ def func(x, y): def test_consistent_measurement_order(self): """Test evaluation proceeds as expected if measurements are returned in the same order to how they were queued on the tape""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) contents = [] @@ -538,13 +538,13 @@ def circuit(x): qml.RX(x, wires=0) return qml.expval(qml.PauliZ(0)) - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) qn = QNode(circuit, dev) with pytest.raises(qml.QuantumFunctionError, match="Operator RX must act on all wires"): qn(0.5) - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) qn = QNode(circuit, dev) assert np.allclose(qn(0.5), np.cos(0.5), atol=tol, rtol=0) @@ -576,7 +576,7 @@ def test_jit_counts_raises_error(self): jitting raises an error.""" import jax - dev = qml.device("default.qubit.legacy", wires=2, shots=5) + dev = qml.device("default.mixed", wires=2, shots=5) def circuit1(param): qml.Hadamard(0) @@ -610,7 +610,7 @@ def circuit2(param): def test_decorator(tol): """Test that the decorator correctly creates a QNode.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) @qnode(dev) def func(x, y): @@ -656,7 +656,7 @@ def func(): qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)) - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) qn = QNode(func, dev, interface="autograd") for _ in range(2): @@ -680,7 +680,7 @@ def func(): qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)) - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) qn = QNode(func, dev, interface=interface) for _ in range(2): qn() @@ -709,7 +709,7 @@ def func(): qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)) - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) qn = QNode(func, dev, interface=interface) for _ in range(2): qn() @@ -732,7 +732,7 @@ def test_num_exec_caching_device_swap(self): """Tests that if we swapped the original device (e.g., when diff_method='backprop') then the number of executions recorded is correct.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) cache = {} @@ -753,7 +753,7 @@ def test_num_exec_caching_device_swap_two_exec(self): """Tests that if we swapped the original device (e.g., when diff_method='backprop') then the number of executions recorded is correct even with multiple QNode evaluations.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) cache = {} @@ -788,7 +788,7 @@ def test_single_expectation_value_with_argnum_one(self, diff_method, tol): This test relies on the fact that exactly one term of the estimated jacobian will match the expected analytical value. """ - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) x = pnp.array(0.543, requires_grad=True) y = pnp.array(-0.654, requires_grad=True) @@ -812,7 +812,7 @@ def circuit(x, y): def test_no_defer_measurements_if_supported(self, mocker): """Test that the defer_measurements transform is not used during QNode construction if the device supports mid-circuit measurements.""" - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) mocker.patch.object( qml.devices.LegacyDevice, "_capabilities", {"supports_mid_measure": True} ) @@ -830,7 +830,7 @@ def circuit(): assert len(circuit.tape.operations) == 2 assert isinstance(circuit.tape.operations[1], qml.measurements.MidMeasureMP) - @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("dev_name", ["default.qubit", "default.mixed"]) @pytest.mark.parametrize("first_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize("sec_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize( @@ -883,14 +883,14 @@ def conditional_ry_qnode(x, y): def test_sampling_with_mcm(self, basis_state, mocker): """Tests that a QNode with qml.sample and mid-circuit measurements returns the expected results.""" - dev = qml.device("default.qubit.legacy", wires=3, shots=1000) + dev = qml.device("default.mixed", wires=3, shots=1000) first_par = np.pi @qml.qnode(dev) def cry_qnode(x): """QNode where we apply a controlled Y-rotation.""" - qml.BasisStatePreparation(basis_state, wires=[0, 1]) + qml.BasisState(basis_state, wires=[0, 1]) qml.CRY(x, wires=[0, 1]) return qml.sample(qml.PauliZ(1)) @@ -898,7 +898,7 @@ def cry_qnode(x): def conditional_ry_qnode(x): """QNode where the defer measurements transform is applied by default under the hood.""" - qml.BasisStatePreparation(basis_state, wires=[0, 1]) + qml.BasisState(basis_state, wires=[0, 1]) m_0 = qml.measure(0) qml.cond(m_0, qml.RY)(x, wires=1) return qml.sample(qml.PauliZ(1)) @@ -915,7 +915,7 @@ def test_conditional_ops_tensorflow(self, interface): """Test conditional operations with TensorFlow.""" import tensorflow as tf - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) @qml.qnode(dev, interface=interface, diff_method="parameter-shift") def cry_qnode(x): @@ -958,7 +958,7 @@ def test_conditional_ops_torch(self, interface): """Test conditional operations with Torch.""" import torch - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) @qml.qnode(dev, interface=interface, diff_method="parameter-shift") def cry_qnode(x): @@ -997,7 +997,7 @@ def test_conditional_ops_jax(self, jax_interface): import jax jnp = jax.numpy - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) @qml.qnode(dev, interface=jax_interface, diff_method="parameter-shift") def cry_qnode(x): @@ -1028,7 +1028,7 @@ def conditional_ry_qnode(x): def test_qnode_does_not_support_nested_queuing(self): """Test that operators in QNodes are not queued to surrounding contexts.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) @qml.qnode(dev) def circuit(): @@ -1048,7 +1048,7 @@ class TestShots: # pylint: disable=unexpected-keyword-arg def test_specify_shots_per_call_sample(self): """Tests that shots can be set per call for a sample return type.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=10) + dev = qml.device("default.mixed", wires=1, shots=10) @qnode(dev) def circuit(a): @@ -1064,7 +1064,7 @@ def circuit(a): def test_specify_shots_per_call_expval(self): """Tests that shots can be set per call for an expectation value. Note: this test has a vanishingly small probability to fail.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=None) + dev = qml.device("default.mixed", wires=1, shots=None) @qnode(dev) def circuit(): @@ -1090,7 +1090,7 @@ def test_no_shots_per_call_if_user_has_shots_qfunc_kwarg(self): """Tests that the per-call shots overwriting is suspended if user has a shots keyword argument, but a warning is raised.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=10) + dev = qml.device("default.mixed", wires=2, shots=10) def circuit(a, shots=0): qml.RX(a, wires=shots) @@ -1114,7 +1114,7 @@ def circuit(a, shots=0): def test_no_shots_per_call_if_user_has_shots_qfunc_arg(self): """Tests that the per-call shots overwriting is suspended if user has a shots argument, but a warning is raised.""" - dev = qml.device("default.qubit.legacy", wires=[0, 1], shots=10) + dev = qml.device("default.mixed", wires=[0, 1], shots=10) def ansatz0(a, shots): qml.RX(a, wires=shots) @@ -1129,7 +1129,7 @@ def ansatz0(a, shots): assert len(circuit(0.8, 1)) == 10 assert circuit.qtape.operations[0].wires.labels == (1,) - dev = qml.device("default.qubit.legacy", wires=2, shots=10) + dev = qml.device("default.mixed", wires=2, shots=10) with pytest.warns( UserWarning, match="The 'shots' argument name is reserved for overriding" @@ -1147,7 +1147,7 @@ def ansatz1(a, shots): def test_shots_setting_does_not_mutate_device(self): """Tests that per-call shots setting does not change the number of shots in the device.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=3) + dev = qml.device("default.mixed", wires=1, shots=3) @qnode(dev) def circuit(a): @@ -1161,7 +1161,7 @@ def circuit(a): def test_warning_finite_shots_dev(self): """Tests that a warning is raised when caching is used with finite shots.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=5) + dev = qml.device("default.mixed", wires=1, shots=5) @qml.qnode(dev, cache={}) def circuit(x): @@ -1176,7 +1176,7 @@ def circuit(x): # pylint: disable=unexpected-keyword-arg def test_warning_finite_shots_override(self): """Tests that a warning is raised when caching is used with finite shots.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=5) + dev = qml.device("default.mixed", wires=1, shots=5) @qml.qnode(dev, cache={}) def circuit(x): @@ -1190,7 +1190,7 @@ def circuit(x): def test_warning_finite_shots_tape(self): """Tests that a warning is raised when caching is used with finite shots.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=5) + dev = qml.device("default.mixed", wires=1, shots=5) with qml.queuing.AnnotatedQueue() as q: qml.RZ(0.3, wires=0) @@ -1205,7 +1205,7 @@ def test_warning_finite_shots_tape(self): def test_no_warning_infinite_shots(self): """Tests that no warning is raised when caching is used with infinite shots.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) @qml.qnode(dev, cache={}) def circuit(x): @@ -1220,7 +1220,7 @@ def circuit(x): @pytest.mark.autograd def test_no_warning_internal_cache_reuse(self): """Tests that no warning is raised when only the internal cache is reused.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=5) + dev = qml.device("default.mixed", wires=1, shots=5) @qml.qnode(dev, cache=True) def circuit(x): @@ -1243,7 +1243,7 @@ def circuit(x): ) def test_tape_shots_set_on_call(self, shots, total_shots, shot_vector): """test that shots are placed on the tape if they are specified during a call.""" - dev = qml.device("default.qubit.legacy", wires=2, shots=5) + dev = qml.device("default.mixed", wires=2, shots=5) def func(x, y): qml.RX(x, wires=0) @@ -1281,7 +1281,7 @@ def qn2(x, y): class TestTransformProgramIntegration: def test_transform_program_modifies_circuit(self): """Test qnode integration with a transform that turns the circuit into just a pauli x.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) def null_postprocessing(results): return results[0] @@ -1314,7 +1314,7 @@ def circuit(x): def tet_transform_program_modifies_results(self): """Test integration with a transform that modifies the result output.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) @qml.transform def pin_result( @@ -1339,7 +1339,7 @@ def circuit(x): def test_transform_order_circuit_processing(self): """Test that transforms are applied in the correct order in integration.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) def null_postprocessing(results): return results[0] @@ -1388,7 +1388,7 @@ def circuit2(x): def test_transform_order_postprocessing(self): """Test that transform postprocessing is called in the right order.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) def scale_by_factor(results, factor): return results[0] * factor @@ -1467,7 +1467,8 @@ class TestTapeExpansion: ) def test_device_expansion(self, diff_method, mode, mocker): """Test expansion of an unsupported operation on the device""" - dev = qml.device("default.qubit.legacy", wires=1) + + dev = DefaultQubitLegacy(wires=1) # pylint: disable=too-few-public-methods class UnsupportedOp(qml.operation.Operation): @@ -1500,7 +1501,7 @@ def circuit(x): def test_no_gradient_expansion(self, mocker): """Test that an unsupported operation with defined gradient recipe is not expanded""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) # pylint: disable=too-few-public-methods class UnsupportedOp(qml.operation.Operation): @@ -1529,7 +1530,7 @@ def circuit(x): def test_gradient_expansion(self, mocker): """Test that a *supported* operation with no gradient recipe is expanded when applying the gradient transform, but not for execution.""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) # pylint: disable=too-few-public-methods class PhaseShift(qml.PhaseShift): @@ -1559,7 +1560,7 @@ def circuit(x): def test_hamiltonian_expansion_analytic(self): """Test result if there are non-commuting groups and the number of shots is None""" - dev = qml.device("default.qubit.legacy", wires=3, shots=None) + dev = qml.device("default.mixed", wires=3, shots=None) obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] c = np.array([-0.6543, 0.24, 0.54]) @@ -1578,7 +1579,7 @@ def circuit(): def test_hamiltonian_expansion_finite_shots(self, mocker): """Test that the Hamiltonian is expanded if there are non-commuting groups and the number of shots is finite""" - dev = qml.device("default.qubit.legacy", wires=3, shots=50000) + dev = qml.device("default.mixed", wires=3, shots=50000) obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] c = np.array([-0.6543, 0.24, 0.54]) @@ -1605,7 +1606,7 @@ def circuit(): def test_multiple_hamiltonian_expansion_finite_shots(self, grouping): """Test that multiple Hamiltonians works correctly (sum_expand should be used)""" - dev = qml.device("default.qubit.legacy", wires=3, shots=50000) + dev = qml.device("default.mixed", wires=3, shots=50000) obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)] c = np.array([-0.6543, 0.24, 0.54]) @@ -1626,7 +1627,7 @@ def circuit(): def test_expansion_multiple_qwc_observables(self, mocker): """Test that the QNode correctly expands tapes that return multiple measurements of commuting observables""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) obs = [qml.PauliX(0), qml.PauliX(0) @ qml.PauliY(1)] @qml.qnode(dev) diff --git a/tests/test_return_types.py b/tests/test_return_types.py index fda205d4f15..823a13e2c24 100644 --- a/tests/test_return_types.py +++ b/tests/test_return_types.py @@ -133,6 +133,26 @@ def circuit(x): assert res[0].shape == () assert isinstance(res[0], (np.ndarray, np.float64)) + @pytest.mark.parametrize("device", devices) + def test_vn_entanglement_entropy(self, device, interface, shots): + """Return a single vn entanglement entropy.""" + dev = qml.device(device, wires=2, shots=shots) + + def circuit(x): + qml.Hadamard(wires=[0]) + qml.CRX(x, wires=[0, 1]) + return qml.vn_entanglement_entropy(wires0=[0], wires1=[1]) + + qnode = qml.QNode(circuit, dev) + qnode.construct([0.5], {}) + + if dev.shots: + pytest.skip("cannot return analytic measurements with finite shots.") + res = qml.execute(tapes=[qnode.tape], device=dev, gradient_fn=None, interface=interface) + + assert res[0].shape == () + assert isinstance(res[0], (np.ndarray, np.float64)) + @pytest.mark.parametrize("device", devices) def test_mutual_info(self, device, interface, shots): """Return a single mutual information.""" diff --git a/tests/test_return_types_legacy.py b/tests/test_return_types_legacy.py index be46b6848de..118697f79f6 100644 --- a/tests/test_return_types_legacy.py +++ b/tests/test_return_types_legacy.py @@ -22,31 +22,13 @@ test_wires = [2, 3, 4] -devices = ["default.qubit.legacy", "default.mixed"] +devices = ["default.mixed"] @pytest.mark.parametrize("interface, shots", [["autograd", None], ["auto", 100]]) class TestSingleReturnExecute: """Test that single measurements return behavior does not change.""" - @pytest.mark.parametrize("wires", test_wires) - def test_state_default(self, wires, interface, shots): - """Return state with default.qubit.""" - dev = qml.device("default.qubit.legacy", wires=wires, shots=shots) - - def circuit(x): - qml.Hadamard(wires=[0]) - qml.CRX(x, wires=[0, 1]) - return qml.state() - - qnode = qml.QNode(circuit, dev) - qnode.construct([0.5], {}) - - res = qml.execute(tapes=[qnode.tape], device=dev, gradient_fn=None, interface=interface) - - assert res[0].shape == (2**wires,) - assert isinstance(res[0], np.ndarray) - @pytest.mark.parametrize("wires", test_wires) def test_state_mixed(self, wires, interface, shots): """Return state with default.mixed.""" @@ -193,7 +175,7 @@ def test_sample(self, measurement, interface, shots): if shots is None: pytest.skip("Sample requires finite shots.") - dev = qml.device("default.qubit.legacy", wires=2, shots=shots) + dev = qml.device("default.mixed", wires=2, shots=shots) def circuit(x): qml.Hadamard(wires=[0]) @@ -214,7 +196,7 @@ def test_counts(self, measurement, interface, shots): if shots is None: pytest.skip("Counts requires finite shots.") - dev = qml.device("default.qubit.legacy", wires=2, shots=shots) + dev = qml.device("default.mixed", wires=2, shots=shots) def circuit(x): qml.Hadamard(wires=[0]) @@ -1185,7 +1167,7 @@ def return_type(self): DummyMeasurement(obs=qml.PauliZ(0)) tape = qml.tape.QuantumScript.from_queue(q) - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) with pytest.raises( qml.QuantumFunctionError, match="Unsupported return type specified for observable" @@ -1196,7 +1178,7 @@ def test_state_return_with_other_types(self): """Test that an exception is raised when a state is returned along with another return type""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) with qml.queuing.AnnotatedQueue() as q: qml.PauliX(wires=0) @@ -1210,10 +1192,10 @@ def test_state_return_with_other_types(self): ): qml.execute(tapes=[tape], device=dev, gradient_fn=None) - def test_entropy_no_custom_wires(self): - """Test that entropy cannot be returned with custom wires.""" + def test_vn_entropy_no_custom_wires(self): + """Test that vn_entropy cannot be returned with custom wires.""" - dev = qml.device("default.qubit.legacy", wires=["a", 1]) + dev = qml.device("default.mixed", wires=["a", 1]) with qml.queuing.AnnotatedQueue() as q: qml.PauliX(wires="a") @@ -1226,10 +1208,26 @@ def test_entropy_no_custom_wires(self): ): qml.execute(tapes=[tape], device=dev, gradient_fn=None) + def test_vn_entanglement_entropy_no_custom_wires(self): + """Test that vn_entanglement_entropy cannot be returned with custom wires.""" + + dev = qml.device("default.mixed", wires=["a", 1]) + + with qml.queuing.AnnotatedQueue() as q: + qml.PauliX(wires="a") + qml.vn_entanglement_entropy(wires0=["a"], wires1=["b"]) + + tape = qml.tape.QuantumScript.from_queue(q) + with pytest.raises( + qml.QuantumFunctionError, + match="Returning the Von Neumann entanglement entropy is not supported when using custom wire labels", + ): + qml.execute(tapes=[tape], device=dev, gradient_fn=None) + def test_custom_wire_labels_error(self): """Tests that an error is raised when mutual information is measured with custom wire labels""" - dev = qml.device("default.qubit.legacy", wires=["a", "b"]) + dev = qml.device("default.mixed", wires=["a", "b"]) with qml.queuing.AnnotatedQueue() as q: qml.PauliX(wires="a") diff --git a/tests/test_vqe.py b/tests/test_vqe.py index ca7ea9a9e38..39548f5f7c5 100644 --- a/tests/test_vqe.py +++ b/tests/test_vqe.py @@ -279,16 +279,12 @@ def test_cost_expvals(self, coeffs, observables, expected): # pylint: disable=protected-access @pytest.mark.torch @pytest.mark.slow - @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) @pytest.mark.parametrize("shots", [None, [(8000, 5)], [(8000, 5), (9000, 4)]]) - def test_optimize_torch(self, dev_name, shots): + def test_optimize_torch(self, shots): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the Torch interface.""" - if dev_name == "default.qubit.legacy" and shots is None: - pytest.xfail(reason="DQ legacy does not count hardware executions in analytic mode") - - dev = qml.device(dev_name, wires=4, shots=shots) + dev = qml.device("default.qubit", wires=4, shots=shots) hamiltonian1 = copy.copy(big_hamiltonian) hamiltonian2 = copy.copy(big_hamiltonian) @@ -331,16 +327,12 @@ def test_optimize_torch(self, dev_name, shots): # pylint: disable=protected-access @pytest.mark.tf @pytest.mark.slow - @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) @pytest.mark.parametrize("shots", [None, [(8000, 5)], [(8000, 5), (9000, 4)]]) - def test_optimize_tf(self, shots, dev_name): + def test_optimize_tf(self, shots): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the TensorFlow interface.""" - if dev_name == "default.qubit.legacy" and shots is None: - pytest.xfail(reason="DQ legacy does not count hardware executions in analytic mode") - - dev = qml.device(dev_name, wires=4, shots=shots) + dev = qml.device("default.qubit", wires=4, shots=shots) hamiltonian1 = copy.copy(big_hamiltonian) hamiltonian2 = copy.copy(big_hamiltonian) @@ -381,16 +373,12 @@ def test_optimize_tf(self, shots, dev_name): # pylint: disable=protected-access @pytest.mark.autograd @pytest.mark.slow - @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) @pytest.mark.parametrize("shots", [None, [(8000, 5)], [(8000, 5), (9000, 4)]]) - def test_optimize_autograd(self, shots, dev_name): + def test_optimize_autograd(self, shots): """Test that a Hamiltonian cost function is the same with and without grouping optimization when using the autograd interface.""" - if dev_name == "default.qubit.legacy" and shots is None: - pytest.xfail(reason="DQ legacy does not count hardware executions in analytic mode") - - dev = qml.device(dev_name, wires=4, shots=shots) + dev = qml.device("default.qubit", wires=4, shots=shots) hamiltonian1 = copy.copy(big_hamiltonian) hamiltonian2 = copy.copy(big_hamiltonian) diff --git a/tests/transforms/test_batch_input.py b/tests/transforms/test_batch_input.py index 3d97e981ed2..2499aafdcdd 100644 --- a/tests/transforms/test_batch_input.py +++ b/tests/transforms/test_batch_input.py @@ -200,6 +200,9 @@ def circuit2(data, weights): assert np.allclose(res, indiv_res) +@pytest.mark.filterwarnings( + "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning" +) def test_basis_state_preparation(mocker): """Test that batching works for BasisStatePreparation""" dev = qml.device("default.qubit", wires=3) diff --git a/tests/transforms/test_batch_params.py b/tests/transforms/test_batch_params.py index ad4daddeb69..29b89ebda4a 100644 --- a/tests/transforms/test_batch_params.py +++ b/tests/transforms/test_batch_params.py @@ -174,6 +174,9 @@ def circuit2(data, weights): assert np.allclose(res, indiv_res) +@pytest.mark.filterwarnings( + "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning" +) def test_basis_state_preparation(mocker): """Test that batching works for BasisStatePreparation""" dev = qml.device("default.qubit", wires=4) diff --git a/tests/transforms/test_cliffordt_transform.py b/tests/transforms/test_cliffordt_transform.py index 08184b9c075..ee6d685bdc1 100644 --- a/tests/transforms/test_cliffordt_transform.py +++ b/tests/transforms/test_cliffordt_transform.py @@ -285,7 +285,7 @@ def test_raise_with_cliffordt_decomposition(self): @pytest.mark.parametrize("op", [qml.U1(1.0, wires=["b"])]) def test_raise_with_rot_decomposition(self, op): - """Test that exception is correctly raise when decomposing parameterized gates for which we already don't have a recipe""" + """Test that exception is correctly raise when decomposing parametrized gates for which we already don't have a recipe""" with pytest.raises( ValueError, @@ -314,7 +314,7 @@ def qfunc(): with pytest.raises( NotImplementedError, - match=r"Currently we only support Solovay-Kitaev \('sk'\) decompostion", + match=r"Currently we only support Solovay-Kitaev \('sk'\) decomposition", ): decomposed_qfunc() diff --git a/tests/transforms/test_defer_measurements.py b/tests/transforms/test_defer_measurements.py index 3d5e98791ea..b85de016d3f 100644 --- a/tests/transforms/test_defer_measurements.py +++ b/tests/transforms/test_defer_measurements.py @@ -918,7 +918,7 @@ def test_hermitian_queued(self): assert isinstance(meas_op1, qml.CNOT) assert meas_op1.wires == qml.wires.Wires([0, 5]) - # Check the underlying CNOT for reseting measured wire + # Check the underlying CNOT for resetting measured wire meas_op1 = tape.operations[1] assert isinstance(meas_op1, qml.CNOT) assert meas_op1.wires == qml.wires.Wires([5, 0]) @@ -1347,6 +1347,9 @@ def quantum_control_circuit(rads): class TestTemplates: """Tests templates being conditioned on mid-circuit measurement outcomes.""" + @pytest.mark.filterwarnings( + "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning" + ) def test_basis_state_prep(self): """Test the basis state prep template conditioned on mid-circuit measurement outcomes.""" diff --git a/tests/transforms/test_optimization/test_optimization_utils.py b/tests/transforms/test_optimization/test_optimization_utils.py index ff31cd999c6..19145b7ce53 100644 --- a/tests/transforms/test_optimization/test_optimization_utils.py +++ b/tests/transforms/test_optimization/test_optimization_utils.py @@ -238,8 +238,17 @@ def test_jacobian_jax(self, use_jit): special_angles = np.array(list(product(special_points, repeat=6))).reshape((-1, 2, 3)) random_angles = np.random.random((1000, 2, 3)) # Need holomorphic derivatives and complex inputs because the output matrices are complex - all_angles = jax.numpy.concatenate([special_angles, random_angles], dtype=complex) - jac_fn = lambda fn: jax.vmap(jax.jacobian(fn, holomorphic=True)) + all_angles = jax.numpy.concatenate([special_angles, random_angles]) + + # We need to define the Jacobian function manually because fuse_rot_angles is not guaranteed to be holomorphic, + # and jax.jacobian requires real-valued outputs for non-holomorphic functions. + def jac_fn(fn): + real_fn = lambda arg: qml.math.real(fn(arg)) + imag_fn = lambda arg: qml.math.imag(fn(arg)) + real_jac_fn = jax.vmap(jax.jacobian(real_fn)) + imag_jac_fn = jax.vmap(jax.jacobian(imag_fn)) + return lambda arg: real_jac_fn(arg) + 1j * imag_jac_fn(arg) + jit_fn = jax.jit if use_jit else None self.run_jacobian_test(all_angles, jac_fn, is_batched=True, jit_fn=jit_fn) diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py index 96b0d1e3af8..c84bfe90bfb 100644 --- a/tests/transforms/test_qcut.py +++ b/tests/transforms/test_qcut.py @@ -2507,7 +2507,7 @@ def func(x): ) -@pytest.mark.parametrize("dev_fn", [qml.devices.DefaultQubitLegacy, qml.devices.DefaultQubit]) +@pytest.mark.parametrize("dev_fn", [qml.devices.DefaultQubit]) class TestCutCircuitMCTransform: """ Tests that the `cut_circuit_mc` transform gives the correct results. diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py index 3575034d08c..55f1d1d04f0 100644 --- a/tests/transforms/test_tape_expand.py +++ b/tests/transforms/test_tape_expand.py @@ -19,6 +19,7 @@ import numpy as np import pytest +from default_qubit_legacy import DefaultQubitLegacy import pennylane as qml from pennylane.wires import Wires @@ -66,7 +67,7 @@ def test_device_and_stopping_expansion(self): """Test that passing a device alongside a stopping condition ensures that all operations are expanded to match the devices default gate set""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = DefaultQubitLegacy(wires=1) expand_fn = qml.transforms.create_expand_fn(device=dev, depth=10, stop_at=self.crit_0) with qml.queuing.AnnotatedQueue() as q: @@ -82,7 +83,7 @@ def test_device_and_stopping_expansion(self): def test_device_only_expansion(self): """Test that passing a device ensures that all operations are expanded to match the devices default gate set""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = DefaultQubitLegacy(wires=1) expand_fn = qml.transforms.create_expand_fn(device=dev, depth=10) with qml.queuing.AnnotatedQueue() as q: @@ -418,7 +419,18 @@ def custom_rot(phi, theta, omega, wires): # Decompose a template into another template def custom_basic_entangler_layers(weights, wires, **kwargs): # pylint: disable=unused-argument - cnot_broadcast = qml.tape.make_qscript(qml.broadcast)(qml.CNOT, pattern="ring", wires=wires) + def cnot_circuit(wires): + n_wires = len(wires) + + if n_wires == 2: + qml.CNOT(wires) + return + + for wire in wires: + op_wires = [wire % n_wires, (wire + 1) % n_wires] + qml.CNOT(op_wires) + + cnot_broadcast = qml.tape.make_qscript(cnot_circuit)(wires) return [ qml.AngleEmbedding(weights[0], wires=wires), *cnot_broadcast, @@ -428,7 +440,7 @@ def custom_basic_entangler_layers(weights, wires, **kwargs): class TestCreateCustomDecompExpandFn: """Tests for the custom_decomps argument for devices""" - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_string_and_operator_allowed(self, device_name): """Test that the custom_decomps dictionary accepts both strings and operator classes as keys.""" @@ -449,7 +461,7 @@ def circuit(): assert op.name != "Hadamard" assert op.name != "CNOT" - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_no_custom_decomp(self, device_name): """Test that sending an empty dictionary results in no decompositions.""" @@ -475,7 +487,7 @@ def circuit(): orig_op.name == decomp_op.name for orig_op, decomp_op in zip(original_ops, decomp_ops) ] - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_no_custom_decomp_template(self, device_name): """Test that sending an empty dictionary results in no decomposition when a template is involved, except the decomposition expected from the device.""" @@ -501,9 +513,7 @@ def circuit(): orig_op.name == decomp_op.name for orig_op, decomp_op in zip(original_ops, decomp_ops) ] - @pytest.mark.parametrize( - "device_name", ["default.qubit", "default.qubit.legacy", "lightning.qubit"] - ) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed", "lightning.qubit"]) def test_one_custom_decomp(self, device_name): """Test that specifying a single custom decomposition works as expected.""" @@ -528,31 +538,7 @@ def circuit(): assert decomp_ops[2].name == "CNOT" - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) - def test_no_decomp_with_depth_zero(self, device_name): - """Test that specifying a single custom decomposition works as expected.""" - - custom_decomps = {"Hadamard": custom_hadamard, "CNOT": custom_cnot} - with pytest.warns( - qml.PennyLaneDeprecationWarning, match="The decomp_depth argument is deprecated" - ): - decomp_dev = qml.device( - device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=0 - ) - - @qml.qnode(decomp_dev) - def circuit(): - qml.Hadamard(wires=0) - qml.CNOT(wires=[0, 1]) - return qml.expval(qml.PauliZ(0)) - - decomp_ops = qml.workflow.construct_batch(circuit, level=None)()[0][0].operations - - assert len(decomp_ops) == 2 - assert decomp_ops[0].name == "Hadamard" - assert decomp_ops[1].name == "CNOT" - - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_one_custom_decomp_gradient(self, device_name): """Test that gradients are still correctly computed after a decomposition that performs transpilation.""" @@ -583,7 +569,7 @@ def circuit(x): decomp_ops = qml.workflow.construct_batch(decomp_qnode, level=None)(x)[0][0].operations assert all(op.name == name for op, name in zip(decomp_ops, expected_ops)) - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_nested_custom_decomp(self, device_name): """Test that specifying two custom decompositions that have interdependence works as expected.""" @@ -621,7 +607,7 @@ def circuit(): assert decomp_ops[4].name == "CZ" - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_nested_custom_decomp_with_template(self, device_name): """Test that specifying two custom decompositions that have interdependence works as expected even when there is a template.""" @@ -667,7 +653,7 @@ def circuit(): assert np.isclose(decomp_ops[6].parameters[0], np.pi / 2) assert decomp_ops[6].wires == Wires(1) - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_custom_decomp_template_to_template(self, device_name): """Test that decomposing a template into another template and some gates yields the correct results.""" @@ -702,53 +688,7 @@ def circuit(): assert decomp_ops[4].name == "CNOT" assert decomp_ops[4].wires == Wires([0, 1]) - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) - def test_custom_decomp_different_depth(self, device_name): - """Test that alternative expansion depths can be specified.""" - - # BasicEntanglerLayers custom decomposition involves AngleEmbedding. If - # expansion depth is 2, the AngleEmbedding will still be decomposed into - # RX (since it's not a supported operation on the device), but the RX will - # not be further decomposed even though the custom decomposition is specified. - custom_decomps = {"BasicEntanglerLayers": custom_basic_entangler_layers, "RX": custom_rx} - - with pytest.warns( - qml.PennyLaneDeprecationWarning, match="The decomp_depth argument is deprecated" - ): - decomp_dev_2 = qml.device( - device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=2 - ) - - decomp_dev_3 = qml.device( - device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=3 - ) - - def circuit(): - qml.BasicEntanglerLayers([[0.1, 0.2]], wires=[0, 1]) - return qml.expval(qml.PauliZ(0)) - - circuit2 = qml.QNode(circuit, decomp_dev_2) - circuit3 = qml.QNode(circuit, decomp_dev_3) - - decomp_ops = qml.workflow.construct_batch(circuit2, level=None)()[0][0].operations - - assert len(decomp_ops) == 3 - - assert decomp_ops[0].name == "RX" - assert np.isclose(decomp_ops[0].parameters[0], 0.1) - assert decomp_ops[0].wires == Wires(0) - - assert decomp_ops[1].name == "RX" - assert np.isclose(decomp_ops[1].parameters[0], 0.2) - assert decomp_ops[1].wires == Wires(1) - - assert decomp_ops[2].name == "CNOT" - assert decomp_ops[2].wires == Wires([0, 1]) - - decomp_ops = qml.workflow.construct_batch(circuit3, level=None)()[0][0].operations - assert len(decomp_ops) == 5 - - @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.mixed"]) def test_custom_decomp_with_adjoint(self, device_name): """Test that applying an adjoint in the circuit results in the adjoint undergoing the custom decomposition.""" @@ -788,7 +728,12 @@ def compute_decomposition(wires): original_decomp = CustomOp(0).decomposition() custom_decomps = {CustomOp: lambda wires: [qml.T(wires), qml.T(wires)]} - decomp_dev = qml.device(device_name, wires=2, custom_decomps=custom_decomps) + if device_name == "default.qubit.legacy": + decomp_dev = DefaultQubitLegacy(wires=2) + expand_fn = qml.transforms.create_decomp_expand_fn(custom_decomps, decomp_dev) + decomp_dev.custom_expand(expand_fn) + else: + decomp_dev = qml.device(device_name, wires=2, custom_decomps=custom_decomps) @qml.qnode(decomp_dev) def circuit(): @@ -809,7 +754,7 @@ def circuit(): def test_custom_decomp_in_separate_context_legacy_opmath(self): """Test that the set_decomposition context manager works.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) @qml.qnode(dev) def circuit(): diff --git a/tests/transforms/test_transpile.py b/tests/transforms/test_transpile.py index 4e77010e01e..aa2e7530ff8 100644 --- a/tests/transforms/test_transpile.py +++ b/tests/transforms/test_transpile.py @@ -63,7 +63,7 @@ def test_transpile_invalid_coupling(self): with pytest.raises(ValueError, match=err_msg): transpiled_qnode(0.1, 0.2, 0.3) - def test_transpile_raise_not_implemented_hamiltonain_mmt(self): + def test_transpile_raise_not_implemented_hamiltonian_mmt(self): """test that error is raised when measurement is expectation of a Hamiltonian""" dev = qml.device("default.qubit", wires=[0, 1, 2, 3]) coeffs = [1] diff --git a/tests/workflow/test_construct_batch.py b/tests/workflow/test_construct_batch.py index 1ca4e1b4c49..47e52c010e6 100644 --- a/tests/workflow/test_construct_batch.py +++ b/tests/workflow/test_construct_batch.py @@ -157,7 +157,7 @@ def circuit(x): def test_get_transform_program_legacy_device_interface(self): """Test the contents of the transform program with the legacy device interface.""" - dev = qml.device("default.qubit.legacy", wires=5) + dev = qml.device("default.mixed", wires=5) @qml.transforms.merge_rotations @qml.qnode(dev, diff_method="backprop") @@ -334,7 +334,7 @@ def test_device_transforms_legacy_interface(self, level): """Test that the device transforms can be selected with level=device or None without trainable parameters""" @qml.transforms.cancel_inverses - @qml.qnode(qml.device("default.qubit.legacy", wires=2, shots=50)) + @qml.qnode(qml.device("default.mixed", wires=2, shots=50)) def circuit(order): qml.Permute(order, wires=(0, 1, 2)) qml.X(0)