Ejemplo n.º 1
0
        def cost_fn(a, p, device):
            tape = JacobianTape()

            with tape:
                qml.RX(a, wires=0)
                U3(*p, wires=0)
                qml.expval(qml.PauliX(0))

            tape = tape.expand()

            assert [i.name
                    for i in tape.operations] == ["RX", "Rot", "PhaseShift"]
            assert np.all(
                tape.get_parameters() == [a, p[2], p[0], -p[2], p[1] + p[2]])

            return tape.execute(device=device)
Ejemplo n.º 2
0
    def test_get_parameters(self):
        """Test that the get parameters function correctly sets and returns the
        trainable parameters"""
        a = torch.tensor(0.1, requires_grad=True)
        b = torch.tensor(0.2)
        c = torch.tensor(0.3, requires_grad=True)
        d = 0.4

        with TorchInterface.apply(JacobianTape()) as tape:
            qml.Rot(a, b, c, wires=0)
            qml.RX(d, wires=1)
            qml.CNOT(wires=[0, 1])
            qml.expval(qml.PauliX(0))

        assert tape.trainable_params == {0, 2}
        assert np.all(tape.get_parameters() == [a, c])
Ejemplo n.º 3
0
    def test_execution(self):
        """Test execution"""
        a = tf.Variable(0.1)
        dev = qml.device("default.qubit", wires=1)

        with tf.GradientTape() as tape:
            with TFInterface.apply(JacobianTape()) as qtape:
                qml.RY(a, wires=0)
                qml.RX(0.2, wires=0)
                qml.expval(qml.PauliZ(0))

            assert qtape.trainable_params == {0}
            res = qtape.execute(dev)

        assert isinstance(res, tf.Tensor)
        assert res.shape == (1, )
Ejemplo n.º 4
0
    def test_return_state_hessian_error(self):
        """Test error raised if circuit returns the state."""
        psi = np.array([1, 0, 1, 0]) / np.sqrt(2)

        with JacobianTape() as tape:
            qml.QubitStateVector(psi, wires=[0, 1])
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[1])
            qml.CNOT(wires=[0, 1])
            qml.state()

        with pytest.raises(
            ValueError,
            match=r"The Hessian method does not support circuits that return the state",
        ):
            tape.hessian(None)
Ejemplo n.º 5
0
    def test_ragged_output(self):
        """Test that the Jacobian is correctly returned for a tape
        with ragged output"""
        dev = qml.device("default.qubit", wires=3)
        params = [1.0, 1.0, 1.0]

        with JacobianTape() as tape:
            qml.RX(params[0], wires=[0])
            qml.RY(params[1], wires=[1])
            qml.RZ(params[2], wires=[2])
            qml.CNOT(wires=[0, 1])
            qml.probs(wires=0)
            qml.probs(wires=[1, 2])

        res = tape.jacobian(dev)
        assert res.shape == (6, 3)
Ejemplo n.º 6
0
    def test_numeric_unknown_order(self):
        """Test that an exception is raised if the finite-difference
        order is not supported"""
        dev = qml.device("default.qubit", wires=2)
        params = [0.1, 0.2]

        with JacobianTape() as tape:
            qml.RX(1, wires=[0])
            qml.RY(1, wires=[1])
            qml.RZ(1, wires=[2])
            qml.CNOT(wires=[0, 1])

            qml.expval(qml.PauliZ(0) @ qml.PauliX(1) @ qml.PauliZ(2))

        with pytest.raises(ValueError, match="Order must be 1 or 2"):
            tape.jacobian(dev, order=3)
Ejemplo n.º 7
0
    def test_repeated_interface_construction(self):
        """Test that the interface is correctly applied multiple times"""
        with TFInterface.apply(JacobianTape()) as tape:
            qml.RX(0.5, wires=0)
            qml.expval(qml.PauliX(0))

        assert tape.interface == "tf"
        assert isinstance(tape, TFInterface)
        assert tape.__bare__ == JacobianTape
        assert tape.dtype is tf.float64

        TFInterface.apply(tape, dtype=tf.float32)
        assert tape.interface == "tf"
        assert isinstance(tape, TFInterface)
        assert tape.__bare__ == JacobianTape
        assert tape.dtype is tf.float32
