예제 #1
0
    def test_multidim_array_parameter(self, shape, tol):
        """Tests that arguments which are multidimensional arrays are
        properly evaluated and differentiated in QubitQNodes."""

        n = np.prod(shape)
        base_array = np.linspace(-1.0, 1.0, n)
        multidim_array = np.reshape(base_array, shape)

        def circuit(w):
            for k in range(n):
                qml.RX(w[np.unravel_index(k, shape)], wires=k)  # base_array[k]
            return tuple(qml.expval(qml.PauliZ(idx)) for idx in range(n))

        dev = qml.device("default.qubit", wires=n)
        circuit = QubitQNode(circuit, dev)

        # circuit evaluations
        circuit_output = circuit(multidim_array)
        expected_output = np.cos(base_array)
        assert circuit_output == pytest.approx(expected_output, abs=tol)

        # circuit jacobians
        circuit_jacobian = circuit.jacobian([multidim_array])
        expected_jacobian = -np.diag(np.sin(base_array))
        assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
예제 #2
0
    def test_differentiate_positional_multidim(self, tol):
        """Tests that all positional arguments are differentiated
        when they are multidimensional."""
        def circuit(a, b):
            qml.RX(a[0], wires=0)
            qml.RX(a[1], wires=1)
            qml.RX(b[2, 1], wires=2)
            return qml.expval(qml.PauliZ(0)), qml.expval(
                qml.PauliZ(1)), qml.expval(qml.PauliZ(2))

        dev = qml.device("default.qubit", wires=3)
        circuit = QubitQNode(circuit, dev)

        a = np.array([-np.sqrt(2), -0.54])
        b = np.array([np.pi / 7] * 6).reshape([3, 2])
        circuit_output = circuit(a, b)
        expected_output = np.cos(np.array([a[0], a[1], b[-1, 0]]))
        assert circuit_output == pytest.approx(expected_output, abs=tol)

        # circuit jacobians
        circuit_jacobian = circuit.jacobian([a, b])
        expected_jacobian = np.array([
            [-np.sin(a[0])] + [0.0] * 7,  # expval 0
            [0.0, -np.sin(a[1])] + [0.0] * 6,  # expval 1
            [0.0] * 2 + [0.0] * 5 + [-np.sin(b[2, 1])],
        ])  # expval 2
        assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
    def test_evaluate_diag_metric_tensor(self, tol):
        """Test that a diagonal metric tensor evaluates correctly"""
        dev = qml.device("default.qubit", wires=2)

        def circuit(a, b, c):
            qml.RX(a, wires=0)
            qml.RY(b, wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(c, wires=1)
            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1))

        circuit = QubitQNode(circuit, dev)

        a = 0.432
        b = 0.12
        c = -0.432

        # evaluate metric tensor
        g = circuit.metric_tensor((a, b, c))

        # check that the metric tensor is correct
        expected = (np.array([
            1,
            np.cos(a)**2,
            (3 - 2 * np.cos(a)**2 * np.cos(2 * b) - np.cos(2 * a)) / 4
        ]) / 4)
        assert np.allclose(g, np.diag(expected), atol=tol, rtol=0)
    def test_construct_subcircuit(self):
        """Test correct subcircuits constructed"""
        dev = qml.device("default.qubit", wires=2)

        def circuit(a, b, c):
            qml.RX(a, wires=0)
            qml.RY(b, wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(c, wires=1)
            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1))

        circuit = QubitQNode(circuit, dev)
        circuit.metric_tensor([1, 1, 1], only_construct=True)
        res = circuit._metric_tensor_subcircuits

        # first parameter subcircuit
        assert len(res[(0, )]["queue"]) == 0
        assert res[(0, )]["scale"] == [-0.5]
        assert isinstance(res[(0, )]["observable"][0], qml.PauliX)

        # second parameter subcircuit
        assert len(res[(1, )]["queue"]) == 1
        assert res[(1, )]["scale"] == [-0.5]
        assert isinstance(res[(1, )]["queue"][0], qml.RX)
        assert isinstance(res[(1, )]["observable"][0], qml.PauliY)

        # third parameter subcircuit
        assert len(res[(2, )]["queue"]) == 3
        assert res[(2, )]["scale"] == [1]
        assert isinstance(res[(2, )]["queue"][0], qml.RX)
        assert isinstance(res[(2, )]["queue"][1], qml.RY)
        assert isinstance(res[(2, )]["queue"][2], qml.CNOT)
        assert isinstance(res[(2, )]["observable"][0], qml.Hermitian)
        assert np.all(res[(
            2, )]["observable"][0].params[0] == qml.PhaseShift.generator[0])
