def test_first_order_observable(self, tol):
        """Test variance of a first order CV observable"""
        dev = qml.device("default.gaussian", wires=1)

        r = 0.543
        phi = -0.654

        with CVParamShiftTape() as tape:
            qml.Squeezing(r, 0, wires=0)
            qml.Rotation(phi, wires=0)
            var(qml.X(0))

        tape.trainable_params = {0, 2}

        res = tape.execute(dev)
        expected = np.exp(2 * r) * np.sin(phi) ** 2 + np.exp(-2 * r) * np.cos(phi) ** 2
        assert np.allclose(res, expected, atol=tol, rtol=0)

        # circuit jacobians
        grad_F = tape.jacobian(dev, method="numeric")
        grad_A = tape.jacobian(dev, method="analytic")
        expected = np.array(
            [
                [
                    2 * np.exp(2 * r) * np.sin(phi) ** 2 - 2 * np.exp(-2 * r) * np.cos(phi) ** 2,
                    2 * np.sinh(2 * r) * np.sin(2 * phi),
                ]
            ]
        )
        assert np.allclose(grad_A, expected, atol=tol, rtol=0)
        assert np.allclose(grad_F, expected, atol=tol, rtol=0)
Exemple #2
0
    def test_measurement_expansion(self):
        """Test that measurement expansion works as expected"""
        with QuantumTape() as tape:
            # expands into 2 PauliX
            qml.BasisState(np.array([1, 1]), wires=[0, "a"])
            qml.CNOT(wires=[0, "a"])
            qml.RY(0.2, wires="a")
            probs(wires=0)
            # expands into RY on wire b
            expval(qml.PauliZ("a") @ qml.Hadamard("b"))
            # expands into QubitUnitary on wire 0
            var(qml.Hermitian(np.array([[1, 2], [2, 4]]), wires=[0]))

        new_tape = tape.expand(expand_measurements=True)

        assert len(new_tape.operations) == 6

        expected = [
            qml.operation.Probability, qml.operation.Expectation,
            qml.operation.Variance
        ]
        assert [
            m.return_type is r for m, r in zip(new_tape.measurements, expected)
        ]

        expected = [None, None, None]
        assert [m.obs is r for m, r in zip(new_tape.measurements, expected)]

        expected = [None, [1, -1, -1, 1], [0, 5]]
        assert [
            m.eigvals is r for m, r in zip(new_tape.measurements, expected)
        ]
    def test_no_poly_xp_support_variance(self, mocker, monkeypatch, caplog):
        """Test that if a device does not support PolyXP
        and the variance parameter-shift rule is required,
        we fallback to finite differences."""
        dev = qml.device("default.gaussian", wires=1)

        monkeypatch.delitem(dev._observable_map, "PolyXP")

        with CVParamShiftTape() as tape:
            qml.Rotation(1.0, wires=[0])
            var(qml.X(0))

        tape.trainable_params = {0}
        assert tape.analytic_pd == tape.parameter_shift_var

        spy1 = mocker.spy(tape, "parameter_shift_first_order")
        spy2 = mocker.spy(tape, "parameter_shift_second_order")
        spy_numeric = mocker.spy(tape, "numeric_pd")

        with pytest.warns(UserWarning, match="does not support the PolyXP observable"):
            tape.jacobian(dev, method="analytic")

        spy1.assert_not_called()
        spy2.assert_not_called()
        spy_numeric.assert_called()
    def test_gradients_gaussian_circuit(self, op, obs, mocker, tol):
        """Tests that the gradients of circuits of gaussian gates match between the
        finite difference and analytic methods."""
        tol = 1e-2

        args = np.linspace(0.2, 0.5, op.num_params)

        with CVParamShiftTape() as tape:
            qml.Displacement(0.5, 0, wires=0)
            op(*args, wires=range(op.num_wires))
            qml.Beamsplitter(1.3, -2.3, wires=[0, 1])
            qml.Displacement(-0.5, 0.1, wires=0)
            qml.Squeezing(0.5, -1.5, wires=0)
            qml.Rotation(-1.1, wires=0)
            var(obs(wires=0))

        dev = qml.device("default.gaussian", wires=2)
        res = tape.execute(dev)

        tape._update_gradient_info()
        tape.trainable_params = set(range(2, 2 + op.num_params))

        # check that every parameter is analytic
        for i in range(op.num_params):
            assert tape._par_info[2 + i]["grad_method"][0] == "A"

        spy = mocker.spy(CVParamShiftTape, "parameter_shift_first_order")
        grad_F = tape.jacobian(dev, method="numeric")
        grad_A = tape.jacobian(dev, method="analytic")
        grad_A2 = tape.jacobian(dev, method="analytic", force_order2=True)

        assert np.allclose(grad_A2, grad_F, atol=tol, rtol=0)
        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
    def test_error_analytic_second_order(self):
        """Test exception raised if attempting to use a second
        order observable to compute the variance derivative analytically"""
        dev = qml.device("default.gaussian", wires=1)

        with CVParamShiftTape() as tape:
            qml.Displacement(1.0, 0, wires=0)
            var(qml.NumberOperator(0))

        tape.trainable_params = {0}

        with pytest.raises(ValueError, match=r"cannot be used with the argument\(s\) \{0\}"):
            tape.jacobian(dev, method="analytic")