Ejemplo n.º 8
0
    def test_get_parameters(self):
        """Test that the get_parameters function correctly gets the trainable parameters and all
        parameters, depending on the trainable_only argument"""
        a = np.array(0.1, requires_grad=True)
        b = np.array(0.2, requires_grad=False)
        c = np.array(0.3, requires_grad=True)
        d = np.array(0.4, requires_grad=False)

        with AutogradInterface.apply(JacobianTape()) as tape:
            qml.Rot(a, b, c, wires=0)
            qml.RX(d, wires=1)
            qml.CNOT(wires=[0, 1])
            qml.expval(qml.PauliX(0))

        assert tape.trainable_params == [0, 2]
        assert np.all(tape.get_parameters(trainable_only=True) == [a, c])
        assert np.all(tape.get_parameters(trainable_only=False) == [a, b, c, d])
Ejemplo n.º 9
0
    def test_execution_on_tf_device(self):
        """Test execution on a TF device"""
        tf = pytest.importorskip("tensorflow")

        a = torch.tensor(0.1, requires_grad=True)
        dev = qml.device("default.qubit.tf", wires=1)

        with TorchInterface.apply(JacobianTape()) as tape:
            qml.RY(a, wires=0)
            qml.RX(torch.tensor(0.2), wires=0)
            qml.expval(qml.PauliZ(0))

        assert tape.trainable_params == [0]
        res = tape.execute(dev)

        assert isinstance(res, torch.Tensor)
        assert res.shape == (1, )
Ejemplo n.º 10
0
    def test_analytic_method(self, mocker):
        """Test that calling the Jacobian with method=analytic correctly
        calls the analytic_pd method"""
        mock = mocker.patch("pennylane.tape.JacobianTape._grad_method")
        mock.return_value = "A"

        with JacobianTape() as tape:
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[0])
            qml.expval(qml.PauliY(0))

        dev = qml.device("default.qubit", wires=1)
        tape.analytic_pd = mocker.Mock()
        tape.analytic_pd.return_value = [[QuantumTape()], lambda res: np.array([1.0])]

        tape.jacobian(dev, method="analytic")
        assert len(tape.analytic_pd.call_args_list) == 2
Ejemplo n.º 11
0
    def test_non_differentiable_error(self):
        """Test error raised if attempting to differentiate with respect to a
        non-differentiable argument"""
        psi = np.array([1, 0, 1, 0]) / np.sqrt(2)

        with JacobianTape() as tape:
            qml.QubitStateVector(psi, wires=[0, 1])
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[1])
            qml.CNOT(wires=[0, 1])
            qml.probs(wires=[0, 1])

        # by default all parameters are assumed to be trainable
        with pytest.raises(
            ValueError, match=r"Cannot differentiate with respect to parameter\(s\) {0}"
        ):
            tape.hessian(None)
Ejemplo n.º 12
0
    def expand(self):
        """Expand the measurement of an observable to a unitary
        rotation and a measurement in the computational basis.

        Returns:
            .JacobianTape: a quantum tape containing the operations
            required to diagonalize the observable

        **Example**

        Consider a measurement process consisting of the expectation
        value of an Hermitian observable:

        >>> H = np.array([[1, 2], [2, 4]])
        >>> obs = qml.Hermitian(H, wires=['a'])
        >>> m = MeasurementProcess(Expectation, obs=obs)

        Expanding this out:

        >>> tape = m.expand()

        We can see that the resulting tape has the qubit unitary applied,
        and a measurement process with no observable, but the eigenvalues
        specified:

        >>> print(tape.operations)
        [QubitUnitary(array([[-0.89442719,  0.4472136 ],
              [ 0.4472136 ,  0.89442719]]), wires=['a'])]
        >>> print(tape.measurements[0].eigvals)
        [0. 5.]
        >>> print(tape.measurements[0].obs)
        None
        """
        if self.obs is None:
            raise NotImplementedError(
                "Cannot expand a measurement process with no observable.")

        from pennylane.tape import JacobianTape  # pylint: disable=import-outside-toplevel

        with JacobianTape() as tape:
            self.obs.diagonalizing_gates()
            MeasurementProcess(self.return_type,
                               wires=self.obs.wires,
                               eigvals=self.obs.eigvals)

        return tape
