Exemple #1
0
    def test_correct_number_of_executions_torch(self):
        """Test that number of executions are tracked in the torch interface."""
        torch = pytest.importorskip("torch")

        def func():
            qml.Hadamard(wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=2)
        qn = QNode(func, dev, interface="torch")
        for i in range(2):
            qn()

        assert dev.num_executions == 2

        qn2 = QNode(func, dev, interface="torch")
        for i in range(3):
            qn2()

        assert dev.num_executions == 5

        # qubit of different interface
        qn3 = QNode(func, dev, interface="autograd")
        qn3()

        assert dev.num_executions == 6
    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)

        def circuit(x, y, z):
            qml.Hadamard(wires=0)
            qml.RX(0.543, wires=0)
            qml.CNOT(wires=[0, 1])

            qml.Rot(x, y, z, wires=0)

            qml.Rot(1.3, -2.3, 0.5, wires=[0])
            qml.RZ(-0.5, wires=0)
            qml.RY(0.5, wires=1)
            qml.CNOT(wires=[0, 1])

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

        qnode1 = QNode(circuit, dev, diff_method="adjoint")
        spy = mocker.spy(dev, "adjoint_jacobian")

        grad_fn = qml.grad(qnode1)
        grad_A = grad_fn(*args)

        spy.assert_called()

        qnode2 = QNode(circuit, dev, diff_method="finite-diff")
        grad_fn = qml.grad(qnode2)
        grad_F = grad_fn(*args)

        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
    def test_interface_torch(self, dev):
        """Test if gradients agree between the adjoint and finite-diff methods when using the
        Torch interface"""
        torch = pytest.importorskip("torch")

        def f(params1, params2):
            qml.RX(0.4, wires=[0])
            qml.RZ(params1 * torch.sqrt(params2), wires=[0])
            qml.RY(torch.cos(params2), wires=[0])
            return qml.expval(qml.PauliZ(0))

        params1 = torch.tensor(0.3, requires_grad=True)
        params2 = torch.tensor(0.4, requires_grad=True)

        qnode1 = QNode(f, dev, interface="torch", diff_method="adjoint")
        qnode2 = QNode(f, dev, interface="torch", diff_method="finite-diff")

        res1 = qnode1(params1, params2)
        res1.backward()

        grad_adjoint = params1.grad, params2.grad

        res2 = qnode2(params1, params2)
        res2.backward()

        grad_fd = params1.grad, params2.grad

        assert np.allclose(grad_adjoint, grad_fd)
