arela
Version:
AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.
132 lines • 4.97 kB
JavaScript
/**
* Detect communities using Infomap algorithm
*
* Infomap uses information flow (random walk) to find communities.
* This is conceptually superior to modularity optimization for codebases
* because it models how information flows through dependencies.
*
* For now, we'll use a simplified implementation that mimics Infomap's
* information-flow approach with the refined weighting model from research.
*/
export function detectCommunitiesInfomap(graph, options = {}) {
const { directed = true, twoLevel = true, numTrials = 20, seed = 42, silent = true, } = options;
// For now, use a simplified flow-based clustering approach
// This implements the key insight from research: directory edges create "traps"
// Step 1: Apply refined weighting model
const weightedGraph = applyRefinedWeights(graph);
// Step 2: Find connected components with strong internal flow
const communities = findFlowCommunities(weightedGraph);
// Step 3: Apply post-processing filter for singletons
const filtered = classifySingletons(communities, weightedGraph);
return filtered;
}
/**
* Apply refined weighting model from research
*
* Key insight: Directory edges should be STRONGER than import edges
* This creates "information flow traps" within features
*
* Note: The graph loader already creates directory edges with weight 1.0
* We just need to use them as-is since they already represent strong coupling
*/
function applyRefinedWeights(graph) {
// The graph already has the correct structure:
// - Directory edges (created by loadGraph) have weight 1.0
// - Import edges (from DB) have weight based on import count
// We don't need to modify anything - just return the graph as-is
return graph;
}
/**
* Find communities based on flow (connected components with strong internal edges)
*/
function findFlowCommunities(graph) {
const visited = new Set();
const communities = [];
let communityId = 0;
for (const node of graph.nodes) {
if (visited.has(node.id))
continue;
// BFS to find connected component with strong edges (weight >= 0.5)
const community = [];
const queue = [node.id];
visited.add(node.id);
while (queue.length > 0) {
const current = queue.shift();
community.push(current);
// Find neighbors connected by strong edges
for (const edge of graph.edges) {
if (edge.weight < 0.5)
continue; // Only follow strong edges (directory edges)
let neighbor = null;
if (edge.from === current && !visited.has(edge.to)) {
neighbor = edge.to;
}
else if (edge.to === current && !visited.has(edge.from)) {
neighbor = edge.from;
}
if (neighbor !== null) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
communities.push({
id: `slice-${communityId++}`,
nodes: community,
});
}
return communities;
}
/**
* Classify singleton communities
*
* From research: We need to distinguish between:
* - "Good" singletons: main.go (entry point) - KEEP
* - "Bad" singletons: shared/utils.go (hub) - IGNORE
*
* Heuristic:
* - High in-degree (>3), low out-degree (<=1) = Shared Utility (IGNORE)
* - Otherwise = Singleton Slice (KEEP)
*/
function classifySingletons(communities, graph) {
const filtered = [];
for (const community of communities) {
if (community.nodes.length > 1) {
// Multi-node feature slice - KEEP
filtered.push(community);
}
else {
// Singleton - classify it
const nodeId = community.nodes[0];
const inDegree = getInDegree(graph, nodeId);
const outDegree = getOutDegree(graph, nodeId);
if (inDegree > 3 && outDegree <= 1) {
// High in-degree, low out-degree = Shared Utility (utils.go)
// IGNORE - don't add to filtered list
const node = graph.nodes.find(n => n.id === nodeId);
console.log(`Filtering out shared utility: ${node?.path || nodeId} (in=${inDegree}, out=${outDegree})`);
continue;
}
else {
// Low in-degree, high out-degree = Main entry point (main.go)
// OR isolated file
// KEEP
filtered.push(community);
}
}
}
return filtered;
}
/**
* Calculate in-degree for a node
*/
function getInDegree(graph, nodeId) {
return graph.edges.filter(e => e.to === nodeId).length;
}
/**
* Calculate out-degree for a node
*/
function getOutDegree(graph, nodeId) {
return graph.edges.filter(e => e.from === nodeId).length;
}
//# sourceMappingURL=infomap.js.map