Repository: PennyLaneAI/pennylane
Pull request #3946 (2023-03-24 17:46:20)

Limit default Gaussian and parameter shift CV to single measurement with the new return type
**Description of the Change:**
- Default Gaussian works with the new return type one but only for single measurement
- The parameter shift CV and other derivatives work with default Gaussian but only for single measurement


codecov[bot]: ## [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3946?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3946](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3946?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (17ab3c0) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/340f1c8d2241d0f5947af87dd4565ea555dcbcbd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (340f1c8) will **decrease** coverage by `0.01%`.
> The diff coverage is `92.85%`.

```diff
@@            Coverage Diff             @@
##           master    #3946      +/-   ##
==========================================
- Coverage   99.74%   99.73%   -0.01%     
==========================================
  Files         345      345              
  Lines       30163    30191      +28     
==========================================
+ Hits        30085    30111      +26     
- Misses         78       80       +2     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3946?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/devices/default\_gaussian.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3946?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9nYXVzc2lhbi5weQ==) | `99.35% <86.66%> (-0.65%)` | :arrow_down: |
| [pennylane/gradients/parameter\_shift\_cv.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3946?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2dyYWRpZW50cy9wYXJhbWV0ZXJfc2hpZnRfY3YucHk=) | `99.61% <100.00%> (+0.01%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3946/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

rmoyard: @albi3ro Changelog added but there is no deprecation.

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 807c3dcd2..60a39c87f 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -28,6 +28,9 @@
 
 <h3>Improvements</h3>
 
+* The default gaussian device and parameter shift cv support the new return system but only for single measurement.
+  [(3946)](https://github.com/PennyLaneAI/pennylane/pull/3946)
+
 * Improve the efficiency of `tapering()`, `tapering_hf()` and `clifford()`.
   [(3942)](https://github.com/PennyLaneAI/pennylane/pull/3942)
 
diff --git a/pennylane/devices/default_gaussian.py b/pennylane/devices/default_gaussian.py
index d665c4e0e..5e3ae15d5 100644
--- a/pennylane/devices/default_gaussian.py
+++ b/pennylane/devices/default_gaussian.py
@@ -888,3 +888,17 @@ class DefaultGaussian(Device):
     @property
     def observables(self):
         return set(self._observable_map.keys())
+
+    # pylint: disable=arguments-differ
+    def execute(self, operations, observables):
+        if qml.active_return():
+            if len(observables) > 1:
+                raise qml.QuantumFunctionError("Default gaussian only support single measurements.")
+        return super().execute(operations, observables)
+
+    def batch_execute(self, circuits):
+        if not qml.active_return():
+            return super().batch_execute(circuits)
+        results = super().batch_execute(circuits)
+        results = [qml.math.squeeze(res) for res in results]
+        return results
diff --git a/pennylane/gradients/parameter_shift_cv.py b/pennylane/gradients/parameter_shift_cv.py
index 735aa1cda..80ee28d5e 100644
--- a/pennylane/gradients/parameter_shift_cv.py
+++ b/pennylane/gradients/parameter_shift_cv.py
@@ -264,6 +264,7 @@ def var_param_shift(tape, dev_wires, argnum=None, shifts=None, gradient_recipes=
 
         # return d(var(A))/dp = d<A^2>/dp -2 * <A> * d<A>/dp for the variances (mask==True)
         # d<A>/dp for plain expectations (mask==False)
+        pdA = qml.math.stack(pdA)
         return qml.math.where(mask, pdA2 - 2 * f0 * pdA, pdA)
 
     return gradient_tapes, processing_fn
@@ -467,7 +468,6 @@ def second_order_param_shift(tape, dev_wires, argnum=None, shifts=None, gradient
             g = qml.math.convert_like(g, results[0])
             if hasattr(g, "dtype") and g.dtype is np.dtype("object"):
                 grads[i] = qml.math.hstack(g)
-
         return qml.math.T(qml.math.stack(grads))
 
     return gradient_tapes, processing_fn
@@ -651,6 +651,10 @@ def param_shift_cv(
         >>> fn(qml.execute(gradient_tapes, dev, None))
         array([[-0.32487113, -0.4054074 , -0.87049853,  0.4054074 ]])
     """
+    if qml.active_return() and len(tape.measurements) > 1:
+        raise ValueError(
+            "Computing the gradient of CV circuits that return more than one measurement is not possible."
+        )
 
     # perform gradient method validation
     if any(isinstance(m, StateMP) for m in tape.measurements):
@@ -757,6 +761,19 @@ def param_shift_cv(
             grads.append(f(results[start : start + s]))
             start += s
 
-        return sum(grads)
+        # For expval param shift with multiple params
+        if qml.active_return():
+            if isinstance(grads[0], tuple):
+                grads = [qml.math.stack(g) for g in grads]
+
+        jacobian = sum(grads)
+
+        if qml.active_return():
+            if jacobian.shape != ():
+                if jacobian.shape[0] == 1:
+                    jacobian = jacobian[0]
+                if len(argnum) > 1:
+                    jacobian = tuple(j for j in jacobian)
+        return jacobian
 
     return gradient_tapes, processing_fn


Test patch:
diff --git a/tests/devices/test_default_gaussian.py b/tests/devices/test_default_gaussian.py
index 7f586b112..7df8b02bc 100644
--- a/tests/devices/test_default_gaussian.py
+++ b/tests/devices/test_default_gaussian.py
@@ -854,3 +854,20 @@ class TestDefaultGaussianIntegration:
         ):
             circuit()
         assert dev.shots == sum(shots)
+
+    def test_new_return_type_error_multi_measurements(self):
+        """Test that multiple measurements raise an error with the new return type."""
+        qml.enable_return()
+        dev = qml.device("default.gaussian", wires=2)
+
+        @qml.qnode(dev)
+        def circuit():
+            """Test quantum function"""
+            return qml.sample(qml.X(0)), qml.expval(qml.X(1))
+
+        with pytest.raises(
+            qml.QuantumFunctionError,
+            match="Default gaussian only support single measurements.",
+        ):
+            circuit()
+        qml.disable_return()
diff --git a/tests/returntypes/autograd/test_autograd_qnode_new.py b/tests/returntypes/autograd/test_autograd_qnode_new.py
index 0df7f6a69..a0455dcaf 100644
--- a/tests/returntypes/autograd/test_autograd_qnode_new.py
+++ b/tests/returntypes/autograd/test_autograd_qnode_new.py
@@ -1390,7 +1390,6 @@ class TestQubitIntegration:
         assert np.allclose(jac, expected, atol=tol, rtol=0)
 
 
-@pytest.mark.xfail(reason="CV variable needs to be update for the new return types.")
 @pytest.mark.parametrize(
     "diff_method,kwargs",
     [
diff --git a/tests/returntypes/gradients/test_cv_gradients_new.py b/tests/returntypes/gradients/test_cv_gradients_new.py
new file mode 100644
index 000000000..403655f39
--- /dev/null
+++ b/tests/returntypes/gradients/test_cv_gradients_new.py
@@ -0,0 +1,348 @@
+# Copyright 2018-2020 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Unit tests for computing Autograd gradients of quantum functions.
+"""
+# pylint: disable=no-member,redefined-outer-name,too-few-public-methods,no-value-for-parameter,not-callable,no-self-use,unexpected-keyword-arg
+
+import pytest
+import autograd
+import numpy as np
+import pennylane.numpy as anp  # only to be used inside classical computational nodes
+import pennylane as qml
+
+
+alpha = 0.5  # displacement in tests
+hbar = 2
+mag_alphas = np.linspace(0, 1.5, 5)
+thetas = np.linspace(-2 * np.pi, 2 * np.pi, 8)
+sqz_vals = np.linspace(0.0, 1.0, 5)
+
+
+class PolyN(qml.ops.PolyXP):
+    "Mimics NumberOperator using the arbitrary 2nd order observable interface. Results should be identical."
+
+    def __init__(self, wires):
+        hbar = 2
+        q = np.diag([-0.5, 0.5 / hbar, 0.5 / hbar])
+        super().__init__(q, wires=wires)
+        self.name = "PolyXP"
+
+
+@pytest.fixture(scope="module")
+def gaussian_dev():
+    """Gaussian device."""
+    return qml.device("default.gaussian", wires=2)
+
+
+@pytest.fixture(scope="module")
+def grad_fn_R(gaussian_dev):
+    """Gradient with autograd."""
+
+    @qml.qnode(gaussian_dev)
+    def circuit(y):
+        qml.Displacement(alpha, 0.0, wires=[0])
+        qml.Rotation(y, wires=[0])
+        return qml.expval(qml.X(0))
+
+    return autograd.grad(circuit)
+
+
+@pytest.fixture(scope="module")
+def grad_fn_BS(gaussian_dev):
+    """Gradient with autograd."""
+
+    @qml.qnode(gaussian_dev)
+    def circuit(y):
+        qml.Displacement(alpha, 0.0, wires=[0])
+        qml.Beamsplitter(y, 0, wires=[0, 1])
+        return qml.expval(qml.X(0))
+
+    return autograd.grad(circuit)
+
+
+@pytest.fixture(scope="module")
+def grad_fn_D(gaussian_dev):
+    """Gradient with autograd."""
+
+    @qml.qnode(gaussian_dev)
+    def circuit(r, phi):
+        qml.Displacement(r, phi, wires=[0])
+        return qml.expval(qml.X(0))
+
+    return autograd.grad(circuit)
+
+
+@pytest.fixture(scope="module")
+def grad_fn_S(gaussian_dev):
+    """Gradient with autograd."""
+
+    @qml.qnode(gaussian_dev)
+    def circuit(y):
+        qml.Displacement(alpha, 0.0, wires=[0])
+        qml.Squeezing(y, 0.0, wires=[0])
+        return qml.expval(qml.X(0))
+
+    return autograd.grad(circuit)
+
+
+@pytest.fixture(scope="module")
+def grad_fn_S_Fock(gaussian_dev):
+    """Gradient with autograd."""
+
+    @qml.qnode(gaussian_dev)
+    def circuit(y):
+        qml.Squeezing(y, 0.0, wires=[0])
+        return qml.expval(qml.FockStateProjector(np.array([2, 0]), wires=[0, 1]))
+
+    return autograd.grad(circuit)
+
+
+class TestCVGradient:
+    """Tests of the automatic gradient method for CV circuits."""
+
+    @pytest.mark.parametrize("theta", thetas)
+    def test_rotation_gradient(self, theta, grad_fn_R, tol):
+        "Tests that the automatic gradient of a phase space rotation is correct."
+
+        autograd_val = grad_fn_R(theta)
+        # qfunc evalutes to hbar * alpha * cos(theta)
+        manualgrad_val = -hbar * alpha * np.sin(theta)
+        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
+
+    @pytest.mark.parametrize("theta", thetas)
+    def test_beamsplitter_gradient(self, theta, grad_fn_BS, tol):
+        "Tests that the automatic gradient of a beamsplitter is correct."
+
+        autograd_val = grad_fn_BS(theta)
+        # qfunc evalutes to hbar * alpha * cos(theta)
+        manualgrad_val = -hbar * alpha * np.sin(theta)
+        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
+
+    @pytest.mark.parametrize("mag", mag_alphas)
+    @pytest.mark.parametrize("theta", thetas)
+    def test_displacement_gradient(self, mag, theta, grad_fn_D, tol):
+        "Tests that the automatic gradient of a phase space displacement is correct."
+
+        # alpha = mag * np.exp(1j * theta)
+        autograd_val = grad_fn_D(mag, theta)
+        # qfunc evalutes to hbar * Re(alpha)
+        manualgrad_val = hbar * np.cos(theta)
+        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
+
+    @pytest.mark.parametrize("r", sqz_vals)
+    def test_squeeze_gradient(self, r, grad_fn_S, tol):
+        "Tests that the automatic gradient of a phase space squeezing is correct."
+
+        autograd_val = grad_fn_S(r)
+        # qfunc evaluates to -exp(-r) * hbar * Re(alpha)
+        manualgrad_val = -np.exp(-r) * hbar * alpha
+        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
+
+    @pytest.mark.parametrize("r", sqz_vals[1:])  # formula is not valid for r=0
+    def test_number_state_gradient(self, r, grad_fn_S_Fock, tol):
+        "Tests that the automatic gradient of a squeezed state with number state expectation is correct."
+
+        # (d/dr) |<2|S(r)>|^2 = 0.5 tanh(r)^3 (2 csch(r)^2 - 1) sech(r)
+        autograd_val = grad_fn_S_Fock(r)
+        manualgrad_val = 0.5 * np.tanh(r) ** 3 * (2 / (np.sinh(r) ** 2) - 1) / np.cosh(r)
+        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
+
+    @pytest.mark.autograd
+    @pytest.mark.parametrize("O", [qml.ops.X, qml.ops.NumberOperator])
+    @pytest.mark.parametrize(
+        "make_gate",
+        [lambda x: qml.Rotation(x, wires=0), lambda x: qml.ControlledPhase(x, wires=[0, 1])],
+    )
+    def test_cv_gradients_gaussian_circuit(self, make_gate, O, gaussian_dev, tol):
+        """Tests that the gradients of circuits of gaussian gates match
+        between the finite difference and analytic methods."""
+
+        tol = 1e-5
+        par = anp.array(0.4, requires_grad=True)
+
+        def circuit(x):
+            qml.Displacement(0.5, 0, wires=0)
+            make_gate(x)
+            qml.Beamsplitter(1.3, -2.3, wires=[0, 1])
+            qml.Displacement(-0.5, 0.1, wires=0)
+            qml.Squeezing(0.5, -1.5, wires=0)
+            qml.Rotation(-1.1, wires=0)
+            return qml.expval(O(wires=0))
+
+        q = qml.QNode(circuit, gaussian_dev)
+
+        grad_F = qml.gradients.finite_diff(q)(par)
+        grad_A2 = qml.gradients.param_shift_cv(q, dev=gaussian_dev, force_order2=True)(par)
+        if O.ev_order == 1:
+            grad_A = qml.gradients.param_shift_cv(q, dev=gaussian_dev)(par)
+            # the different methods agree
+            assert qml.math.shape(grad_A) == qml.math.shape(grad_F) == ()
+            assert np.allclose(grad_A, grad_F, atol=tol)
+
+        # the different methods agree
+        assert qml.math.shape(grad_A2) == qml.math.shape(grad_F) == ()
+        assert np.allclose(grad_A2, grad_F, atol=tol)
+
+    @pytest.mark.autograd
+    def test_cv_gradients_multiple_gate_parameters(self, gaussian_dev, tol):
+        "Tests that gates with multiple free parameters yield correct gradients."
+        par = anp.array([0.4, -0.3, -0.7, 0.2], requires_grad=True)
+
+        def qf(r0, phi0, r1, phi1):
+            qml.Squeezing(r0, phi0, wires=[0])
+            qml.Squeezing(r1, phi1, wires=[0])
+            return qml.expval(qml.NumberOperator(0))
+
+        q = qml.QNode(qf, gaussian_dev)
+        q(*par)
+        grad_F = qml.gradients.finite_diff(q)(*par)
+        grad_A = qml.gradients.param_shift_cv(q, dev=gaussian_dev)(*par)
+        grad_A2 = qml.gradients.param_shift_cv(q, dev=gaussian_dev, force_order2=True)(*par)
+
+        # the different methods agree
+        assert qml.math.allclose(grad_A, grad_F, atol=tol, rtol=0)
+        assert qml.math.allclose(grad_A2, grad_F, atol=tol, rtol=0)
+
+        # check against the known analytic formula
+        r0, phi0, r1, phi1 = par
+        dn = np.zeros([4])
+        dn[0] = np.cosh(2 * r1) * np.sinh(2 * r0) + np.cos(phi0 - phi1) * np.cosh(2 * r0) * np.sinh(
+            2 * r1
+        )
+        dn[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1)
+        dn[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh(2 * r0) + np.cosh(2 * r0) * np.sinh(
+            2 * r1
+        )
+        dn[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1)
+
+        assert all(qml.math.isclose(dn[i], grad_F[i], atol=tol, rtol=0) for i in range(4))
+
+    @pytest.mark.autograd
+    def test_cv_gradients_repeated_gate_parameters(self, gaussian_dev, tol):
+        "Tests that repeated use of a free parameter in a multi-parameter gate yield correct gradients."
+        par = anp.array([0.2, 0.3], requires_grad=True)
+
+        def qf(x, y):
+            qml.Displacement(x, 0, wires=[0])
+            qml.Squeezing(y, -1.3 * y, wires=[0])
+            return qml.expval(qml.X(0))
+
+        q = qml.QNode(qf, gaussian_dev)
+        q(*par)
+        grad_F = qml.gradients.finite_diff(q)(*par)
+        grad_A = qml.gradients.param_shift_cv(q, dev=gaussian_dev)(*par)
+        grad_A2 = qml.gradients.param_shift_cv(q, dev=gaussian_dev, force_order2=True)(*par)
+
+        # the different methods agree
+        assert qml.math.allclose(grad_A, grad_F, atol=tol, rtol=0)
+        assert qml.math.allclose(grad_A2, grad_F, atol=tol, rtol=0)
+
+    @pytest.mark.autograd
+    def test_cv_gradients_parameters_inside_array(self, gaussian_dev, tol):
+        "Tests that free parameters inside an array passed to an Operation yield correct gradients."
+        par = anp.array([0.4, 1.3], requires_grad=True)
+
+        def qf(x, y):
+            qml.Displacement(0.5, 0, wires=[0])
+            qml.Squeezing(x, 0, wires=[0])
+            M = np.zeros((5, 5))
+            M[1, 1] = y
+            M[1, 2] = 1.0
+            M[2, 1] = 1.0
+            return qml.expval(qml.PolyXP(M, [0, 1]))
+
+        q = qml.QNode(qf, gaussian_dev)
+        q(*par)
+        grad_F = qml.gradients.finite_diff(q, hybrid=False)(*par)
+        grad_A2 = qml.gradients.param_shift_cv(
+            q, dev=gaussian_dev, force_order2=True, hybrid=False
+        )(*par)
+
+        # the different methods agree
+        assert grad_A2 == pytest.approx(grad_F, abs=tol)
+
+    @pytest.mark.autograd
+    def test_cv_gradient_fanout(self, gaussian_dev, tol):
+        "Tests that qnodes can compute the correct gradient when the same parameter is used in multiple gates."
+        par = anp.array([0.5, 1.3], requires_grad=True)
+
+        def circuit(x, y):
+            qml.Displacement(x, 0, wires=[0])
+            qml.Rotation(y, wires=[0])
+            qml.Displacement(0, x, wires=[0])
+            return qml.expval(qml.X(0))
+
+        q = qml.QNode(circuit, gaussian_dev)
+        q(*par)
+        grad_F = qml.gradients.finite_diff(q)(*par)
+        grad_A = qml.gradients.param_shift_cv(q, dev=gaussian_dev)(*par)
+        grad_A2 = qml.gradients.param_shift_cv(q, dev=gaussian_dev, force_order2=True)(*par)
+
+        # the different methods agree
+        assert qml.math.allclose(grad_A, grad_F, atol=tol, rtol=0)
+        assert qml.math.allclose(grad_A2, grad_F, atol=tol, rtol=0)
+
+    @pytest.mark.autograd
+    def test_CVOperation_with_heisenberg_and_no_params(self, gaussian_dev, tol):
+        """An integration test for InterferometerUnitary, a gate that supports analytic
+        differentiation if succeeding the gate to be differentiated, but cannot be
+        differentiated itself.
+
+        This ensures that, assuming the _heisenberg_rep is defined, the quantum
+        gradient analytic method can still be used, and returns the correct result.
+        """
+
+        x = anp.array(0.5, requires_grad=True)
+
+        U = np.array(
+            [
+                [0.51310276 + 0.81702166j, 0.13649626 + 0.22487759j],
+                [0.26300233 + 0.00556194j, -0.96414101 - 0.03508489j],
+            ]
+        )
+
+        def circuit(x):
+            qml.Displacement(x, 0, wires=0)
+            qml.InterferometerUnitary(U, wires=[0, 1])
+            return qml.expval(qml.X(0))
+
+        qnode = qml.QNode(circuit, gaussian_dev)
+        qnode(x)
+        grad_F = qml.gradients.finite_diff(qnode)(x)
+        grad_A = qml.gradients.param_shift_cv(qnode, dev=gaussian_dev)(x)
+        grad_A2 = qml.gradients.param_shift_cv(qnode, dev=gaussian_dev, force_order2=True)(x)
+
+        # the different methods agree
+        assert qml.math.shape(grad_A) == qml.math.shape(grad_A2) == qml.math.shape(grad_F) == ()
+        assert np.allclose(grad_A, grad_F, atol=tol)
+        assert np.allclose(grad_A2, grad_F, atol=tol)
+
+    def test_cv_gradient_multiple_measurement_error(self, gaussian_dev):
+        """Tests multiple measurements are not supported."""
+        par = anp.array([0.5, 1.3], requires_grad=True)
+
+        def circuit(x, y):
+            qml.Displacement(x, 0, wires=[0])
+            qml.Rotation(y, wires=[0])
+            qml.Displacement(0, x, wires=[0])
+            return qml.expval(qml.X(0)), qml.expval(qml.NumberOperator(0))
+
+        q = qml.QNode(circuit, gaussian_dev)
+
+        with pytest.raises(
+            ValueError,
+            match="Computing the gradient of CV circuits that return more than one measurement is not possible.",
+        ):
+            qml.gradients.param_shift_cv(q, dev=gaussian_dev)(*par)
diff --git a/tests/returntypes/jax/test_jax_qnode_new.py b/tests/returntypes/jax/test_jax_qnode_new.py
index 4435d9b91..032420fd2 100644
--- a/tests/returntypes/jax/test_jax_qnode_new.py
+++ b/tests/returntypes/jax/test_jax_qnode_new.py
@@ -1321,8 +1321,6 @@ class TestQubitIntegrationHigherOrder:
         assert np.allclose(res, expected, atol=tol, rtol=0)
 
 
-# TODO: Add CV test when return types and custom diff are compatible
-@pytest.mark.xfail(reason="CV variables with new return types.")
 @pytest.mark.parametrize(
     "diff_method,kwargs",
     [
diff --git a/tests/returntypes/tf/test_tensorflow_qnode_new.py b/tests/returntypes/tf/test_tensorflow_qnode_new.py
index 4910ba69e..77b1ce1dc 100644
--- a/tests/returntypes/tf/test_tensorflow_qnode_new.py
+++ b/tests/returntypes/tf/test_tensorflow_qnode_new.py
@@ -1107,7 +1107,6 @@ class TestQubitIntegration:
         assert np.allclose(grad, expected, atol=tol, rtol=0)
 
 
-@pytest.mark.xfail(reason="TODO: CV devices for new return types.")
 @pytest.mark.parametrize(
     "diff_method,kwargs",
     [
@@ -1129,7 +1128,7 @@ class TestCV:
         r = tf.Variable(0.543, dtype=tf.float64)
         phi = tf.Variable(-0.654, dtype=tf.float64)
 
-        @qnode(dev, interface=interface, diff_method=diff_method, **kwargs)
+        @qnode(dev, diff_method=diff_method, **kwargs)
         def circuit(r, phi):
             qml.Squeezing(r, 0, wires=0)
             qml.Rotation(phi, wires=0)
@@ -1158,7 +1157,7 @@ class TestCV:
         n = tf.Variable(0.12, dtype=tf.float64)
         a = tf.Variable(0.765, dtype=tf.float64)
 
-        @qnode(dev, interface=interface, diff_method=diff_method, **kwargs)
+        @qnode(dev, diff_method=diff_method, **kwargs)
         def circuit(n, a):
             qml.ThermalState(n, wires=0)
             qml.Displacement(a, 0, wires=0)
diff --git a/tests/returntypes/torch/test_torch_qnode_new.py b/tests/returntypes/torch/test_torch_qnode_new.py
index 8481fb198..a77f13740 100644
--- a/tests/returntypes/torch/test_torch_qnode_new.py
+++ b/tests/returntypes/torch/test_torch_qnode_new.py
@@ -1165,8 +1165,6 @@ class TestQubitIntegration:
         assert np.allclose(weights.grad.detach(), expected, atol=tol, rtol=0)
 
 
-# TODO: Add CV to the new return type system
-@pytest.mark.xfail
 @pytest.mark.parametrize(
     "diff_method,kwargs",
     [
diff --git a/tests/tests_passing_pylint.txt b/tests/tests_passing_pylint.txt
index de55fb311..b8e402f84 100644
--- a/tests/tests_passing_pylint.txt
+++ b/tests/tests_passing_pylint.txt
@@ -42,6 +42,7 @@ tests/returntypes/gradients/test_spsa_gradient_shot_vec_new.py
 tests/returntypes/gradients/test_jvp.py
 tests/returntypes/gradients/test_hadamard_gradient_new.py
 tests/returntypes/gradients/test_pulse_gradient_new.py
+tests/returntypes/gradients/test_cv_gradients_new.py
 tests/returntypes/qnn/conftest.py
 tests/returntypes/qnn/test_keras_new.py
 tests/returntypes/qnn/test_qnn_torch_new.py