Exemple #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)
            var(qml.PauliZ(0))

        with pytest.raises(ValueError, match="Variance is not supported"):
            tape.jacobian(dev)
Exemple #7
0
    def test_variance_gradients_agree_finite_differences(self, mocker, tol):
        """Tests that the variance parameter-shift rule agrees with the first and second
        order finite differences"""
        params = np.array([0.1, -1.6, np.pi / 5])

        with QubitParamShiftTape() as tape:
            qml.RX(params[0], wires=[0])
            qml.CNOT(wires=[0, 1])
            qml.RY(-1.6, wires=[0])
            qml.RY(params[1], wires=[1])
            qml.CNOT(wires=[1, 0])
            qml.RX(params[2], wires=[0])
            qml.CNOT(wires=[0, 1])
            expval(qml.PauliZ(0)), var(qml.PauliX(1))

        tape.trainable_params = {0, 2, 3}
        dev = qml.device("default.qubit", wires=2)

        spy_numeric = mocker.spy(tape, "numeric_pd")
        spy_analytic = mocker.spy(tape, "analytic_pd")

        grad_F1 = tape.jacobian(dev, method="numeric", order=1)
        grad_F2 = tape.jacobian(dev, method="numeric", order=2)

        spy_numeric.assert_called()
        spy_analytic.assert_not_called()

        grad_A = tape.jacobian(dev, method="analytic")
        spy_analytic.assert_called()

        # gradients computed with different methods must agree
        assert np.allclose(grad_A, grad_F1, atol=tol, rtol=0)
        assert np.allclose(grad_A, grad_F2, atol=tol, rtol=0)
    def test_displaced_thermal_mean_photon_variance(self, tol):
        """Test gradient of the photon variance of a displaced thermal state"""
        dev = qml.device("default.gaussian", wires=1)

        n = 0.12
        a = 0.105

        with CVParamShiftTape() as tape:
            qml.ThermalState(n, wires=0)
            qml.Displacement(a, 0, wires=0)
            var(qml.TensorN(wires=[0]))

        tape.trainable_params = {0, 1}
        grad = tape.jacobian(dev)
        expected = np.array([2 * a ** 2 + 2 * n + 1, 2 * a * (2 * n + 1)])
        assert np.allclose(grad, expected, atol=tol, rtol=0)