Exemple #4
0
    def test_returning_non_measurements(self):
        """Test that an exception is raised if a non-measurement
        is returned from the QNode."""
        dev = qml.device("default.qubit", wires=2)

        def func(x, y):
            qml.RX(x, wires=0)
            qml.RY(y, wires=1)
            qml.CNOT(wires=[0, 1])
            return 5

        qn = QNode(func, dev)

        with pytest.raises(
            qml.QuantumFunctionError, match="must return either a single measurement"
        ):
            qn(5, 1)

        def func(x, y):
            qml.RX(x, wires=0)
            qml.RY(y, wires=1)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), 5

        qn = QNode(func, dev)

        with pytest.raises(
            qml.QuantumFunctionError, match="must return either a single measurement"
        ):
            qn(5, 1)
    def test_qnode(self, mocker, tol):
        """Test that specifying diff_method allows the reversible
        method to be selected"""
        args = np.array([0.54, 0.1, 0.5], requires_grad=True)
        dev = qml.device("default.qubit", wires=2)

        def circuit(x, y, z):
            qml.Hadamard(wires=0)
            qml.RX(0.543, wires=0)
            qml.CNOT(wires=[0, 1])

            qml.Rot(x, y, z, wires=0)

            qml.Rot(1.3, -2.3, 0.5, wires=[0])
            qml.RZ(-0.5, wires=0)
            qml.RY(0.5, wires=1)
            qml.CNOT(wires=[0, 1])

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

        qnode1 = QNode(circuit, dev, diff_method="reversible")
        spy = mocker.spy(ReversibleTape, "analytic_pd")

        grad_fn = qml.grad(qnode1)
        grad_A = grad_fn(*args)

        spy.assert_called()
        assert isinstance(qnode1.qtape, ReversibleTape)

        qnode2 = QNode(circuit, dev, diff_method="finite-diff")
        grad_fn = qml.grad(qnode2)
        grad_F = grad_fn(*args)

        assert not isinstance(qnode2.qtape, ReversibleTape)
        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
    def test_interface_tf(self, dev):
        """Test if gradients agree between the adjoint and finite-diff methods when using the
        TensorFlow interface"""
        tf = pytest.importorskip("tensorflow")

        def f(params1, params2):
            qml.RX(0.4, wires=[0])
            qml.RZ(params1 * tf.sqrt(params2), wires=[0])
            qml.RY(tf.cos(params2), wires=[0])
            return qml.expval(qml.PauliZ(0))

        params1 = tf.Variable(0.3, dtype=tf.float64)
        params2 = tf.Variable(0.4, dtype=tf.float64)

        qnode1 = QNode(f, dev, interface="tf", diff_method="adjoint")
        qnode2 = QNode(f, dev, interface="tf", diff_method="finite-diff")

        with tf.GradientTape() as tape:
            res1 = qnode1(params1, params2)

        g1 = tape.gradient(res1, [params1, params2])

        with tf.GradientTape() as tape:
            res2 = qnode2(params1, params2)

        g2 = tape.gradient(res2, [params1, params2])

        assert np.allclose(g1, g2)
    def test_gradient_repeated_gate_parameters(self, mocker, tol):
        """Tests that repeated use of a free parameter in a
        multi-parameter gate yield correct gradients."""
        dev = qml.device("default.qubit", wires=1)
        params = np.array([0.8, 1.3], requires_grad=True)

        def circuit(params):
            qml.RX(np.array(np.pi / 4, requires_grad=False), wires=[0])
            qml.Rot(params[1], params[0], 2 * params[0], wires=[0])
            return qml.expval(qml.PauliX(0))

        spy_numeric = mocker.spy(JacobianTape, "numeric_pd")
        spy_analytic = mocker.spy(ReversibleTape, "analytic_pd")

        cost = QNode(circuit, dev, diff_method="finite-diff")
        grad_fn = qml.grad(cost)
        grad_F = grad_fn(params)

        spy_numeric.assert_called()
        spy_analytic.assert_not_called()

        cost = QNode(circuit, dev, diff_method="reversible")
        grad_fn = qml.grad(cost)
        grad_A = grad_fn(params)

        spy_analytic.assert_called()

        # the different methods agree
        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
Exemple #8
0
    def test_invalid_interface(self):
        """Test that an exception is raised for an invalid interface"""
        dev = qml.device("default.qubit", wires=1)

        with pytest.raises(qml.QuantumFunctionError,
                           match="Unknown interface"):
            QNode(None, dev, interface="something")
Exemple #9
0
 def test_backprop_error(self):
     """Test if an error is raised when caching is used with the backprop diff_method"""
     dev = qml.device("default.qubit", wires=2, cache=10)
     with pytest.raises(
             qml.QuantumFunctionError,
             match="Device caching is incompatible with the backprop"):
         QNode(qfunc, dev, diff_method="backprop")
Exemple #10
0
    def test_multi_thread(self, enable_tape_mode):
        """Test that multi-threaded queuing in tape mode works correctly"""
        n_qubits = 4
        n_batches = 5
        dev = qml.device("default.qubit", wires=n_qubits)


        def circuit(inputs, weights):
            for index, input in enumerate(inputs):
                qml.RY(input, wires=index)
            for index in range(n_qubits - 1):
                qml.CNOT(wires=(index, index + 1))
            for index, weight in enumerate(weights):
                qml.RX(weight, wires=index)
            return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_qubits)]

        weight_shapes = {"weights": (n_qubits)}

        try:
            qnode = QNodeCollection([QNode(circuit, dev) for _ in range(n_batches)])
        except Exception as e:
            pytest.fail("QNodeCollection cannot be instantiated")
        x = np.random.rand(n_qubits).astype(np.float64)
        p = np.random.rand(weight_shapes["weights"]).astype(np.float64)
        try:
            for _ in range(10):
                qnode(x, p, parallel=True)
        except:
            pytest.fail("Multi-threading on QuantumTape failed")
