Esempio n. 1
0
def mitigate_depolarizing_noise(K, num_wires, method, use_entries=None):
    r"""Estimate depolarizing noise rate(s) using on the diagonal entries of a kernel
    matrix and mitigate the noise, assuming a global depolarizing noise model.

    Args:
        K (array[float]): Noisy kernel matrix.
        num_wires (int): Number of wires/qubits of the quantum embedding kernel.
        method (``'single'`` | ``'average'`` | ``'split_channel'``): Strategy for mitigation

            * ``'single'``: An alias for ``'average'`` with ``len(use_entries)=1``.
            * ``'average'``: Estimate a global noise rate based on the average of the diagonal
              entries in ``use_entries``, which need to be measured on the quantum computer.
            * ``'split_channel'``: Estimate individual noise rates per embedding, requiring
              all diagonal entries to be measured on the quantum computer.
        use_entries (array[int]): Diagonal entries to use if method in ``['single', 'average']``.
            If ``None``, defaults to ``[0]`` (``'single'``) or ``range(len(K))`` (``'average'``).

    Returns:
        array[float]: Mitigated kernel matrix.

    Reference:
        This method is introduced in Section V in
        `arXiv:2105.02276 <https://arxiv.org/abs/2105.02276>`_.

    **Example:**

    For an example usage of ``mitigate_depolarizing_noise`` please refer to the
    `PennyLane demo on the kernel module <https://github.com/PennyLaneAI/qml/tree/master/demonstrations/tutorial_kernel_module.py>`_ or `the postprocessing demo for arXiv:2105.02276 <https://github.com/thubregtsen/qhack/blob/master/paper/post_processing_demo.py>`_.
    """
    dim = 2**num_wires

    if method == "single":
        if use_entries is None:
            use_entries = (0, )
        diagonal_element = K[use_entries[0], use_entries[0]]
        noise_rate = (1 - diagonal_element) * dim / (dim - 1)
        mitigated_matrix = (K - noise_rate / dim) / (1 - noise_rate)

    elif method == "average":
        if use_entries is None:
            diagonal_elements = np.diag(K)
        else:
            diagonal_elements = np.diag(K)[np.array(use_entries)]
        noise_rates = (1 - diagonal_elements) * dim / (dim - 1)
        mean_noise_rate = np.mean(noise_rates)
        mitigated_matrix = (K - mean_noise_rate / dim) / (1 - mean_noise_rate)

    elif method == "split_channel":
        eff_noise_rates = np.clip((1 - np.diag(K)) * dim / (dim - 1), 0.0, 1.0)
        noise_rates = 1 - np.sqrt(1 - eff_noise_rates)
        inverse_noise = (-np.outer(noise_rates, noise_rates) +
                         noise_rates.reshape(
                             (1, len(K))) + noise_rates.reshape((len(K), 1)))
        mitigated_matrix = (K - inverse_noise / dim) / (1 - inverse_noise)

    return mitigated_matrix
    def test_evaluate_diag_metric_tensor_classical_processing(self, tol):
        """Test that a diagonal metric tensor evaluates correctly
        when the QNode includes classical processing."""
        dev = qml.device("default.qubit", wires=2)

        def circuit(a, b):
            # The classical processing function is
            #     f: ([a0, a1], b) -> (a1, a0, b)
            # So the classical Jacobians will be a permutation matrix and an identity matrix:
            #     classical_jacobian(circuit)(a, b) == ([[0, 1], [1, 0]], [[1]])
            qml.RX(a[1], wires=0)
            qml.RY(a[0], wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(b, wires=1)
            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1))

        circuit = qml.QNode(circuit, dev)

        a = np.array([0.432, 0.1])
        b = 0.12

        # evaluate metric tensor
        g = qml.metric_tensor(circuit, approx="block-diag")(a, b)
        assert isinstance(g, tuple)
        assert len(g) == 2
        assert g[0].shape == (len(a), len(a))
        assert g[1].shape == tuple()

        # check that the metric tensor is correct
        expected = np.array([np.cos(a[1]) ** 2, 1]) / 4
        assert np.allclose(g[0], np.diag(expected), atol=tol, rtol=0)

        expected = (3 - 2 * np.cos(a[1]) ** 2 * np.cos(2 * a[0]) - np.cos(2 * a[1])) / 16
        assert np.allclose(g[1], expected, atol=tol, rtol=0)