예제 #5
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 pennylane.plugins.default_qubit 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_evaluate_subcircuits(self, tol):
        """Test subcircuits evaluate correctly"""
        dev = qml.device("default.qubit", wires=2)

        def circuit(a, b, c):
            qml.RX(a, wires=0)
            qml.RY(b, wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(c, wires=1)
            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1))

        circuit = QubitQNode(circuit, dev)

        a = 0.432
        b = 0.12
        c = -0.432

        # evaluate subcircuits
        circuit.metric_tensor((a, b, c))

        # first parameter subcircuit
        res = circuit._metric_tensor_subcircuits[(0, )]["result"]
        expected = 0.25
        assert np.allclose(res, expected, atol=tol, rtol=0)

        # second parameter subcircuit
        res = circuit._metric_tensor_subcircuits[(1, )]["result"]
        expected = np.cos(a)**2 / 4
        assert np.allclose(res, expected, atol=tol, rtol=0)

        # third parameter subcircuit
        res = circuit._metric_tensor_subcircuits[(2, )]["result"]
        expected = (3 - 2 * np.cos(a)**2 * np.cos(2 * b) - np.cos(2 * a)) / 16
        assert np.allclose(res, expected, atol=tol, rtol=0)
예제 #7
0
    def test_no_following_observable(self, operable_mock_device_2_wires):
        """Test that the gradient is 0 if no observables succeed"""
        def circuit(x):
            qml.RX(x, wires=[1])
            return qml.expval(qml.PauliZ(0))

        q = QubitQNode(circuit, operable_mock_device_2_wires)
        q._construct([1.0], {})
        assert q.par_to_grad_method == {0: "0"}
예제 #8
0
    def test_param_unused(self, operable_mock_device_2_wires):
        """Test that the gradient is 0 of an unused parameter"""
        def circuit(x, y):
            qml.RX(x, wires=[0])
            return qml.expval(qml.PauliZ(0))

        q = QubitQNode(circuit, operable_mock_device_2_wires)
        q._construct([1.0, 1.0], {})
        assert q.par_to_grad_method == {0: "A", 1: "0"}
예제 #9
0
    def test_not_differentiable(self, operable_mock_device_2_wires):
        """Test that an operation with grad_method=None is marked as
        non-differentiable"""
        def circuit(x):
            qml.BasisState(x, wires=[1])
            return qml.expval(qml.PauliZ(0))

        q = QubitQNode(circuit, operable_mock_device_2_wires)
        q._construct([np.array([1.0])], {})
        assert q.par_to_grad_method == {0: None}