Ejemplo n.º 13
0
    def test_device_method(self, mocker):
        """Test that calling the Jacobian with method=device correctly
        calls the device_pd method"""
        with JacobianTape() as tape:
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[0])
            qml.expval(qml.PauliY(0))

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

        dev.jacobian = mocker.Mock()
        tape.device_pd(dev)
        dev.jacobian.assert_called_once()

        dev.jacobian = mocker.Mock()
        tape.jacobian(dev, method="device")
        dev.jacobian.assert_called_once()
Ejemplo n.º 14
0
    def test_get_parameters(self):
        """Test that the get parameters function correctly sets and returns the
        trainable parameters"""
        a = tf.Variable(0.1)
        b = tf.constant(0.2)
        c = tf.Variable(0.3)
        d = 0.4

        with tf.GradientTape() as tape:
            with TFInterface.apply(JacobianTape()) as qtape:
                qml.Rot(a, b, c, wires=0)
                qml.RX(d, wires=1)
                qml.CNOT(wires=[0, 1])
                qml.expval(qml.PauliX(0))

        assert qtape.trainable_params == [0, 2]
        assert np.all(qtape.get_parameters() == [a, c])
Ejemplo n.º 15
0
    def test_ragged_differentiation(self, monkeypatch, tol):
        """Tests correct output shape and evaluation for a tape
        with prob and expval outputs"""
        dev = qml.device("default.qubit.tf", wires=2)
        x = tf.Variable(0.543, dtype=tf.float64)
        y = tf.Variable(-0.654, dtype=tf.float64)

        def _asarray(args, dtype=tf.float64):
            res = [tf.reshape(i, [-1]) for i in args]
            res = tf.concat(res, axis=0)
            return tf.cast(res, dtype=dtype)

        # The current DefaultQubitTF device provides an _asarray method that does
        # not work correctly for ragged arrays. For ragged arrays, we would like _asarray to
        # flatten the array. Here, we patch the _asarray method on the device to achieve this
        # behaviour; once the tape has moved from the beta folder, we should implement
        # this change directly in the device.
        monkeypatch.setattr(dev, "_asarray", _asarray)

        with tf.GradientTape() as tape:
            with JacobianTape() as qtape:
                qml.RX(x, wires=[0])
                qml.RY(y, wires=[1])
                qml.CNOT(wires=[0, 1])
                qml.expval(qml.PauliZ(0))
                qml.probs(wires=[1])

            res = qtape.execute(dev)

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

        res = tape.jacobian(res, [x, y])
        expected = np.array([
            [
                -tf.sin(x), -tf.sin(x) * tf.cos(y) / 2,
                tf.cos(y) * tf.sin(x) / 2
            ],
            [0, -tf.cos(x) * tf.sin(y) / 2,
             tf.cos(x) * tf.sin(y) / 2],
        ])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 16
0
    def test_single_output_value(self, tol):
        """Tests correct Jacobian and output shape for a CV tape
        with a single output"""
        dev = qml.device("default.gaussian", wires=2)
        n = 0.543
        a = -0.654

        with JacobianTape() as tape:
            qml.ThermalState(n, wires=0)
            qml.Displacement(a, 0, wires=0)
            qml.var(qml.NumberOperator(0))

        tape.trainable_params = {0, 1}
        res = tape.jacobian(dev)
        assert res.shape == (1, 2)

        expected = np.array([2 * a**2 + 2 * n + 1, 2 * a * (2 * n + 1)])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 17
0
        def cost_fn(a, p, device):
            tape = JacobianTape()

            with tape:
                qml.RX(a, wires=0)
                U3(*p, wires=0)
                qml.expval(qml.PauliX(0))

            tape = AutogradInterface.apply(tape.expand())

            assert tape.trainable_params == {1, 2, 3, 4}
            assert [i.name
                    for i in tape.operations] == ["RX", "Rot", "PhaseShift"]
            assert np.all(
                np.array(tape.get_parameters()) ==
                [p[2], p[0], -p[2], p[1] + p[2]])

            return tape.execute(device=device)
