<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def inverse(self):
 r"""Return the pseudo-inverse filter bank. The pseudo-inverse of the *analysis filter bank* :math:`g` is the *synthesis filter bank* :math:`g^+` such that .. math:: g(L)^+ g(L) = I, where :math:`I` is the identity matrix, and the *synthesis operator* .. math:: g(L)^+ = (g(L)\top g(L))^{-1} g(L)^\top = (g_1(L)^+, \dots, g_F(L)^+) \in \mathbb{R}^{N \times NF} is the left pseudo-inverse of the analysis operator :math:`g(L)`. Note that :math:`g_i(L)^+` is the pseudo-inverse of :math:`g_i(L)`, :math:`N` is the number of vertices, and :math:`F` is the number of filters in the bank. The above relation holds, and the reconstruction is exact, if and only if :math:`g(L)` is a frame. To be a frame, the rows of :math:`g(L)` must span the whole space (i.e., :math:`g(L)` must have full row rank). That is the case if the lower frame bound :math:`A > 0`. If :math:`g(L)` is not a frame, the reconstruction :math:`g(L)^+ g(L) x` will be the closest to :math:`x` in the least square sense. While there exists infinitely many inverses of the analysis operator of a frame, the pseudo-inverse is unique and corresponds to the *canonical dual* of the filter kernel. The *frame operator* of :math:`g^+` is :math:`g(L)^+ (g(L)^+)^\top = (g(L)\top g(L))^{-1}`, the inverse of the frame operator of :math:`g`. Similarly, its *frame bounds* are :math:`A^{-1}` and :math:`B^{-1}`, where :math:`A` and :math:`B` are the frame bounds of :math:`g`. If :math:`g` is tight (i.e., :math:`A=B`), the canonical dual is given by :math:`g^+ = g / A` (i.e., :math:`g^+_i = g_i / A \ \forall i`). Returns ------- inverse : :class:`pygsp.filters.Filter` The pseudo-inverse filter bank, which synthesizes (or reconstructs) a signal from its coefficients using the canonical dual frame. See Also -------- estimate_frame_bounds: estimate the frame bounds Examples -------- error: 3e-14 A(g)*B(h) = 0.687 * 1.457 = 1.000 B(g)*A(h) = 1.994 * 0.501 = 1.000 """


        A, _ = self.estimate_frame_bounds()
        if A == 0:
            _logger.warning('The filter bank is not invertible as it is not '
                            'a frame (lower frame bound A=0).')

        def kernel(g, i, x):
            y = g.evaluate(x).T
            z = np.linalg.pinv(np.expand_dims(y, axis=-1)).squeeze(axis=-2)
            return z[:, i]  # Return one filter.

        kernels = [partial(kernel, self, i) for i in range(self.n_filters)]

        return Filter(self.G, kernels)