Esempio n. 3
0
    def test_polyxp(self, tol):
        """Test that PolyXP works as expected"""
        cutoff_dim = 12
        a = 0.14321
        nbar = 0.2234

        hbar = 2
        dev = qml.device("strawberryfields.fock", wires=1, hbar=hbar, cutoff_dim=cutoff_dim)
        Q = np.array([0, 1, 0])  # x expectation

        @qml.qnode(dev)
        def circuit(x):
            qml.Displacement(x, 0, wires=0)
            return qml.expval(qml.PolyXP(Q, 0))

        # test X expectation
        assert np.allclose(circuit(a), hbar * a, atol=tol, rtol=0)

        Q = np.diag([-0.5, 1 / (2 * hbar), 1 / (2 * hbar)])  # mean photon number

        @qml.qnode(dev)
        def circuit(x):
            qml.ThermalState(nbar, wires=0)
            qml.Displacement(x, 0, wires=0)
            return qml.expval(qml.PolyXP(Q, 0))

        # test X expectation
        assert np.allclose(circuit(a), nbar + np.abs(a) ** 2, atol=tol, rtol=0)
Esempio n. 4
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)
    def test_warning(self, tol):
        """Test that a warning is emitted"""
        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev)
        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))

        a = 0.432
        b = 0.12
        c = -0.432

        # evaluate metric tensor
        with pytest.warns(UserWarning, match="has been deprecated"):
            g = circuit.metric_tensor(a, b, c, approx="block-diag")

        # 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_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 = qml.QNode(circuit, dev)

        a = 0.432
        b = 0.12
        c = -0.432

        # evaluate metric tensor
        g = qml.metric_tensor(circuit, approx="block-diag")(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)
Esempio n. 7
0
    def test_polyxp(self):
        """Test that PolyXP works as expected"""
        self.logTestName()

        cutoff_dim = 12
        a = 0.14321
        nbar = 0.2234

        hbar = 2
        dev = qml.device('strawberryfields.fock',
                         wires=1,
                         hbar=hbar,
                         cutoff_dim=cutoff_dim)
        Q = np.array([0, 1, 0])  # x expectation

        @qml.qnode(dev)
        def circuit(x):
            qml.Displacement(x, 0, wires=0)
            return qml.expval(qml.PolyXP(Q, 0))

        # test X expectation
        self.assertAlmostEqual(circuit(a), hbar * a)

        Q = np.diag([-0.5, 1 / (2 * hbar),
                     1 / (2 * hbar)])  # mean photon number

        @qml.qnode(dev)
        def circuit(x):
            qml.ThermalState(nbar, wires=0)
            qml.Displacement(x, 0, wires=0)
            return qml.expval(qml.PolyXP(Q, 0))

        # test X expectation
        self.assertAlmostEqual(circuit(a), nbar + np.abs(a)**2)
Esempio n. 8
0
    def test_supported_gates(self):
        """Test that all supported gates work correctly"""
        self.logTestName()
        a = 0.312

        dev = qml.device('default.gaussian', wires=2)

        for g, qop in dev._operation_map.items():
            log.debug('\tTesting gate %s...', g)
            self.assertTrue(dev.supported(g))
            dev.reset()

            op = getattr(qml.ops, g)
            if op.num_wires == 0:
                wires = list(range(2))
            else:
                wires = list(range(op.num_wires))

            @qml.qnode(dev)
            def circuit(*x):
                """Reference quantum function"""
                qml.Displacement(a, 0, wires=[0])
                op(*x, wires=wires)
                return qml.expval.X(0)

            # compare to reference result
            def reference(*x):
                """reference circuit"""
                if g == 'GaussianState':
                    return x[0][0]

                if g == 'Displacement':
                    alpha = x[0] * np.exp(1j * x[1])
                    return (alpha + a).real * np.sqrt(2 * hbar)

                if 'State' in g:
                    mu, _ = qop(*x, hbar=hbar)
                    return mu[0]

                S = qop(*x)

                # calculate the expected output
                if op.num_wires == 1:
                    S = block_diag(S,
                                   np.identity(2))[:,
                                                   [0, 2, 1, 3]][[0, 2, 1, 3]]

                return (S @ np.array([a.real, a.imag, 0, 0]) *
                        np.sqrt(2 * hbar))[0]

            if g == 'GaussianState':
                p = [
                    np.array([0.432, 0.123, 0.342, 0.123]),
                    np.diag([0.5234] * 4)
                ]
            else:
                p = [0.432423, -0.12312, 0.324][:op.num_params]

            self.assertAllEqual(circuit(*p), reference(*p))
