def search_information(adjacency, transform=None, has_memory=False):
    """
    Calculates search information of `adjacency`

    Computes the amount of information (measured in bits) that a random walker
    needs to follow the shortest path between a given pair of nodes.

    Parameters
    ----------
    adjacency : (N x N) array_like
        Weighted/unweighted, direct/undirected connection weight/length array
    transform : str, optional
        If `adjacency` is a connection weight array, specify a transform to map
        input connection weights to connection lengths. Options include ['log',
        'inv'], where 'log' is `-np.log(adjacency)` and 'inv' is `1/adjacency`.
        Default: None
    has_memory : bool, optional
        This flag defines whether or not the random walker "remembers" its
        previous step, which has the effect of reducing the amount of
        information needed to find the next state. Default: False

    Returns
    -------
    SI : (N x N) ndarray
        Pair-wise search information array. Note that `SI[i,j]` may be
        different from `SI[j,i]``; hence, `SI` is not a symmetric matrix even
        when `adjacency` is symmetric.

    References
    ----------
    .. [1] Goni, J., van den Heuvel, M. P., Avena-Koenigsberger, A., de
       Mendizabal, N. V., Betzel, R. F., Griffa, A., Hagmann, P.,
       Corominas-Murtra, B., Thiran, J-P., & Sporns, O. (2014). Resting-brain
       functional connectivity predicted by analytic measures of network
       communication. Proceedings of the National Academy of Sciences, 111(2),
       833-838.
    .. [2] Rosvall, M., Trusina, A., Minnhagen, P., & Sneppen, K. (2005).
       Networks and cities: An information perspective. Physical Review
       Letters, 94(2), 028701.
    """

    N = len(adjacency)

    if np.allclose(adjacency, adjacency.T):
        flag_triu = True
    else:
        flag_triu = False

    T = np.linalg.solve(np.diag(np.sum(adjacency, axis=1)), adjacency)
    _, hops, Pmat = distance_wei_floyd(adjacency, transform)

    SI = np.zeros((N, N))
    SI[np.eye(N) > 0] = np.nan

    for i in range(N):
        for j in range(N):
            if (j > i and flag_triu) or (not flag_triu and i != j):
                path = retrieve_shortest_path(i, j, hops, Pmat)
                lp = len(path) - 1
                if flag_triu:
                    if np.any(path):
                        pr_step_ff = np.zeros(lp)
                        pr_step_bk = np.zeros(lp)
                        if has_memory:
                            pr_step_ff[0] = T[path[0], path[1]]
                            pr_step_bk[lp-1] = T[path[lp], path[lp-1]]
                            for z in range(1, lp):
                                pr_step_ff[z] = T[path[z], path[z+1]] / (1 - T[path[z-1], path[z]])
                                pr_step_bk[lp-z-1] = T[path[lp-z], path[lp-z-1]] / (1 - T[path[lp-z+1], path[lp-z]])
                        else:
                            for z in range(lp):
                                pr_step_ff[z] = T[path[z], path[z+1]]
                                pr_step_bk[z] = T[path[z+1], path[z]]

                        prob_sp_ff = np.prod(pr_step_ff)
                        prob_sp_bk = np.prod(pr_step_bk)
                        SI[i, j] = -np.log2(prob_sp_ff)
                        SI[j, i] = -np.log2(prob_sp_bk)
                else:
                    if np.any(path):
                        pr_step_ff = np.zeros(lp)
                        if has_memory:
                            pr_step_ff[0] = T[path[0], path[1]]
                            for z in range(1, lp):
                                pr_step_ff[z] = T[path[z], path[z+1]] / (1 - T[path[z-1], path[z]])
                        else:
                            for z in range(lp):
                                pr_step_ff[z] = T[path[z], path[z+1]]

                        prob_sp_ff = np.prod(pr_step_ff)
                        SI[i, j] = -np.log2(prob_sp_ff)
                    else:
                        SI[i, j] = np.inf

    return SI
Calculates search information of `adjacency`

    Computes the amount of information (measured in bits) that a random walker
    needs to follow the shortest path between a given pair of nodes.

    Parameters
    ----------
    adjacency : (N x N) array_like
        Weighted/unweighted, direct/undirected connection weight/length array
    transform : str, optional
        If `adjacency` is a connection weight array, specify a transform to map
        input connection weights to connection lengths. Options include ['log',
        'inv'], where 'log' is `-np.log(adjacency)` and 'inv' is `1/adjacency`.
        Default: None
    has_memory : bool, optional
        This flag defines whether or not the random walker "remembers" its
        previous step, which has the effect of reducing the amount of
        information needed to find the next state. Default: False

    Returns
    -------
    SI : (N x N) ndarray
        Pair-wise search information array. Note that `SI[i,j]` may be
        different from `SI[j,i]``; hence, `SI` is not a symmetric matrix even
        when `adjacency` is symmetric.

    References
    ----------
    .. [1] Goni, J., van den Heuvel, M. P., Avena-Koenigsberger, A., de
       Mendizabal, N. V., Betzel, R. F., Griffa, A., Hagmann, P.,
       Corominas-Murtra, B., Thiran, J-P., & Sporns, O. (2014). Resting-brain
       functional connectivity predicted by analytic measures of network
       communication. Proceedings of the National Academy of Sciences, 111(2),
       833-838.
    .. [2] Rosvall, M., Trusina, A., Minnhagen, P., & Sneppen, K. (2005).
       Networks and cities: An information perspective. Physical Review
       Letters, 94(2), 028701.