예제 #10
0
    def test_pauli_rotation_gradient(self, G, theta, tol):
        """Tests that the automatic gradients of Pauli rotations are correct."""
        def circuit(x):
            qml.RX(x, wires=[0])
            return qml.expval(qml.PauliZ(0))

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

        autograd_val = circuit.jacobian([theta])
        manualgrad_val = (circuit(theta + np.pi / 2) -
                          circuit(theta - np.pi / 2)) / 2
        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
    def test_no_generator(self):
        """Test exception is raised if subcircuit contains an
        operation with no generator"""
        dev = qml.device("default.qubit", wires=1)

        def circuit(a):
            qml.Rot(a, 0, 0, wires=0)
            return qml.expval(qml.PauliX(0))

        circuit = QubitQNode(circuit, dev)

        with pytest.raises(QuantumFunctionError,
                           match="has no defined generator"):
            circuit.metric_tensor([1], only_construct=True)
    def test_array_parameters_autograd(self, tol, qubit_device_2_wires):
        """Test that gradients of array parameters give
        same results as positional arguments."""

        par = [0.5, 0.54, 0.3]

        def ansatz(x, y, z):
            qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3),
                                 wires=[0, 1])
            qml.Rot(x, y, z, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        def circuit1(x, y, z):
            return ansatz(x, y, z)

        def circuit2(x, array):
            return ansatz(x, array[0], array[1])

        def circuit3(array):
            return ansatz(*array)

        circuit1 = to_autograd(QubitQNode(circuit1, qubit_device_2_wires))
        grad1 = autograd.grad(circuit1, argnum=[0, 1, 2])

        # three positional parameters
        jac = circuit1.jacobian(par)
        ag = grad1(*par)
        ag = np.array([ag])
        assert jac == pytest.approx(ag, abs=tol)

        circuit2 = to_autograd(QubitQNode(circuit2, qubit_device_2_wires))
        grad2 = autograd.grad(circuit2, argnum=[0, 1])

        # one scalar, one array
        temp = [par[0], np.array(par[1:])]
        jac = circuit2.jacobian(temp)
        ag = grad2(*temp)
        ag = np.r_[ag][np.newaxis, :]
        assert jac == pytest.approx(ag, abs=tol)

        circuit3 = to_autograd(QubitQNode(circuit3, qubit_device_2_wires))
        grad3 = autograd.grad(circuit3, argnum=0)

        # one array
        temp = [np.array(par)]
        jac = circuit3.jacobian(temp)
        ag = grad3(*temp)[np.newaxis, :]
        assert jac == pytest.approx(ag, abs=tol)
    def test_hybrid_gradients_autograd_numpy(self, qubit_device_2_wires, tol):
        "Test the gradient of a hybrid computation requiring autograd.numpy functions."

        def circuit(x, y):
            "Quantum node."
            qml.RX(x, wires=[0])
            qml.CNOT(wires=[0, 1])
            qml.RY(y, wires=[0])
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

        quantum = to_autograd(QubitQNode(circuit, qubit_device_2_wires))

        def classical(p):
            "Classical node, requires autograd.numpy functions."
            return anp.exp(anp.sum(quantum(p[0], anp.log(p[1]))))

        def d_classical(a, b, method):
            "Gradient of classical computed symbolically, can use normal numpy functions."
            val = classical((a, b))
            J = quantum.jacobian((a, np.log(b)), method=method)
            return val * np.array([J[0, 0] + J[1, 0], (J[0, 1] + J[1, 1]) / b])

        param = np.array([-0.1259, 1.53])
        y0 = classical(param)
        grad_classical = autograd.jacobian(classical)
        grad_auto = grad_classical(param)

        grad_fd1 = d_classical(*param, 'F')
        grad_angle = d_classical(*param, 'A')

        # gradients computed with different methods must agree
        assert grad_fd1 == pytest.approx(grad_angle, abs=tol)
        assert grad_fd1 == pytest.approx(grad_auto, abs=tol)
        assert grad_angle == pytest.approx(grad_auto, abs=tol)
    def test_qfunc_gradients(self, qubit_device_2_wires, tol):
        "Tests that the various ways of computing the gradient of a qfunc all agree."

        def circuit(x, y, z):
            qml.RX(x, wires=[0])
            qml.CNOT(wires=[0, 1])
            qml.RY(-1.6, wires=[0])
            qml.RY(y, wires=[1])
            qml.CNOT(wires=[1, 0])
            qml.RX(z, wires=[0])
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        qnode = to_autograd(QubitQNode(circuit, qubit_device_2_wires))
        params = np.array([0.1, -1.6, np.pi / 5])

        # manual gradients
        grad_fd1 = qnode.jacobian(params, method='F', options={'order': 1})
        grad_fd2 = qnode.jacobian(params, method='F', options={'order': 2})
        grad_angle = qnode.jacobian(params, method='A')

        # automatic gradient
        grad_fn = autograd.grad(qnode, argnum=[0, 1, 2])
        grad_auto = np.array([grad_fn(*params)])

        # gradients computed with different methods must agree
        assert grad_fd1 == pytest.approx(grad_fd2, abs=tol)
        assert grad_fd1 == pytest.approx(grad_angle, abs=tol)
        assert grad_fd1 == pytest.approx(grad_auto, abs=tol)
    def test_qnode_array_parameters_2_vector_return(self, qubit_device_2_wires,
                                                    tol):
        """Test that QNode can take arrays as input arguments, and that they interact properly with Autograd.
           Test case for a circuit that returns a 2-vector."""
        def circuit(dummy1, array, dummy2):
            qml.RY(0.5 * array[0, 1], wires=0)
            qml.RY(-0.5 * array[1, 1], wires=0)
            qml.RY(array[1, 0], wires=1)
            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1))

        node = to_autograd(QubitQNode(circuit, qubit_device_2_wires))

        args = (0.46, np.array([[2.0, 3.0, 0.3], [7.0, 4.0, 2.1]]), -0.13)
        grad_target = (
            np.array(1.0),
            np.array([[0.5, 0.43879, 0], [0, -0.43879, 0]]),
            np.array(-0.4),
        )
        cost_target = 1.03257

        def cost(x, array, y):
            c = node(0.111, array, 4.5)[0]
            return c + 0.5 * array[0, 0] + x - 0.4 * y

        cost_grad = autograd.grad(cost, argnum=[0, 1, 2])
        computed_grad = cost_grad(*args)

        assert cost(*args) == pytest.approx(cost_target, abs=tol)

        assert computed_grad[0] == pytest.approx(grad_target[0], abs=tol)
        assert computed_grad[1] == pytest.approx(grad_target[1], abs=tol)
        assert computed_grad[2] == pytest.approx(grad_target[2], abs=tol)
    def test_multiple_expectation_jacobian_positional(self, tol,
                                                      qubit_device_2_wires):
        """Tests that qnodes using positional arguments return
        correct gradients for multiple expectation values."""
        par = [0.5, 0.54, 0.3]

        def circuit(x, y, z):
            qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3),
                                 wires=[0, 1])
            qml.Rot(x, y, z, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))

        circuit = to_autograd(QubitQNode(circuit, qubit_device_2_wires))

        # compare our manual Jacobian computation to theoretical result
        expected_jac = self.expected_jacobian(*par)
        res = circuit.jacobian(par)
        assert expected_jac == pytest.approx(res, abs=tol)

        # compare our manual Jacobian computation to autograd
        # not sure if this is the intended usage of jacobian
        jac0 = autograd.jacobian(circuit, 0)
        jac1 = autograd.jacobian(circuit, 1)
        jac2 = autograd.jacobian(circuit, 2)
        res = np.stack([jac0(*par), jac1(*par), jac2(*par)]).T

        assert expected_jac == pytest.approx(res, abs=tol)

        #compare with what we get if argnum is a list
        jac = autograd.jacobian(circuit, argnum=[0, 1, 2])
