def test_multiple_rx_gradient(self, tol): """Tests that the gradient of multiple RX gates in a circuit yeilds the correct result.""" dev = qml.device("default.qubit", wires=3) params = np.array([np.pi, np.pi / 2, np.pi / 3]) with ReversibleTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) qml.RX(params[2], wires=2) for idx in range(3): expval(qml.PauliZ(idx)) circuit_output = tape.execute(dev) expected_output = np.cos(params) assert np.allclose(circuit_output, expected_output, atol=tol, rtol=0) # circuit jacobians circuit_jacobian = tape.jacobian(dev, method="analytic") expected_jacobian = -np.diag(np.sin(params)) assert np.allclose(circuit_jacobian, expected_jacobian, atol=tol, rtol=0)
def test_diff_circuit_construction(self, mocker): """Test that the diff circuit is correctly constructed""" dev = qml.device("default.qubit", wires=2) with ReversibleTape() as tape: qml.PauliX(wires=0) qml.RX(0.542, wires=0) qml.RY(0.542, wires=0) expval(qml.PauliZ(0)) spy = mocker.spy(dev, "execute") tape.jacobian(dev) tape0 = spy.call_args_list[0][0][0] tape1 = spy.call_args_list[1][0][0] tape2 = spy.call_args_list[2][0][0] assert tape0 is tape assert len(tape1.operations) == 3 assert not tape1.measurements assert tape1.operations[0].name == "RY.inv" assert tape1.operations[1].name == "PauliX" assert tape1.operations[2].name == "RY" assert len(tape2.operations) == 1 assert not tape2.measurements assert tape2.operations[0].name == "PauliY"
def test_ry_gradient(self, par, mocker, tol): """Test that the gradient of the RY gate matches the exact analytic formula. Further, make sure the correct gradient methods are being called.""" with ReversibleTape() as tape: qml.RY(par, wires=[0]) expval(qml.PauliX(0)) tape.trainable_params = {0} dev = qml.device("default.qubit", wires=1) spy_numeric = mocker.spy(tape, "numeric_pd") spy_analytic = mocker.spy(tape, "analytic_pd") # gradients exact = np.cos(par) grad_F = tape.jacobian(dev, method="numeric") spy_numeric.assert_called() spy_analytic.assert_not_called() spy_device = mocker.spy(tape, "execute_device") grad_A = tape.jacobian(dev, method="analytic") spy_analytic.assert_called() spy_device.assert_called_once( ) # check that the state was only pre-computed once # different methods must agree assert np.allclose(grad_F, exact, atol=tol, rtol=0) assert np.allclose(grad_A, exact, atol=tol, rtol=0)
def test_probs_exception(self): """Tests that an exception is raised when probability is used with the ReversibleTape.""" # TODO: remove this test when this support is added dev = qml.device("default.qubit", wires=2) with ReversibleTape() as tape: qml.PauliX(wires=0) qml.RX(0.542, wires=0) probs(wires=[0, 1]) with pytest.raises(ValueError, match="Probability is not supported"): tape.jacobian(dev)
def test_var_exception(self): """Tests that an exception is raised when variance is used with the ReversibleTape.""" # TODO: remove this test when this support is added dev = qml.device("default.qubit", wires=2) with ReversibleTape() as tape: qml.PauliX(wires=0) qml.RX(0.542, wires=0) var(qml.PauliZ(0)) with pytest.raises(ValueError, match="Variance is not supported"): tape.jacobian(dev)
def test_controlled_rotation_gates_exception(self, op, name): """Tests that an exception is raised when a controlled rotation gate is used with the ReversibleTape.""" # TODO: remove this test when this support is added dev = qml.device("default.qubit", wires=2) with ReversibleTape() as tape: qml.PauliX(wires=0) op(0.542, wires=[0, 1]) expval(qml.PauliZ(0)) with pytest.raises( ValueError, match="The {} gate is not currently supported".format(name)): tape.jacobian(dev)
def test_phaseshift_exception(self): """Tests that an exception is raised when a PhaseShift gate is used with the ReversibleTape.""" # TODO: remove this test when this support is added dev = qml.device("default.qubit", wires=1) with ReversibleTape() as tape: qml.PauliX(wires=0) qml.PhaseShift(0.542, wires=0) expval(qml.PauliZ(0)) with pytest.raises( ValueError, match="The PhaseShift gate is not currently supported"): tape.jacobian(dev)
def test_pauli_rotation_gradient(self, G, theta, tol): """Tests that the automatic gradients of Pauli rotations are correct.""" dev = qml.device("default.qubit", wires=1) with ReversibleTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) G(theta, wires=[0]) expval(qml.PauliZ(0)) tape.trainable_params = {1} autograd_val = tape.jacobian(dev, method="analytic") # compare to finite differences numeric_val = tape.jacobian(dev, method="numeric") assert np.allclose(autograd_val, numeric_val, atol=tol, rtol=0)
def test_Rot_gradient(self, theta, tol): """Tests that the automatic gradient of a arbitrary Euler-angle-parameterized gate is correct.""" dev = qml.device("default.qubit", wires=1) params = np.array([theta, theta**3, np.sqrt(2) * theta]) with ReversibleTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.Rot(*params, wires=[0]) expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} autograd_val = tape.jacobian(dev, method="analytic") # compare to finite differences numeric_val = tape.jacobian(dev, method="numeric") assert np.allclose(autograd_val, numeric_val, atol=tol, rtol=0)
def test_gradients(self, op, obs, mocker, tol): """Tests that the gradients of circuits match between the finite difference and analytic methods.""" args = np.linspace(0.2, 0.5, op.num_params) with ReversibleTape() as tape: qml.Hadamard(wires=0) qml.RX(0.543, wires=0) qml.CNOT(wires=[0, 1]) op(*args, wires=range(op.num_wires)) 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]) expval(obs(wires=0)) expval(qml.PauliZ(wires=1)) dev = qml.device("default.qubit", wires=2) res = tape.execute(dev) tape._update_gradient_info() tape.trainable_params = set(range(1, 1 + op.num_params)) # check that every parameter is analytic for i in range(op.num_params): assert tape._par_info[1 + i]["grad_method"][0] == "A" grad_F = tape.jacobian(dev, method="numeric") spy = mocker.spy(ReversibleTape, "analytic_pd") spy_execute = mocker.spy(tape, "execute_device") grad_A = tape.jacobian(dev, method="analytic") spy.assert_called() # check that the execute device method has only been called # once, for all parameters. spy_execute.assert_called_once() assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def test_rx_gradient(self, tol): """Test that the gradient of the RX gate matches the known formula.""" dev = qml.device("default.qubit", wires=2) a = 0.7418 with ReversibleTape() as tape: qml.RX(a, wires=0) expval(qml.PauliZ(0)) circuit_output = tape.execute(dev) expected_output = np.cos(a) assert np.allclose(circuit_output, expected_output, atol=tol, rtol=0) # circuit jacobians circuit_jacobian = tape.jacobian(dev, method="analytic") expected_jacobian = -np.sin(a) assert np.allclose(circuit_jacobian, expected_jacobian, atol=tol, rtol=0)
def test_gradient_gate_with_multiple_parameters(self, tol): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] with ReversibleTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} dev = qml.device("default.qubit", wires=1) grad_A = tape.jacobian(dev, method="analytic") grad_F = tape.jacobian(dev, method="numeric") # gradient has the correct shape and every element is nonzero assert grad_A.shape == (1, 3) assert np.count_nonzero(grad_A) == 3 # the different methods agree assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def test_matrix_elem(self, wires, vec1, obs, vec2, expected): """Tests for the helper function _matrix_elem""" dev = qml.device("default.qubit", wires=wires) tape = ReversibleTape() res = tape._matrix_elem(vec1, obs, vec2, dev) assert res == expected