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/deprecations.rst b/doc/development/deprecations.rst
index 22b478ab3a5..f44598fc765 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -9,12 +9,18 @@ deprecations are listed below.
Pending deprecations
--------------------
+* 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
process the diff method.
@@ -27,12 +33,6 @@ 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.
@@ -89,6 +89,12 @@ Other deprecations
Completed deprecation cycles
----------------------------
+* 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/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 5fb81f60344..e06f5ca9809 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -66,6 +66,10 @@
Breaking changes 💔
+* 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)
@@ -96,6 +100,10 @@
Deprecations 👋
+* 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)
+
* The ``QubitStateVector`` template is deprecated.
Instead, use ``StatePrep``.
[(#6172)](https://github.com/PennyLaneAI/pennylane/pull/6172)
@@ -143,10 +151,12 @@ This release contains contributions from (in alphabetical order):
Guillermo Alonso,
Utkarsh Azad,
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/devices/default_mixed.py b/pennylane/devices/default_mixed.py
index d7a3a55e0b5..d0284e4b3e8 100644
--- a/pennylane/devices/default_mixed.py
+++ b/pennylane/devices/default_mixed.py
@@ -763,8 +763,9 @@ def execute(self, circuit, **kwargs):
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.
+ # VnEntropy, MutualInfo: Computed for the state
+ # prior to measurement. So, readout error need not be applied to the
+ # corresponding device wires.
continue
wires_list.append(m.wires)
self.measured_wires = qml.wires.Wires.all_wires(wires_list)
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_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/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/tests/devices/test_default_mixed.py b/tests/devices/test_default_mixed.py
index a6bf45665a4..86cdbf02430 100644
--- a/tests/devices/test_default_mixed.py
+++ b/tests/devices/test_default_mixed.py
@@ -1203,13 +1203,14 @@ 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"""
+ """Tests the output of qml.vn_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.mutual_info(wires0=[0], wires1=[1], log_base=2),
)
res = circuit()
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/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..0efc59cf1a1 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]])
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/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)]