예제 #17
0
    def test_gradient_repeated_gate_parameters(self, tol):
        """Tests that repeated use of a free parameter in a
        multi-parameter gate yield correct gradients."""
        par = [0.8, 1.3]

        def qf(x, y):
            qml.RX(np.pi / 4, wires=[0])
            qml.Rot(y, x, 2 * x, wires=[0])
            return qml.expval(qml.PauliX(0))

        dev = qml.device("default.qubit", wires=1)
        q = QubitQNode(qf, dev)
        grad_A = q.jacobian(par, method="A")
        grad_F = q.jacobian(par, method="F")

        # the different methods agree
        assert grad_A == pytest.approx(grad_F, abs=tol)
    def test_generator_no_expval(self, monkeypatch):
        """Test exception is raised if subcircuit contains an
        operation with generator object that is not an observable"""
        dev = qml.device("default.qubit", wires=1)

        def circuit(a):
            qml.RX(a, wires=0)
            return qml.expval(qml.PauliX(0))

        circuit = QubitQNode(circuit, dev)

        with monkeypatch.context() as m:
            m.setattr("pennylane.RX.generator", [qml.RX, 1])

            with pytest.raises(QuantumFunctionError,
                               match="no corresponding observable"):
                circuit.metric_tensor([1], only_construct=True)
    def test_interface_str(self, qubit_device_2_wires):
        """Test that the interface string is correctly identified
        as numpy"""
        def circuit(x, y, z):
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))

        circuit = to_autograd(QubitQNode(circuit, qubit_device_2_wires))
        assert circuit.interface == "autograd"
