Ejemplo n.º 1
0
    def test_jacobian_agrees_backprop_parameter_shift(self, tol):
        """Test that jacobian of a QNode with an attached default.qubit.autograd device
        gives the correct result with respect to the parameter-shift method"""
        p = np.array([0.43316321, 0.2162158, 0.75110998, 0.94714242],
                     requires_grad=True)

        def circuit(x):
            for i in range(0, len(p), 2):
                qml.RX(x[i], wires=0)
                qml.RY(x[i + 1], wires=1)
            for i in range(2):
                qml.CNOT(wires=[i, i + 1])
            return qml.expval(qml.PauliZ(0)), qml.var(qml.PauliZ(1))

        dev1 = qml.device("default.qubit.autograd", wires=3)
        dev2 = qml.device("default.qubit.autograd", wires=3)

        circuit1 = qml.QNode(circuit,
                             dev1,
                             diff_method="backprop",
                             interface="autograd")
        circuit2 = qml.QNode(circuit, dev2, diff_method="parameter-shift")

        assert circuit1.diff_options["method"] == "backprop"
        assert circuit2.diff_options["method"] == "analytic"

        res = circuit1(p)

        assert np.allclose(res, circuit2(p), atol=tol, rtol=0)

        grad_fn = qml.jacobian(circuit1, 0)
        res = grad_fn(p)
        assert np.allclose(res, qml.jacobian(circuit2)(p), atol=tol, rtol=0)
Ejemplo n.º 2
0
def test_numerical_analytic_diff_agree(init_state, tol):
    """Test that the finite difference and parameter shift rule
    provide the same Jacobian."""
    w = 4
    dev = qml.device("default.qubit", wires=w)
    state = init_state(w)

    def circuit(x, y, z):
        for i in range(w):
            qml.RX(x, wires=i)
            qml.PhaseShift(z, wires=i)
            qml.RY(y, wires=i)

        qml.CNOT(wires=[0, 1])
        qml.CNOT(wires=[1, 2])
        qml.CNOT(wires=[2, 3])

        return qml.probs(wires=[1, 3])


    params = [0.543, -0.765, -0.3]

    circuit_F = qml.QNode(circuit, dev, diff_method="finite-diff")
    circuit_A = qml.QNode(circuit, dev, diff_method="parameter-shift")
    res_F = qml.jacobian(circuit_F)(*params)
    res_A = qml.jacobian(circuit_A)(*params)

    # Both jacobians should be of shape (2**prob.wires, num_params)
    assert res_F.shape == (2**2, 3)
    assert res_F.shape == (2**2, 3)

    # Check that they agree up to numeric tolerance
    assert np.allclose(res_F, res_A, atol=tol, rtol=0)
Ejemplo n.º 3
0
    def test_tensor_number_displaced(self, dev, tol):
        """Test the variance of the TensorN observable for a displaced state"""

        @qml.qnode(dev)
        def circuit(a, phi):
            qml.Displacement(a, phi, wires=0)
            qml.Displacement(a, phi, wires=1)
            return qml.var(qml.TensorN(wires=[0, 1]))

        a = 0.4
        phi = -0.12

        expected = a ** 4 * (1 + 2 * a ** 2)

        var = circuit(a, phi)
        assert np.allclose(var, expected, atol=tol, rtol=0)

        # differentiate with respect to parameter a
        res = qml.jacobian(circuit, argnum=0)(a, phi).flat
        expected_gradient = 4 * (a ** 3 + 3 * a ** 5)
        assert np.allclose(res, expected_gradient, atol=tol, rtol=0)

        # differentiate with respect to parameter phi
        res = qml.jacobian(circuit, argnum=1)(a, phi).flat
        expected_gradient = 0
        assert np.allclose(res, expected_gradient, atol=tol, rtol=0)