Exemple #11
0
    def test_basic_tape_construction(self, tol):
        """Test that a quantum tape is properly constructed"""
        dev = qml.device("default.qubit", wires=2)

        def func(x, y):
            qml.RX(x, wires=0)
            qml.RY(y, wires=1)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        qn = QNode(func, dev)

        x = 0.12
        y = 0.54

        res = qn(x, y)

        assert isinstance(qn.qtape, JacobianTape)
        assert len(qn.qtape.operations) == 3
        assert len(qn.qtape.observables) == 1
        assert qn.qtape.num_params == 2

        expected = qn.qtape.execute(dev)
        assert np.allclose(res, expected, atol=tol, rtol=0)

        # when called, a new quantum tape is constructed
        old_tape = qn.qtape
        res2 = qn(x, y)

        assert np.allclose(res, res2, atol=tol, rtol=0)
        assert qn.qtape is not old_tape
Exemple #12
0
    def test_unknown_diff_method(self):
        """Test that an exception is raised for an unknown differentiation method"""
        dev = qml.device("default.qubit", wires=1)

        with pytest.raises(
            qml.QuantumFunctionError, match="Differentiation method hello not recognized"
        ):
            QNode(None, dev, diff_method="hello")
Exemple #13
0
    def test_diff_method(self, mocker):
        """Test that a user-supplied diff-method correctly returns the right
        quantum tape, interface, and diff method."""
        dev = qml.device("default.qubit", wires=1)

        mock_best = mocker.patch("pennylane.tape.QNode.get_best_method")
        mock_best.return_value = 1, 2, 3, {"method": "best"}

        mock_backprop = mocker.patch(
            "pennylane.tape.QNode._validate_backprop_method")
        mock_backprop.return_value = 4, 5, 6, {"method": "backprop"}

        mock_device = mocker.patch(
            "pennylane.tape.QNode._validate_device_method")
        mock_device.return_value = 7, 8, 9, {"method": "device"}

        qn = QNode(None, dev, diff_method="best")
        assert qn._tape == mock_best.return_value[0]
        assert qn.interface == mock_best.return_value[1]
        assert qn.diff_options["method"] == mock_best.return_value[3]["method"]

        qn = QNode(None, dev, diff_method="backprop")
        assert qn._tape == mock_backprop.return_value[0]
        assert qn.interface == mock_backprop.return_value[1]
        assert qn.diff_options["method"] == mock_backprop.return_value[3][
            "method"]
        mock_backprop.assert_called_once()

        qn = QNode(None, dev, diff_method="device")
        assert qn._tape == mock_device.return_value[0]
        assert qn.interface == mock_device.return_value[1]
        assert qn.diff_options["method"] == mock_device.return_value[3][
            "method"]
        mock_device.assert_called_once()

        qn = QNode(None, dev, diff_method="finite-diff")
        assert qn._tape == JacobianTape
        assert qn.diff_options["method"] == "numeric"

        qn = QNode(None, dev, diff_method="parameter-shift")
        assert qn._tape == QubitParamShiftTape
        assert qn.diff_options["method"] == "analytic"

        # check that get_best_method was only ever called once
        mock_best.assert_called_once()
Exemple #14
0
    def test_invalid_interface(self):
        """Test that an exception is raised for an invalid interface"""
        dev = qml.device("default.qubit", wires=1)
        test_interface = "something"
        expected_error = (
            fr"Unknown interface {test_interface}\. Interface must be "
            r"one of \['autograd', 'torch', 'tf', 'jax'\]\.")

        with pytest.raises(qml.QuantumFunctionError, match=expected_error):
            QNode(None, dev, interface="something")
Exemple #15
0
    def test_caching(self, mocker):
        """Test that caching occurs when the cache attribute is above zero"""
        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev)

        qn(0.1, 0.2)
        spy = mocker.spy(DefaultQubit, "apply")
        qn(0.1, 0.2)

        spy.assert_not_called()
        assert len(dev._cache_execute) == 1
Exemple #16
0
    def test_no_caching(self, mocker):
        """Test that no caching occurs when the cache attribute is equal to zero"""
        dev = qml.device("default.qubit", wires=2, cache=0)
        qn = QNode(qfunc, dev)

        spy = mocker.spy(DefaultQubit, "apply")
        qn(0.1, 0.2)
        qn(0.1, 0.2)

        assert len(spy.call_args_list) == 2
        assert len(dev._cache_execute) == 0
