UNPKG

entropyx

Version:

A simple data mining library, written in TypeScript

129 lines 5.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FastNewman = void 0; const community_manager_1 = require("@/utils/community-manager"); const max_heap_1 = require("@/utils/max-heap"); function makeKey(a, b) { return a < b ? `${a}#${b}` : `${b}#${a}`; } class FastNewman { constructor(options = {}) { this.history = []; this.options = { ...options }; } async fit(adjacencyMatrix) { const nodeCount = adjacencyMatrix.length; const adjacency = new Map(); const degrees = new Array(nodeCount).fill(0); for (let i = 0; i < nodeCount; i++) { const neighbors = []; for (let j = 0; j < nodeCount; j++) { if (adjacencyMatrix[i][j] !== 0) { neighbors.push(j); degrees[i]++; } } adjacency.set(i, neighbors); } const totalDegree = degrees.reduce((acc, deg) => acc + deg, 0); const edgeCount = totalDegree / 2; const cm = new community_manager_1.CommunityManager(nodeCount, degrees, edgeCount); const commEdges = new Map(); for (let node = 0; node < nodeCount; node++) { const neighbors = adjacency.get(node) ?? []; for (const neighbor of neighbors) { if (neighbor <= node) continue; const commA = cm.nodeToComm[node]; const commB = cm.nodeToComm[neighbor]; if (commA === commB) continue; if (!commEdges.has(commA)) commEdges.set(commA, new Map()); if (!commEdges.has(commB)) commEdges.set(commB, new Map()); commEdges.get(commA).set(commB, (commEdges.get(commA).get(commB) ?? 0) + 1); commEdges.get(commB).set(commA, (commEdges.get(commB).get(commA) ?? 0) + 1); } } const heap = new max_heap_1.MaxHeap(); const insertedPairs = new Set(); for (const [comm, neighborMap] of commEdges.entries()) { for (const [nbr, edgeCountBetween] of neighborMap.entries()) { const key = makeKey(comm, nbr); if (insertedPairs.has(key)) continue; insertedPairs.add(key); const delta = cm.deltaQ(comm, nbr, edgeCountBetween); heap.insert({ deltaQ: delta, commA: comm, commB: nbr }); } } let currentQ = this.computeInitialModularity(cm); this.history.push({ q: currentQ, communities: this.extractCommunities(cm) }); const desired = this.options.numberOfCommunities ?? 1; while (heap.size() > 0) { if (cm.commDegree.size <= desired) break; const candidate = heap.extractMax(); if (candidate.deltaQ <= 0) break; if (!cm.commDegree.has(candidate.commA) || !cm.commDegree.has(candidate.commB)) { continue; } const mergedId = cm.mergeCommunities(candidate.commA, candidate.commB); currentQ += candidate.deltaQ; this.history.push({ q: currentQ, communities: this.extractCommunities(cm) }); const newNeighbors = new Map(); const updateNeighbors = (oldComm) => { const nbrMap = commEdges.get(oldComm); if (!nbrMap) return; for (const [nbr, count] of nbrMap.entries()) { if (nbr === candidate.commA || nbr === candidate.commB) continue; newNeighbors.set(nbr, (newNeighbors.get(nbr) ?? 0) + count); const neighborMap = commEdges.get(nbr); if (neighborMap) { neighborMap.delete(candidate.commA); neighborMap.delete(candidate.commB); neighborMap.set(mergedId, (neighborMap.get(mergedId) ?? 0) + count); } } }; updateNeighbors(candidate.commA); updateNeighbors(candidate.commB); commEdges.delete(candidate.commA); commEdges.delete(candidate.commB); commEdges.set(mergedId, newNeighbors); for (const [nbr, edgeCountBetween] of newNeighbors.entries()) { const delta = cm.deltaQ(mergedId, nbr, edgeCountBetween); heap.insert({ deltaQ: delta, commA: mergedId, commB: nbr }); } } const best = this.history.reduce((acc, cur) => (cur.q > acc.q ? cur : acc)); return { communities: best.communities, modularityHistory: this.history, }; } computeInitialModularity(cm) { const m = cm.totalEdges; let sum = 0; cm.commDegree.forEach((deg) => { const fraction = deg / (2 * m); sum += fraction * fraction; }); return -sum; } extractCommunities(cm) { const communities = new Map(); cm.nodeToComm.forEach((comm, node) => { if (!communities.has(comm)) communities.set(comm, []); communities.get(comm).push(node); }); return Array.from(communities.values()); } } exports.FastNewman = FastNewman; //# sourceMappingURL=fast-newman.js.map