Ejemplo n.º 4
0
    def test_hessian_ragged(self, dev_name, diff_method, mocker, tol):
        """Test hessian calculation of a ragged QNode"""
        if diff_method not in {"parameter-shift", "backprop"}:
            pytest.skip("Test only supports parameter-shift or backprop")

        dev = qml.device(dev_name, wires=2)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(x):
            qml.RY(x[0], wires=0)
            qml.RX(x[1], wires=0)
            qml.RY(x[0], wires=1)
            qml.RX(x[1], wires=1)
            return qml.expval(qml.PauliZ(0)), qml.probs(wires=1)

        x = np.array([1.0, 2.0], requires_grad=True)
        res = circuit(x)

        a, b = x

        expected_res = [
            np.cos(a) * np.cos(b),
            0.5 + 0.5 * np.cos(a) * np.cos(b),
            0.5 - 0.5 * np.cos(a) * np.cos(b),
        ]
        assert np.allclose(res, expected_res, atol=tol, rtol=0)

        jac_fn = qml.jacobian(circuit)
        g = jac_fn(x)

        expected_g = [
            [-np.sin(a) * np.cos(b), -np.cos(a) * np.sin(b)],
            [-0.5 * np.sin(a) * np.cos(b), -0.5 * np.cos(a) * np.sin(b)],
            [0.5 * np.sin(a) * np.cos(b), 0.5 * np.cos(a) * np.sin(b)],
        ]
        assert np.allclose(g, expected_g, atol=tol, rtol=0)

        spy = mocker.spy(JacobianTape, "hessian")
        hess = qml.jacobian(jac_fn)(x)

        if diff_method == "backprop":
            spy.assert_not_called()
        elif diff_method == "parameter-shift":
            spy.assert_called_once()

        expected_hess = [
            [
                [-np.cos(a) * np.cos(b), np.sin(a) * np.sin(b)],
                [np.sin(a) * np.sin(b), -np.cos(a) * np.cos(b)],
            ],
            [
                [-0.5 * np.cos(a) * np.cos(b), 0.5 * np.sin(a) * np.sin(b)],
                [0.5 * np.sin(a) * np.sin(b), -0.5 * np.cos(a) * np.cos(b)],
            ],
            [
                [0.5 * np.cos(a) * np.cos(b), -0.5 * np.sin(a) * np.sin(b)],
                [-0.5 * np.sin(a) * np.sin(b), 0.5 * np.cos(a) * np.cos(b)],
            ],
        ]
        assert np.allclose(hess, expected_hess, atol=tol, rtol=0)
Ejemplo n.º 5
0
    def test_backward_mode(self, mocker):
        """Test that backward mode uses the `device.batch_execute` and `device.gradients` pathway"""
        dev = qml.device("default.qubit", wires=1)
        spy_execute = mocker.spy(qml.devices.DefaultQubit, "batch_execute")
        spy_gradients = mocker.spy(qml.devices.DefaultQubit, "gradients")

        def cost(a):
            with qml.tape.JacobianTape() as tape:
                qml.RY(a[0], wires=0)
                qml.RX(a[1], wires=0)
                qml.expval(qml.PauliZ(0))

            return execute(
                [tape],
                dev,
                gradient_fn="device",
                mode="backward",
                gradient_kwargs={"method": "adjoint_jacobian"},
            )[0]

        a = np.array([0.1, 0.2], requires_grad=True)
        cost(a)

        assert dev.num_executions == 1
        spy_execute.assert_called()
        spy_gradients.assert_not_called()

        qml.jacobian(cost)(a)
        spy_gradients.assert_called()