Exemple #17
0
    def test_gradient_autograd(self, mocker):
        """Test that caching works when calculating the gradient using the autograd
        interface"""
        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev, interface="autograd")
        d_qnode = qml.grad(qn)
        args = [0.1, 0.2]

        d_qnode(*args)
        spy = mocker.spy(DefaultQubit, "apply")
        d_qnode(*args)
        spy.assert_not_called()
Exemple #18
0
    def test_add_to_cache_execute(self):
        """Test that the _cache_execute attribute is added to when the device is executed"""
        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev)

        result = qn(0.1, 0.2)
        cache_execute = dev._cache_execute
        hashed = qn.qtape.graph.hash

        assert len(cache_execute) == 1
        assert hashed in cache_execute
        assert np.allclose(cache_execute[hashed], result)
Exemple #19
0
    def test_drop_from_cache(self):
        """Test that the first entry of the _cache_execute dictionary is the first to be dropped
         from the dictionary once it becomes full"""
        dev = qml.device("default.qubit", wires=2, cache=2)
        qn = QNode(qfunc, dev)

        qn(0.1, 0.2)
        first_hash = list(dev._cache_execute.keys())[0]

        qn(0.1, 0.3)
        assert first_hash in dev._cache_execute
        qn(0.1, 0.4)
        assert first_hash not in dev._cache_execute
Exemple #20
0
    def test_correct_number_of_executions_autograd(self):
        """Test that number of executions are tracked in the autograd interface."""
        qml.enable_tape()

        def func():
            qml.Hadamard(wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=2)
        qn = QNode(func, dev, interface="autograd")

        for i in range(2):
            qn()

        assert dev.num_executions == 2

        qn2 = QNode(func, dev, interface="autograd")
        for i in range(3):
            qn2()

        assert dev.num_executions == 5
Exemple #21
0
    def test_fill_cache(self):
        """Test that the cache is added to until it reaches its maximum size (in this case 10),
        and then maintains that size upon subsequent additions."""
        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev)

        args = np.arange(20)

        for i, arg in enumerate(args[:10]):
            qn(0.1, arg)
            assert len(dev._cache_execute) == i + 1

        for arg in args[10:]:
            qn(0.1, arg)
            assert len(dev._cache_execute) == 10
Exemple #22
0
    def test_diff_method_expansion(self, monkeypatch, mocker):
        """Test that a QNode with tape expansion during construction
        preserves the differentiation method."""

        class MyDev(qml.devices.DefaultQubit):
            """Dummy device that supports device Jacobians"""

            @classmethod
            def capabilities(cls):
                capabilities = super().capabilities().copy()
                capabilities.update(
                    provides_jacobian=True,
                )
                return capabilities

            def jacobian(self, *args, **kwargs):
                return np.zeros((2, 4))

        dev = MyDev(wires=2)

        def func(x, y):
            # the U2 operation is not supported on default.qubit
            # and is decomposed.
            qml.U2(x, y, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.probs(wires=0)

        qn = QNode(func, dev, diff_method="device", h=1e-8, order=2)

        assert qn.diff_options["method"] == "device"
        assert qn.diff_options["h"] == 1e-8
        assert qn.diff_options["order"] == 2

        x = 0.12
        y = 0.54

        spy = mocker.spy(JacobianTape, "expand")
        res = qn(x, y)

        spy.assert_called_once()
        assert qn.qtape.jacobian_options["method"] == "device"
        assert qn.qtape.jacobian_options["h"] == 1e-8
        assert qn.qtape.jacobian_options["order"] == 2

        spy = mocker.spy(JacobianTape, "jacobian")
        jac = qml.jacobian(qn)(x, y)

        assert spy.call_args_list[0][1]["method"] == "device"
Exemple #23
0
    def test_caching_multiple_values(self, mocker):
        """Test that multiple device executions with different params are cached and accessed on
        subsequent executions"""
        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev)

        args = np.arange(10)

        for arg in args[:10]:
            qn(0.1, arg)

        spy = mocker.spy(DefaultQubit, "apply")
        for arg in args[:10]:
            qn(0.1, arg)

        spy.assert_not_called()