Esempio n. 9
0
def prepare_and_sample(weights):

    # Variational circuit generating a guess for the solution vector |x>
    variational_block(weights)

    # We assume that the system is measured in the computational basis.
    # If we label each basis state with a decimal integer j = 0, 1, ... 2 ** n_qubits - 1,
    # this is equivalent to a measurement of the following diagonal observable.
    basis_obs = qml.Hermitian(np.diag(range(2 ** n_qubits)), wires=range(n_qubits))

    return qml.sample(basis_obs)
 def setUp(self):
     self.fnames = ['test_function_1', 'test_function_2', 'test_function_3']
     self.univariate_funcs = [
         np.sin, lambda x: np.exp(x / 10.), lambda x: x**2
     ]
     self.grad_uni_fns = [
         np.cos, lambda x: np.exp(x / 10.) / 10., lambda x: 2 * x
     ]
     self.multivariate_funcs = [
         lambda x: np.sin(x[0]) + np.cos(x[1]),
         lambda x: np.exp(x[0] / 3) * np.tanh(x[1]),
         lambda x: np.sum(x_**2 for x_ in x)
     ]
     self.grad_multi_funcs = [
         lambda x: np.array([np.cos(x[0]), -np.sin(x[1])]),
         lambda x: np.array([
             np.exp(x[0] / 3) / 3 * np.tanh(x[1]),
             np.exp(x[0] / 3) * (1 - np.tanh(x[1])**2)
         ]), lambda x: np.array([2 * x_ for x_ in x])
     ]
     self.mvar_mdim_funcs = [
         lambda x: np.sin(x[0, 0]) + np.cos(x[1, 0]),
         lambda x: np.exp(x[0, 0] / 3) * np.tanh(x[1, 0]),
         lambda x: np.sum(x_[0]**2 for x_ in x)
     ]
     self.grad_mvar_mdim_funcs = [
         lambda x: np.array([[np.cos(x[0, 0])], [-np.sin(x[[1]])]]),
         lambda x: np.array([[np.exp(x[0, 0] / 3) / 3 * np.tanh(x[
             1, 0])], [np.exp(x[0, 0] / 3) * (1 - np.tanh(x[1, 0])**2)]]),
         lambda x: np.array([[2 * x_[0]] for x_ in x])
     ]
     self.margs_fns = [
         lambda x, y: np.sin(x) + np.cos(y),
         lambda x, y: np.exp(x / 3) * np.tanh(y),
         lambda x, y: np.sum(x_**2 for x_ in [x, y])
     ]
     self.grad_margs_funcs = [
         lambda x, y: (np.cos(x), -np.sin(y)), lambda x, y:
         (np.exp(x / 3) / 3 * np.tanh(y), np.exp(x / 3) *
          (1 - np.tanh(y)**2)), lambda x, y: (2 * x, 2 * y)
     ]
     self.margs_mdim_fns = [
         lambda x, y: (np.sin(x), np.cos(y)), lambda x, y:
         (np.exp(x / 3) * np.tanh(y), np.sinh(x * y)), lambda x, y:
         (x**2 + y**2, x * y)
     ]
     self.grad_margs_mdim_funcs = [
         lambda x, y: np.diag([np.cos(x), -np.sin(y)]),
         lambda x, y: np.array([[
             np.exp(x / 3) / 3 * np.tanh(y),
             np.exp(x / 3) * np.sech(y)**2
         ], [np.cosh(x * y) * y, np.cosh(x * y) * x]]),
         lambda x, y: np.array([[2 * x, 2 * y], [y, x]])
     ]
Esempio n. 11
0
    def test_squeezing(self):
        """Test the squeezing symplectic transform."""
        self.logTestName()

        r = 0.543
        phi = 0.123
        S = squeezing(r, phi)

        # apply to an identity covariance matrix
        out = S @ S.T
        expected = rotation(phi/2) @ np.diag(np.exp([-2*r, 2*r])) @ rotation(phi/2).T
        self.assertAllAlmostEqual(out, expected, delta=self.tol)