Ejemplo n.º 6
0
    def test_advanced_classical_processing_arguments(self, tol):
        """Test that a gradient transform acts on QNodes
        correctly when the QNode arguments are classically processed,
        and the input weights and the output weights have weird shape."""
        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev)
        def circuit(weights):
            qml.RX(weights[0, 0] ** 2, wires=[0])
            qml.RY(weights[0, 1], wires=[1])
            qml.CNOT(wires=[0, 1])
            return qml.probs(wires=[0, 1])

        w = np.array([[0.543, -0.654], [0.0, 0.0]], requires_grad=True)
        res = qml.gradients.param_shift(circuit)(w)
        assert res.shape == (4, 2, 2)

        expected = qml.jacobian(circuit)(w)
        assert np.allclose(res, expected, atol=tol, rtol=0)

        # when executed with hybrid=False, only the quantum jacobian is returned
        res = qml.gradients.param_shift(circuit, hybrid=False)(w)
        assert res.shape == (4, 2)

        @qml.qnode(dev)
        def circuit(weights):
            qml.RX(weights[0], wires=[0])
            qml.RY(weights[1], wires=[1])
            qml.CNOT(wires=[0, 1])
            return qml.probs(wires=[0, 1])

        w = np.array([0.543 ** 2, -0.654], requires_grad=True)
        expected = qml.jacobian(circuit)(w)
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 7
0
    def test_caching_adjoint_backward(self):
        """Test that caching reduces the number of adjoint evaluations
        when mode=backward"""
        dev = qml.device("default.qubit", wires=2)
        params = np.array([0.1, 0.2, 0.3])

        def cost(a, cache):
            with qml.tape.JacobianTape() as tape:
                qml.RY(a[0], wires=0)
                qml.RX(a[1], wires=0)
                qml.RY(a[2], wires=0)
                qml.expval(qml.PauliZ(0))
                qml.expval(qml.PauliZ(1))

            return execute(
                [tape],
                dev,
                gradient_fn="device",
                cache=cache,
                mode="backward",
                gradient_kwargs={"method": "adjoint_jacobian"},
            )[0]

        # Without caching, 3 evaluations are required.
        # 1 for the forward pass, and one per output dimension
        # on the backward pass.
        qml.jacobian(cost)(params, cache=None)
        assert dev.num_executions == 3

        # With caching, only 2 evaluations are required. One
        # for the forward pass, and one for the backward pass.
        dev._num_executions = 0
        jac_fn = qml.jacobian(cost)
        grad1 = jac_fn(params, cache=True)
        assert dev.num_executions == 2
    def test_hessian_transform_is_differentiable_torch(self):
        """Test that the 3rd derivate can be calculated via auto-differentiation in Torch
        (1d -> 1d)"""
        torch = pytest.importorskip("torch")

        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev, diff_method="parameter-shift", max_diff=3)
        def circuit(x):
            qml.RX(x[1], wires=0)
            qml.RY(x[0], wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.probs(wires=[0, 1])

        x = np.array([0.1, 0.2], requires_grad=True)
        x_torch = torch.tensor([0.1, 0.2],
                               dtype=torch.float64,
                               requires_grad=True)

        expected = qml.jacobian(qml.jacobian(qml.jacobian(circuit)))(x)
        circuit.interface = "torch"
        jacobian_fn = torch.autograd.functional.jacobian
        torch_deriv = jacobian_fn(qml.gradients.param_shift_hessian(circuit),
                                  x_torch)[0]

        assert np.allclose(expected, torch_deriv)
    def test_fewer_device_invocations_vector_output(self):
        """Test that the hessian invokes less hardware executions than double differentiation
        (1d -> 1d)"""

        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev, diff_method="parameter-shift", max_diff=2)
        def circuit(x):
            qml.RX(x[0], wires=0)
            qml.CNOT(wires=[0, 1])
            qml.RY(x[1], wires=0)
            qml.RZ(x[2], wires=1)
            return qml.probs(wires=[0, 1])

        x = np.array([0.1, 0.2, 0.3], requires_grad=True)

        with qml.Tracker(dev) as tracker:
            hessian = qml.gradients.param_shift_hessian(circuit)(x)
            hessian_qruns = tracker.totals["executions"]
            expected = qml.jacobian(qml.jacobian(circuit))(x)
            jacobian_qruns = tracker.totals["executions"] - hessian_qruns

        assert np.allclose(hessian, expected)
        assert hessian_qruns < jacobian_qruns
        assert hessian_qruns <= 2**2 * 6  # 6 = (3+2-1)C(2)
        assert hessian_qruns <= 3**3
Ejemplo n.º 10
0
def test_integration_chunk_observables():
    """Integration tests that compare to default.qubit for a large circuit with multiple expectation values. Expvals are generated in parallelized chunks."""
    dev_def = qml.device("default.qubit", wires=range(4))
    dev_lightning = qml.device("lightning.qubit", wires=range(4))
    dev_lightning_batched = qml.device("lightning.qubit",
                                       wires=range(4),
                                       batch_obs=True)

    def circuit(params):
        circuit_ansatz(params, wires=range(4))
        return [qml.expval(qml.PauliZ(i)) for i in range(4)]

    n_params = 30
    params = np.linspace(0, 10, n_params)

    qnode_def = qml.QNode(circuit, dev_def)
    qnode_lightning = qml.QNode(circuit, dev_lightning, diff_method="adjoint")
    qnode_lightning_batched = qml.QNode(circuit,
                                        dev_lightning_batched,
                                        diff_method="adjoint")

    j_def = qml.jacobian(qnode_def)(params)
    j_lightning = qml.jacobian(qnode_lightning)(params)
    j_lightning_batched = qml.jacobian(qnode_lightning_batched)(params)

    assert np.allclose(j_def, j_lightning)
    assert np.allclose(j_def, j_lightning_batched)
    def test_autograd_with_other_device(self):
        """Test passing an extra device to the QNode wrapper."""
        ansatz = fubini_ansatz2
        params = fubini_params[2]

        exp_fn = autodiff_metric_tensor(ansatz, self.num_wires)
        expected = qml.jacobian(exp_fn)(*params)
        dev = qml.device("default.qubit", wires=self.num_wires)
        dev2 = qml.device("default.qubit.autograd", wires=self.num_wires)

        @qml.qnode(dev, interface="autograd")
        def circuit(*params):
            """Circuit with dummy output to create a QNode."""
            ansatz(*params, dev.wires)
            return qml.expval(qml.PauliZ(0))

        mt = qml.jacobian(qml.adjoint_metric_tensor(circuit,
                                                    device=dev2))(*params)

        if isinstance(mt, tuple):
            assert all(
                qml.math.allclose(_mt, _exp)
                for _mt, _exp in zip(mt, expected))
        else:
            assert qml.math.allclose(mt, expected)