Exemple #24
0
    def test_gradient_torch(self, mocker):
        """Test that caching works when calculating the gradient using the Torch interface"""
        import torch

        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev, interface="torch")
        args0 = torch.tensor(0.1, requires_grad=True)
        args1 = torch.tensor(0.2)

        res = qn(args0, args1)
        res.backward()
        assert args0.grad is not None

        spy = mocker.spy(DefaultQubit, "apply")
        res = qn(args0, args1)
        res.backward()
        spy.assert_not_called()
Exemple #25
0
    def test_consistent_measurement_order(self):
        """Test evaluation exceeds as expected if measurements are returned in the
        same order to how they were queued on the tape"""
        dev = qml.device("default.qubit", wires=2)

        def func(x, y):
            global op1, op2, op3, m1, m2
            op1 = qml.RX(x, wires=0)
            op2 = qml.RY(y, wires=1)
            op3 = qml.CNOT(wires=[0, 1])
            m1 = qml.expval(qml.PauliZ(0))
            m2 = qml.expval(qml.PauliX(1))
            return [m1, m2]

        qn = QNode(func, dev)
        qn(5, 1)  # evaluate the QNode
        assert qn.qtape.operations == [op1, op2, op3]
        assert qn.qtape.measurements == [m1, m2]
Exemple #26
0
    def test_inconsistent_measurement_order(self):
        """Test that an exception is raised if measurements are returned in an
        order different to how they were queued on the tape"""
        dev = qml.device("default.qubit", wires=2)

        def func(x, y):
            qml.RX(x, wires=0)
            qml.RY(y, wires=1)
            qml.CNOT(wires=[0, 1])
            m = qml.expval(qml.PauliZ(0))
            return qml.expval(qml.PauliX(1)), m

        qn = QNode(func, dev)

        with pytest.raises(
            qml.QuantumFunctionError,
            match="measurements must be returned in the order they are measured",
        ):
            qn(5, 1)
Exemple #27
0
    def test_import_error(self, mocker):
        """Test that an exception is caught on import error"""
        mock = mocker.patch("pennylane.tape.interfaces.torch.TorchInterface.apply")
        mock.side_effect = ImportError()

        def func(x, y):
            qml.RX(x, wires=0)
            qml.RY(y, wires=1)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=2)
        qn = QNode(func, dev, interface="torch")

        with pytest.raises(
            qml.QuantumFunctionError,
            match="PyTorch not found. Please install the latest version of PyTorch to enable the 'torch' interface",
        ):
            qn(0.1, 0.1)
Exemple #28
0
    def test_import_error(self, mocker):
        """Test that an exception is caught on import error"""
        tf = pytest.importorskip("tensorflow", minversion="2.1")
        mock = mocker.patch("pennylane.tape.interfaces.tf.TFInterface.apply")
        mock.side_effect = ImportError()

        def func(x, y):
            qml.RX(x, wires=0)
            qml.RY(y, wires=1)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=2)
        qn = QNode(func, dev, interface="tf", diff_method="parameter-shift")

        with pytest.raises(
            qml.QuantumFunctionError,
            match="TensorFlow not found. Please install the latest version of TensorFlow to enable the 'tf' interface",
        ):
            qn(0.1, 0.1)
Exemple #29
0
    def test_jacobian(self, tol):
        """Test the jacobian computation"""
        dev = qml.device("default.qubit", wires=2)

        def func(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)

        qn = QNode(func, dev, h=1e-8, order=2)
        assert qn.diff_options["h"] == 1e-8
        assert qn.diff_options["order"] == 2

        x = 0.12
        y = 0.54

        res = qn(x, y)
        jac = qn.qtape.jacobian(dev, params=[0.45, 0.1])

        assert jac.shape == (4, 2)
Exemple #30
0
    def test_gradient_tf(self, mocker):
        """Test that caching works when calculating the gradient using the TF interface"""
        import tensorflow as tf

        dev = qml.device("default.qubit", wires=2, cache=10)
        qn = QNode(qfunc, dev, interface="tf")
        args0 = tf.Variable(0.1)
        args1 = tf.Variable(0.2)

        with tf.GradientTape() as tape:
            res = qn(args0, args1)

        grad = tape.gradient(res, args0)
        assert grad is not None

        spy = mocker.spy(DefaultQubit, "apply")
        with tf.GradientTape() as tape:
            res = qn(args0, args1)

        tape.gradient(res, args0)
        spy.assert_not_called()