Ejemplo n.º 18
0
    def test_no_trainable_parameters(self, mocker, tol):
        """Test evaluation if there are no trainable parameters"""
        spy = mocker.spy(JacobianTape, "jacobian")
        dev = qml.device("default.qubit.tf", wires=2)

        with tf.GradientTape() as tape:
            with JacobianTape() as qtape:
                qml.RY(0.2, wires=0)
                qml.RX(tf.constant(0.1), wires=0)
                qml.CNOT(wires=[0, 1])
                qml.expval(qml.PauliZ(0))
                qml.expval(qml.PauliZ(1))

            res = qtape.execute(dev)

        assert res.shape == (2, )
        assert isinstance(res, tf.Tensor)
        spy.assert_not_called()
Ejemplo n.º 19
0
    def test_single_expectation_value(self, tol):
        """Tests correct output shape and evaluation for a tape
        with a single expval output"""
        dev = qml.device("default.qubit", wires=2)
        x = 0.543
        y = -0.654

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

        res = tape.jacobian(dev)
        assert res.shape == (1, 2)

        expected = np.array([[-np.sin(y) * np.sin(x), np.cos(y) * np.cos(x)]])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 20
0
    def test_single_expectation_value_with_argnum_all(self, tol):
        """Tests correct output shape and evaluation for a tape
        with a single expval output where all parameters are chose to compute
        the jacobian"""
        dev = qml.device("default.qubit", wires=2)
        x = 0.543
        y = -0.654

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

        res = tape.jacobian(dev, argnum=[0, 1])  # <--- we choose both trainable parameters
        assert res.shape == (1, 2)

        expected = np.array([[-np.sin(y) * np.sin(x), np.cos(y) * np.cos(x)]])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 21
0
    def test_prob_expectation_values(self, tol):
        """Tests correct output shape and evaluation for a tape
        with prob and expval outputs"""
        dev = qml.device("default.qubit", wires=2)
        x = 0.543
        y = -0.654

        with JacobianTape() as tape:
            qml.RX(x, wires=[0])
            qml.RY(y, wires=[1])
            qml.CNOT(wires=[0, 1])
            qml.expval(qml.PauliZ(0))
            qml.probs(wires=[0, 1])

        res = tape.jacobian(dev)
        assert res.shape == (5, 2)

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

        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 22
0
    def test_multiple_output_values(self, tol):
        """Tests correct output shape and evaluation for a tape
        with multiple outputs"""
        dev = qml.device("default.gaussian", wires=2)
        n = 0.543
        a = -0.654

        with JacobianTape() as tape:
            qml.ThermalState(n, wires=0)
            qml.Displacement(a, 0, wires=0)
            qml.expval(qml.NumberOperator(1))
            qml.var(qml.NumberOperator(0))

        tape.trainable_params = {0, 1}
        res = tape.jacobian(dev)
        assert res.shape == (2, 2)

        expected = np.array([[0, 0], [2 * a ** 2 + 2 * n + 1, 2 * a * (2 * n + 1)]])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Ejemplo n.º 23
0
    def test_no_trainable_parameters(self, tol):
        """Test evaluation if there are no trainable parameters"""
        dev = qml.device("default.qubit", wires=2)

        with tf.GradientTape() as tape:

            with TFInterface.apply(JacobianTape()) as qtape:
                qml.RY(0.2, wires=0)
                qml.RX(tf.constant(0.1), wires=0)
                qml.CNOT(wires=[0, 1])
                qml.expval(qml.PauliZ(0))
                qml.expval(qml.PauliZ(1))

            assert qtape.trainable_params == []

            res = qtape.execute(dev)

        assert res.shape == (2, )
        assert isinstance(res, tf.Tensor)
Ejemplo n.º 24
0
    def test_matrix_parameter(self, U, mocker, tol):
        """Test that the TF interface works correctly
        with a matrix parameter"""
        spy = mocker.spy(JacobianTape, "jacobian")
        a = tf.Variable(0.1, dtype=tf.float64)

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

        with tf.GradientTape() as tape:
            with JacobianTape() as qtape:
                qml.QubitUnitary(U, wires=0)
                qml.RY(a, wires=0)
                qml.expval(qml.PauliZ(0))
            res = qtape.execute(dev)

        assert np.allclose(res, -tf.cos(a), atol=tol, rtol=0)

        res = tape.jacobian(res, a)
        assert np.allclose(res, tf.sin(a), atol=tol, rtol=0)
        spy.assert_not_called()
