Source code for orbitsi.orbit.orca

"""Vertex orbit counts by ORCA, through the _orca_cpp extension."""
import networkx as nx
import numpy as np
from _orca_cpp import orca_count
from .orbit_converter import OrbitMatrixConverter


[docs] class ORCAOrbitCounter: """Vertex orbit counts of a graph by ORCA (graphlets of 4 vertices only). Parameters ---------- G : networkx.Graph The graph; self-loops are ignored. size : {4}, default 4 Graphlet size (15 orbits). """ def __init__(self, G: nx.Graph, size: int = 4): if size != 4: raise ValueError("The ORCA backend supports graphlet size 4 only; use EVOKE for size 5.") self.G = G self.size = size self.counts = None self.converter = OrbitMatrixConverter() def _nx_to_cpp_adjlist(self): """Adjacency lists over positions in sorted node order, without self-loops, and the sorted nodes.""" nodes = sorted(self.G.nodes()) index = {node: i for i, node in enumerate(nodes)} adj = [[index[nbr] for nbr in self.G.neighbors(node) if nbr != node] for node in nodes] return adj, nodes
[docs] def count_orbits(self) -> dict: """Count the induced orbits: ``{node: list of counts}``, also kept in ``self.counts``.""" adj, nodes = self._nx_to_cpp_adjlist() rows = orca_count(adj, self.size) self.counts = {node: row for node, row in zip(nodes, rows)} return self.counts
[docs] def get_orbits(self, induced: bool = False) -> np.ndarray: """The orbit counts, one row per node in sorted node order. Parameters ---------- induced : bool, default False Induced counts (ORCA's own) instead of non-induced ones. Returns ------- numpy.ndarray An ``n x 15`` int64 array. """ if self.counts is None: self.count_orbits() orbit_matrix = np.array([self.counts[node] for node in sorted(self.counts)], dtype=np.int64) orbit_matrix = orbit_matrix.reshape(len(self.counts), 15) return orbit_matrix if induced else self.converter.induced_to_noninduced(orbit_matrix)
[docs] @staticmethod def count_csr(offsets, nbors, size=4, orbits=None): """Non-induced orbit counts of a graph in CSR form. Parameters ---------- offsets, nbors : array_like of int The graph over vertices 0..n-1: the neighbours of vertex ``i`` are ``nbors[offsets[i]:offsets[i + 1]]``. size : {4}, default 4 Graphlet size. orbits : iterable of int, optional Accepted for :meth:`EVOKEOrbitCounter.count_csr`'s interface; all orbits are counted. Returns ------- numpy.ndarray An ``n x 15`` int64 array. """ if size != 4: raise ValueError("The ORCA backend supports graphlet size 4 only; use EVOKE for size 5.") offsets = np.asarray(offsets, dtype=np.int64) adj = [a.tolist() for a in np.split(np.asarray(nbors, dtype=np.int64), offsets[1:-1])] if len(offsets) > 1 else [] induced = np.array(orca_count(adj, 4), dtype=np.int64).reshape(len(adj), 15) return OrbitMatrixConverter().induced_to_noninduced(induced)