def test_param_unused(self, operable_mock_device_2_wires):
        """Test that the gradient is 0 of an unused parameter"""
        def circuit(x, y):
            qml.RX(x, wires=[0])
            return qml.expval(qml.PauliZ(0))

        q = JacobianQNode(circuit, operable_mock_device_2_wires)
        q._construct([1.0, 1.0], {})
        assert q.par_to_grad_method == {0: "F", 1: "0"}
    def test_no_following_observable(self, operable_mock_device_2_wires):
        """Test that the gradient is 0 if no observables succeed"""
        def circuit(x):
            qml.RX(x, wires=[1])
            return qml.expval(qml.PauliZ(0))

        q = JacobianQNode(circuit, operable_mock_device_2_wires)
        q._construct([1.0], {})
        assert q.par_to_grad_method == {0: "0"}
    def test_all_finite_difference(self, operable_mock_device_2_wires):
        """Finite difference is the best method in almost all cases"""
        def circuit(x, y, z):
            qml.Rot(x, y, z, wires=[0])
            return qml.expval(qml.PauliZ(0))

        q = JacobianQNode(circuit, operable_mock_device_2_wires)
        q._construct([1.0, 1.0, 1.0], {})
        assert q.par_to_grad_method == {0: "F", 1: "F", 2: "F"}
    def test_not_differentiable(self, operable_mock_device_2_wires):
        """Test that an operation with grad_method=None is marked as
        non-differentiable"""
        def circuit(x):
            qml.BasisState(x, wires=[1])
            return qml.expval(qml.PauliZ(0))

        q = JacobianQNode(circuit, operable_mock_device_2_wires)
        q._construct([np.array([1.0])], {})
        assert q.par_to_grad_method == {0: None}
    def test_unknown_gradient_method(self, operable_mock_device_2_wires):
        """ The gradient method is unknown."""
        def circuit(x):
            qml.Rot(0.3, x, -0.2, wires=[0])
            return qml.expval(qml.PauliZ(0))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(ValueError, match="Unknown gradient method"):
            node.jacobian(0.5, method="unknown")
    def test_wrong_order_in_finite_difference(self, operable_mock_device_2_wires):
        """Finite difference are attempted with wrong order."""

        def circuit(x):
            qml.Rot(0.3, x, -0.2, wires=[0])
            return qml.expval(qml.PauliZ(0))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(ValueError, match="Order must be 1 or 2"):
            node.jacobian(0.5, method="F", options={'order': 3})
    def test_indices_not_unique(self, operable_mock_device_2_wires):
        """The Jacobian is requested for non-unique indices."""

        def circuit(x):
            qml.Rot(0.3, x, -0.2, wires=[0])
            return qml.expval(qml.PauliZ(0))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(ValueError, match="Parameter indices must be unique."):
            node.jacobian(0.5, wrt=[0, 0])
    def test_operator_not_supporting_pd_analytic(self, operable_mock_device_2_wires):
        """Differentiating wrt. a parameter that appears
        as an argument to an operation that does not support parameter-shift derivatives."""

        def circuit(x):
            qml.RX(x, wires=[0])
            return qml.expval(qml.Hermitian(np.diag([x, 0]), 0))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(ValueError, match="analytic gradient method cannot be used with"):
            node.jacobian(0.5, method="A")
    def test_gradient_of_sample(self, operable_mock_device_2_wires):
        """Differentiation of a sampled output."""

        def circuit(x):
            qml.RX(x, wires=[0])
            return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliX(1))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(QuantumFunctionError,
                           match="Circuits that include sampling can not be differentiated."):
            node.jacobian(1.0)
    def test_nondifferentiable_operator(self, operable_mock_device_2_wires):
        """Differentiating wrt. a parameter
        that appears as an argument to a nondifferentiable operator."""

        def circuit(x):
            qml.BasisState(np.array([x, 0]), wires=[0, 1])  # not differentiable
            qml.RX(x, wires=[0])
            return qml.expval(qml.PauliZ(0))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(ValueError, match="Cannot differentiate with respect to the parameters"):
            node.jacobian(0.5)
    def test_interface_str(self, qubit_device_2_wires):
        """Test that the interface string is correctly identified
        as None"""
        def circuit(x, y, z):
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))

        circuit = JacobianQNode(circuit, qubit_device_2_wires)
        assert circuit.interface == None
def test_finite_difference_options(qubit_device_1_wire, order, h, monkeypatch):
    """Test that the _pd_finite_diff method for calculating the finite difference gradient
    correctly receives the options set when the JacobianQNode was instantiated."""

    def circuit(x):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    q = JacobianQNode(circuit, qubit_device_1_wire, order=order, h=h)

    _pd_finite_diff = mock.MagicMock(return_value=0)

    with monkeypatch.context() as m:
        m.setattr("pennylane.qnodes.jacobian.JacobianQNode._pd_finite_diff", _pd_finite_diff)
        q.jacobian(0.2)
        call_kwargs = _pd_finite_diff.call_args[1]

        assert call_kwargs["order"] == order
        assert call_kwargs["h"] == h
    def test_bogus_gradient_method_set(self, operable_mock_device_2_wires):
        """The gradient method set is bogus."""

        def circuit(x):
            qml.RX(x, wires=[0])
            return qml.expval(qml.PauliZ(0))

        # in mutable mode, the grad method would be
        # recomputed and overwritten from the
        # bogus value 'J'. Caching stops this from happening.
        node = JacobianQNode(circuit, operable_mock_device_2_wires, mutable=False)

        node.evaluate([0.0], {})
        node.par_to_grad_method[0] = "J"

        with pytest.raises(ValueError, match="Unknown gradient method"):
            node.jacobian(0.5)
    def test_indices_nonexistant(self, operable_mock_device_2_wires):
        """ The Jacobian is requested for non-existant parameters."""

        def circuit(x):
            qml.Rot(0.3, x, -0.2, wires=[0])
            return qml.expval(qml.PauliZ(0))

        node = JacobianQNode(circuit, operable_mock_device_2_wires)

        with pytest.raises(ValueError, match="Tried to compute the gradient with respect to"):
            node.jacobian(0.5, wrt=[0, 6])

        with pytest.raises(ValueError, match="Tried to compute the gradient with respect to"):
            node.jacobian(0.5, wrt=[1, -1])