Esempio n. 12
0
    def test_z_rotation(self, tol):
        """Test z rotation is correct"""

        # test identity for theta=0
        assert np.allclose(Rotz(0), np.identity(2), atol=tol, rtol=0)

        # test identity for theta=pi/2
        expected = np.diag(np.exp([-1j * np.pi / 4, 1j * np.pi / 4]))
        assert np.allclose(Rotz(np.pi / 2), expected, atol=tol, rtol=0)

        # test identity for theta=pi
        assert np.allclose(Rotz(np.pi), -1j * Z, atol=tol, rtol=0)
Esempio n. 13
0
    def test_single_qubit_vqe_using_expval_h_multiple_input_params(
            self, tol, recwarn):
        """Test single-qubit VQE by returning qml.expval(H) in the QNode and
        check for the correct QNG value every step, the correct parameter updates, and
        correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)
        coeffs = [1, 1]
        obs_list = [qml.PauliX(0), qml.PauliZ(0)]

        H = qml.Hamiltonian(coeffs=coeffs, observables=obs_list)

        @qml.qnode(dev)
        def circuit(x, y, wires=0):
            qml.RX(x, wires=wires)
            qml.RY(y, wires=wires)
            return qml.expval(H)

        eta = 0.01
        x = np.array(0.011, requires_grad=True)
        y = np.array(0.022, requires_grad=True)

        def gradient(params):
            """Returns the gradient"""
            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
            return np.array([da, db])

        eta = 0.01
        num_steps = 200

        opt = qml.QNGOptimizer(eta)

        # optimization for 200 steps total
        for t in range(num_steps):
            theta = np.array([x, y])
            x, y = opt.step(circuit, x, y)

            # check metric tensor
            res = opt.metric_tensor
            exp = np.diag([0.25, (np.cos(x)**2) / 4])
            assert np.allclose(res, exp, atol=0.00001, rtol=0)

            # check parameter update
            theta_new = np.array([x, y])
            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
            assert np.allclose(dtheta,
                               theta - theta_new,
                               atol=0.000001,
                               rtol=0)

        # check final cost
        assert np.allclose(circuit(x, y), -1.41421356, atol=tol, rtol=0)
        assert len(recwarn) == 0
    def test_hermitian(self, theta, phi, varphi, monkeypatch, tol):
        """Test that a tensor product involving qml.Hermitian works correctly"""
        dev = qml.device("default.tensor", wires=3)
        dev.reset()
        dev.apply("RX", wires=[0], par=[theta])
        dev.apply("RX", wires=[1], par=[phi])
        dev.apply("RX", wires=[2], par=[varphi])
        dev.apply("CNOT", wires=[0, 1], par=[])
        dev.apply("CNOT", wires=[1, 2], par=[])

        A = np.array([
            [-6, 2 + 1j, -3, -5 + 2j],
            [2 - 1j, 0, 2 - 1j, -5 + 4j],
            [-3, 2 + 1j, 0, -4 + 3j],
            [-5 - 2j, -5 - 4j, -4 - 3j, -6],
        ])

        with monkeypatch.context() as m:
            m.setattr("numpy.random.choice", lambda x, y, p: (x, p))
            s1, p = dev.sample(["PauliZ", "Hermitian"], [[0], [1, 2]],
                               [[], [A]])

        # s1 should only contain the eigenvalues of
        # the hermitian matrix tensor product Z
        Z = np.diag([1, -1])
        eigvals = np.linalg.eigvalsh(np.kron(Z, A))
        assert set(np.round(s1, 8)).issubset(set(np.round(eigvals, 8)))

        mean = s1 @ p
        expected = 0.5 * (-6 * np.cos(theta) *
                          (np.cos(varphi) + 1) - 2 * np.sin(varphi) *
                          (np.cos(theta) + np.sin(phi) - 2 * np.cos(phi)) +
                          3 * np.cos(varphi) * np.sin(phi) + np.sin(phi))
        assert np.allclose(mean, expected, atol=tol, rtol=0)

        var = (s1**2) @ p - (s1 @ p).real**2
        expected = (
            1057 - np.cos(2 * phi) + 12 *
            (27 + np.cos(2 * phi)) * np.cos(varphi) -
            2 * np.cos(2 * varphi) * np.sin(phi) *
            (16 * np.cos(phi) + 21 * np.sin(phi)) + 16 * np.sin(2 * phi) - 8 *
            (-17 + np.cos(2 * phi) + 2 * np.sin(2 * phi)) * np.sin(varphi) -
            8 * np.cos(2 * theta) *
            (3 + 3 * np.cos(varphi) + np.sin(varphi))**2 - 24 * np.cos(phi) *
            (np.cos(phi) + 2 * np.sin(phi)) * np.sin(2 * varphi) -
            8 * np.cos(theta) *
            (4 * np.cos(phi) *
             (4 + 8 * np.cos(varphi) + np.cos(2 * varphi) -
              (1 + 6 * np.cos(varphi)) * np.sin(varphi)) + np.sin(phi) *
             (15 + 8 * np.cos(varphi) - 11 * np.cos(2 * varphi) +
              42 * np.sin(varphi) + 3 * np.sin(2 * varphi)))) / 16
        assert np.allclose(var, expected, atol=tol, rtol=0)
Esempio n. 15
0
    def test_z_rotation(self):
        """Test z rotation is correct"""
        self.logTestName()

        # test identity for theta=0
        self.assertAllAlmostEqual(Rotz(0), np.identity(2), delta=self.tol)

        # test identity for theta=pi/2
        expected = np.diag(np.exp([-1j * np.pi / 4, 1j * np.pi / 4]))
        self.assertAllAlmostEqual(Rotz(np.pi / 2), expected, delta=self.tol)

        # test identity for theta=pi
        self.assertAllAlmostEqual(Rotz(np.pi), -1j * Z, delta=self.tol)
Esempio n. 16
0
    def test_single_qubit_vqe(self, tol):
        """Test single-qubit VQE has the correct QNG value
        every step, the correct parameter updates,
        and correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)

        def circuit(params, wires=0):
            qml.RX(params[0], wires=wires)
            qml.RY(params[1], wires=wires)

        coeffs = [1, 1]
        obs_list = [
            qml.PauliX(0),
            qml.PauliZ(0)
        ]

        qnodes = qml.map(circuit, obs_list, dev, measure='expval')
        cost_fn = qml.dot(coeffs, qnodes)

        def gradient(params):
            """Returns the gradient"""
            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
            return np.array([da, db])

        eta = 0.01
        init_params = np.array([0.011, 0.012])
        num_steps = 200

        opt = qml.QNGOptimizer(eta)
        theta = init_params

        # optimization for 200 steps total
        for t in range(num_steps):
            theta_new = opt.step(cost_fn, theta,
                                 metric_tensor_fn=qnodes.qnodes[0].metric_tensor)

            # check metric tensor
            res = opt.metric_tensor
            exp = np.diag([0.25, (np.cos(theta[0]) ** 2)/4])
            assert np.allclose(res, exp, atol=tol, rtol=0)

            # check parameter update
            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
            assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0)

            theta = theta_new

        # check final cost
        assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