Ejemplo n.º 12
0
def test_unwrap_autograd_backward():
    """Test that unwrapping a tape with Autograd parameters
    works as expected during a backwards pass"""
    from pennylane import numpy as anp
    from autograd.numpy.numpy_boxes import ArrayBox

    p = [
        anp.tensor([0.1, 0.5, 0.3], requires_grad=True),
        anp.tensor(0.2, requires_grad=False),
    ]

    def cost(*p):
        with qml.tape.QuantumTape() as tape:
            qml.RX(p[0][0], wires=0)
            qml.RY(p[1], wires=0)
            qml.PhaseShift(p[0][1], wires=0)
            qml.RZ(p[0][2], wires=0)

        with tape.unwrap() as unwrapped_tape:
            # inside the context manager, all parameters
            # will be unwrapped to NumPy arrays
            params = tape.get_parameters(trainable_only=False)
            assert all(isinstance(i, float) for i in params)
            assert np.allclose(params, [0.1, 0.2, 0.5, 0.3])
            assert tape.trainable_params == {0, 2, 3}

        # outside the context, the original parameters have been restored.
        params = tape.get_parameters(trainable_only=False)
        assert any(isinstance(i, ArrayBox) for i in params)

        return p[0][0] * p[1]**2 * anp.sin(p[0][1]) * anp.exp(-0.5 * p[0][2])

    qml.grad(cost)(*p)
    qml.jacobian(qml.grad(cost))(*p)
Ejemplo n.º 13
0
    def test_tensor_number_displaced_squeezed_pd_squeezing(
        self, dev, disp_sq_circuit, pars, reverted_pars, tol
    ):
        """Test the variance of the TensorN observable for a squeezed displaced
        state

        The analytic expression for the partial derivate wrt r of the second
        squeezing operation can be obtained by passing the parameters of
        operations acting on the second mode first (using reverted_pars).
        """

        def pd_sr(rs0, phis0, rd0, phid0, rs1, phis1, rd1, phid1):
            """Analytic expression for the partial derivative with respect to
            the r argument of the first squeezing operation (rs0)"""
            return (
                (
                    0.25
                    + rd0 ** 2 * (-0.25 - 2 * rd1 ** 2 + 2 * rd1 ** 4)
                    + (-(rd1 ** 2) + rd0 ** 2 * (-1 + 6 * rd1 ** 2)) * np.cosh(2 * rs1)
                    + (-0.25 + 1.25 * rd0 ** 2) * np.cosh(4 * rs1)
                )
                * np.sinh(2 * rs0)
                + (
                    -(rd1 ** 2)
                    + rd1 ** 4
                    + (-0.5 + 2.5 * rd1 ** 2) * np.cosh(2 * rs1)
                    + 0.5 * np.cosh(4 * rs1)
                )
                * np.sinh(4 * rs0)
                + rd1 ** 2
                * np.cos(2 * phid1 - phis1)
                * ((1 - 4 * rd0 ** 2) * np.sinh(2 * rs0) - 1.5 * np.sinh(4 * rs0))
                * np.sinh(2 * rs1)
                + rd0 ** 2
                * np.cos(2 * phid0 - phis0)
                * np.cosh(2 * rs0)
                * (
                    -0.25
                    + 2 * rd1 ** 2
                    - 2 * rd1 ** 4
                    + (1 - 4 * rd1 ** 2) * np.cosh(2 * rs1)
                    - 0.75 * np.cosh(4 * rs1)
                    + 2 * rd1 ** 2 * np.cos(2 * phid1 - phis1) * np.sinh(2 * rs1)
                )
            )

        # differentiate wrt r of the first squeezing operation (rs0)
        grad = qml.jacobian(disp_sq_circuit, argnum=0)(*pars)
        expected_gradient = pd_sr(*pars)
        assert np.allclose(grad, expected_gradient, atol=tol, rtol=0)

        # differentiate wrt r of the second squeezing operation (rs1)
        grad = qml.jacobian(disp_sq_circuit, argnum=4)(*pars)

        #
        expected_gradient = pd_sr(*reverted_pars)
        assert np.allclose(grad, expected_gradient, atol=tol, rtol=0)