Exemple #9
0
    def test_involutory_and_noninvolutory_variance(self, mocker, tol):
        """Tests a qubit Hermitian observable that is not involutory alongside
        a involutory observable."""
        spy_analytic_var = mocker.spy(QubitParamShiftTape,
                                      "parameter_shift_var")
        spy_numeric = mocker.spy(QubitParamShiftTape, "numeric_pd")
        spy_execute = mocker.spy(QubitParamShiftTape, "execute_device")

        dev = qml.device("default.qubit", wires=2)
        A = np.array([[4, -1 + 6j], [-1 - 6j, 2]])
        a = 0.54

        with QubitParamShiftTape() as tape:
            qml.RX(a, wires=0)
            qml.RX(a, wires=1)
            var(qml.PauliZ(0))
            var(qml.Hermitian(A, 1))

        tape.trainable_params = {0, 1}

        res = tape.execute(dev)
        expected = [
            1 - np.cos(a)**2,
            (39 / 2) - 6 * np.sin(2 * a) + (35 / 2) * np.cos(2 * a)
        ]
        assert np.allclose(res, expected, atol=tol, rtol=0)

        spy_execute.call_args_list = []

        # circuit jacobians
        gradA = tape.jacobian(dev, method="analytic")
        spy_analytic_var.assert_called()
        spy_numeric.assert_not_called()
        assert len(spy_execute.call_args_list) == 1 + 2 * 4

        spy_execute.call_args_list = []

        gradF = tape.jacobian(dev, method="numeric")
        spy_numeric.assert_called()
        assert len(spy_execute.call_args_list) == 1 + 2

        expected = [
            2 * np.sin(a) * np.cos(a), -35 * np.sin(2 * a) - 12 * np.cos(2 * a)
        ]
        assert np.diag(gradA) == pytest.approx(expected, abs=tol)
        assert np.diag(gradF) == pytest.approx(expected, abs=tol)
Exemple #10
0
    def test_var_expectation_values(self, tol):
        """Tests correct output shape and evaluation for a tape
        with expval and var outputs"""
        dev = qml.device("default.qubit", wires=2)
        x = 0.543
        y = -0.654

        with QubitParamShiftTape() as tape:
            qml.RX(x, wires=[0])
            qml.RY(y, wires=[1])
            qml.CNOT(wires=[0, 1])
            expval(qml.PauliZ(0))
            var(qml.PauliX(1))

        res = tape.jacobian(dev, method="analytic")
        assert res.shape == (2, 2)

        expected = np.array([[-np.sin(x), 0], [0, -2 * np.cos(y) * np.sin(y)]])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Exemple #11
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)

        a = 0.54
        b = -0.423
        c = 0.123

        with QubitParamShiftTape() as tape:
            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])
            var(qml.PauliZ(0))
            expval(qml.PauliZ(1))
            var(qml.PauliZ(2))

        res = tape.execute(dev)
        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 np.allclose(res, expected, atol=tol, rtol=0)

        # # circuit jacobians
        gradA = tape.jacobian(dev, method="analytic")
        gradF = tape.jacobian(dev, method="numeric")
        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 gradA == pytest.approx(expected, abs=tol)
        assert gradF == pytest.approx(expected, abs=tol)
    def test_squeezed_mean_photon_variance(self, tol):
        """Test gradient of the photon variance of a displaced thermal state"""
        dev = qml.device("default.gaussian", wires=1)

        r = 0.12
        phi = 0.105

        with CVParamShiftTape() as tape:
            qml.Squeezing(r, 0, wires=0)
            qml.Rotation(phi, wires=0)
            var(qml.X(wires=[0]))

        tape.trainable_params = {0, 2}
        grad = tape.jacobian(dev, method="analytic")
        expected = np.array(
            [
                2 * np.exp(2 * r) * np.sin(phi) ** 2 - 2 * np.exp(-2 * r) * np.cos(phi) ** 2,
                2 * np.sinh(2 * r) * np.sin(2 * phi),
            ]
        )
        assert np.allclose(grad, expected, atol=tol, rtol=0)
    def test_variance(self):
        """If the variance of the observable is first order, then
        parameter-shift is supported. If the observable is second order,
        however, only finite-differences is supported."""

        with CVParamShiftTape() as tape:
            qml.Rotation(1.0, wires=[0])
            var(qml.P(0))  # first order

        assert tape._grad_method(0) == "A"

        with CVParamShiftTape() as tape:
            qml.Rotation(1.0, wires=[0])
            var(qml.NumberOperator(0))  # second order

        assert tape._grad_method(0) == "F"

        with CVParamShiftTape() as tape:
            qml.Rotation(1.0, wires=[0])
            qml.Rotation(1.0, wires=[1])
            qml.Beamsplitter(0.5, 0.0, wires=[0, 1])
            var(qml.NumberOperator(0))  # second order
            expval(qml.NumberOperator(1))

        assert tape._grad_method(0) == "F"
        assert tape._grad_method(1) == "F"
        assert tape._grad_method(2) == "F"
        assert tape._grad_method(3) == "F"
    def test_second_order_cv(self, tol):
        """Test variance of a second order CV expectation value"""
        dev = qml.device("default.gaussian", wires=1)

        n = 0.12
        a = 0.765

        with CVParamShiftTape() as tape:
            qml.ThermalState(n, wires=0)
            qml.Displacement(a, 0, wires=0)
            var(qml.NumberOperator(0))

        tape.trainable_params = {0, 1}

        res = tape.execute(dev)
        expected = n ** 2 + n + np.abs(a) ** 2 * (1 + 2 * n)
        assert np.allclose(res, expected, atol=tol, rtol=0)

        # circuit jacobians
        grad_F = tape.jacobian(dev, method="numeric")
        expected = np.array([[2 * a ** 2 + 2 * n + 1, 2 * a * (2 * n + 1)]])
        assert np.allclose(grad_F, expected, atol=tol, rtol=0)
    def test_expval_and_variance(self, tol):
        """Test that the gradient works for a combination of CV expectation
        values and variances"""
        dev = qml.device("default.gaussian", wires=3)

        a, b = [0.54, -0.423]

        with CVParamShiftTape() as tape:
            qml.Displacement(0.5, 0, wires=0)
            qml.Squeezing(a, 0, wires=0)
            qml.Squeezing(b, 0, wires=1)
            qml.Beamsplitter(0.6, -0.3, wires=[0, 1])
            qml.Squeezing(-0.3, 0, wires=2)
            qml.Beamsplitter(1.4, 0.5, wires=[1, 2])
            var(qml.X(0))
            expval(qml.X(1))
            var(qml.X(2))

        tape.trainable_params = {2, 4}

        # jacobians must match
        grad_F = tape.jacobian(dev, method="numeric")
        grad_A = tape.jacobian(dev, method="analytic")
        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
