def lap_pe(g, k, padding=False, return_eigval=False):
    r"""Laplacian Positional Encoding, as introduced in
    `Benchmarking Graph Neural Networks
    <https://arxiv.org/abs/2003.00982>`__

    This function computes the laplacian positional encodings as the
    k smallest non-trivial eigenvectors.

    Parameters
    ----------
    g : DGLGraph
        The input graph. Must be homogeneous and bidirected.
    k : int
        Number of smallest non-trivial eigenvectors to use for positional
        encoding.
    padding : bool, optional
        If False, raise an exception when k>=n. Otherwise, add zero paddings
        in the end of eigenvectors and 'nan' paddings in the end of eigenvalues
        when k>=n. Default: False. n is the number of nodes in the given graph.
    return_eigval : bool, optional
        If True, return laplacian eigenvalues together with eigenvectors.
        Otherwise, return laplacian eigenvectors only.
        Default: False.

    Returns
    -------
    Tensor or (Tensor, Tensor)
        Return the laplacian positional encodings of shape :math:`(N, k)`,
        where :math:`N` is the number of nodes in the input graph, when
        :attr:`return_eigval` is False. The eigenvalues of shape :math:`N` is
        additionally returned as the second element when :attr:`return_eigval`
        is True.

    Example
    -------
    >>> import dgl
    >>> g = dgl.graph(([0,1,2,3,1,2,3,0], [1,2,3,0,0,1,2,3]))
    >>> dgl.lap_pe(g, 2)
    tensor([[ 7.0711e-01, -6.4921e-17],
            [ 3.0483e-16, -7.0711e-01],
            [-7.0711e-01, -2.4910e-16],
            [ 9.9288e-17,  7.0711e-01]])
    >>> dgl.lap_pe(g, 5, padding=True)
    tensor([[ 7.0711e-01, -6.4921e-17,  5.0000e-01,  0.0000e+00,  0.0000e+00],
            [ 3.0483e-16, -7.0711e-01, -5.0000e-01,  0.0000e+00,  0.0000e+00],
            [-7.0711e-01, -2.4910e-16,  5.0000e-01,  0.0000e+00,  0.0000e+00],
            [ 9.9288e-17,  7.0711e-01, -5.0000e-01,  0.0000e+00,  0.0000e+00]])
    >>> dgl.lap_pe(g, 5, padding=True, return_eigval=True)
    (tensor([[-7.0711e-01,  6.4921e-17, -5.0000e-01,  0.0000e+00,  0.0000e+00],
             [-3.0483e-16,  7.0711e-01,  5.0000e-01,  0.0000e+00,  0.0000e+00],
             [ 7.0711e-01,  2.4910e-16, -5.0000e-01,  0.0000e+00,  0.0000e+00],
             [-9.9288e-17, -7.0711e-01,  5.0000e-01,  0.0000e+00,  0.0000e+00]]),
     tensor([1., 1., 2., nan, nan]))
    """
    # check for the "k < n" constraint
    n = g.num_nodes()
    if not padding and n <= k:
        assert (
            "the number of eigenvectors k must be smaller than the number of "
            + f"nodes n, {k} and {n} detected."
        )

    # get laplacian matrix as I - D^-0.5 * A * D^-0.5
    A = g.adj_external(scipy_fmt="csr")  # adjacency matrix
    N = sparse.diags(
        F.asnumpy(g.in_degrees()).clip(1) ** -0.5, dtype=float
    )  # D^-1/2
    L = sparse.eye(g.num_nodes()) - N * A * N

    # select eigenvectors with smaller eigenvalues O(n + klogk)
    if k + 1 < n - 1:
        # Use scipy if k + 1 < n - 1 for memory efficiency.
        EigVal, EigVec = scipy.sparse.linalg.eigs(
            L, k=k + 1, which="SR", ncv=4 * k, tol=1e-2
        )
        max_freqs = k
        topk_indices = EigVal.argsort()[1:]
    else:
        # Fallback to numpy since scipy.sparse do not support this case.
        EigVal, EigVec = np.linalg.eig(L.toarray())
        max_freqs = min(n - 1, k)
        kpartition_indices = np.argpartition(EigVal, max_freqs)[: max_freqs + 1]
        topk_eigvals = EigVal[kpartition_indices]
        topk_indices = kpartition_indices[topk_eigvals.argsort()][1:]

    # Since scipy may return complex value, to avoid crashing in NN code,
    # convert them to real number.
    topk_EigVal = EigVal[topk_indices].real
    topk_EigVec = EigVec[:, topk_indices].real
    eigvals = F.tensor(topk_EigVal, dtype=F.float32)

    # get random flip signs
    rand_sign = 2 * (np.random.rand(max_freqs) > 0.5) - 1.0
    PE = F.astype(F.tensor(rand_sign * topk_EigVec), F.float32)

    # add paddings
    if n <= k:
        temp_EigVec = F.zeros(
            [n, k - n + 1], dtype=F.float32, ctx=F.context(PE)
        )
        PE = F.cat([PE, temp_EigVec], dim=1)
        temp_EigVal = F.tensor(np.full(k - n + 1, np.nan), F.float32)
        eigvals = F.cat([eigvals, temp_EigVal], dim=0)

    if return_eigval:
        return PE, eigvals
    return PE
