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
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