Esempio n. 1
0
    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])
            qml.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)
Esempio n. 2
0
    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):
                qml.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)
Esempio n. 3
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)
            qml.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"
Esempio n. 4
0
    def test_hamiltonian_error(self):
        """Tests that an exception is raised when a Hamiltonian
        is used with the ReversibleTape."""
        with ReversibleTape() as tape:
            qml.RX(0.542, wires=0)
            qml.expval(1.0 * qml.PauliZ(0) @ qml.PauliX(1))

        dev = qml.device("default.qubit", wires=2)

        with pytest.raises(qml.QuantumFunctionError, match="does not support Hamiltonian"):
            tape.jacobian(dev, method="analytic")
Esempio n. 5
0
    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)
            qml.expval(qml.PauliZ(0))

        with pytest.raises(ValueError, match="The PhaseShift gate is not currently supported"):
            tape.jacobian(dev)
Esempio n. 6
0
    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)
            qml.var(qml.PauliZ(0))

        with pytest.raises(ValueError, match="Variance is not supported"):
            tape.jacobian(dev)
Esempio n. 7
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)
            qml.probs(wires=[0, 1])

        with pytest.raises(ValueError, match="Probability is not supported"):
            tape.jacobian(dev)
Esempio n. 8
0
    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])
            qml.expval(qml.PauliZ(0))

        with pytest.raises(ValueError, match=f"The {name} gate is not currently supported"):
            tape.jacobian(dev)
Esempio n. 9
0
    def test_finite_shots_warning(self):
        """Test that a warning is raised when calling the jacobian with a device with finite shots"""

        with ReversibleTape() as tape:
            qml.RX(0.1, wires=0)
            qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=1, shots=1)

        with pytest.warns(
            UserWarning,
            match="Requested reversible differentiation to be computed with finite shots.",
        ):
            tape.jacobian(dev)
Esempio n. 10
0
    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])
            qml.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)
Esempio n. 11
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)
            qml.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)
Esempio n. 12
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])
            qml.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)
Esempio n. 13
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])

            qml.expval(obs(wires=0))
            qml.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)
Esempio n. 14
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])
            qml.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)
Esempio n. 15
0
    def test_rot_diff_circuit_construction(self, mocker):
        """Test that the diff circuit is correctly constructed for the Rot gate"""
        dev = qml.device("default.qubit", wires=2)

        with ReversibleTape() as tape:
            qml.PauliX(wires=0)
            qml.Rot(0.1, 0.2, 0.3, wires=0)
            qml.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]
        tape3 = spy.call_args_list[3][0][0]

        assert tape0 is tape

        assert len(tape1.operations) == 6
        assert len(tape1.measurements) == 1
        assert tape1.operations[0].name == "QubitStateVector"
        assert tape1.operations[1].name == "RZ.inv"
        assert tape1.operations[2].name == "RY.inv"
        assert tape1.operations[3].name == "PauliZ"
        assert tape1.operations[4].name == "RY"
        assert tape1.operations[5].name == "RZ"

        assert len(tape2.operations) == 4
        assert len(tape1.measurements) == 1
        assert tape1.operations[0].name == "QubitStateVector"
        assert tape2.operations[1].name == "RZ.inv"
        assert tape2.operations[2].name == "PauliY"
        assert tape2.operations[3].name == "RZ"

        assert len(tape3.operations) == 2
        assert len(tape1.measurements) == 1
        assert tape1.operations[0].name == "QubitStateVector"
        assert tape3.operations[1].name == "PauliZ"
Esempio n. 16
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
Esempio n. 17
0
 def test_matrix_elem(self, wires, vec1, obs, vec2, expected):
     """Tests for the helper function _matrix_elem"""
     tape = ReversibleTape()
     res = tape._matrix_elem(vec1, obs, vec2, qml.wires.Wires(range(wires)))
     assert res == expected