Esempio n. 17
0
    def test_involutory_and_noninvolutory_variance(self, tol):
        """Tests a qubit Hermitian observable that is not involutory alongside
        an involutory observable."""
        dev = qml.device("default.qubit", wires=2)
        A = np.array([[4, -1 + 6j], [-1 - 6j, 2]])
        a = 0.54

        with qml.tape.JacobianTape() as tape:
            qml.RX(a, wires=0)
            qml.RX(a, wires=1)
            qml.var(qml.PauliZ(0))
            qml.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)

        # circuit jacobians
        tapes, fn = qml.gradients.param_shift(tape)
        gradA = fn(dev.batch_execute(tapes))
        assert len(tapes) == 1 + 2 * 4

        tapes, fn = qml.gradients.finite_diff(tape)
        gradF = fn(dev.batch_execute(tapes))
        assert len(tapes) == 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)
Esempio n. 18
0
    def test_single_qubit_vqe_using_vqecost(self, tol, recwarn):
        """Test single-qubit VQE using ExpvalCost
        has the correct QNG value every step, the correct parameter updates,
        and correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)

        def circuit(params, wires=0):
            qml.RX(params[0], wires=wires)
            qml.RY(params[1], wires=wires)

        coeffs = [1, 1]
        obs_list = [qml.PauliX(0), qml.PauliZ(0)]

        h = qml.Hamiltonian(coeffs=coeffs, observables=obs_list)

        cost_fn = qml.ExpvalCost(ansatz=circuit, hamiltonian=h, device=dev)

        def gradient(params):
            """Returns the gradient"""
            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
            return np.array([da, db])

        eta = 0.01
        init_params = np.array([0.011, 0.012], requires_grad=True)
        num_steps = 200

        opt = qml.QNGOptimizer(eta)
        theta = init_params

        # optimization for 200 steps total
        for t in range(num_steps):
            theta_new = opt.step(cost_fn, theta)

            # check metric tensor
            res = opt.metric_tensor
            exp = np.diag([0.25, (np.cos(theta[0])**2) / 4])
            assert np.allclose(res, exp, atol=tol, rtol=0)

            # check parameter update
            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
            assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0)

            theta = theta_new

        # check final cost
        assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
        assert len(recwarn) == 0
Esempio n. 19
0
    def test_multiple_rx_gradient_pauliz(self, tol, dev):
        """Tests that the gradient of multiple RX gates in a circuit yields the correct result."""
        params = np.array([np.pi, np.pi / 2, np.pi / 3])

        with qml.tape.QuantumTape() 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 jacobians
        dev_jacobian = dev.adjoint_jacobian(tape)
        expected_jacobian = -np.diag(np.sin(params))
        assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0)
Esempio n. 20
0
    def test_qubit_rotation(self, tol):
        """Test qubit rotation has the correct QNG value
        every step, the correct parameter updates,
        and correct cost after 200 steps"""
        dev = qml.device("default.qubit", wires=1)

        @qml.qnode(dev)
        def circuit(params):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            return qml.expval(qml.PauliZ(0))

        def gradient(params):
            """Returns the gradient of the above circuit"""
            da = -np.sin(params[0]) * np.cos(params[1])
            db = -np.cos(params[0]) * np.sin(params[1])
            return np.array([da, db])

        eta = 0.01
        init_params = np.array([0.011, 0.012])
        num_steps = 200

        opt = qml.QNGOptimizer(eta)
        theta = init_params

        # optimization for 200 steps total
        for t in range(num_steps):
            theta_new = opt.step(circuit, theta)

            # check metric tensor
            res = opt.metric_tensor_inv
            exp = np.diag([4, 4 / (np.cos(theta[0])**2)])
            assert np.allclose(res, exp, atol=tol, rtol=0)

            # check parameter update
            dtheta = eta * exp @ gradient(theta)
            assert np.allclose(dtheta, theta - theta_new, atol=tol, rtol=0)

            theta = theta_new

        # check final cost
        assert np.allclose(circuit(theta), -0.9963791, atol=tol, rtol=0)
Esempio n. 21
0
    def test_multiple_rx_gradient_hermitian(self, tol, dev):
        """Tests that the gradient of multiple RX gates in a circuit yields the correct result
        with Hermitian observable
        """
        params = np.array([np.pi, np.pi / 2, np.pi / 3])

        with qml.tape.QuantumTape() 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.Hermitian([[1, 0], [0, -1]], wires=[idx]))

        tape.trainable_params = {0, 1, 2}
        # circuit jacobians
        dev_jacobian = dev.adjoint_jacobian(tape)
        expected_jacobian = -np.diag(np.sin(params))

        assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0)
Esempio n. 22
0
    def test_hermitian_diagonalizing_gates_integration(self, observable,
                                                       eigvals, eigvecs, tol):
        """Tests that the diagonalizing_gates method of the Hermitian class contains contains a gate that diagonalizes the
        given observable."""
        num_wires = 2

        tensor_obs = np.kron(observable, observable)
        eigvals = np.kron(eigvals, eigvals)

        dev = qml.device('default.qubit', wires=num_wires)

        diag_gates = qml.Hermitian.diagonalizing_gates(tensor_obs,
                                                       wires=list(
                                                           range(num_wires)))

        assert len(diag_gates) == 1

        U = diag_gates[0].parameters[0]
        x = U @ tensor_obs @ U.conj().T
        assert np.allclose(np.diag(np.sort(eigvals)), x, atol=tol, rtol=0)
def get_model_data(fun, params):
    """Computes the coefficients for the classical model, E^(A), E^(B), E^(C), and E^(D).
    Args:
        fun (callable): Cost function.
        params (array[float]): Parameter position theta_0 at which to build the model.
    Returns:
        E_A (float): Coefficients E^(A)
        E_B (array[float]): Coefficients E^(B)
        E_C (array[float]): Coefficients E^(C)
        E_D (array[float]): Coefficients E^(D) (upper right triangular entries)
    """
    num_params = len(params)
    E_A = fun(params)
    # E_B contains the gradient.
    E_B = qml.grad(fun)(params)
    hessian = qml.jacobian(qml.grad(fun))(params)
    # E_C contains the slightly adapted diagonal of the Hessian.
    E_C = np.diag(hessian) + E_A / 2
    # E_D contains the off-diagonal parts of the Hessian.
    # We store each pair (k, l) only once, namely the upper triangle.
    E_D = np.triu(hessian, 1)

    return E_A, E_B, E_C, E_D
Esempio n. 24
0
    def test_device_wire_expansion(self, tol):
        """Test that the transformation works correctly
        for the case where the transformation applies to more wires
        than the observable."""

        # create a 3-mode symmetric transformation
        wires = qml.wires.Wires([0, "a", 2])
        ndim = 1 + 2 * len(wires)

        Z = np.arange(ndim**2).reshape(ndim, ndim)
        Z = Z.T + Z

        obs = qml.NumberOperator(0)
        res = _transform_observable(obs, Z, device_wires=wires)

        # The Heisenberg representation of the number operator
        # is (X^2 + P^2) / (2*hbar) - 1/2. We use the ordering
        # I, X0, Xa, X2, P0, Pa, P2.
        A = np.diag([-0.5, 0.25, 0.25, 0, 0, 0, 0])
        expected = A @ Z + Z @ A

        assert isinstance(res, qml.PolyXP)
        assert res.wires == wires
        assert np.allclose(res.data[0], expected, atol=tol, rtol=0)
Esempio n. 25
0
# get the fourier transformed values and seperate argument and absolute value
complex_vals = np.fft.fft(c)
scale_arg = np.angle(complex_vals)
scale_abs = np.absolute(complex_vals)

# get the required squeeze scaling argument
r1 = -np.log(scale_abs)

# perform a neural network feed forward using the transformed Fourier matrix, the required phase shift using a rotation gate (phase gate),
# the required squeeze scaling and the Fourier matrix

circ_res = quantum_circ1(x=x,
                         phi_x=phi_x,
                         U1=F_H,
                         U2=F,
                         r=r1,
                         phi_r=phi_r,
                         phi_rot=scale_arg)

# theoretical transformation
mat = F @ np.diag(scale_abs * np.exp(1J * scale_arg)) @ F_H

# applied theoretical result to the input
theo_res = np.real(mat @ x)

# difference int the theoretical transform and the actual result of the circuit
print('theo')
print(theo_res)
print('actual')
print(circ_res)
Esempio n. 26
0
class AutogradBox(qml.math.TensorBox):
    """Implements the :class:`~.TensorBox` API for ``pennylane.numpy`` tensors.

    For more details, please refer to the :class:`~.TensorBox` documentation.
    """

    abs = wrap_output(lambda self: np.abs(self.data))
    angle = wrap_output(lambda self: np.angle(self.data))
    arcsin = wrap_output(lambda self: np.arcsin(self.data))
    cast = wrap_output(lambda self, dtype: np.tensor(self.data, dtype=dtype))
    diag = staticmethod(wrap_output(lambda values, k=0: np.diag(values, k=k)))
    expand_dims = wrap_output(
        lambda self, axis: np.expand_dims(self.data, axis=axis))
    ones_like = wrap_output(lambda self: np.ones_like(self.data))
    reshape = wrap_output(lambda self, shape: np.reshape(self.data, shape))
    sqrt = wrap_output(lambda self: np.sqrt(self.data))
    sum = wrap_output(lambda self, axis=None, keepdims=False: np.sum(
        self.data, axis=axis, keepdims=keepdims))
    T = wrap_output(lambda self: self.data.T)
    squeeze = wrap_output(lambda self: self.data.squeeze())

    @staticmethod
    def astensor(tensor):
        return np.tensor(tensor)

    @staticmethod
    @wrap_output
    def concatenate(values, axis=0):
        return np.concatenate(AutogradBox.unbox_list(values), axis=axis)

    @staticmethod
    @wrap_output
    def dot(x, y):
        x, y = AutogradBox.unbox_list([x, y])

        if x.ndim == 0 and y.ndim == 0:
            return x * y

        if x.ndim == 2 and y.ndim == 2:
            return x @ y

        return np.dot(x, y)

    @property
    def interface(self):
        return "autograd"

    def numpy(self):
        if hasattr(self.data, "_value"):
            # Catches the edge case where the data is an Autograd arraybox,
            # which only occurs during backpropagation.
            return self.data._value

        return self.data.numpy()

    @property
    def requires_grad(self):
        return self.data.requires_grad

    @wrap_output
    def scatter_element_add(self, index, value):
        size = self.data.size
        flat_index = np.ravel_multi_index(index, self.shape)
        t = [0] * size
        t[flat_index] = value
        self.data = self.data + np.array(t).reshape(self.shape)
        return self.data

    @property
    def shape(self):
        return self.data.shape

    @staticmethod
    @wrap_output
    def stack(values, axis=0):
        return np.stack(AutogradBox.unbox_list(values), axis=axis)

    @wrap_output
    def take(self, indices, axis=None):
        indices = self.astensor(indices)

        if axis is None:
            return self.data.flatten()[indices]

        fancy_indices = [slice(None)] * axis + [indices]
        return self.data[tuple(fancy_indices)]

    @staticmethod
    @wrap_output
    def where(condition, x, y):
        return np.where(condition, *AutogradBox.unbox_list([x, y]))
Esempio n. 27
0
def basisvector2eigenvalues(basis_vector):
    n_basis_vector = -1
    for n_basis_vector, value in enumerate(basis_vector):
        if value == 1:
            break
    eigenvalues = [1] * n_qubits
    for i in reversed(range(n_qubits)):
        if n_basis_vector - 2**i >= 0:
            eigenvalues[i] = -1
            n_basis_vector -= 2**i
            if n_basis_vector == 0:
                break
    return list(reversed(eigenvalues))


basis_states = np.diag(range(2**n_qubits))


@qml.qnode(device)
def qft_U_iqft(thetas, input_state):
    # loss = 0
    # for i, basis_state in enumerate(basis_states):
    # i = 1
    prepare_state(input_state)
    # qft_U(thetas)
    # qft()
    qml.RZ(thetas[0], wires=0)
    iqft()
    return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]

Esempio n. 28
0
def comp_basis_measurement(wires):
    n_wires = len(wires)
    return qml.Hermitian(np.diag(range(2**n_wires)), wires=wires)
Esempio n. 29
0
def prep_par(par, op):
    "Convert par into a list of parameters that op expects."
    if op.par_domain == "A":
        return [np.diag([x, 1]) for x in par]
    return par
Esempio n. 30
0
            -0.69254712 - 2.56963068e-02j,
            -0.15484858 + 6.57298384e-02j,
            -0.53082141 + 7.18073414e-02j,
            -0.41060450 - 1.89462315e-01j,
        ],
        [
            -0.09686189 - 3.15085273e-01j,
            -0.53241387 - 1.99491763e-01j,
            0.56928622 + 3.97704398e-01j,
            -0.28671074 - 6.01574497e-02j,
        ],
    ]
)


U_toffoli = np.diag([1 for i in range(8)])
U_toffoli[6:8, 6:8] = np.array([[0, 1], [1, 0]])

U_swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])

U_cswap = np.array([[1, 0, 0, 0, 0, 0, 0, 0],
                    [0, 1, 0, 0, 0, 0, 0, 0],
                    [0, 0, 1, 0, 0, 0, 0, 0],
                    [0, 0, 0, 1, 0, 0, 0, 0],
                    [0, 0, 0, 0, 1, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 1, 0],
                    [0, 0, 0, 0, 0, 1, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 1]])


H = np.array(