Ejemplo n.º 25
0
    def test_y0(self, mocker):
        """Test that if first order finite differences is used, then
        the tape is executed only once using the current parameter
        values."""
        dev = qml.device("default.qubit", wires=2)
        execute_spy = mocker.spy(dev, "execute")
        numeric_spy = mocker.spy(JacobianTape, "numeric_pd")

        with JacobianTape() as tape:
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[0])
            qml.expval(qml.PauliZ(0))

        res = tape.jacobian(dev, order=1)

        # the execute device method is called once per parameter,
        # plus one global call
        assert len(execute_spy.call_args_list) == tape.num_params + 1
        assert "y0" in numeric_spy.call_args_list[0][1]
        assert "y0" in numeric_spy.call_args_list[1][1]
Ejemplo n.º 26
0
    def test_no_trainable_parameters(self, mocker):
        """Test that if the tape has no trainable parameters, no
        subroutines are called and the returned Jacobian is empty"""
        numeric_spy = mocker.spy(JacobianTape, "numeric_pd")
        analytic_spy = mocker.spy(JacobianTape, "analytic_pd")

        with JacobianTape() as tape:
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[1])
            qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=2)
        tape.trainable_params = {}

        res = tape.jacobian(dev)
        assert res.size == 0
        assert np.all(res == np.array([[]]))

        numeric_spy.assert_not_called()
        analytic_spy.assert_not_called()
Ejemplo n.º 27
0
    def test_choose_params_and_methods(self, diff_methods, argnum):
        """Test that the _choose_params_and_methods helper method returns
        expected results"""
        tape = JacobianTape()
        tape._trainable_params = list(range(len(diff_methods)))
        res = list(tape._choose_params_with_methods(diff_methods, argnum))

        num_all_params = len(diff_methods)

        assert all(k in range(num_all_params) for k, _ in res)
        assert all(v in diff_methods for _, v in res)

        if argnum is None:
            num_params = num_all_params
        elif isinstance(argnum, int):
            num_params = 1
        else:
            num_params = len(argnum)

        assert len(res) == num_params
Ejemplo n.º 28
0
    def test_jacobian_options(self, mocker, tol):
        """Test setting jacobian options"""
        spy = mocker.spy(JacobianTape, "numeric_pd")

        a = torch.tensor([0.1, 0.2], requires_grad=True)

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

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

        tape.jacobian_options = {"h": 1e-8, "order": 2}
        res = tape.execute(dev)
        res.backward()

        for args in spy.call_args_list:
            assert args[1]["order"] == 2
            assert args[1]["h"] == 1e-8
Ejemplo n.º 29
0
    def test_matrix_parameter(self, U, tol):
        """Test that the Torch interface works correctly
        with a matrix parameter"""
        a_val = 0.1
        a = torch.tensor(a_val, requires_grad=True)

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

        with TorchInterface.apply(JacobianTape()) as tape:
            qml.QubitUnitary(U, wires=0)
            qml.RY(a, wires=0)
            qml.expval(qml.PauliZ(0))

        assert tape.trainable_params == {1}
        res = tape.execute(dev)

        assert np.allclose(res.detach().numpy(), -np.cos(a_val), atol=tol, rtol=0)

        res.backward()
        assert np.allclose(a.grad, np.sin(a_val), atol=tol, rtol=0)
Ejemplo n.º 30
0
    def test_non_differentiable(self):
        """Test that a non-differentiable parameter is
        correctly marked"""
        psi = np.array([1, 0, 1, 0]) / np.sqrt(2)

        with JacobianTape() as tape:
            qml.QubitStateVector(psi, wires=[0, 1])
            qml.RX(0.543, wires=[0])
            qml.RY(-0.654, wires=[1])
            qml.CNOT(wires=[0, 1])
            qml.probs(wires=[0, 1])

        assert tape._grad_method(0) is None
        assert tape._grad_method(1) == "F"
        assert tape._grad_method(2) == "F"

        tape._update_gradient_info()

        assert tape._par_info[0]["grad_method"] is None
        assert tape._par_info[1]["grad_method"] == "F"
        assert tape._par_info[2]["grad_method"] == "F"