Ejemplo n.º 14
0
 def test_sin_warns_jacobian(self, tol):
     """Test that a warning is raised if a positional argument without the
     requires_grad attribute set is passed when differentiating a classical
     vector valued function."""
     arr = onp.array([0.1, 0.2, 0.3])
     with pytest.warns(
             UserWarning,
             match="inputs have to explicitly specify requires_grad=True"):
         qml.jacobian(np.sin)(arr)
Ejemplo n.º 15
0
    def test_hessian_vector_valued_separate_args(self, dev_name, diff_method,
                                                 mocker, tol):
        """Test hessian calculation of a vector valued QNode that has separate input arguments"""
        if diff_method not in {"parameter-shift", "backprop"}:
            pytest.skip("Test only supports parameter-shift or backprop")

        dev = qml.device(dev_name, wires=1)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(a, b):
            qml.RY(a, wires=0)
            qml.RX(b, wires=0)
            return qml.probs(wires=0)

        a = np.array(1.0, requires_grad=True)
        b = np.array(2.0, requires_grad=True)
        res = circuit(a, b)

        expected_res = [
            0.5 + 0.5 * np.cos(a) * np.cos(b),
            0.5 - 0.5 * np.cos(a) * np.cos(b)
        ]
        assert np.allclose(res, expected_res, atol=tol, rtol=0)

        jac_fn = qml.jacobian(circuit)
        g = jac_fn(a, b)

        expected_g = [
            [-0.5 * np.sin(a) * np.cos(b), -0.5 * np.cos(a) * np.sin(b)],
            [0.5 * np.sin(a) * np.cos(b), 0.5 * np.cos(a) * np.sin(b)],
        ]
        assert np.allclose(g, expected_g, atol=tol, rtol=0)

        spy = mocker.spy(JacobianTape, "hessian")
        hess = qml.jacobian(jac_fn)(a, b)

        if diff_method == "backprop":
            spy.assert_not_called()
        elif diff_method == "parameter-shift":
            # Hessian will have been called 4 times, for every permutation of a, b
            # since autograd will treat them as separate arguments.
            assert spy.call_count == 4

        expected_hess = [
            [
                [-0.5 * np.cos(a) * np.cos(b), 0.5 * np.sin(a) * np.sin(b)],
                [0.5 * np.cos(a) * np.cos(b), -0.5 * np.sin(a) * np.sin(b)],
            ],
            [
                [0.5 * np.sin(a) * np.sin(b), -0.5 * np.cos(a) * np.cos(b)],
                [-0.5 * np.sin(a) * np.sin(b), 0.5 * np.cos(a) * np.cos(b)],
            ],
        ]
        assert np.allclose(hess, expected_hess, atol=tol, rtol=0)
Ejemplo n.º 16
0
 def _jacobian(*args, **kwargs):
     if argnum is None:
         jac = qml.jacobian(classical_preprocessing)(*args, **kwargs)
     elif np.isscalar(argnum):
         jac = qml.jacobian(classical_preprocessing,
                            argnum=argnum)(*args, **kwargs)
     else:
         jac = tuple((qml.jacobian(classical_preprocessing,
                                   argnum=i)(*args, **kwargs)
                      for i in argnum))
     return jac