예제 #20
0
    def test_expval_and_variance(self, tol):
        """Test that the qnode works for a combination of expectation
        values and variances"""
        dev = qml.device("default.qubit", wires=3)

        def circuit(a, b, c):
            qml.RX(a, wires=0)
            qml.RY(b, wires=1)
            qml.CNOT(wires=[1, 2])
            qml.RX(c, wires=2)
            qml.CNOT(wires=[0, 1])
            qml.RZ(c, wires=2)
            return qml.var(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)), qml.var(
                qml.PauliZ(2))

        circuit = QubitQNode(circuit, dev)

        a = 0.54
        b = -0.423
        c = 0.123
        var = circuit(a, b, c)
        expected = np.array([
            np.sin(a)**2,
            np.cos(a) * np.cos(b),
            0.25 * (3 - 2 * np.cos(b)**2 * np.cos(2 * c) - np.cos(2 * b)),
        ])
        assert var == pytest.approx(expected, abs=tol)

        # # circuit jacobians
        gradA = circuit.jacobian([a, b, c], method="A")
        gradF = circuit.jacobian([a, b, c], method="F")
        expected = np.array([
            [2 * np.cos(a) * np.sin(a), -np.cos(b) * np.sin(a), 0],
            [
                0,
                -np.cos(a) * np.sin(b),
                0.5 *
                (2 * np.cos(b) * np.cos(2 * c) * np.sin(b) + np.sin(2 * b)),
            ],
            [0, 0, np.cos(b)**2 * np.sin(2 * c)],
        ]).T
        assert gradF == pytest.approx(expected, abs=tol)
        assert gradA == pytest.approx(expected, abs=tol)
예제 #21
0
    def test_gradient_parameters_inside_array(self, tol):
        """Tests that free parameters inside an array passed to
        an Operation yield correct gradients."""
        par = [0.8, 1.3]

        def qf(x, y):
            qml.RX(x, wires=[0])
            qml.RY(x, wires=[0])
            return qml.expval(qml.Hermitian(np.diag([y, 1]), 0))

        dev = qml.device("default.qubit", wires=1)
        q = QubitQNode(qf, dev)
        grad = q.jacobian(par)
        grad_F = q.jacobian(par, method="F")

        # par[0] can use the "A" method, par[1] cannot
        assert q.par_to_grad_method == {0: "A", 1: "F"}
        # the different methods agree
        assert grad == pytest.approx(grad_F, abs=tol)
예제 #22
0
    def test_differentiate_second_positional(self, tol):
        """Tests that the second positional arguments are differentiated."""
        def circuit3(a, b):
            qml.RX(b, wires=0)
            return qml.expval(qml.PauliZ(0))

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

        a = 0.7418
        b = -5.0
        circuit_output = circuit3(a, b)
        expected_output = np.cos(b)
        assert circuit_output == pytest.approx(expected_output, abs=tol)

        # circuit jacobians
        circuit_jacobian = circuit3.jacobian([a, b])
        expected_jacobian = np.array([[0, -np.sin(b)]])
        assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
예제 #23
0
    def test_involutory_variance(self, tol):
        """Tests qubit observable that are involutory"""
        def circuit(a):
            qml.RX(a, wires=0)
            return qml.var(qml.PauliZ(0))

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

        a = 0.54
        var = circuit(a)
        expected = 1 - np.cos(a)**2
        assert var == pytest.approx(expected, abs=tol)

        # circuit jacobians
        gradA = circuit.jacobian([a], method="A")
        gradF = circuit.jacobian([a], method="F")
        expected = 2 * np.sin(a) * np.cos(a)
        assert gradF == pytest.approx(expected, abs=tol)
        assert gradA == pytest.approx(expected, abs=tol)
예제 #24
0
    def test_differentiate_all_positional(self, tol):
        """Tests that all positional arguments are differentiated."""
        def circuit1(a, b, c):
            qml.RX(a, wires=0)
            qml.RX(b, wires=1)
            qml.RX(c, wires=2)
            return tuple(qml.expval(qml.PauliZ(idx)) for idx in range(3))

        dev = qml.device("default.qubit", wires=3)
        circuit1 = QubitQNode(circuit1, dev)

        vals = np.array([np.pi, np.pi / 2, np.pi / 3])
        circuit_output = circuit1(*vals)
        expected_output = np.cos(vals)
        assert circuit_output == pytest.approx(expected_output, abs=tol)

        # circuit jacobians
        circuit_jacobian = circuit1.jacobian(vals)
        expected_jacobian = -np.diag(np.sin(vals))
        assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
예제 #25
0
    def test_fanout(self, tol):
        """Tests qubit observable with repeated parameters"""
        def circuit(a):
            qml.RX(a, wires=0)
            qml.RY(a, wires=0)
            return qml.var(qml.PauliZ(0))

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

        a = 0.54
        var = circuit(a)
        expected = 0.5 * np.sin(a)**2 * (np.cos(2 * a) + 3)
        assert var == pytest.approx(expected, abs=tol)

        # circuit jacobians
        gradA = circuit.jacobian([a], method="A")
        gradF = circuit.jacobian([a], method="F")
        expected = 4 * np.sin(a) * np.cos(a)**3
        assert gradA == pytest.approx(expected, abs=tol)
        assert gradF == pytest.approx(expected, abs=tol)