Exemple #16
0
    def test_involutory_variance(self, mocker, tol):
        """Tests qubit observable that are involutory"""
        spy_analytic_var = mocker.spy(QubitParamShiftTape,
                                      "parameter_shift_var")
        spy_numeric = mocker.spy(QubitParamShiftTape, "numeric_pd")
        spy_execute = mocker.spy(QubitParamShiftTape, "execute_device")

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

        with QubitParamShiftTape() as tape:
            qml.RX(a, wires=0)
            var(qml.PauliZ(0))

        res = tape.execute(dev)
        expected = 1 - np.cos(a)**2
        assert np.allclose(res, expected, atol=tol, rtol=0)

        spy_execute.call_args_list = []

        # circuit jacobians
        gradA = tape.jacobian(dev, method="analytic")
        spy_analytic_var.assert_called()
        spy_numeric.assert_not_called()
        assert len(spy_execute.call_args_list) == 1 + 2 * 1

        spy_execute.call_args_list = []

        gradF = tape.jacobian(dev, method="numeric")
        spy_numeric.assert_called()
        assert len(spy_execute.call_args_list) == 2

        expected = 2 * np.sin(a) * np.cos(a)

        assert gradF == pytest.approx(expected, abs=tol)
        assert gradA == pytest.approx(expected, abs=tol)
Exemple #17
0
    def test_tensor_observables_tensor_matmul(self):
        """Test that tensor observables are correctly processed from the annotated
        queue". Here, wetest multiple tensor observables constructed via matmul
        between two tensor observables."""

        with QuantumTape() as tape:
            op = qml.RX(1.0, wires=0)
            t_obs1 = qml.PauliZ(0) @ qml.PauliX(1)
            t_obs2 = qml.PauliY(2) @ qml.PauliZ(3)
            t_obs = t_obs1 @ t_obs2
            m = var(t_obs)

        assert tape.operations == [op]
        assert tape.observables == [t_obs]
        assert tape.measurements[0].return_type is qml.operation.Variance
        assert tape.measurements[0].obs is t_obs