census.py
2.6 kB · python · 94 lines
1import numpy as np2from typing import Dict, TYPE_CHECKING3from mrlypy.core.errors import MrlyError45if TYPE_CHECKING:6 from .models import Network78# PRIMITIVES910def branches(network: "Network") -> int:11 return len(network.branches)1213def nodes(network: "Network") -> int:14 return len(network.nodes)1516def total_length(network: "Network") -> float:17 positions = network.positions()18 total = 0.019 for branch in network.branches:20 a = positions[branch.parent]21 b = positions[branch.child]22 total += float(np.linalg.norm(a - b))23 return total2425def tips(network: "Network") -> int:26 degree = network.degree()27 if len(degree) == 0:28 return 029 return int((degree == 1).sum())3031def junctions(network: "Network") -> int:32 degree = network.degree()33 if len(degree) == 0:34 return 035 return int((degree >= 3).sum())3637def components(network: "Network") -> int:38 n = len(network.nodes)39 if n == 0:40 return 041 adjacency = network.adjacency()42 seen = np.zeros(n, dtype=bool)43 count = 044 for start in range(n):45 if seen[start]:46 continue47 count += 148 stack = [start]49 seen[start] = True50 while stack:51 current = stack.pop()52 for neighbor in adjacency[current]:53 if not seen[neighbor]:54 seen[neighbor] = True55 stack.append(neighbor)56 return count5758# FRACTAL DIMENSION - BOX COUNTING ON NODE POSITIONS5960def fractal_dimension(network: "Network", samples: int = 12) -> float:61 positions = network.positions()62 if len(positions) < 2:63 return 0.064 mins = positions.min(axis=0)65 maxs = positions.max(axis=0)66 extent = float((maxs - mins).max())67 if extent == 0:68 return 0.069 normalized = (positions - mins) / extent70 sizes = np.geomspace(1.0, 1.0 / 256.0, samples)71 counts = []72 used = []73 for size in sizes:74 keys = np.floor(normalized / size).astype(np.int64)75 unique = {tuple(row) for row in keys}76 counts.append(len(unique))77 used.append(size)78 log_inv_size = np.log(1.0 / np.array(used))79 log_count = np.log(np.array(counts))80 slope = np.polyfit(log_inv_size, log_count, 1)[0]81 return float(slope)8283# PUBLIC8485def census(network: "Network") -> Dict[str, float]:86 return {87 "nodes": nodes(network),88 "branches": branches(network),89 "tips": tips(network),90 "junctions": junctions(network),91 "components": components(network),92 "total_length": round(total_length(network), 6),93 "fractal_dimension": round(fractal_dimension(network), 4),94 }