def test_fanout_multiple_params(self, reused_p, other_p, tol): """Tests that the correct gradient is computed for qnodes which use the same parameter in multiple gates.""" from gate_data import Rotx as Rx, Roty as Ry, Rotz as Rz def expZ(state): return np.abs(state[0]) ** 2 - np.abs(state[1]) ** 2 extra_param = 0.31 def circuit(reused_param, other_param): qml.RX(extra_param, wires=[0]) qml.RY(reused_param, wires=[0]) qml.RZ(other_param, wires=[0]) qml.RX(reused_param, wires=[0]) return qml.expval(qml.PauliZ(0)) dev = qml.device("default.qubit", wires=1) f = QubitQNode(circuit, dev) zero_state = np.array([1., 0.]) # analytic gradient grad_A = f.jacobian([reused_p, other_p]) # manual gradient grad_true0 = (expZ(Rx(reused_p) @ Rz(other_p) @ Ry(reused_p + np.pi / 2) @ Rx(extra_param) @ zero_state) \ -expZ(Rx(reused_p) @ Rz(other_p) @ Ry(reused_p - np.pi / 2) @ Rx(extra_param) @ zero_state)) / 2 grad_true1 = (expZ(Rx(reused_p + np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state) \ -expZ(Rx(reused_p - np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state)) / 2 grad_true = grad_true0 + grad_true1 # product rule assert grad_A[0, 0] == pytest.approx(grad_true, abs=tol)
def test_qnode_gradient_fanout(self, qubit_device_1_wire, tol): "Tests that the correct gradient is computed for qnodes which use the same parameter in multiple gates." def expZ(state): return np.abs(state[0])**2 - np.abs(state[1])**2 extra_param = 0.31 def circuit(reused_param, other_param): qml.RX(extra_param, wires=[0]) qml.RY(reused_param, wires=[0]) qml.RZ(other_param, wires=[0]) qml.RX(reused_param, wires=[0]) return qml.expval(qml.PauliZ(0)) f = qml.QNode(circuit, qubit_device_1_wire) zero_state = np.array([1., 0.]) for reused_p in thetas: reused_p = reused_p**3 / 19 for other_p in thetas: other_p = other_p**2 / 11 # autograd gradient grad = autograd.grad(f) grad_eval = grad(reused_p, other_p) # manual gradient grad_true0 = (expZ(Rx(reused_p) @ Rz(other_p) @ Ry(reused_p + np.pi / 2) @ Rx(extra_param) @ zero_state) \ -expZ(Rx(reused_p) @ Rz(other_p) @ Ry(reused_p - np.pi / 2) @ Rx(extra_param) @ zero_state)) / 2 grad_true1 = (expZ(Rx(reused_p + np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state) \ -expZ(Rx(reused_p - np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state)) / 2 grad_true = grad_true0 + grad_true1 # product rule assert grad_eval == pytest.approx(grad_true, abs=tol)
def test_fanout_multiple_params(self, reused_p, other_p, tol, mocker, dev): """Tests that the correct gradient is computed for qnodes which use the same parameter in multiple gates.""" from gate_data import Rotx as Rx, Roty as Ry, Rotz as Rz def expZ(state): return np.abs(state[0]) ** 2 - np.abs(state[1]) ** 2 extra_param = np.array(0.31, requires_grad=False) @qnode(dev, diff_method="adjoint") def cost(p1, p2): qml.RX(extra_param, wires=[0]) qml.RY(p1, wires=[0]) qml.RZ(p2, wires=[0]) qml.RX(p1, wires=[0]) return qml.expval(qml.PauliZ(0)) zero_state = np.array([1.0, 0.0]) cost(reused_p, other_p) spy = mocker.spy(dev, "adjoint_jacobian") # analytic gradient grad_fn = qml.grad(cost) grad_D = grad_fn(reused_p, other_p) spy.assert_called_once() # manual gradient grad_true0 = ( expZ( Rx(reused_p) @ Rz(other_p) @ Ry(reused_p + np.pi / 2) @ Rx(extra_param) @ zero_state ) - expZ( Rx(reused_p) @ Rz(other_p) @ Ry(reused_p - np.pi / 2) @ Rx(extra_param) @ zero_state ) ) / 2 grad_true1 = ( expZ( Rx(reused_p + np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state ) - expZ( Rx(reused_p - np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state ) ) / 2 expected = grad_true0 + grad_true1 # product rule assert np.allclose(grad_D[0], expected, atol=tol, rtol=0)
def test_fanout_multiple_params(self, reused_p, other_p, tol): """Tests that the correct gradient is computed for qnodes which use the same parameter in multiple gates.""" from gate_data import Rotx as Rx, Roty as Ry, Rotz as Rz def expZ(state): return np.abs(state[0])**2 - np.abs(state[1])**2 dev = qml.device("default.qubit", wires=1) extra_param = np.array(0.31, requires_grad=False) @qnode(dev) def cost(p1, p2): qml.RX(extra_param, wires=[0]) qml.RY(p1, wires=[0]) qml.RZ(p2, wires=[0]) qml.RX(p1, wires=[0]) return expval(qml.PauliZ(0)) zero_state = np.array([1.0, 0.0]) # analytic gradient grad_fn = qml.grad(cost) grad_A = grad_fn(reused_p, other_p) # manual gradient grad_true0 = (expZ( Rx(reused_p) @ Rz(other_p) @ Ry(reused_p + np.pi / 2) @ Rx(extra_param) @ zero_state) - expZ( Rx(reused_p) @ Rz(other_p) @ Ry(reused_p - np.pi / 2) @ Rx(extra_param) @ zero_state)) / 2 grad_true1 = (expZ( Rx(reused_p + np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state) - expZ( Rx(reused_p - np.pi / 2) @ Rz(other_p) @ Ry(reused_p) @ Rx(extra_param) @ zero_state)) / 2 expected = grad_true0 + grad_true1 # product rule assert np.allclose(grad_A[0], expected, atol=tol, rtol=0)