def test_differentiable_expand(self, mocker, tol): """Test that operation and nested tapes expansion is differentiable""" spy = mocker.spy(QuantumTape, "jacobian") mock = mocker.patch.object(qml.operation.Operation, "do_check_domain", False) class U3(qml.U3): def expand(self): tape = QuantumTape() theta, phi, lam = self.data wires = self.wires tape._ops += [ qml.Rot(lam, theta, -lam, wires=wires), qml.PhaseShift(phi + lam, wires=wires), ] return tape qtape = QuantumTape() dev = qml.device("default.qubit.tf", wires=1) a = np.array(0.1) p = tf.Variable([0.1, 0.2, 0.3], dtype=tf.float64) with tf.GradientTape() as tape: with qtape: qml.RX(a, wires=0) U3(p[0], p[1], p[2], wires=0) expval(qml.PauliX(0)) qtape = qtape.expand() assert [i.name for i in qtape.operations] == ["RX", "Rot", "PhaseShift"] assert np.all( qtape.get_parameters() == [a, p[2], p[0], -p[2], p[1] + p[2]]) res = qtape.execute(device=dev) expected = tf.cos(a) * tf.cos(p[1]) * tf.sin( p[0]) + tf.sin(a) * (tf.cos(p[2]) * tf.sin(p[1]) + tf.cos(p[0]) * tf.cos(p[1]) * tf.sin(p[2])) assert np.allclose(res, expected, atol=tol, rtol=0) res = tape.jacobian(res, p) expected = np.array([ tf.cos(p[1]) * (tf.cos(a) * tf.cos(p[0]) - tf.sin(a) * tf.sin(p[0]) * tf.sin(p[2])), tf.cos(p[1]) * tf.cos(p[2]) * tf.sin(a) - tf.sin(p[1]) * (tf.cos(a) * tf.sin(p[0]) + tf.cos(p[0]) * tf.sin(a) * tf.sin(p[2])), tf.sin(a) * (tf.cos(p[0]) * tf.cos(p[1]) * tf.cos(p[2]) - tf.sin(p[1]) * tf.sin(p[2])), ]) assert np.allclose(res, expected, atol=tol, rtol=0) spy.assert_not_called()
def cost_fn(a, p, device): tape = QuantumTape() with tape: qml.RX(a, wires=0) U3(*p, wires=0) 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)
def cost_fn(a, p, device): tape = QuantumTape() with tape: qml.RX(a, wires=0) U3(*p, wires=0) 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)
def test_differentiable_expand(self, mocker, tol): """Test that operation and nested tapes expansion is differentiable""" mock = mocker.patch.object(qml.operation.Operation, "do_check_domain", False) class U3(qml.U3): def expand(self): tape = QuantumTape() theta, phi, lam = self.data wires = self.wires tape._ops += [ qml.Rot(lam, theta, -lam, wires=wires), qml.PhaseShift(phi + lam, wires=wires), ] return tape tape = QuantumTape() dev = qml.device("default.qubit", wires=1) a = np.array(0.1) p_val = [0.1, 0.2, 0.3] p = torch.tensor(p_val, requires_grad=True) with tape: qml.RX(a, wires=0) U3(p[0], p[1], p[2], wires=0) expval(qml.PauliX(0)) tape = TorchInterface.apply(tape.expand()) assert tape.trainable_params == {1, 2, 3, 4} assert [i.name for i in tape.operations] == ["RX", "Rot", "PhaseShift"] tape_params = [i.detach().numpy() for i in tape.get_parameters()] assert np.allclose( tape_params, [p_val[2], p_val[0], -p_val[2], p_val[1] + p_val[2]], atol=tol, rtol=0) res = tape.execute(device=dev) expected = np.cos(a) * np.cos(p_val[1]) * np.sin(p_val[0]) + np.sin( a) * (np.cos(p_val[2]) * np.sin(p_val[1]) + np.cos(p_val[0]) * np.cos(p_val[1]) * np.sin(p_val[2])) assert np.allclose(res.detach().numpy(), expected, atol=tol, rtol=0) res.backward() expected = np.array([ np.cos(p_val[1]) * (np.cos(a) * np.cos(p_val[0]) - np.sin(a) * np.sin(p_val[0]) * np.sin(p_val[2])), np.cos(p_val[1]) * np.cos(p_val[2]) * np.sin(a) - np.sin(p_val[1]) * (np.cos(a) * np.sin(p_val[0]) + np.cos(p_val[0]) * np.sin(a) * np.sin(p_val[2])), np.sin(a) * (np.cos(p_val[0]) * np.cos(p_val[1]) * np.cos(p_val[2]) - np.sin(p_val[1]) * np.sin(p_val[2])), ]) assert np.allclose(p.grad, expected, atol=tol, rtol=0)