예제 #26
0
    def test_Rot_gradient(self, theta, tol):
        """Tests that the automatic gradient of a arbitrary Euler-angle-parameterized gate is correct."""
        def circuit(x, y, z):
            qml.Rot(x, y, z, wires=[0])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=1)
        circuit = QubitQNode(circuit, dev)
        eye = np.eye(3)

        angle_inputs = np.array([theta, theta**3, np.sqrt(2) * theta])
        autograd_val = circuit.jacobian(angle_inputs)
        manualgrad_val = np.zeros((1, 3))

        for idx in range(3):
            onehot_idx = eye[idx]
            param1 = angle_inputs + np.pi / 2 * onehot_idx
            param2 = angle_inputs - np.pi / 2 * onehot_idx
            manualgrad_val[0, idx] = (circuit(*param1) - circuit(*param2)) / 2

        assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
예제 #27
0
    def test_keywordarg_not_differentiated(self, tol):
        """Tests that qnodes do not differentiate w.r.t. keyword arguments."""
        par = np.array([0.5, 0.54])

        def circuit1(weights, x=0.3):
            qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3),
                                 wires=[0, 1])
            qml.Rot(weights[0], weights[1], x, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))

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

        def circuit2(weights):
            qml.QubitStateVector(np.array([1, 0, 1, 1]) / np.sqrt(3),
                                 wires=[0, 1])
            qml.Rot(weights[0], weights[1], 0.3, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliY(1))

        circuit2 = QubitQNode(circuit2, dev)

        res1 = circuit1.jacobian([par])
        res2 = circuit2.jacobian([par])
        assert res1 == pytest.approx(res2, abs=tol)
예제 #28
0
    def test_non_involutory_variance(self, tol):
        """Tests a qubit Hermitian observable that is not involutory"""
        A = np.array([[4, -1 + 6j], [-1 - 6j, 2]])

        def circuit(a):
            qml.RX(a, wires=0)
            return qml.var(qml.Hermitian(A, 0))

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

        a = 0.54
        var = circuit(a)
        expected = (39 / 2) - 6 * np.sin(2 * a) + (35 / 2) * np.cos(2 * a)
        assert var == pytest.approx(expected, abs=tol)

        # circuit jacobians
        gradA = circuit.jacobian([a], method="A")
        gradF = circuit.jacobian([a], method="F")
        expected = -35 * np.sin(2 * a) - 12 * np.cos(2 * a)
        assert gradA == pytest.approx(expected, abs=tol)
        assert gradF == pytest.approx(expected, abs=tol)
예제 #29
0
    def test_differentiate_second_third_positional(self, tol):
        """Tests that the second and third positional arguments are differentiated."""
        def circuit4(a, b, c):
            qml.RX(b, wires=0)
            qml.RX(c, wires=1)
            return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

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

        a = 0.7418
        b = -5.0
        c = np.pi / 7
        circuit_output = circuit4(a, b, c)
        expected_output = np.array([np.cos(b), np.cos(c)])
        assert circuit_output == pytest.approx(expected_output, abs=tol)

        # circuit jacobians
        circuit_jacobian = circuit4.jacobian([a, b, c])
        expected_jacobian = np.array([[0.0, -np.sin(b), 0.0],
                                      [0.0, 0.0, -np.sin(c)]])
        assert circuit_jacobian == pytest.approx(expected_jacobian, abs=tol)
예제 #30
0
    def test_gradient_gate_with_multiple_parameters(self, tol):
        """Tests that gates with multiple free parameters yield correct gradients."""
        par = [0.5, 0.3, -0.7]

        def qf(x, y, z):
            qml.RX(0.4, wires=[0])
            qml.Rot(x, y, z, wires=[0])
            qml.RY(-0.2, wires=[0])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=1)
        q = QubitQNode(qf, dev)
        value = q(*par)
        grad_A = q.jacobian(par, method="A")
        grad_F = q.jacobian(par, method="F")

        # analytic method works for every parameter
        assert q.par_to_grad_method == {0: "A", 1: "A", 2: "A"}
        # 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 grad_A == pytest.approx(grad_F, abs=tol)