diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 0eb8ba08d43..288955460f5 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -505,7 +505,7 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
```
```pycon
- >>> tape = tape.expand(depth=2)
+ >>> tape = tape.expand(depth=1)
>>> print(tape.draw(wire_order=Wires(all_wires)))
c0: ──────────────╭C──────────────────────╭C──────────┤
c1: ──────────────├C──────────────────────├C──────────┤
@@ -577,6 +577,19 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
Bug fixes
+* Fixes a bug with `qml.math.cast` where the `MottonenStatePreparation` operation expected
+ a float type instead of double.
+ [(#1400)](https://github.com/XanaduAI/pennylane/pull/1400)
+
+* Fixes a bug where a copy of `qml.ControlledQubitUnitary` was non-functional as it did not have all the necessary information.
+[(#1411)](https://github.com/PennyLaneAI/pennylane/pull/1411)
+
+* Warns when adjoint or reversible differentiation specified or called on a device with finite shots.
+ [(#1406)](https://github.com/PennyLaneAI/pennylane/pull/1406)
+
+* Fixes the differentiability of the operations `IsingXX` and `IsingZZ` for Autograd, Jax and Tensorflow.
+ [(#1390)](https://github.com/PennyLaneAI/pennylane/pull/1390)
+
* Fixes a bug where multiple identical Hamiltonian terms will produce a
different result with ``optimize=True`` using ``ExpvalCost``.
[(#1405)](https://github.com/XanaduAI/pennylane/pull/1405)
@@ -586,7 +599,7 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
[(#1392)](https://github.com/XanaduAI/pennylane/pull/1392)
* Fixes floating point errors with `diff_method="finite-diff"` and `order=1` when parameters are `float32`.
-[(#1381)](https://github.com/PennyLaneAI/pennylane/pull/1381)
+ [(#1381)](https://github.com/PennyLaneAI/pennylane/pull/1381)
* Fixes a bug where `qml.ctrl` would fail to transform gates that had no
control defined and no decomposition defined.
@@ -647,9 +660,10 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
This release contains contributions from (in alphabetical order):
Marius Aglitoiu, Vishnu Ajith, Thomas Bromley, Jack Ceroni, Alaric Cheng, Miruna Daian, Olivia Di Matteo,
+
Tanya Garg, Christian Gogolin, Diego Guala, Anthony Hayes, Ryan Hill, Josh Izaac, Pavan Jayasinha, Nathan Killoran,
-Christina Lee, Ryan Levy, Nahum Sá, Maria Schuld, Johannes Jakob Meyer, Brian Shi, Antal Száva, David Wierichs,
-Vincent Wong, Alberto Maldonado, Ashish Panigrahi.
+Christina Lee, Ryan Levy, Johannes Jakob Meyer, Romain Moyard, Nahum Sá, Maria Schuld, Brian Shi, Antal Száva,
+David Wierichs, Vincent Wong, Alberto Maldonado, Ashish Panigrahi.
# Release 0.15.1 (current release)
diff --git a/pennylane/__init__.py b/pennylane/__init__.py
index f7371f829f8..1524d56e6b7 100644
--- a/pennylane/__init__.py
+++ b/pennylane/__init__.py
@@ -53,6 +53,7 @@
qfunc_transform,
single_tape_transform,
quantum_monte_carlo,
+ apply_controlled_Q,
)
from pennylane.utils import inv
from pennylane.vqe import ExpvalCost, Hamiltonian, VQECost
diff --git a/pennylane/_qubit_device.py b/pennylane/_qubit_device.py
index b75b83ac0b5..5300cd088e5 100644
--- a/pennylane/_qubit_device.py
+++ b/pennylane/_qubit_device.py
@@ -854,6 +854,13 @@ def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False):
if not hasattr(m.obs, "base_name"):
m.obs.base_name = None # This is needed for when the observable is a tensor product
+ if self.shots is not None:
+ warnings.warn(
+ "Requested adjoint differentiation to be computed with finite shots."
+ " The derivative is always exact when using the adjoint differentiation method.",
+ UserWarning,
+ )
+
# Initialization of state
if starting_state is not None:
ket = self._reshape(starting_state, [2] * self.num_wires)
diff --git a/pennylane/circuit_graph.py b/pennylane/circuit_graph.py
index f57da751fd1..38679a3524c 100644
--- a/pennylane/circuit_graph.py
+++ b/pennylane/circuit_graph.py
@@ -68,7 +68,7 @@ def _list_at_index_or_none(ls, idx):
return None
-def is_returned_observable(op):
+def _is_returned_observable(op):
"""Helper for the condition of having an observable or
measurement process in the return statement.
@@ -523,7 +523,7 @@ def greedy_layers(self, wire_order=None, show_all_wires=False):
observables[wire] = [None] * self.max_simultaneous_measurements
for op in self._grid[wire]:
- if is_returned_observable(op):
+ if _is_returned_observable(op):
obs_idx = mp_map[op]
observables[wire][obs_idx] = op
diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py
index cc1a611e01e..ee4b1e0f82d 100644
--- a/pennylane/devices/autograd_ops.py
+++ b/pennylane/devices/autograd_ops.py
@@ -28,6 +28,7 @@
II = np.eye(4, dtype=C_DTYPE)
ZZ = np.array(kron(Z, Z), dtype=C_DTYPE)
+XX = np.array(kron(X, X), dtype=C_DTYPE)
IX = np.array(kron(I, X), dtype=C_DTYPE)
IY = np.array(kron(I, Y), dtype=C_DTYPE)
@@ -182,6 +183,44 @@ def MultiRZ(theta, n):
return np.exp(-1j * theta / 2 * pauli_eigs(n))
+def IsingXX(phi):
+ r"""Ising XX coupling gate
+
+ .. math:: XX(\phi) = \begin{bmatrix}
+ \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
+ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
+ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
+ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
+ \end{bmatrix}.
+
+ Args:
+ phi (float): rotation angle :math:`\phi`
+ Returns:
+ array[complex]: unitary 4x4 rotation matrix
+ """
+ return np.cos(phi / 2) * II - 1j * np.sin(phi / 2) * XX
+
+
+def IsingZZ(phi):
+ r"""Ising ZZ coupling gate
+
+ .. math:: ZZ(\phi) = \begin{bmatrix}
+ e^{-i \phi / 2} & 0 & 0 & 0 \\
+ 0 & e^{i \phi / 2} & 0 & 0 \\
+ 0 & 0 & e^{i \phi / 2} & 0 \\
+ 0 & 0 & 0 & e^{-i \phi / 2}
+ \end{bmatrix}.
+
+ Args:
+ phi (float): rotation angle :math:`\phi`
+ Returns:
+ array[complex]: unitary 4x4 rotation matrix
+ """
+ e_m = np.exp(-1j * phi / 2)
+ e = np.exp(1j * phi / 2)
+ return np.array([[e_m, 0, 0, 0], [0, e, 0, 0], [0, 0, e, 0], [0, 0, 0, e_m]])
+
+
def SingleExcitation(phi):
r"""Single excitation rotation.
diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py
index 8571b863467..d06d383f4b7 100644
--- a/pennylane/devices/default_qubit_autograd.py
+++ b/pennylane/devices/default_qubit_autograd.py
@@ -94,6 +94,8 @@ class DefaultQubitAutograd(DefaultQubit):
"CRZ": autograd_ops.CRZ,
"CRot": autograd_ops.CRot,
"MultiRZ": autograd_ops.MultiRZ,
+ "IsingXX": autograd_ops.IsingXX,
+ "IsingZZ": autograd_ops.IsingZZ,
"SingleExcitation": autograd_ops.SingleExcitation,
"SingleExcitationPlus": autograd_ops.SingleExcitationPlus,
"SingleExcitationMinus": autograd_ops.SingleExcitationMinus,
diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py
index 3d99a19280b..67913669af7 100644
--- a/pennylane/devices/default_qubit_jax.py
+++ b/pennylane/devices/default_qubit_jax.py
@@ -147,6 +147,8 @@ def circuit():
"CRZ": jax_ops.CRZ,
"CRot": jax_ops.CRot,
"MultiRZ": jax_ops.MultiRZ,
+ "IsingXX": jax_ops.IsingXX,
+ "IsingZZ": jax_ops.IsingZZ,
"SingleExcitation": jax_ops.SingleExcitation,
"SingleExcitationPlus": jax_ops.SingleExcitationPlus,
"SingleExcitationMinus": jax_ops.SingleExcitationMinus,
diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py
index b60281d7f5f..df0dba8dd08 100644
--- a/pennylane/devices/default_qubit_tf.py
+++ b/pennylane/devices/default_qubit_tf.py
@@ -141,6 +141,8 @@ class DefaultQubitTF(DefaultQubit):
"CRY": tf_ops.CRY,
"CRZ": tf_ops.CRZ,
"CRot": tf_ops.CRot,
+ "IsingXX": tf_ops.IsingXX,
+ "IsingZZ": tf_ops.IsingZZ,
"SingleExcitation": tf_ops.SingleExcitation,
"SingleExcitationPlus": tf_ops.SingleExcitationPlus,
"SingleExcitationMinus": tf_ops.SingleExcitationMinus,
diff --git a/pennylane/devices/jax_ops.py b/pennylane/devices/jax_ops.py
index dae1c675b01..9d9555ae719 100644
--- a/pennylane/devices/jax_ops.py
+++ b/pennylane/devices/jax_ops.py
@@ -28,6 +28,7 @@
II = jnp.eye(4, dtype=C_DTYPE)
ZZ = jnp.array(jnp.kron(Z, Z), dtype=C_DTYPE)
+XX = jnp.array(jnp.kron(X, X), dtype=C_DTYPE)
IX = jnp.array(jnp.kron(I, X), dtype=C_DTYPE)
IY = jnp.array(jnp.kron(I, Y), dtype=C_DTYPE)
@@ -182,6 +183,44 @@ def MultiRZ(theta, n):
return jnp.exp(-1j * theta / 2 * pauli_eigs(n))
+def IsingXX(phi):
+ r"""Ising XX coupling gate.
+
+ .. math:: XX(\phi) = \begin{bmatrix}
+ \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
+ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
+ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
+ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
+ \end{bmatrix}.
+
+ Args:
+ phi (float): rotation angle :math:`\phi`
+ Returns:
+ array[complex]: unitary 4x4 rotation matrix
+ """
+ return jnp.cos(phi / 2) * II - 1j * jnp.sin(phi / 2) * XX
+
+
+def IsingZZ(phi):
+ r"""Ising ZZ coupling gate
+
+ .. math:: ZZ(\phi) = \begin{bmatrix}
+ e^{-i \phi / 2} & 0 & 0 & 0 \\
+ 0 & e^{i \phi / 2} & 0 & 0 \\
+ 0 & 0 & e^{i \phi / 2} & 0 \\
+ 0 & 0 & 0 & e^{-i \phi / 2}
+ \end{bmatrix}.
+
+ Args:
+ phi (float): rotation angle :math:`\phi`
+ Returns:
+ array[complex]: unitary 4x4 rotation matrix
+ """
+ e_m = jnp.exp(-1j * phi / 2)
+ e = jnp.exp(1j * phi / 2)
+ return jnp.array([[e_m, 0, 0, 0], [0, e, 0, 0], [0, 0, e, 0], [0, 0, 0, e_m]])
+
+
def SingleExcitation(phi):
r"""Single excitation rotation.
diff --git a/pennylane/devices/tests/test_measurements.py b/pennylane/devices/tests/test_measurements.py
index b72813d08b0..fe91ae7439a 100755
--- a/pennylane/devices/tests/test_measurements.py
+++ b/pennylane/devices/tests/test_measurements.py
@@ -816,8 +816,8 @@ def test_projector(self, device, tol, skip_if):
skip_if(dev, {"supports_tensor_observables": False})
- theta = 0.432
- phi = 0.123
+ theta = 1.432
+ phi = 1.123
varphi = -0.543
@qml.qnode(dev)
diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py
index ff3c8a700e5..febaa6c8d7c 100644
--- a/pennylane/devices/tf_ops.py
+++ b/pennylane/devices/tf_ops.py
@@ -28,6 +28,7 @@
II = tf.eye(4, dtype=C_DTYPE)
ZZ = tf.constant(kron(Z, Z), dtype=C_DTYPE)
+XX = tf.constant(kron(X, X), dtype=C_DTYPE)
IX = tf.constant(kron(I, X), dtype=C_DTYPE)
IY = tf.constant(kron(I, Y), dtype=C_DTYPE)
@@ -193,6 +194,46 @@ def CRot(a, b, c):
return tf.linalg.diag(CRZ(c)) @ (CRY(b) @ tf.linalg.diag(CRZ(a)))
+def IsingXX(phi):
+ r"""Ising XX coupling gate
+
+ .. math:: XX(\phi) = \begin{bmatrix}
+ \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
+ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
+ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
+ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
+ \end{bmatrix}.
+
+ Args:
+ phi (float): rotation angle :math:`\phi`
+ Returns:
+ tf.Tensor[complex]: unitary 4x4 rotation matrix
+ """
+ phi = tf.cast(phi, dtype=C_DTYPE)
+ return tf.cos(phi / 2) * II - 1j * tf.sin(phi / 2) * XX
+
+
+def IsingZZ(phi):
+ r"""Ising ZZ coupling gate
+
+ .. math:: ZZ(\phi) = \begin{bmatrix}
+ e^{-i \phi / 2} & 0 & 0 & 0 \\
+ 0 & e^{i \phi / 2} & 0 & 0 \\
+ 0 & 0 & e^{i \phi / 2} & 0 \\
+ 0 & 0 & 0 & e^{-i \phi / 2}
+ \end{bmatrix}.
+
+ Args:
+ phi (float): rotation :math:`\phi`
+ Returns:
+ tf.Tensor[complex]: unitary 4x4 rotation matrix
+ """
+ phi = tf.cast(phi, dtype=C_DTYPE)
+ e_m = tf.exp(-1j * phi / 2)
+ e = tf.exp(1j * phi / 2)
+ return tf.convert_to_tensor([[e_m, 0, 0, 0], [0, e, 0, 0], [0, 0, e, 0], [0, 0, 0, e_m]])
+
+
def SingleExcitation(phi):
r"""Single excitation rotation.
diff --git a/pennylane/fourier/coefficients.py b/pennylane/fourier/coefficients.py
index a3f3eb78334..2e71c0db16c 100644
--- a/pennylane/fourier/coefficients.py
+++ b/pennylane/fourier/coefficients.py
@@ -80,10 +80,10 @@ def coefficients(f, n_inputs, degree, lowpass_filter=False, filter_threshold=Non
@qml.qnode(dev)
def circuit(weights, inpt):
qml.RX(inpt[0], wires='a')
- qml.Rot(0.1, 0.2, 0.3, wires='a')
+ qml.Rot(*weights[0], wires='a')
qml.RY(inpt[1], wires='a')
- qml.Rot(-4.1, 3.2, 1.3, wires='a')
+ qml.Rot(*weights[1], wires='a')
return qml.expval(qml.PauliZ(wires='a'))
@@ -94,7 +94,7 @@ def circuit(weights, inpt):
``weights``:
>>> from functools import partial
- >>> weights = np.array([0.5, 0.2])
+ >>> weights = np.array([[0.1, 0.2, 0.3], [-4.1, 3.2, 1.3]])
>>> partial_circuit = partial(circuit, weights)
Now we must specify the number of inputs, and the maximum desired
diff --git a/pennylane/kernels/postprocessing.py b/pennylane/kernels/postprocessing.py
index 70b5aec01a0..c3c3a3d714d 100644
--- a/pennylane/kernels/postprocessing.py
+++ b/pennylane/kernels/postprocessing.py
@@ -267,27 +267,48 @@ def mitigate_depolarizing_noise(K, num_wires, method, use_entries=None):
**Example:**
For an example usage of ``mitigate_depolarizing_noise`` please refer to the
- `PennyLane demo on the kernel module `_ or `the postprocessing demo for arXiv:2105.02276 `_.
+ `PennyLane demo on the kernel module `_ or `the postprocessing demo for arXiv:2105.02276 `_.
"""
dim = 2 ** num_wires
if method == "single":
if use_entries is None:
use_entries = (0,)
+
+ if K[use_entries[0], use_entries[0]] <= (1 / dim):
+ raise ValueError(
+ "The single noise mitigation method cannot be applied "
+ "as the single diagonal element specified is too small."
+ )
+
diagonal_element = K[use_entries[0], use_entries[0]]
noise_rate = (1 - diagonal_element) * dim / (dim - 1)
mitigated_matrix = (K - noise_rate / dim) / (1 - noise_rate)
elif method == "average":
+
if use_entries is None:
diagonal_elements = np.diag(K)
else:
diagonal_elements = np.diag(K)[np.array(use_entries)]
+
+ if np.mean(diagonal_elements) <= 1 / dim:
+ raise ValueError(
+ "The average noise mitigation method cannot be applied "
+ "as the average of the used diagonal terms is too small."
+ )
+
noise_rates = (1 - diagonal_elements) * dim / (dim - 1)
mean_noise_rate = np.mean(noise_rates)
mitigated_matrix = (K - mean_noise_rate / dim) / (1 - mean_noise_rate)
elif method == "split_channel":
+ if np.any(np.diag(K) <= 1 / dim):
+ raise ValueError(
+ "The split channel noise mitigation method cannot be applied "
+ "to the input matrix as its diagonal terms are too small."
+ )
+
eff_noise_rates = np.clip((1 - np.diag(K)) * dim / (dim - 1), 0.0, 1.0)
noise_rates = 1 - np.sqrt(1 - eff_noise_rates)
inverse_noise = (
@@ -295,6 +316,12 @@ def mitigate_depolarizing_noise(K, num_wires, method, use_entries=None):
+ noise_rates.reshape((1, len(K)))
+ noise_rates.reshape((len(K), 1))
)
+
mitigated_matrix = (K - inverse_noise / dim) / (1 - inverse_noise)
+ else:
+ raise ValueError(
+ "Incorrect noise depolarization mitigation method specified. "
+ "Accepted strategies are: 'single', 'average' and 'split_channel'."
+ )
return mitigated_matrix
diff --git a/pennylane/math/multi_dispatch.py b/pennylane/math/multi_dispatch.py
index 783a7e45acf..3193c7b49a1 100644
--- a/pennylane/math/multi_dispatch.py
+++ b/pennylane/math/multi_dispatch.py
@@ -173,13 +173,13 @@ def diag(values, k=0):
**Example**
>>> x = [1., 2., tf.Variable(3.)]
- >>> diag(x)
+ >>> qml.math.diag(x)
>>> y = tf.Variable([0.65, 0.2, 0.1])
- >>> diag(y, k=-1)
+ >>> qml.math.diag(y, k=-1)
>> x = torch.tensor([1, 2])
>>> y = torch.tensor([3., 4.])
- >>> cast(x, y)
+ >>> cast_like(x, y)
tensor([1., 2.])
"""
dtype = ar.to_numpy(tensor2).dtype.type
@@ -139,7 +139,7 @@ def convert_like(tensor1, tensor2):
>>> x = np.array([1, 2])
>>> y = tf.Variable([3, 4])
- >>> cast(x, y)
+ >>> convert_like(x, y)
"""
return np.asarray(tensor1, like=get_interface(tensor2))
@@ -189,7 +189,6 @@ def requires_grad(tensor):
For example, Torch tensors and PennyLane tensors track trainability
as a property of the tensor itself. TensorFlow, on the other hand,
-
only tracks trainability if being watched by a gradient tape.
Args:
diff --git a/pennylane/operation.py b/pennylane/operation.py
index 1142c28cbdb..fea8a597ee4 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -256,11 +256,9 @@ def __copy__(self):
cls = self.__class__
copied_op = cls.__new__(cls)
copied_op.data = self.data.copy()
- copied_op._wires = self.wires
- copied_op._name = self._name
-
- if hasattr(self, "_inverse"):
- copied_op._inverse = self._inverse
+ for attr, value in vars(self).items():
+ if attr != "data":
+ setattr(copied_op, attr, value)
return copied_op
diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py
index 97fd3ce7581..de82446bd06 100644
--- a/pennylane/ops/qubit.py
+++ b/pennylane/ops/qubit.py
@@ -1797,6 +1797,7 @@ class IsingXX(Operation):
num_wires = 2
par_domain = "R"
grad_method = "A"
+ generator = [np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]), -1 / 2]
@classmethod
def _matrix(cls, *params):
@@ -1848,6 +1849,7 @@ class IsingZZ(Operation):
num_wires = 2
par_domain = "R"
grad_method = "A"
+ generator = [np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]), -1 / 2]
@staticmethod
def decomposition(phi, wires):
diff --git a/pennylane/qaoa/cycle.py b/pennylane/qaoa/cycle.py
index 67cd6832706..9ea355bec50 100644
--- a/pennylane/qaoa/cycle.py
+++ b/pennylane/qaoa/cycle.py
@@ -408,7 +408,7 @@ def _inner_out_flow_constraint_hamiltonian(graph: nx.DiGraph, node) -> Hamiltoni
d_{i}^{out}(d_{i}^{out} - 2)\mathbb{I}
- 2(d_{i}^{out}-1)\sum_{j,(i,j)\in E}\hat{Z}_{ij} +
- ( \sum_{j,(i,j)\in E}\hat{Z}_{ij}) )^{2}
+ ( \sum_{j,(i,j)\in E}\hat{Z}_{ij} )^{2}
Args:
graph (nx.DiGraph): the directed graph specifying possible edges
diff --git a/pennylane/qnode.py b/pennylane/qnode.py
index d0e9a0d7ecc..3381eaea837 100644
--- a/pennylane/qnode.py
+++ b/pennylane/qnode.py
@@ -263,8 +263,8 @@ def get_best_method(device, interface):
This method attempts to determine support for differentiation
methods using the following order:
- * ``"backprop"``
* ``"device"``
+ * ``"backprop"``
* ``"parameter-shift"``
* ``"finite-diff"``
@@ -386,6 +386,13 @@ def _validate_reversible_method(device, interface):
f"The {device.short_name} device does not support reversible differentiation."
)
+ if device.shots is not None:
+ warnings.warn(
+ "Requested reversible differentiation to be computed with finite shots."
+ " Reversible differentiation always calculated exactly.",
+ UserWarning,
+ )
+
return ReversibleTape, interface, device, {"method": "analytic"}
@staticmethod
@@ -419,6 +426,13 @@ def _validate_adjoint_method(device, interface):
f"The {device.short_name} device does not support adjoint differentiation."
)
+ if device.shots is not None:
+ warnings.warn(
+ "Requested adjoint differentiation to be computed with finite shots."
+ " Adjoint differentiation always calculated exactly.",
+ UserWarning,
+ )
+
jac_options = {"method": "device", "jacobian_method": "adjoint_jacobian"}
# reuse the forward pass
# torch and tensorflow can cache the state
diff --git a/pennylane/tape/reversible.py b/pennylane/tape/reversible.py
index 4396baacc24..d181f752575 100644
--- a/pennylane/tape/reversible.py
+++ b/pennylane/tape/reversible.py
@@ -18,6 +18,7 @@
import copy
from functools import reduce
from string import ascii_letters as ABC
+import warnings
import numpy as np
@@ -35,7 +36,7 @@ class ReversibleTape(JacobianTape):
.. note::
- The reversible analytic differentation method has the following restrictions:
+ The reversible analytic differentiation method has the following restrictions:
* As it requires knowledge of the statevector, only statevector simulator devices can be used.
@@ -246,6 +247,13 @@ def jacobian(self, device, params=None, **options):
# before each Jacobian call, so that the statevector is calculated only once.
self._final_state = None
+ if device.shots is not None:
+ warnings.warn(
+ "Requested reversible differentiation to be computed with finite shots."
+ " Reversible differentiation always calculated exactly.",
+ UserWarning,
+ )
+
return super().jacobian(device, params, **options)
def analytic_pd(self, idx, params, **options):
diff --git a/pennylane/templates/state_preparations/mottonen.py b/pennylane/templates/state_preparations/mottonen.py
index 860eeb80f50..fe3ca856a52 100644
--- a/pennylane/templates/state_preparations/mottonen.py
+++ b/pennylane/templates/state_preparations/mottonen.py
@@ -203,6 +203,10 @@ def _get_alpha_y(a, n, k):
with np.errstate(divide="ignore", invalid="ignore"):
division = numerator / denominator
+ # Cast the numerator and denominator to ensure compatibility with interfaces
+ division = qml.math.cast(division, np.float64)
+ denominator = qml.math.cast(denominator, np.float64)
+
division = qml.math.where(denominator != 0.0, division, 0.0)
return 2 * qml.math.arcsin(qml.math.sqrt(division))
diff --git a/pennylane/transforms/__init__.py b/pennylane/transforms/__init__.py
index 688893390b8..0460240dd09 100644
--- a/pennylane/transforms/__init__.py
+++ b/pennylane/transforms/__init__.py
@@ -46,6 +46,7 @@
~adjoint
~ctrl
~transforms.invisible
+ ~apply_controlled_Q
~quantum_monte_carlo
Transforms that act on tapes
@@ -84,4 +85,4 @@
from .metric_tensor import metric_tensor, metric_tensor_tape
from .specs import specs
from .qfunc_transforms import make_tape, single_tape_transform, qfunc_transform
-from .qmc import quantum_monte_carlo
+from .qmc import apply_controlled_Q, quantum_monte_carlo
diff --git a/tests/collections/conftest.py b/tests/collections/conftest.py
index cb5142d475d..d8f5a3a370e 100644
--- a/tests/collections/conftest.py
+++ b/tests/collections/conftest.py
@@ -41,7 +41,7 @@ def qnodes(interface, tf_support, torch_support, jax_support):
pytest.skip("Skipped, no tf support")
if interface == "jax" and not jax_support:
- pytest.skip("Skipped, no tf support")
+ pytest.skip("Skipped, no jax support")
dev1 = qml.device("default.qubit", wires=2)
dev2 = qml.device("default.qubit", wires=2)
diff --git a/tests/kernels/test_kernels.py b/tests/kernels/test_kernels.py
index 57ed308f62e..7724d83f29e 100644
--- a/tests/kernels/test_kernels.py
+++ b/tests/kernels/test_kernels.py
@@ -494,3 +494,91 @@ def test_mitigate_depolarizing_noise_split_channel(self, input, expected_output)
noise rates per datapoint."""
output = kern.mitigate_depolarizing_noise(input, self.num_wires, "split_channel")
assert np.allclose(output, expected_output)
+
+
+class TestErrorForNonRealistic:
+ """Tests that the noise mitigation techniques raise an error whenever the
+ used quantities are too small."""
+
+ def test_mitigate_depolarizing_noise_wrong_method(self, recwarn):
+ """Test that an error is raised when specifying an incorrect method."""
+ with pytest.raises(
+ ValueError, match="Incorrect noise depolarization mitigation method specified"
+ ):
+ qml.kernels.mitigate_depolarizing_noise(np.array([0]), 4, method="some_dummy_strat")
+
+ def test_mitigate_depolarizing_noise_average_method_error(self, recwarn):
+ """Test that an error is raised when using the average method for the
+ mitigation of depolarizing noise with a matrix that has too small diagonal
+ entries."""
+ num_wires = 6
+ wires = range(num_wires)
+
+ dev = qml.device("default.qubit", wires=num_wires)
+
+ @qml.qnode(dev)
+ def kernel_circuit(x1, x2):
+ qml.templates.AngleEmbedding(x1, wires=wires)
+ qml.adjoint(qml.templates.AngleEmbedding)(x2, wires=wires)
+ return qml.probs(wires)
+
+ kernel = lambda x1, x2: kernel_circuit(x1, x2)[0]
+
+ # "Training feature vectors"
+ X_train = qml.numpy.tensor(
+ [[0.73096199, 0.19012506, 0.57223395], [0.78126872, 0.53535039, 0.31160784]],
+ requires_grad=True,
+ )
+
+ # Create symmetric square kernel matrix (for training)
+ K = qml.kernels.square_kernel_matrix(X_train, kernel)
+
+ # Add some (symmetric) Gaussian noise to the kernel matrix.
+ N = qml.numpy.tensor(
+ [[-2.33010045, -2.22195441], [-0.40680862, 0.21785961]], requires_grad=True
+ )
+ K += (N + N.T) / 2
+ with pytest.raises(
+ ValueError, match="The average noise mitigation method cannot be applied"
+ ):
+ qml.kernels.mitigate_depolarizing_noise(K, num_wires, method="average")
+
+ @pytest.mark.parametrize(
+ "msg, method", [("single", "single"), ("split channel", "split_channel")]
+ )
+ def test_mitigate_depolarizing_noise_error(self, msg, method):
+ """Test that an error is raised for the mitigation of depolarizing
+ noise with a matrix that has too small specified entries for the
+ single and the split channel strategies."""
+ num_wires = 6
+ wires = range(num_wires)
+
+ dev = qml.device("default.qubit", wires=num_wires)
+
+ @qml.qnode(dev)
+ def kernel_circuit(x1, x2):
+ qml.templates.AngleEmbedding(x1, wires=wires)
+ qml.adjoint(qml.templates.AngleEmbedding)(x2, wires=wires)
+ return qml.probs(wires)
+
+ kernel = lambda x1, x2: kernel_circuit(x1, x2)[0]
+
+ # "Training feature vectors"
+ X_train = qml.numpy.tensor(
+ [[0.39375865, 0.50895605, 0.30720779], [0.34389837, 0.7043728, 0.40067889]],
+ requires_grad=True,
+ )
+
+ # Create symmetric square kernel matrix (for training)
+ K = qml.kernels.square_kernel_matrix(X_train, kernel)
+
+ # Add some (symmetric) Gaussian noise to the kernel matrix.
+ N = qml.numpy.tensor(
+ [[-1.15035284, 0.36726945], [0.26436627, -0.59287149]], requires_grad=True
+ )
+ K += (N + N.T) / 2
+
+ with pytest.raises(
+ ValueError, match=f"The {msg} noise mitigation method cannot be applied"
+ ):
+ qml.kernels.mitigate_depolarizing_noise(K, num_wires, method=method)
diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py
index ed4ca06b0fc..e6b7a5a80e0 100644
--- a/tests/ops/test_qubit_ops.py
+++ b/tests/ops/test_qubit_ops.py
@@ -18,10 +18,12 @@
import re
import pytest
import functools
+import copy
import numpy as np
from numpy.linalg import multi_dot
from scipy.stats import unitary_group
from scipy.linalg import expm
+from pennylane import numpy as npp
import pennylane as qml
from pennylane.wires import Wires
@@ -406,10 +408,78 @@ def circuit(basis_state):
(qml.Toffoli, Toffoli),
]
+PARAMETRIZED_OPERATIONS = [
+ qml.RX(0.123, wires=0),
+ qml.RY(1.434, wires=0),
+ qml.RZ(2.774, wires=0),
+ qml.S(wires=0),
+ qml.SX(wires=0),
+ qml.T(wires=0),
+ qml.CNOT(wires=[0, 1]),
+ qml.CZ(wires=[0, 1]),
+ qml.CY(wires=[0, 1]),
+ qml.SWAP(wires=[0, 1]),
+ qml.ISWAP(wires=[0, 1]),
+ qml.CSWAP(wires=[0, 1, 2]),
+ qml.PauliRot(0.123, "Y", wires=0),
+ qml.IsingXX(0.123, wires=[0, 1]),
+ qml.IsingZZ(0.123, wires=[0, 1]),
+ qml.Rot(0.123, 0.456, 0.789, wires=0),
+ qml.Toffoli(wires=[0, 1, 2]),
+ qml.PhaseShift(2.133, wires=0),
+ qml.ControlledPhaseShift(1.777, wires=[0, 2]),
+ qml.CPhase(1.777, wires=[0, 2]),
+ qml.MultiRZ(0.112, wires=[1, 2, 3]),
+ qml.CRX(0.836, wires=[2, 3]),
+ qml.CRY(0.721, wires=[2, 3]),
+ qml.CRZ(0.554, wires=[2, 3]),
+ qml.U1(0.123, wires=0),
+ qml.U2(3.556, 2.134, wires=0),
+ qml.U3(2.009, 1.894, 0.7789, wires=0),
+ qml.Hadamard(wires=0),
+ qml.PauliX(wires=0),
+ qml.PauliZ(wires=0),
+ qml.PauliY(wires=0),
+ qml.CRot(0.123, 0.456, 0.789, wires=[0, 1]),
+ qml.QubitUnitary(np.eye(2) * 1j, wires=0),
+ qml.DiagonalQubitUnitary(np.array([1.0, 1.0j]), wires=1),
+ qml.ControlledQubitUnitary(np.eye(2) * 1j, wires=[0], control_wires=[2]),
+ qml.MultiControlledX(control_wires=[0, 1], wires=2, control_values="01"),
+ qml.SingleExcitation(0.123, wires=[0, 3]),
+ qml.SingleExcitationPlus(0.123, wires=[0, 3]),
+ qml.SingleExcitationMinus(0.123, wires=[0, 3]),
+ qml.DoubleExcitation(0.123, wires=[0, 1, 2, 3]),
+ qml.DoubleExcitationPlus(0.123, wires=[0, 1, 2, 3]),
+ qml.DoubleExcitationMinus(0.123, wires=[0, 1, 2, 3]),
+ qml.QubitSum(wires=[0, 1, 2]),
+]
+
class TestOperations:
"""Tests for the operations"""
+ @pytest.mark.parametrize("op_cls, mat", NON_PARAMETRIZED_OPERATIONS)
+ def test_nonparametrized_op_copy(self, op_cls, mat, tol):
+ """Tests that copied nonparametrized ops function as expected"""
+ op = op_cls(wires=range(op_cls.num_wires))
+ copied_op = copy.copy(op)
+ np.testing.assert_allclose(op.matrix, copied_op.matrix, atol=tol)
+
+ op._inverse = True
+ copied_op2 = copy.copy(op)
+ np.testing.assert_allclose(op.matrix, copied_op2.matrix, atol=tol)
+
+ @pytest.mark.parametrize("op", PARAMETRIZED_OPERATIONS)
+ def test_parametrized_op_copy(self, op, tol):
+ """Tests that copied parametrized ops function as expected"""
+ copied_op = copy.copy(op)
+ np.testing.assert_allclose(op.matrix, copied_op.matrix, atol=tol)
+
+ op.inv()
+ copied_op2 = copy.copy(op)
+ np.testing.assert_allclose(op.matrix, copied_op2.matrix, atol=tol)
+ op.inv()
+
@pytest.mark.parametrize("ops, mat", NON_PARAMETRIZED_OPERATIONS)
def test_matrices(self, ops, mat, tol):
"""Test matrices of non-parametrized operations are correct"""
@@ -417,54 +487,7 @@ def test_matrices(self, ops, mat, tol):
res = op.matrix
assert np.allclose(res, mat, atol=tol, rtol=0)
- @pytest.mark.parametrize(
- "op",
- [
- qml.RX(0.123, wires=0),
- qml.RY(1.434, wires=0),
- qml.RZ(2.774, wires=0),
- qml.S(wires=0),
- qml.SX(wires=0),
- qml.T(wires=0),
- qml.CNOT(wires=[0, 1]),
- qml.CZ(wires=[0, 1]),
- qml.CY(wires=[0, 1]),
- qml.SWAP(wires=[0, 1]),
- qml.ISWAP(wires=[0, 1]),
- qml.CSWAP(wires=[0, 1, 2]),
- qml.PauliRot(0.123, "Y", wires=0),
- qml.IsingXX(0.123, wires=[0, 1]),
- qml.IsingZZ(0.123, wires=[0, 1]),
- qml.Rot(0.123, 0.456, 0.789, wires=0),
- qml.Toffoli(wires=[0, 1, 2]),
- qml.PhaseShift(2.133, wires=0),
- qml.ControlledPhaseShift(1.777, wires=[0, 2]),
- qml.CPhase(1.777, wires=[0, 2]),
- qml.MultiRZ(0.112, wires=[1, 2, 3]),
- qml.CRX(0.836, wires=[2, 3]),
- qml.CRY(0.721, wires=[2, 3]),
- qml.CRZ(0.554, wires=[2, 3]),
- qml.U1(0.123, wires=0),
- qml.U2(3.556, 2.134, wires=0),
- qml.U3(2.009, 1.894, 0.7789, wires=0),
- qml.Hadamard(wires=0),
- qml.PauliX(wires=0),
- qml.PauliZ(wires=0),
- qml.PauliY(wires=0),
- qml.CRot(0.123, 0.456, 0.789, wires=[0, 1]),
- qml.QubitUnitary(np.eye(2) * 1j, wires=0),
- qml.DiagonalQubitUnitary(np.array([1.0, 1.0j]), wires=1),
- qml.ControlledQubitUnitary(np.eye(2) * 1j, wires=[0], control_wires=[2]),
- qml.MultiControlledX(control_wires=[0, 1], wires=2, control_values="01"),
- qml.SingleExcitation(0.123, wires=[0, 3]),
- qml.SingleExcitationPlus(0.123, wires=[0, 3]),
- qml.SingleExcitationMinus(0.123, wires=[0, 3]),
- qml.DoubleExcitation(0.123, wires=[0, 1, 2, 3]),
- qml.DoubleExcitationPlus(0.123, wires=[0, 1, 2, 3]),
- qml.DoubleExcitationMinus(0.123, wires=[0, 1, 2, 3]),
- qml.QubitSum(wires=[0, 1, 2]),
- ],
- )
+ @pytest.mark.parametrize("op", PARAMETRIZED_OPERATIONS)
def test_adjoint_unitaries(self, op, tol):
op_d = op.adjoint()
res1 = np.dot(op.matrix, op_d.matrix)
@@ -777,6 +800,241 @@ def test_isingxx_decomposition(self, tol):
assert np.allclose(decomposed_matrix, op.matrix, atol=tol, rtol=0)
+ device_methods = [
+ ["default.qubit", "finite-diff"],
+ ["default.qubit", "parameter-shift"],
+ ["default.qubit", "backprop"],
+ ["default.qubit", "adjoint"],
+ ]
+
+ phis = [0.1, 0.2, 0.3]
+
+ configuration = []
+
+ for phi in phis:
+ for device, method in device_methods:
+ configuration.append([device, method, phi])
+
+ @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+ def test_isingxx_autograd_grad(self, tol, dev_name, diff_method, phi):
+ """Test the gradient for the gate IsingXX."""
+ dev = qml.device(dev_name, wires=2)
+
+ psi_0 = 0.1
+ psi_1 = 0.2
+ psi_2 = 0.3
+ psi_3 = 0.4
+
+ init_state = npp.array([psi_0, psi_1, psi_2, psi_3], requires_grad=False)
+ norm = np.linalg.norm(init_state)
+ init_state /= norm
+
+ @qml.qnode(dev, diff_method=diff_method, interface="autograd")
+ def circuit(phi):
+ qml.QubitStateVector(init_state, wires=[0, 1])
+ qml.IsingXX(phi, wires=[0, 1])
+ return qml.expval(qml.PauliZ(0))
+
+ phi = npp.array(0.1, requires_grad=True)
+
+ expected = (
+ 0.5
+ * (1 / norm ** 2)
+ * (
+ -np.sin(phi) * (psi_0 ** 2 + psi_1 ** 2 - psi_2 ** 2 - psi_3 ** 2)
+ + 2
+ * np.sin(phi / 2)
+ * np.cos(phi / 2)
+ * (-(psi_0 ** 2) - psi_1 ** 2 + psi_2 ** 2 + psi_3 ** 2)
+ )
+ )
+
+ res = qml.grad(circuit)(phi)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
+
+ @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+ def test_isingzz_autograd_grad(self, tol, dev_name, diff_method, phi):
+ """Test the gradient for the gate IsingZZ."""
+ dev = qml.device(dev_name, wires=2)
+
+ psi_0 = 0.1
+ psi_1 = 0.2
+ psi_2 = 0.3
+ psi_3 = 0.4
+
+ init_state = npp.array([psi_0, psi_1, psi_2, psi_3], requires_grad=False)
+ norm = np.linalg.norm(init_state)
+ init_state /= norm
+
+ @qml.qnode(dev, diff_method=diff_method, interface="autograd")
+ def circuit(phi):
+ qml.QubitStateVector(init_state, wires=[0, 1])
+ qml.IsingZZ(phi, wires=[0, 1])
+ return qml.expval(qml.PauliX(0))
+
+ phi = npp.array(0.1, requires_grad=True)
+
+ expected = (1 / norm ** 2) * (-2 * (psi_0 * psi_2 + psi_1 * psi_3) * np.sin(phi))
+
+ res = qml.grad(circuit)(phi)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
+
+ @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+ def test_isingxx_jax_grad(self, tol, dev_name, diff_method, phi):
+ """Test the gradient for the gate IsingXX."""
+
+ if diff_method in {"finite-diff"}:
+ pytest.skip("Test does not support finite-diff")
+
+ if diff_method in {"parameter-shift"}:
+ pytest.skip("Test does not support parameter-shift")
+
+ jax = pytest.importorskip("jax")
+ jnp = pytest.importorskip("jax.numpy")
+
+ dev = qml.device(dev_name, wires=2)
+
+ psi_0 = 0.1
+ psi_1 = 0.2
+ psi_2 = 0.3
+ psi_3 = 0.4
+
+ init_state = jnp.array([psi_0, psi_1, psi_2, psi_3])
+ norm = jnp.linalg.norm(init_state)
+ init_state = init_state / norm
+
+ @qml.qnode(dev, diff_method=diff_method, interface="jax")
+ def circuit(phi):
+ qml.QubitStateVector(init_state, wires=[0, 1])
+ qml.IsingXX(phi, wires=[0, 1])
+ return qml.expval(qml.PauliZ(0))
+
+ phi = jnp.array(0.1)
+
+ expected = (
+ 0.5
+ * (1 / norm ** 2)
+ * (
+ -np.sin(phi) * (psi_0 ** 2 + psi_1 ** 2 - psi_2 ** 2 - psi_3 ** 2)
+ + 2
+ * np.sin(phi / 2)
+ * np.cos(phi / 2)
+ * (-(psi_0 ** 2) - psi_1 ** 2 + psi_2 ** 2 + psi_3 ** 2)
+ )
+ )
+
+ res = jax.grad(circuit, argnums=0)(phi)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
+
+ @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+ def test_isingzz_jax_grad(self, tol, dev_name, diff_method, phi):
+ """Test the gradient for the gate IsingZZ."""
+
+ if diff_method in {"finite-diff"}:
+ pytest.skip("Test does not support finite-diff")
+
+ if diff_method in {"parameter-shift"}:
+ pytest.skip("Test does not support parameter-shift")
+
+ jax = pytest.importorskip("jax")
+ jnp = pytest.importorskip("jax.numpy")
+
+ dev = qml.device(dev_name, wires=2)
+
+ psi_0 = 0.1
+ psi_1 = 0.2
+ psi_2 = 0.3
+ psi_3 = 0.4
+
+ init_state = jnp.array([psi_0, psi_1, psi_2, psi_3])
+ norm = jnp.linalg.norm(init_state)
+ init_state = init_state / norm
+
+ @qml.qnode(dev, diff_method=diff_method, interface="jax")
+ def circuit(phi):
+ qml.QubitStateVector(init_state, wires=[0, 1])
+ qml.IsingZZ(phi, wires=[0, 1])
+ return qml.expval(qml.PauliX(0))
+
+ phi = jnp.array(0.1)
+
+ expected = (1 / norm ** 2) * (-2 * (psi_0 * psi_2 + psi_1 * psi_3) * np.sin(phi))
+
+ res = jax.grad(circuit, argnums=0)(phi)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
+
+ @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+ def test_isingxx_tf_grad(self, tol, dev_name, diff_method, phi):
+ """Test the gradient for the gate IsingXX."""
+ tf = pytest.importorskip("tensorflow", minversion="2.1")
+
+ dev = qml.device(dev_name, wires=2)
+
+ psi_0 = tf.Variable(0.1, dtype=tf.complex128)
+ psi_1 = tf.Variable(0.2, dtype=tf.complex128)
+ psi_2 = tf.Variable(0.3, dtype=tf.complex128)
+ psi_3 = tf.Variable(0.4, dtype=tf.complex128)
+
+ init_state = tf.Variable([psi_0, psi_1, psi_2, psi_3], dtype=tf.complex128)
+ norm = tf.norm(init_state)
+ init_state = init_state / norm
+
+ @qml.qnode(dev, interface="tf", diff_method=diff_method)
+ def circuit(phi):
+ qml.QubitStateVector(init_state, wires=[0, 1])
+ qml.IsingXX(phi, wires=[0, 1])
+ return qml.expval(qml.PauliZ(0))
+
+ phi = tf.Variable(0.1, dtype=tf.complex128)
+
+ expected = (
+ 0.5
+ * (1 / norm ** 2)
+ * (
+ -tf.sin(phi) * (psi_0 ** 2 + psi_1 ** 2 - psi_2 ** 2 - psi_3 ** 2)
+ + 2
+ * tf.sin(phi / 2)
+ * tf.cos(phi / 2)
+ * (-(psi_0 ** 2) - psi_1 ** 2 + psi_2 ** 2 + psi_3 ** 2)
+ )
+ )
+
+ with tf.GradientTape() as tape:
+ result = circuit(phi)
+ res = tape.gradient(result, phi)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
+
+ @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+ def test_isingzz_tf_grad(self, tol, dev_name, diff_method, phi):
+ """Test the gradient for the gate IsingZZ."""
+ tf = pytest.importorskip("tensorflow", minversion="2.1")
+
+ dev = qml.device(dev_name, wires=2)
+
+ psi_0 = tf.Variable(0.1, dtype=tf.complex128)
+ psi_1 = tf.Variable(0.2, dtype=tf.complex128)
+ psi_2 = tf.Variable(0.3, dtype=tf.complex128)
+ psi_3 = tf.Variable(0.4, dtype=tf.complex128)
+
+ init_state = tf.Variable([psi_0, psi_1, psi_2, psi_3], dtype=tf.complex128)
+ norm = tf.norm(init_state)
+ init_state = init_state / norm
+
+ @qml.qnode(dev, interface="tf", diff_method=diff_method)
+ def circuit(phi):
+ qml.QubitStateVector(init_state, wires=[0, 1])
+ qml.IsingZZ(phi, wires=[0, 1])
+ return qml.expval(qml.PauliX(0))
+
+ phi = tf.Variable(0.1, dtype=tf.complex128)
+
+ expected = (1 / norm ** 2) * (-2 * (psi_0 * psi_2 + psi_1 * psi_3) * np.sin(phi))
+
+ with tf.GradientTape() as tape:
+ result = circuit(phi)
+ res = tape.gradient(result, phi)
+ assert np.allclose(res, expected, atol=tol, rtol=0)
+
def test_isingzz_decomposition(self, tol):
"""Tests that the decomposition of the IsingZZ gate is correct"""
param = 0.1234
diff --git a/tests/tape/test_qnode.py b/tests/tape/test_qnode.py
index d0d10b7e5a8..4504236dff8 100644
--- a/tests/tape/test_qnode.py
+++ b/tests/tape/test_qnode.py
@@ -261,6 +261,60 @@ def test_validate_adjoint_invalid_device(self):
with pytest.raises(ValueError, match="The default.gaussian device does not"):
QNode._validate_adjoint_method(dev, "tf")
+ def test_validate_adjoint_finite_shots(self):
+ """Test that a UserWarning is raised when device has finite shots"""
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning, match="Requested adjoint differentiation to be computed with finite shots."
+ ):
+ QNode._validate_adjoint_method(dev, "autograd")
+
+ def test_adjoint_finite_shots(self):
+ """Tests that UserWarning is raised with the adjoint differentiation method
+ on QNode construction when the device has finite shots
+ """
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning, match="Requested adjoint differentiation to be computed with finite shots."
+ ):
+
+ @qml.qnode(dev, diff_method="adjoint")
+ def circ():
+ return qml.expval(qml.PauliZ(0))
+
+ def test_validate_reversible_finite_shots(self):
+ """Test that a UserWarning is raised when validating the reversible differentiation method
+ and using a device that has finite shots
+ """
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning,
+ match="Requested reversible differentiation to be computed with finite shots.",
+ ):
+ QNode._validate_reversible_method(dev, "autograd")
+
+ def test_reversible_finite_shots(self):
+ """Tests that UserWarning is raised with the reversible differentiation method
+ on QNode construction when the device has finite shots
+ """
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning,
+ match="Requested reversible differentiation to be computed with finite shots.",
+ ):
+
+ @qml.qnode(dev, diff_method="reversible")
+ def circ():
+ return qml.expval(qml.PauliZ(0))
+
def test_qnode_print(self):
"""Test that printing a QNode object yields the right information."""
dev = qml.device("default.qubit", wires=1)
diff --git a/tests/tape/test_reversible.py b/tests/tape/test_reversible.py
index a83633a7a3a..d391a7f68d0 100644
--- a/tests/tape/test_reversible.py
+++ b/tests/tape/test_reversible.py
@@ -11,7 +11,7 @@
# 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 qubit parameter-shift QubitParamShiftTape"""
+"""Unit tests for the ReversibleTape"""
import pytest
from pennylane import numpy as np
@@ -160,6 +160,21 @@ def test_phaseshift_exception(self):
class TestGradients:
"""Jacobian integration tests for qubit expectations."""
+ def test_finite_shots_warning(self):
+ """Test that a warning is raised when calling the jacobian with a device with finite shots"""
+
+ with ReversibleTape() as tape:
+ qml.RX(0.1, wires=0)
+ qml.expval(qml.PauliZ(0))
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning,
+ match="Requested reversible differentiation to be computed with finite shots.",
+ ):
+ tape.jacobian(dev)
+
@pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7))
@pytest.mark.parametrize("G", [qml.RX, qml.RY, qml.RZ])
def test_pauli_rotation_gradient(self, G, theta, tol):
@@ -369,6 +384,27 @@ def test_gradient_gate_with_multiple_parameters(self, tol):
class TestQNodeIntegration:
"""Test QNode integration with the reversible method"""
+ def test_finite_shots_warning(self):
+ """Test that a warning is raised when calling the jacobian with a device with finite shots"""
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning,
+ match="Requested reversible differentiation to be computed with finite shots.",
+ ):
+
+ @qml.qnode(dev, diff_method="reversible")
+ def circ(x):
+ qml.RX(x, wires=0)
+ return qml.expval(qml.PauliZ(0))
+
+ with pytest.warns(
+ UserWarning,
+ match="Requested reversible differentiation to be computed with finite shots.",
+ ):
+ qml.grad(circ)(0.1)
+
def test_qnode(self, mocker, tol):
"""Test that specifying diff_method allows the reversible
method to be selected"""
diff --git a/tests/templates/test_state_preparations/test_mottonen_state_prep.py b/tests/templates/test_state_preparations/test_mottonen_state_prep.py
index 44de7c4df8e..9958585e24c 100644
--- a/tests/templates/test_state_preparations/test_mottonen_state_prep.py
+++ b/tests/templates/test_state_preparations/test_mottonen_state_prep.py
@@ -18,6 +18,8 @@
import numpy as np
import pennylane as qml
from pennylane import numpy as pnp
+
+torch = pytest.importorskip("torch", minversion="1.3")
from pennylane.templates.state_preparations.mottonen import gray_code, _get_alpha_y
@@ -342,3 +344,31 @@ def circuit(state_vector):
return qml.expval(qml.PauliZ(0))
qml.grad(circuit)(state_vector)
+
+
+class TestCasting:
+ """Test that the Mottonen state preparation ensures the compatibility with
+ interfaces by using casting'"""
+
+ @pytest.mark.parametrize(
+ "inputs, expected",
+ [
+ (
+ torch.tensor([0.0, 0.7, 0.7, 0.0], requires_grad=True),
+ [0.0, 0.5, 0.5, 0.0],
+ ),
+ (torch.tensor([0.1, 0.0, 0.0, 0.1], requires_grad=True), [0.5, 0.0, 0.0, 0.5]),
+ ],
+ )
+ def test_scalar_torch(self, inputs, expected):
+ """Test that MottonenStatePreparation can be correctly used with the Torch interface."""
+ dev = qml.device("default.qubit", wires=2)
+
+ @qml.qnode(dev, interface="torch")
+ def circuit(inputs):
+ qml.templates.MottonenStatePreparation(inputs, wires=[0, 1])
+ return qml.probs(wires=[0, 1])
+
+ inputs = inputs / torch.linalg.norm(inputs)
+ res = circuit(inputs)
+ assert np.allclose(res.detach().numpy(), expected, atol=1e-6, rtol=0)
diff --git a/tests/test_qubit_device_adjoint_jacobian.py b/tests/test_qubit_device_adjoint_jacobian.py
index b421090067d..910886a4dd9 100644
--- a/tests/test_qubit_device_adjoint_jacobian.py
+++ b/tests/test_qubit_device_adjoint_jacobian.py
@@ -39,6 +39,19 @@ def test_not_expval(self, dev):
with pytest.raises(qml.QuantumFunctionError, match="Adjoint differentiation method does"):
dev.adjoint_jacobian(tape)
+ def test_finite_shots_warns(self):
+ """Tests warning raised when finite shots specified"""
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with qml.tape.JacobianTape() as tape:
+ qml.expval(qml.PauliZ(0))
+
+ with pytest.warns(
+ UserWarning, match="Requested adjoint differentiation to be computed with finite shots."
+ ):
+ dev.adjoint_jacobian(tape)
+
def test_unsupported_op(self, dev):
"""Test if a QuantumFunctionError is raised for an unsupported operation, i.e.,
multi-parameter operations that are not qml.Rot"""
@@ -239,6 +252,25 @@ class TestAdjointJacobianQNode:
def dev(self):
return qml.device("default.qubit", wires=2)
+ def test_finite_shots_warning(self):
+ """Tests that a warning is raised when computing the adjoint diff on a device with finite shots"""
+
+ dev = qml.device("default.qubit", wires=1, shots=1)
+
+ with pytest.warns(
+ UserWarning, match="Requested adjoint differentiation to be computed with finite shots."
+ ):
+
+ @qml.qnode(dev, diff_method="adjoint")
+ def circ(x):
+ qml.RX(x, wires=0)
+ return qml.expval(qml.PauliZ(0))
+
+ with pytest.warns(
+ UserWarning, match="Requested adjoint differentiation to be computed with finite shots."
+ ):
+ qml.grad(circ)(0.1)
+
def test_qnode(self, mocker, tol, dev):
"""Test that specifying diff_method allows the adjoint method to be selected"""
args = np.array([0.54, 0.1, 0.5], requires_grad=True)