Ejemplo n.º 17
0
    def test_caching_param_shift_hessian(self, num_params, tol):
        """Test that, when using parameter-shift transform,
        caching reduces the number of evaluations to their optimum
        when computing Hessians."""
        dev = qml.device("default.qubit", wires=2)
        params = np.arange(1, num_params + 1) / 10

        N = len(params)

        def cost(x, cache):
            with qml.tape.JacobianTape() as tape:
                qml.RX(x[0], wires=[0])
                qml.RY(x[1], wires=[1])

                for i in range(2, num_params):
                    qml.RZ(x[i], wires=[i % 2])

                qml.CNOT(wires=[0, 1])
                qml.var(qml.PauliZ(0) @ qml.PauliX(1))

            return execute([tape], dev, gradient_fn=param_shift, cache=cache, max_diff=2)[0]

        # No caching: number of executions is not ideal
        hess1 = qml.jacobian(qml.grad(cost))(params, cache=False)

        if num_params == 2:
            # compare to theoretical result
            x, y, *_ = params
            expected = np.array(
                [
                    [2 * np.cos(2 * x) * np.sin(y) ** 2, np.sin(2 * x) * np.sin(2 * y)],
                    [np.sin(2 * x) * np.sin(2 * y), -2 * np.cos(x) ** 2 * np.cos(2 * y)],
                ]
            )
            assert np.allclose(expected, hess1, atol=tol, rtol=0)

        expected_runs = 1  # forward pass
        expected_runs += 2 * N  # Jacobian
        expected_runs += 4 * N + 1  # Hessian diagonal
        expected_runs += 4 * N ** 2  # Hessian off-diagonal
        assert dev.num_executions == expected_runs

        # Use caching: number of executions is ideal
        dev._num_executions = 0
        hess2 = qml.jacobian(qml.grad(cost))(params, cache=True)
        assert np.allclose(hess1, hess2, atol=tol, rtol=0)

        expected_runs_ideal = 1  # forward pass
        expected_runs_ideal += 2 * N  # Jacobian
        expected_runs_ideal += N + 1  # Hessian diagonal
        expected_runs_ideal += 4 * N * (N - 1) // 2  # Hessian off-diagonal
        assert dev.num_executions == expected_runs_ideal
        assert expected_runs_ideal < expected_runs
    def test_autograd_trainable_argnum_no_warn_jacobian(self, recwarn):
        """Test that no warning is raised if positional arguments are marked as
        trainable using the argnum argument."""
        dev = qml.device("default.qubit", wires=5)

        @qml.qnode(dev)
        def test(x):
            qml.RZ(x, wires=[0])
            return qml.probs(wires=[0])

        par = np.array(0.3)
        qml.jacobian(test, argnum=0)(par)
        assert len(recwarn) == 0
    def test_hessian_at_zero(self, x, shift):
        """Tests that the Hessian at vanishing state vector amplitudes
        is correct."""
        dev = qml.device("default.qubit.autograd", wires=1)

        @qml.qnode(dev, interface="autograd", diff_method="backprop")
        def circuit(x):
            qml.RY(shift, wires=0)
            qml.RY(x, wires=0)
            return qml.expval(qml.PauliZ(0))

        assert qml.math.isclose(qml.jacobian(circuit)(x), 0.0)
        assert qml.math.isclose(qml.jacobian(qml.jacobian(circuit))(x), -1.0)
        assert qml.math.isclose(qml.grad(qml.grad(circuit))(x), -1.0)
 def test_simple_operation_autograd(self, invert):
     """Test differentiability for a simple operation with Autograd."""
     device = qml.device("default.qubit.autograd", wires=2)
     x = np.array(self.x, requires_grad=True)
     r_fn = lambda x: qml.math.real(
         _apply_operations(device._state, qml.RX(x, wires=0), device, invert
                           ))
     i_fn = lambda x: qml.math.imag(
         _apply_operations(device._state, qml.RX(x, wires=0), device, invert
                           ))
     out = qml.jacobian(r_fn)(x) + 1j * qml.jacobian(i_fn)(x)
     exp = (np.array([[-np.sin(self.x / 2), 0.0],
                      [-1j * (-1)**invert * np.cos(self.x / 2), 0.0]]) / 2)
     assert np.allclose(out, exp)
    def test_autograd_positional_non_trainable_warns_jacobian(self):
        """Test that a warning is raised if a positional argument without the
        requires_grad attribute set is passed when differentiating a QNode with a
        vector output."""
        dev = qml.device("default.qubit", wires=5)

        @qml.qnode(dev)
        def test(x):
            qml.RY(x, wires=[0])
            return qml.probs(wires=[0])

        with pytest.warns(
                UserWarning,
                match="inputs have to explicitly specify requires_grad=True"):
            qml.jacobian(test)(0.3)
Ejemplo n.º 22
0
    def test_autograd_gradient(self, tol):
        """Tests that the output of the parameter-shift CV transform
        can be differentiated using autograd, yielding second derivatives."""
        dev = qml.device("default.gaussian", wires=1)
        from pennylane.interfaces.autograd import AutogradInterface

        r = 0.12
        phi = 0.105

        def cost_fn(x):
            with AutogradInterface.apply(qml.tape.CVParamShiftTape()) as tape:
                qml.Squeezing(x[0], 0, wires=0)
                qml.Rotation(x[1], wires=0)
                qml.var(qml.X(wires=[0]))

            tapes, fn = param_shift_cv(tape, dev)
            return fn([t.execute(dev) for t in tapes])[0, 1]

        params = np.array([r, phi], requires_grad=True)
        grad = qml.jacobian(cost_fn)(params)
        expected = np.array([
            4 * np.cosh(2 * r) * np.sin(2 * phi),
            4 * np.cos(2 * phi) * np.sinh(2 * r)
        ])
        assert np.allclose(grad, expected, atol=tol, rtol=0)
