Source code for sigmaepsilon.math.graph.utils
"""Graph algorithm helpers (rooted level structures, peripheral nodes)."""
import numpy as np
from numba import jit
from numba.types import int64, Array
from numba.typed import Dict
from ..linalg.sparse import csr_matrix
__all__ = ["rooted_level_structure", "pseudo_peripheral_nodes"]
int64A = Array(int64, 1, "C")
[docs]
@jit(nopython=True, nogil=True, fastmath=False, cache=True)
def rooted_level_structure(adj: csr_matrix, root: int = 0) -> Dict:
"""Turn a sparse adjacency matrix into a rooted level structure.
Parameters
----------
adj : csr_matrix
Adjacency matrix in CSR format.
root : int, Optional
Index of the root node. Default is 0.
Returns
-------
dict
A `numba` dictionary <int[:] : int[:, :]>, where the keys
refer to different levels, and the values are the indices
of nodes on that level.
"""
nN = len(adj.indptr) - 1
rls = Dict.empty(
key_type=int64,
value_type=int64A,
)
level = 0
rls[level] = np.array([root], dtype=np.int64)
nodes = np.zeros(nN, dtype=np.int64)
nodes[root] = 1
levelset = np.zeros(nN, dtype=np.int64)
nE = 1
while nE < nN:
levelset[:] = 0
for node in rls[level]:
neighbours = adj.irow(node)
levelset[neighbours] = 1
for iN in range(nN):
if nodes[iN] == 1:
levelset[iN] = 0
level += 1
rls[level] = np.where(levelset == 1)[0]
nE += len(rls[level])
for iN in range(nN):
if levelset[iN] == 1:
nodes[iN] = 1
return rls
[docs]
@jit(nopython=True, nogil=True, fastmath=False, cache=True)
def pseudo_peripheral_nodes(adj: csr_matrix) -> np.ndarray:
"""Return the indices of nodes that are possible candidates for being peripheral nodes of a graph.
Parameters
----------
adj : csr_matrix
Adjacency matrix in CSR format.
Returns
-------
numpy.ndarray
Integer array of nodal indices.
"""
def length_width(RLS):
length = len(RLS)
width = 0
for i in range(length):
width = max(width, len(RLS[i]))
return length, width
RLS = rooted_level_structure(adj, root=0)
length, width = length_width(RLS)
while True:
nodes = RLS[len(RLS) - 1]
found = False
for _, node in enumerate(nodes):
iRLS = rooted_level_structure(adj, root=node)
iL, iW = length_width(iRLS)
if (iL > length) or (iL == length and iW < width):
RLS = iRLS
length = iL
width = iW
found = True
if not found:
nR = len(RLS[len(RLS) - 1]) + 1
res = np.zeros(nR, dtype=np.int64)
res[:-1] = RLS[len(RLS) - 1]
res[-1] = RLS[0][0]
return res