Laplacian Positional Encoding, as introduced in
    `Benchmarking Graph Neural Networks
    <https://arxiv.org/abs/2003.00982>`__

    This function computes the laplacian positional encodings as the
    k smallest non-trivial eigenvectors.

    Parameters
    ----------
    g : DGLGraph
        The input graph. Must be homogeneous and bidirected.
    k : int
        Number of smallest non-trivial eigenvectors to use for positional
        encoding.
    padding : bool, optional
        If False, raise an exception when k>=n. Otherwise, add zero paddings
        in the end of eigenvectors and 'nan' paddings in the end of eigenvalues
        when k>=n. Default: False. n is the number of nodes in the given graph.
    return_eigval : bool, optional
        If True, return laplacian eigenvalues together with eigenvectors.
        Otherwise, return laplacian eigenvectors only.
        Default: False.

    Returns
    -------
    Tensor or (Tensor, Tensor)
        Return the laplacian positional encodings of shape :math:`(N, k)`,
        where :math:`N` is the number of nodes in the input graph, when
        :attr:`return_eigval` is False. The eigenvalues of shape :math:`N` is
        additionally returned as the second element when :attr:`return_eigval`
        is True.

    Example
    -------
    >>> import dgl
    >>> g = dgl.graph(([0,1,2,3,1,2,3,0], [1,2,3,0,0,1,2,3]))
    >>> dgl.lap_pe(g, 2)
    tensor([[ 7.0711e-01, -6.4921e-17],
            [ 3.0483e-16, -7.0711e-01],
            [-7.0711e-01, -2.4910e-16],
            [ 9.9288e-17,  7.0711e-01]])
    >>> dgl.lap_pe(g, 5, padding=True)
    tensor([[ 7.0711e-01, -6.4921e-17,  5.0000e-01,  0.0000e+00,  0.0000e+00],
            [ 3.0483e-16, -7.0711e-01, -5.0000e-01,  0.0000e+00,  0.0000e+00],
            [-7.0711e-01, -2.4910e-16,  5.0000e-01,  0.0000e+00,  0.0000e+00],
            [ 9.9288e-17,  7.0711e-01, -5.0000e-01,  0.0000e+00,  0.0000e+00]])
    >>> dgl.lap_pe(g, 5, padding=True, return_eigval=True)
    (tensor([[-7.0711e-01,  6.4921e-17, -5.0000e-01,  0.0000e+00,  0.0000e+00],
             [-3.0483e-16,  7.0711e-01,  5.0000e-01,  0.0000e+00,  0.0000e+00],
             [ 7.0711e-01,  2.4910e-16, -5.0000e-01,  0.0000e+00,  0.0000e+00],
             [-9.9288e-17, -7.0711e-01,  5.0000e-01,  0.0000e+00,  0.0000e+00]]),
     tensor([1., 1., 2., nan, nan]))
    
python/dgl/transforms/functional.py