Ejemplo n.º 23
0
    def test_autograd(self, approx_order, strategy, tol):
        """Tests that the output of the finite-difference transform
        can be differentiated using autograd, yielding second derivatives."""
        dev = qml.device("default.qubit.autograd", wires=2)
        params = np.array([0.543, -0.654], requires_grad=True)

        def cost_fn(x):
            with qml.tape.JacobianTape() as tape:
                qml.RX(x[0], wires=[0])
                qml.RY(x[1], wires=[1])
                qml.CNOT(wires=[0, 1])
                qml.expval(qml.PauliZ(0) @ qml.PauliX(1))

            tape.trainable_params = {0, 1}
            tapes, fn = finite_diff(tape, n=1, approx_order=approx_order, strategy=strategy)
            jac = fn(dev.batch_execute(tapes))
            return jac

        res = qml.jacobian(cost_fn)(params)
        x, y = params
        expected = np.array(
            [
                [-np.cos(x) * np.sin(y), -np.cos(y) * np.sin(x)],
                [-np.cos(y) * np.sin(x), -np.cos(x) * np.sin(y)],
            ]
        )
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 24
0
    def test_no_trainable_parameters(self, dev_name, diff_method, tol):
        """Test evaluation and Jacobian if there are no trainable parameters"""
        dev = qml.device(dev_name, wires=2)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(a, b):
            qml.RY(a, wires=0)
            qml.RX(b, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

        a = np.array(0.1, requires_grad=False)
        b = np.array(0.2, requires_grad=False)

        res = circuit(a, b)

        if diff_method == "finite-diff":
            assert circuit.qtape.trainable_params == set()

        assert res.shape == (2,)
        assert isinstance(res, np.ndarray)

        assert not qml.jacobian(circuit)(a, b)

        def cost(a, b):
            return np.sum(circuit(a, b))

        with pytest.warns(UserWarning, match="Output seems independent of input"):
            grad = qml.grad(cost)(a, b)

        assert grad == tuple()
Ejemplo n.º 25
0
    def test_jacobian_no_evaluate(self, dev_name, diff_method, mocker, tol):
        """Test jacobian calculation when no prior circuit evaluation has been performed"""
        spy = mocker.spy(JacobianTape, "jacobian")
        a = np.array(0.1, requires_grad=True)
        b = np.array(0.2, requires_grad=True)

        dev = qml.device(dev_name, wires=2)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(a, b):
            qml.RY(a, wires=0)
            qml.RX(b, wires=1)
            qml.CNOT(wires=[0, 1])
            return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))]

        jac_fn = qml.jacobian(circuit)
        res = jac_fn(a, b)
        expected = [[-np.sin(a), 0], [np.sin(a) * np.sin(b), -np.cos(a) * np.cos(b)]]
        assert np.allclose(res, expected, atol=tol, rtol=0)

        if diff_method == "finite-diff":
            spy.assert_called()
        elif diff_method == "backprop":
            spy.assert_not_called()

        # call the Jacobian with new parameters
        a = np.array(0.6, requires_grad=True)
        b = np.array(0.832, requires_grad=True)

        res = jac_fn(a, b)
        expected = [[-np.sin(a), 0], [np.sin(a) * np.sin(b), -np.cos(a) * np.cos(b)]]
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 26
0
    def test_jacobian(self, dev_name, diff_method, mocker, tol):
        """Test jacobian calculation"""
        spy = mocker.spy(JacobianTape, "jacobian")
        a = np.array(0.1, requires_grad=True)
        b = np.array(0.2, requires_grad=True)

        dev = qml.device(dev_name, wires=2)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(a, b):
            qml.RY(a, wires=0)
            qml.RX(b, wires=1)
            qml.CNOT(wires=[0, 1])
            return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))]

        res = circuit(a, b)

        assert circuit.qtape.trainable_params == {0, 1}
        assert res.shape == (2,)

        expected = [np.cos(a), -np.cos(a) * np.sin(b)]
        assert np.allclose(res, expected, atol=tol, rtol=0)

        res = qml.jacobian(circuit)(a, b)
        expected = [[-np.sin(a), 0], [np.sin(a) * np.sin(b), -np.cos(a) * np.cos(b)]]
        assert np.allclose(res, expected, atol=tol, rtol=0)

        if diff_method == "finite-diff":
            spy.assert_called()
        elif diff_method == "backprop":
            spy.assert_not_called()
