def test_circuit_with_decomposition(self, tol):
        """Tests a PassthruQNode in which the circuit contains an operation
        that needs to be decomposed."""
        theta = 0.543
        phi = -0.234
        lam = 0.654
        p = [theta, phi, lam]

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

        def circuit(weights):
            qml.QubitStateVector(1j * np.array([1, -1]) / np.sqrt(2),
                                 wires=[0])
            qml.U3(weights[0], weights[1], weights[2],
                   wires=[0])  # <--- decomposition is required
            return qml.expval(qml.PauliX(0))

        node = PassthruQNode(circuit, dev, interface="tf")

        params = tf.Variable(p, dtype=tf.float64)

        with tf.GradientTape() as tape:
            tape.watch(params)
            res = node(params)

        expected = np.sin(lam) * np.sin(phi) - np.cos(theta) * np.cos(
            lam) * np.cos(phi)
        assert np.allclose(res, expected, atol=tol, rtol=0)
    def test_evaluate(self, tensornet_tf_device, tol):
        """Tests the evaluation of a PassthruQNode."""
        def circuit(x):
            qml.RX(x[0], wires=0)
            qml.RY(x[1], wires=1)
            qml.CNOT(wires=[0, 1])
            qml.RX(x[2], wires=1)
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

        x = np.array([-1.4, 0.2, 2.1])
        x_tf = tf.Variable(x, dtype=self.DTYPE)

        node = PassthruQNode(circuit, tensornet_tf_device)
        res = node(x_tf)

        # compare to a reference node
        #ref_device = qml.device('default.qubit', wires=2)
        #ref_node = BaseQNode(circuit, ref_device)
        #ref_res = ref_node(x)
        # analytic result
        ref_res = np.cos(x[0]) * np.array([1.0, np.cos(x[1]) * np.cos(x[2])])

        assert isinstance(res, tf.Tensor)
        assert res.shape == (2, )
        assert res.dtype == self.DTYPE
        assert res.numpy() == pytest.approx(ref_res, abs=tol)
def mock_qnode(mock_device):
    """Simple PassthruQNode with default properties."""
    def circuit(x):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    node = PassthruQNode(circuit, mock_device)
    return node
Esempio n. 4
0
    def test_immutable_error(self, mock_device):
        """Test that an error is raised if the mutable=False option is passed
        upon instantiation"""
        def circuit(x):
            qml.RX(x, wires=0)
            return qml.expval(qml.PauliZ(0))

        with pytest.raises(ValueError, match=r"PassthruQNode does not support immutable mode"):
            node = PassthruQNode(circuit, mock_device, mutable=False)
    def test_arraylike_keywordargs(self, tensornet_tf_device, tol):
        """Tests that qnodes use array-like TF objects as keyword-only arguments."""
        def circuit(*, x=None):
            qml.RX(x[0], wires=[0])
            qml.RX(2 * x[1], wires=[1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

        node = PassthruQNode(circuit, tensornet_tf_device)
        x = tf.Variable([1.1, 1.4], dtype=self.DTYPE)
        res = node(x=x)
        assert isinstance(res, tf.Tensor)
        assert res.shape == (2, )
        assert res.dtype == self.DTYPE
    def test_jacobian(self, tensornet_tf_device, vectorize_jacobian, tol):
        """Tests the computing of the Jacobian of a PassthruQNode using TensorFlow."""
        def circuit(phi, theta):
            qml.RX(phi[0], wires=0)
            qml.RY(phi[1], wires=1)
            qml.CNOT(wires=[0, 1])
            qml.RX(theta, wires=1)
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

        ph = np.array([0.7, -1.2])
        th = 1.7
        phi = tf.Variable(ph, dtype=self.DTYPE)
        theta = tf.Variable(th, dtype=self.DTYPE)

        node = PassthruQNode(circuit, tensornet_tf_device)
        # In TF 2, tf.GradientTape.jacobian comes with a vectorization option.
        with tf.GradientTape(persistent=not vectorize_jacobian) as tape:
            tape.watch([phi, theta])
            res = node(phi, theta)
        phi_grad, theta_grad = tape.jacobian(
            res, [phi, theta],
            unconnected_gradients=tf.UnconnectedGradients.ZERO,
            experimental_use_pfor=vectorize_jacobian)

        # compare to a reference Jacobian computed using finite differences
        #ref_device = qml.device('default.qubit', wires=2)
        #ref_node = JacobianQNode(circuit, ref_device)
        #ref_jac = ref_node.jacobian([ph, th])
        # analytic result
        ref_jac = np.array([[-np.sin(ph[0]), 0., 0.],
                            [
                                -np.sin(ph[0]) * np.cos(ph[1]) * np.cos(th),
                                np.cos(ph[0]) * -np.sin(ph[1]) * np.cos(th),
                                np.cos(ph[0]) * np.cos(ph[1]) * -np.sin(th)
                            ]])

        assert isinstance(phi_grad, tf.Tensor)
        assert phi_grad.shape == (2, 2)
        assert phi_grad.dtype == self.DTYPE
        assert phi_grad.numpy() == pytest.approx(ref_jac[:, 0:2], abs=tol)

        assert isinstance(theta_grad, tf.Tensor)
        assert theta_grad.shape == (2, )
        assert theta_grad.dtype == self.DTYPE
        assert theta_grad.numpy() == pytest.approx(ref_jac[:, 2], abs=tol)
    def test_tensor_operations(self, tensornet_tf_device, tol):
        """Tests the evaluation of a PassthruQNode involving algebraic operations between tensor parameters,
        and TF functions acting on them."""
        def circuit(phi, theta):
            x = phi * theta
            qml.RX(x[0], wires=0)
            qml.RY(tf.cos(x[1]), wires=1)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.Hadamard(1))

        node = PassthruQNode(circuit, tensornet_tf_device)

        phi = tf.Variable(np.array([0.7, -1.2]), dtype=self.DTYPE)
        theta = tf.Variable(1.7, dtype=self.DTYPE)
        res = node(phi, theta)
        assert isinstance(res, tf.Tensor)
        assert res.shape == (2, )
        assert res.dtype == self.DTYPE