Ejemplo n.º 27
0
    def test_probability_differentiation(self, dev_name, diff_method, tol):
        """Tests correct output shape and evaluation for a tape
        with prob and expval outputs"""

        dev = qml.device(dev_name, wires=2)
        x = np.array(0.543, requires_grad=True)
        y = np.array(-0.654, requires_grad=True)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(x, y):
            qml.RX(x, wires=[0])
            qml.RY(y, wires=[1])
            qml.CNOT(wires=[0, 1])
            return qml.probs(wires=[0]), qml.probs(wires=[1])

        res = circuit(x, y)

        expected = np.array([
            [np.cos(x / 2)**2, np.sin(x / 2)**2],
            [(1 + np.cos(x) * np.cos(y)) / 2, (1 - np.cos(x) * np.cos(y)) / 2],
        ])
        assert np.allclose(res, expected, atol=tol, rtol=0)

        res = qml.jacobian(circuit)(x, y)
        expected = np.array([
            [[-np.sin(x) / 2, 0],
             [-np.sin(x) * np.cos(y) / 2, -np.cos(x) * np.sin(y) / 2]],
            [
                [np.sin(x) / 2, 0],
                [np.cos(y) * np.sin(x) / 2,
                 np.cos(x) * np.sin(y) / 2],
            ],
        ])

        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 28
0
    def test_jacobian_repeated(self, torch_support, rep, tol):
        """Test that qnode.jacobian applied to the tensornet.tf device
        gives the correct result in the case of repeated parameters"""
        x = 0.43316321
        y = 0.2162158
        z = 0.75110998
        p = np.array([x, y, z])
        dev = qml.device("default.tensor.tf", wires=1, representation=rep)

        @qml.qnode(dev)
        def circuit(x):
            qml.RX(x[1], wires=0)
            qml.Rot(x[0], x[1], x[2], wires=0)
            return qml.expval(qml.PauliZ(0))

        res = circuit(p)
        expected = np.cos(y)**2 - np.sin(x) * np.sin(y)**2
        assert np.allclose(res, expected, atol=tol, rtol=0)

        res = qml.jacobian(circuit)(p)
        expected = np.array([
            -np.cos(x) * np.sin(y)**2,
            -2 * (np.sin(x) + 1) * np.sin(y) * np.cos(y),
            0,
        ])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 29
0
    def test_jacobian_variable_multiply(self, torch_support, rep, tol):
        """Test that qnode.jacobian applied to the tensornet.tf device
        gives the correct result in the case of parameters multiplied by scalars"""
        x = 0.43316321
        y = 0.2162158
        z = 0.75110998

        dev = qml.device("default.tensor.tf", wires=1, representation=rep)

        @qml.qnode(dev)
        def circuit(p):
            qml.RX(3 * p[0], wires=0)
            qml.RY(p[1], wires=0)
            qml.RX(p[2] / 2, wires=0)
            return qml.expval(qml.PauliZ(0))

        res = circuit([x, y, z])
        expected = np.cos(3 * x) * np.cos(y) * np.cos(z / 2) - np.sin(
            3 * x) * np.sin(z / 2)
        assert np.allclose(res, expected, atol=tol, rtol=0)

        res = qml.jacobian(circuit)(np.array([x, y, z]))
        expected = np.array([
            -3 * (np.sin(3 * x) * np.cos(y) * np.cos(z / 2) +
                  np.cos(3 * x) * np.sin(z / 2)),
            -np.cos(3 * x) * np.sin(y) * np.cos(z / 2),
            -0.5 * (np.sin(3 * x) * np.cos(z / 2) +
                    np.cos(3 * x) * np.cos(y) * np.sin(z / 2)),
        ])

        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 30
0
    def test_gradient_with_passthru_autograd(self):
        """Test that the gradient of the state is accessible when using default.qubit.autograd
        with the backprop diff_method."""
        from pennylane import numpy as anp

        dev = qml.device("default.qubit.autograd", wires=1)

        @qnode(dev, interface="autograd", diff_method="backprop")
        def func(x):
            qml.RY(x, wires=0)
            return state()

        x = anp.array(0.1, requires_grad=True)

        def loss_fn(x):
            res = func(x)
            return anp.real(
                res
            )  # This errors without the real. Likely an issue with complex
            # numbers in autograd

        d_loss_fn = qml.jacobian(loss_fn)

        grad = d_loss_fn(x)
        expected = np.array([-0.5 * np.sin(x / 2), 0.5 * np.cos(x / 2)])
        assert np.allclose(grad, expected)