@graphty/algorithms 1.2.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/README.md +375 -276
  2. package/dist/algorithms.d.ts +42 -2
  3. package/dist/algorithms.js +11402 -2
  4. package/dist/algorithms.js.map +1 -1
  5. package/dist/src/algorithms/centrality/betweenness.d.ts +14 -0
  6. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  7. package/dist/src/algorithms/centrality/betweenness.js +144 -169
  8. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  9. package/dist/src/algorithms/centrality/closeness.d.ts +35 -1
  10. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  11. package/dist/src/algorithms/centrality/closeness.js +70 -149
  12. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  13. package/dist/src/algorithms/centrality/degree.d.ts +7 -0
  14. package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
  15. package/dist/src/algorithms/centrality/degree.js +12 -9
  16. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  17. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +57 -0
  18. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
  19. package/dist/src/algorithms/centrality/delta-pagerank-simple.js +304 -0
  20. package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
  21. package/dist/src/algorithms/centrality/delta-pagerank.d.ts +106 -0
  22. package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
  23. package/dist/src/algorithms/centrality/delta-pagerank.js +320 -0
  24. package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
  25. package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
  26. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
  27. package/dist/src/algorithms/centrality/eigenvector.js +11 -4
  28. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
  29. package/dist/src/algorithms/centrality/hits.d.ts +10 -3
  30. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
  31. package/dist/src/algorithms/centrality/hits.js +11 -4
  32. package/dist/src/algorithms/centrality/hits.js.map +1 -1
  33. package/dist/src/algorithms/centrality/index.d.ts +3 -1
  34. package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
  35. package/dist/src/algorithms/centrality/index.js +2 -1
  36. package/dist/src/algorithms/centrality/index.js.map +1 -1
  37. package/dist/src/algorithms/centrality/katz.d.ts +10 -3
  38. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
  39. package/dist/src/algorithms/centrality/katz.js +13 -8
  40. package/dist/src/algorithms/centrality/katz.js.map +1 -1
  41. package/dist/src/algorithms/centrality/pagerank.d.ts +31 -0
  42. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  43. package/dist/src/algorithms/centrality/pagerank.js +61 -1
  44. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  45. package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
  46. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  47. package/dist/src/algorithms/community/girvan-newman.js +67 -57
  48. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  49. package/dist/src/algorithms/community/index.d.ts +2 -0
  50. package/dist/src/algorithms/community/index.d.ts.map +1 -1
  51. package/dist/src/algorithms/community/index.js.map +1 -1
  52. package/dist/src/algorithms/community/label-propagation.d.ts +11 -4
  53. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  54. package/dist/src/algorithms/community/label-propagation.js +60 -38
  55. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  56. package/dist/src/algorithms/community/leiden.d.ts +3 -3
  57. package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
  58. package/dist/src/algorithms/community/leiden.js +70 -44
  59. package/dist/src/algorithms/community/leiden.js.map +1 -1
  60. package/dist/src/algorithms/community/louvain-optimized.d.ts +149 -0
  61. package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
  62. package/dist/src/algorithms/community/louvain-optimized.js +338 -0
  63. package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
  64. package/dist/src/algorithms/community/louvain.d.ts +2 -3
  65. package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
  66. package/dist/src/algorithms/community/louvain.js +35 -81
  67. package/dist/src/algorithms/community/louvain.js.map +1 -1
  68. package/dist/src/algorithms/community/modularity-utils.d.ts +55 -0
  69. package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
  70. package/dist/src/algorithms/community/modularity-utils.js +108 -0
  71. package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
  72. package/dist/src/algorithms/components/connected.d.ts +23 -0
  73. package/dist/src/algorithms/components/connected.d.ts.map +1 -1
  74. package/dist/src/algorithms/components/connected.js +29 -3
  75. package/dist/src/algorithms/components/connected.js.map +1 -1
  76. package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
  77. package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
  78. package/dist/src/algorithms/matching/bipartite.js +18 -40
  79. package/dist/src/algorithms/matching/bipartite.js.map +1 -1
  80. package/dist/src/algorithms/matching/index.d.ts.map +1 -1
  81. package/dist/src/algorithms/matching/index.js.map +1 -1
  82. package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
  83. package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
  84. package/dist/src/algorithms/matching/isomorphism.js +36 -2
  85. package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
  86. package/dist/src/algorithms/mst/index.d.ts.map +1 -1
  87. package/dist/src/algorithms/mst/index.js.map +1 -1
  88. package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
  89. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
  90. package/dist/src/algorithms/mst/kruskal.js +17 -3
  91. package/dist/src/algorithms/mst/kruskal.js.map +1 -1
  92. package/dist/src/algorithms/mst/prim.d.ts +9 -0
  93. package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
  94. package/dist/src/algorithms/mst/prim.js +9 -0
  95. package/dist/src/algorithms/mst/prim.js.map +1 -1
  96. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
  97. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  98. package/dist/src/algorithms/shortest-path/bellman-ford.js +11 -12
  99. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  100. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +44 -0
  101. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
  102. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +195 -0
  103. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
  104. package/dist/src/algorithms/shortest-path/dijkstra.d.ts +20 -2
  105. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  106. package/dist/src/algorithms/shortest-path/dijkstra.js +30 -14
  107. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  108. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
  109. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
  110. package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
  111. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
  112. package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
  113. package/dist/src/algorithms/shortest-path/index.js.map +1 -1
  114. package/dist/src/algorithms/traversal/bfs-unified.d.ts +42 -0
  115. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
  116. package/dist/src/algorithms/traversal/bfs-unified.js +370 -0
  117. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
  118. package/dist/src/algorithms/traversal/bfs-variants.d.ts +77 -0
  119. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
  120. package/dist/src/algorithms/traversal/bfs-variants.js +402 -0
  121. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
  122. package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
  123. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  124. package/dist/src/algorithms/traversal/bfs.js +5 -182
  125. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  126. package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
  127. package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
  128. package/dist/src/algorithms/traversal/dfs.js +48 -0
  129. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  130. package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
  131. package/dist/src/algorithms/traversal/index.js.map +1 -1
  132. package/dist/src/benchmark-all-algorithms.d.ts +25 -0
  133. package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
  134. package/dist/src/benchmark-all-algorithms.js +491 -0
  135. package/dist/src/benchmark-all-algorithms.js.map +1 -0
  136. package/dist/src/clustering/hierarchical.d.ts +23 -15
  137. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  138. package/dist/src/clustering/hierarchical.js +226 -224
  139. package/dist/src/clustering/hierarchical.js.map +1 -1
  140. package/dist/src/clustering/index.d.ts +4 -2
  141. package/dist/src/clustering/index.d.ts.map +1 -1
  142. package/dist/src/clustering/index.js +2 -2
  143. package/dist/src/clustering/index.js.map +1 -1
  144. package/dist/src/clustering/k-core.d.ts +17 -19
  145. package/dist/src/clustering/k-core.d.ts.map +1 -1
  146. package/dist/src/clustering/k-core.js +68 -30
  147. package/dist/src/clustering/k-core.js.map +1 -1
  148. package/dist/src/clustering/mcl.d.ts +6 -0
  149. package/dist/src/clustering/mcl.d.ts.map +1 -1
  150. package/dist/src/clustering/mcl.js +34 -3
  151. package/dist/src/clustering/mcl.js.map +1 -1
  152. package/dist/src/clustering/spectral.d.ts +11 -0
  153. package/dist/src/clustering/spectral.d.ts.map +1 -1
  154. package/dist/src/clustering/spectral.js +99 -47
  155. package/dist/src/clustering/spectral.js.map +1 -1
  156. package/dist/src/core/graph.d.ts +45 -0
  157. package/dist/src/core/graph.d.ts.map +1 -1
  158. package/dist/src/core/graph.js +49 -5
  159. package/dist/src/core/graph.js.map +1 -1
  160. package/dist/src/data-structures/index.d.ts.map +1 -1
  161. package/dist/src/data-structures/index.js.map +1 -1
  162. package/dist/src/data-structures/priority-queue.d.ts +18 -1
  163. package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
  164. package/dist/src/data-structures/priority-queue.js +26 -5
  165. package/dist/src/data-structures/priority-queue.js.map +1 -1
  166. package/dist/src/data-structures/union-find.d.ts +21 -0
  167. package/dist/src/data-structures/union-find.d.ts.map +1 -1
  168. package/dist/src/data-structures/union-find.js +21 -0
  169. package/dist/src/data-structures/union-find.js.map +1 -1
  170. package/dist/src/flow/ford-fulkerson.d.ts +20 -12
  171. package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
  172. package/dist/src/flow/ford-fulkerson.js +170 -217
  173. package/dist/src/flow/ford-fulkerson.js.map +1 -1
  174. package/dist/src/flow/min-cut.d.ts +6 -8
  175. package/dist/src/flow/min-cut.d.ts.map +1 -1
  176. package/dist/src/flow/min-cut.js +34 -12
  177. package/dist/src/flow/min-cut.js.map +1 -1
  178. package/dist/src/index.d.ts +3 -1
  179. package/dist/src/index.d.ts.map +1 -1
  180. package/dist/src/index.js +6 -1
  181. package/dist/src/index.js.map +1 -1
  182. package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
  183. package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
  184. package/dist/src/link-prediction/adamic-adar.js +47 -30
  185. package/dist/src/link-prediction/adamic-adar.js.map +1 -1
  186. package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
  187. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  188. package/dist/src/link-prediction/common-neighbors.js +31 -14
  189. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  190. package/dist/src/link-prediction/index.d.ts.map +1 -1
  191. package/dist/src/optimized/bit-packed.d.ts +193 -0
  192. package/dist/src/optimized/bit-packed.d.ts.map +1 -0
  193. package/dist/src/optimized/bit-packed.js +342 -0
  194. package/dist/src/optimized/bit-packed.js.map +1 -0
  195. package/dist/src/optimized/csr-graph.d.ts +149 -0
  196. package/dist/src/optimized/csr-graph.d.ts.map +1 -0
  197. package/dist/src/optimized/csr-graph.js +404 -0
  198. package/dist/src/optimized/csr-graph.js.map +1 -0
  199. package/dist/src/optimized/direction-optimized-bfs.d.ts +95 -0
  200. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
  201. package/dist/src/optimized/direction-optimized-bfs.js +242 -0
  202. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
  203. package/dist/src/optimized/graph-adapter.d.ts +111 -0
  204. package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
  205. package/dist/src/optimized/graph-adapter.js +194 -0
  206. package/dist/src/optimized/graph-adapter.js.map +1 -0
  207. package/dist/src/optimized/index.d.ts +14 -0
  208. package/dist/src/optimized/index.d.ts.map +1 -0
  209. package/dist/src/optimized/index.js +30 -0
  210. package/dist/src/optimized/index.js.map +1 -0
  211. package/dist/src/pathfinding/astar.d.ts +25 -9
  212. package/dist/src/pathfinding/astar.d.ts.map +1 -1
  213. package/dist/src/pathfinding/astar.js +25 -10
  214. package/dist/src/pathfinding/astar.js.map +1 -1
  215. package/dist/src/pathfinding/utils.d.ts +15 -6
  216. package/dist/src/pathfinding/utils.d.ts.map +1 -1
  217. package/dist/src/pathfinding/utils.js +19 -7
  218. package/dist/src/pathfinding/utils.js.map +1 -1
  219. package/dist/src/research/grsbm.d.ts +0 -1
  220. package/dist/src/research/grsbm.d.ts.map +1 -1
  221. package/dist/src/research/grsbm.js +28 -23
  222. package/dist/src/research/grsbm.js.map +1 -1
  223. package/dist/src/research/index.d.ts.map +1 -1
  224. package/dist/src/research/index.js.map +1 -1
  225. package/dist/src/research/sync.d.ts +0 -1
  226. package/dist/src/research/sync.d.ts.map +1 -1
  227. package/dist/src/research/sync.js +31 -36
  228. package/dist/src/research/sync.js.map +1 -1
  229. package/dist/src/research/terahac.d.ts +2 -1
  230. package/dist/src/research/terahac.d.ts.map +1 -1
  231. package/dist/src/research/terahac.js +39 -7
  232. package/dist/src/research/terahac.js.map +1 -1
  233. package/dist/src/types/index.d.ts +8 -0
  234. package/dist/src/types/index.d.ts.map +1 -1
  235. package/dist/src/utils/algorithm-utilities.d.ts +2 -0
  236. package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
  237. package/dist/src/utils/algorithm-utilities.js +2 -0
  238. package/dist/src/utils/algorithm-utilities.js.map +1 -0
  239. package/dist/src/utils/graph-converters.d.ts +110 -0
  240. package/dist/src/utils/graph-converters.d.ts.map +1 -0
  241. package/dist/src/utils/graph-converters.js +227 -0
  242. package/dist/src/utils/graph-converters.js.map +1 -0
  243. package/dist/src/utils/graph-utilities.d.ts +62 -0
  244. package/dist/src/utils/graph-utilities.d.ts.map +1 -0
  245. package/dist/src/utils/graph-utilities.js +148 -0
  246. package/dist/src/utils/graph-utilities.js.map +1 -0
  247. package/dist/src/utils/index.d.ts +5 -0
  248. package/dist/src/utils/index.d.ts.map +1 -0
  249. package/dist/src/utils/index.js +5 -0
  250. package/dist/src/utils/index.js.map +1 -0
  251. package/dist/src/utils/math-utilities.d.ts +63 -0
  252. package/dist/src/utils/math-utilities.d.ts.map +1 -0
  253. package/dist/src/utils/math-utilities.js +132 -0
  254. package/dist/src/utils/math-utilities.js.map +1 -0
  255. package/dist/src/utils/matrix-utilities.d.ts +2 -0
  256. package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
  257. package/dist/src/utils/matrix-utilities.js +2 -0
  258. package/dist/src/utils/matrix-utilities.js.map +1 -0
  259. package/dist/src/utils/optimization-helpers.d.ts +42 -0
  260. package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
  261. package/dist/src/utils/optimization-helpers.js +59 -0
  262. package/dist/src/utils/optimization-helpers.js.map +1 -0
  263. package/dist/src/utils/priorityQueue.d.ts +24 -0
  264. package/dist/src/utils/priorityQueue.d.ts.map +1 -1
  265. package/dist/src/utils/priorityQueue.js +34 -4
  266. package/dist/src/utils/priorityQueue.js.map +1 -1
  267. package/package.json +58 -39
  268. package/src/algorithms/centrality/betweenness.ts +185 -200
  269. package/src/algorithms/centrality/closeness.ts +93 -194
  270. package/src/algorithms/centrality/degree.ts +18 -22
  271. package/src/algorithms/centrality/delta-pagerank-simple.ts +374 -0
  272. package/src/algorithms/centrality/delta-pagerank.ts +458 -0
  273. package/src/algorithms/centrality/eigenvector.ts +14 -15
  274. package/src/algorithms/centrality/hits.ts +17 -17
  275. package/src/algorithms/centrality/index.ts +20 -13
  276. package/src/algorithms/centrality/katz.ts +17 -25
  277. package/src/algorithms/centrality/pagerank.ts +91 -16
  278. package/src/algorithms/community/girvan-newman.ts +90 -86
  279. package/src/algorithms/community/index.ts +6 -5
  280. package/src/algorithms/community/label-propagation.ts +69 -48
  281. package/src/algorithms/community/leiden.ts +88 -65
  282. package/src/algorithms/community/louvain-optimized.ts +474 -0
  283. package/src/algorithms/community/louvain.ts +52 -122
  284. package/src/algorithms/community/modularity-utils.ts +134 -0
  285. package/src/algorithms/components/connected.ts +40 -17
  286. package/src/algorithms/matching/bipartite.ts +24 -53
  287. package/src/algorithms/matching/index.ts +4 -4
  288. package/src/algorithms/matching/isomorphism.ts +53 -23
  289. package/src/algorithms/mst/index.ts +3 -4
  290. package/src/algorithms/mst/kruskal.ts +21 -6
  291. package/src/algorithms/mst/prim.ts +13 -4
  292. package/src/algorithms/shortest-path/bellman-ford.ts +16 -28
  293. package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
  294. package/src/algorithms/shortest-path/dijkstra.ts +38 -30
  295. package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
  296. package/src/algorithms/shortest-path/index.ts +5 -5
  297. package/src/algorithms/traversal/bfs-unified.ts +437 -0
  298. package/src/algorithms/traversal/bfs-variants.ts +520 -0
  299. package/src/algorithms/traversal/bfs.ts +5 -229
  300. package/src/algorithms/traversal/dfs.ts +61 -42
  301. package/src/algorithms/traversal/index.ts +3 -3
  302. package/src/benchmark-all-algorithms.ts +580 -0
  303. package/src/clustering/hierarchical.ts +268 -272
  304. package/src/clustering/index.ts +8 -6
  305. package/src/clustering/k-core.ts +93 -61
  306. package/src/clustering/mcl.ts +53 -23
  307. package/src/clustering/spectral.ts +130 -72
  308. package/src/core/graph.ts +58 -18
  309. package/src/data-structures/index.ts +2 -2
  310. package/src/data-structures/priority-queue.ts +36 -11
  311. package/src/data-structures/union-find.ts +22 -1
  312. package/src/flow/ford-fulkerson.ts +221 -262
  313. package/src/flow/min-cut.ts +59 -51
  314. package/src/index.ts +9 -2
  315. package/src/link-prediction/adamic-adar.ts +75 -74
  316. package/src/link-prediction/common-neighbors.ts +49 -49
  317. package/src/link-prediction/index.ts +1 -1
  318. package/src/optimized/bit-packed.ts +390 -0
  319. package/src/optimized/csr-graph.ts +518 -0
  320. package/src/optimized/direction-optimized-bfs.ts +310 -0
  321. package/src/optimized/graph-adapter.ts +248 -0
  322. package/src/optimized/index.ts +34 -0
  323. package/src/pathfinding/astar.ts +40 -25
  324. package/src/pathfinding/utils.ts +20 -13
  325. package/src/research/grsbm.ts +36 -39
  326. package/src/research/index.ts +4 -4
  327. package/src/research/sync.ts +41 -54
  328. package/src/research/terahac.ts +63 -21
  329. package/src/types/index.ts +8 -0
  330. package/src/utils/algorithm-utilities.ts +2 -0
  331. package/src/utils/graph-converters.ts +269 -0
  332. package/src/utils/graph-utilities.ts +173 -0
  333. package/src/utils/index.ts +4 -0
  334. package/src/utils/math-utilities.ts +149 -0
  335. package/src/utils/matrix-utilities.ts +2 -0
  336. package/src/utils/optimization-helpers.ts +87 -0
  337. package/src/utils/priorityQueue.ts +40 -6
@@ -5,8 +5,39 @@
5
5
  * or division (top-down). Useful for understanding multi-scale
6
6
  * structure in graphs.
7
7
  */
8
+ /**
9
+ * Convert Graph to adjacency set representation for clustering
10
+ * @param graph - The input graph to convert
11
+ * @returns Map of node IDs to sets of neighbor node IDs
12
+ */
13
+ function graphToAdjacencySet(graph) {
14
+ const adjacency = new Map();
15
+ // Initialize all nodes
16
+ for (const node of graph.nodes()) {
17
+ adjacency.set(String(node.id), new Set());
18
+ }
19
+ // Add edges
20
+ for (const edge of graph.edges()) {
21
+ const source = String(edge.source);
22
+ const target = String(edge.target);
23
+ const sourceSet = adjacency.get(source);
24
+ if (sourceSet) {
25
+ sourceSet.add(target);
26
+ }
27
+ // For undirected graphs, add reverse edge
28
+ if (!graph.isDirected) {
29
+ const targetSet = adjacency.get(target);
30
+ if (targetSet) {
31
+ targetSet.add(source);
32
+ }
33
+ }
34
+ }
35
+ return adjacency;
36
+ }
8
37
  /**
9
38
  * Compute distance between two nodes based on graph structure
39
+ * @param graph - Adjacency map representation of the graph
40
+ * @param distances - Map to store computed distances between nodes
10
41
  */
11
42
  function computeGraphDistance(graph, distances) {
12
43
  // Use BFS to compute shortest path distances
@@ -23,10 +54,7 @@ function computeGraphDistance(graph, distances) {
23
54
  if (neighbors) {
24
55
  for (const neighbor of neighbors) {
25
56
  if (!dist.has(neighbor)) {
26
- const nodeDistance = dist.get(node);
27
- if (nodeDistance !== undefined) {
28
- dist.set(neighbor, nodeDistance + 1);
29
- }
57
+ dist.set(neighbor, (dist.get(node) ?? 0) + 1);
30
58
  queue.push(neighbor);
31
59
  }
32
60
  }
@@ -36,46 +64,55 @@ function computeGraphDistance(graph, distances) {
36
64
  }
37
65
  }
38
66
  /**
39
- * Compute distance between clusters based on linkage method
67
+ * Compute distance between two clusters based on linkage method
68
+ * @param cluster1 - First cluster node
69
+ * @param cluster2 - Second cluster node
70
+ * @param distances - Precomputed distance matrix between nodes
71
+ * @param linkage - Linkage method to use for computing cluster distance
72
+ * @returns The distance between the two clusters
40
73
  */
41
- function clusterDistance(cluster1, cluster2, distances, method) {
42
- if (cluster1.size === 0 || cluster2.size === 0) {
43
- return Infinity;
44
- }
74
+ function clusterDistance(cluster1, cluster2, distances, linkage) {
45
75
  const allDistances = [];
46
- for (const node1 of cluster1) {
47
- for (const node2 of cluster2) {
48
- const dist = distances.get(node1)?.get(node2) ?? Infinity;
49
- allDistances.push(dist);
76
+ for (const node1 of cluster1.members) {
77
+ const node1Distances = distances.get(node1);
78
+ if (!node1Distances) {
79
+ continue;
50
80
  }
81
+ for (const node2 of cluster2.members) {
82
+ const dist = node1Distances.get(node2);
83
+ if (dist !== undefined) {
84
+ allDistances.push(dist);
85
+ }
86
+ }
87
+ }
88
+ if (allDistances.length === 0) {
89
+ return Infinity;
51
90
  }
52
- switch (method) {
91
+ switch (linkage) {
53
92
  case "single":
54
93
  return Math.min(...allDistances);
55
94
  case "complete":
56
95
  return Math.max(...allDistances);
57
96
  case "average":
58
97
  return allDistances.reduce((a, b) => a + b, 0) / allDistances.length;
59
- case "ward":
60
- // Ward's method minimizes within-cluster variance
61
- // For simplicity, we use average here
62
- return allDistances.reduce((a, b) => a + b, 0) / allDistances.length;
98
+ case "ward": {
99
+ // Simplified Ward's method - minimize within-cluster variance
100
+ const size1 = cluster1.members.size;
101
+ const size2 = cluster2.members.size;
102
+ const avgDist = allDistances.reduce((a, b) => a + b, 0) / allDistances.length;
103
+ return avgDist * ((size1 * size2) / (size1 + size2));
104
+ }
63
105
  default:
64
106
  return Math.min(...allDistances);
65
107
  }
66
108
  }
67
109
  /**
68
- * Agglomerative hierarchical clustering
69
- * Builds clusters bottom-up by merging closest pairs
70
- *
71
- * @param graph - Undirected graph
72
- * @param linkage - Linkage method for cluster distance
73
- * @returns Hierarchical clustering result
74
- *
75
- * Time Complexity: O(n³) naive, O(n² log n) with heap
76
- * Space Complexity: O(n²)
110
+ * Internal implementation of agglomerative hierarchical clustering
111
+ * @param graph - Adjacency map representation of the graph
112
+ * @param linkage - Linkage method for cluster distance computation
113
+ * @returns Hierarchical clustering result with dendrogram and cluster assignments
77
114
  */
78
- export function hierarchicalClustering(graph, linkage = "single") {
115
+ function hierarchicalClusteringImpl(graph, linkage = "single") {
79
116
  const nodes = Array.from(graph.keys());
80
117
  const n = nodes.length;
81
118
  if (n === 0) {
@@ -101,152 +138,111 @@ export function hierarchicalClustering(graph, linkage = "single") {
101
138
  const clusterDistances = new Map();
102
139
  // Initialize cluster distances
103
140
  for (let i = 0; i < clusters.length; i++) {
104
- const clusterI = clusters[i];
105
- if (!clusterI) {
141
+ const cluster1 = clusters[i];
142
+ if (!cluster1) {
106
143
  continue;
107
144
  }
108
- clusterDistances.set(clusterI.id, new Map());
145
+ const distMap = new Map();
109
146
  for (let j = i + 1; j < clusters.length; j++) {
110
- const clusterJ = clusters[j];
111
- if (!clusterJ) {
147
+ const cluster2 = clusters[j];
148
+ if (!cluster2) {
112
149
  continue;
113
150
  }
114
- const dist = clusterDistance(clusterI.members, clusterJ.members, distances, linkage);
115
- const clusterIDistances = clusterDistances.get(clusterI.id);
116
- if (clusterIDistances) {
117
- clusterIDistances.set(clusterJ.id, dist);
118
- }
151
+ const dist = clusterDistance(cluster1, cluster2, distances, linkage);
152
+ distMap.set(cluster2.id, dist);
119
153
  }
154
+ clusterDistances.set(cluster1.id, distMap);
120
155
  }
156
+ // Active clusters
157
+ const activeClusters = new Set(clusters);
121
158
  // Merge clusters until only one remains
122
- while (clusters.length > 1) {
159
+ while (activeClusters.size > 1) {
123
160
  // Find closest pair of clusters
124
161
  let minDist = Infinity;
125
- let merge1 = 0;
126
- let merge2 = 0;
127
- for (let i = 0; i < clusters.length; i++) {
128
- for (let j = i + 1; j < clusters.length; j++) {
129
- const clusterI = clusters[i];
130
- const clusterJ = clusters[j];
131
- if (!clusterI || !clusterJ) {
162
+ let mergeCluster1 = null;
163
+ let mergeCluster2 = null;
164
+ for (const cluster1 of activeClusters) {
165
+ const distMap = clusterDistances.get(cluster1.id);
166
+ if (!distMap) {
167
+ continue;
168
+ }
169
+ for (const cluster2 of activeClusters) {
170
+ if (cluster1.id >= cluster2.id) {
132
171
  continue;
133
172
  }
134
- const dist = clusterDistances.get(clusterI.id)?.get(clusterJ.id) ??
135
- clusterDistances.get(clusterJ.id)?.get(clusterI.id) ??
136
- Infinity;
173
+ const dist = distMap.get(cluster2.id) ?? clusterDistances.get(cluster2.id)?.get(cluster1.id) ?? Infinity;
137
174
  if (dist < minDist) {
138
175
  minDist = dist;
139
- merge1 = i;
140
- merge2 = j;
176
+ mergeCluster1 = cluster1;
177
+ mergeCluster2 = cluster2;
141
178
  }
142
179
  }
143
180
  }
144
- // Check if we found a valid merge
145
- if (minDist === Infinity || merge1 === merge2) {
146
- // No more clusters can be merged (disconnected components)
181
+ if (!mergeCluster1 || !mergeCluster2 || minDist === Infinity) {
182
+ // No valid merge found - create forest
183
+ const trees = Array.from(activeClusters);
184
+ const forestRoot = {
185
+ id: `forest-${String(clusterCount++)}`,
186
+ members: new Set(nodes),
187
+ distance: Infinity,
188
+ height: Math.max(...trees.map((t) => t.height)) + 1,
189
+ trees,
190
+ };
191
+ dendrogram.push(forestRoot);
192
+ activeClusters.clear();
193
+ activeClusters.add(forestRoot);
147
194
  break;
148
195
  }
149
196
  // Create new cluster
150
- const cluster1 = clusters[merge1];
151
- const cluster2 = clusters[merge2];
152
- if (!cluster1 || !cluster2) {
153
- continue;
154
- }
155
197
  const newCluster = {
156
198
  id: `cluster-${String(clusterCount++)}`,
157
- members: new Set([...cluster1.members, ...cluster2.members]),
158
- left: cluster1,
159
- right: cluster2,
199
+ members: new Set([...mergeCluster1.members, ...mergeCluster2.members]),
200
+ left: mergeCluster1,
201
+ right: mergeCluster2,
160
202
  distance: minDist,
161
- height: Math.max(cluster1.height, cluster2.height) + 1,
203
+ height: Math.max(mergeCluster1.height, mergeCluster2.height) + 1,
162
204
  };
163
205
  dendrogram.push(newCluster);
164
- // Update cluster distances for new cluster
165
- clusterDistances.set(newCluster.id, new Map());
166
- for (let i = 0; i < clusters.length; i++) {
167
- if (i !== merge1 && i !== merge2) {
168
- const clusterI = clusters[i];
169
- if (!clusterI) {
170
- continue;
171
- }
172
- const newDist = clusterDistance(newCluster.members, clusterI.members, distances, linkage);
173
- const newClusterDistances = clusterDistances.get(newCluster.id);
174
- if (newClusterDistances) {
175
- newClusterDistances.set(clusterI.id, newDist);
176
- }
206
+ // Update distances to new cluster
207
+ const newDistMap = new Map();
208
+ for (const cluster of activeClusters) {
209
+ if (cluster.id === mergeCluster1.id || cluster.id === mergeCluster2.id) {
210
+ continue;
211
+ }
212
+ const dist = clusterDistance(newCluster, cluster, distances, linkage);
213
+ newDistMap.set(cluster.id, dist);
214
+ // Update reverse direction
215
+ const clusterDistMap = clusterDistances.get(cluster.id);
216
+ if (clusterDistMap) {
217
+ clusterDistMap.set(newCluster.id, dist);
177
218
  }
178
219
  }
179
- // Remove old clusters and their distances
180
- clusterDistances.delete(cluster1.id);
181
- clusterDistances.delete(cluster2.id);
182
- for (const [, dists] of clusterDistances) {
183
- dists.delete(cluster1.id);
184
- dists.delete(cluster2.id);
220
+ clusterDistances.set(newCluster.id, newDistMap);
221
+ // Remove merged clusters
222
+ activeClusters.delete(mergeCluster1);
223
+ activeClusters.delete(mergeCluster2);
224
+ clusterDistances.delete(mergeCluster1.id);
225
+ clusterDistances.delete(mergeCluster2.id);
226
+ // Clean up references
227
+ for (const distMap of clusterDistances.values()) {
228
+ distMap.delete(mergeCluster1.id);
229
+ distMap.delete(mergeCluster2.id);
185
230
  }
186
- // Remove merged clusters and add new one
187
- clusters.splice(Math.max(merge1, merge2), 1);
188
- clusters.splice(Math.min(merge1, merge2), 1);
189
- clusters.push(newCluster);
231
+ activeClusters.add(newCluster);
190
232
  }
191
- // Generate clusters at different levels
233
+ // Root is the last remaining cluster
234
+ const root = activeClusters.size > 0 ? Array.from(activeClusters)[0] : dendrogram[dendrogram.length - 1];
235
+ // Generate clusters at different heights
192
236
  const clustersByLevel = new Map();
193
- function extractClustersAtHeight(node, height, clusters) {
194
- if (node.height <= height || !node.left || !node.right) {
195
- clusters.push(node.members);
196
- }
197
- else {
198
- extractClustersAtHeight(node.left, height, clusters);
199
- extractClustersAtHeight(node.right, height, clusters);
200
- }
201
- }
202
- // Handle the case where we have multiple disconnected components
203
- let root;
204
- if (clusters.length === 0) {
237
+ if (!root) {
205
238
  return {
206
239
  root: { id: "empty", members: new Set(), distance: 0, height: 0 },
207
240
  dendrogram: [],
208
241
  clusters: new Map(),
209
242
  };
210
243
  }
211
- else if (clusters.length === 1 && clusters[0]) {
212
- root = clusters[0];
213
- }
214
- else {
215
- // Create a root that combines all remaining clusters (forest)
216
- const allMembers = new Set();
217
- let maxHeight = 0;
218
- for (const cluster of clusters) {
219
- for (const member of cluster.members) {
220
- allMembers.add(member);
221
- }
222
- maxHeight = Math.max(maxHeight, cluster.height);
223
- }
224
- root = {
225
- id: "root-forest",
226
- members: allMembers,
227
- distance: Infinity,
228
- height: maxHeight + 1,
229
- // Store references to the individual trees
230
- trees: [...clusters],
231
- };
232
- dendrogram.push(root);
233
- }
234
244
  for (let h = 0; h <= root.height; h++) {
235
- const clustersAtHeight = [];
236
- if (clusters.length > 1 && h === root.height) {
237
- // At forest root height, return single cluster with all members
238
- clustersAtHeight.push(root.members);
239
- }
240
- else if (clusters.length > 1 && h < root.height) {
241
- // For forest, extract from each tree separately
242
- for (const tree of clusters) {
243
- extractClustersAtHeight(tree, h, clustersAtHeight);
244
- }
245
- }
246
- else {
247
- extractClustersAtHeight(root, h, clustersAtHeight);
248
- }
249
- clustersByLevel.set(h, clustersAtHeight);
245
+ clustersByLevel.set(h, cutDendrogram(root, h));
250
246
  }
251
247
  return {
252
248
  root,
@@ -255,39 +251,42 @@ export function hierarchicalClustering(graph, linkage = "single") {
255
251
  };
256
252
  }
257
253
  /**
258
- * Cut dendrogram at specified height to get clusters
254
+ * Cut dendrogram at specific height to get clusters
255
+ * @param root - Root node of the dendrogram
256
+ * @param height - Height at which to cut the dendrogram
257
+ * @returns Array of sets representing clusters at the specified height
259
258
  */
260
259
  export function cutDendrogram(root, height) {
261
260
  const clusters = [];
262
261
  function traverse(node) {
263
- // Handle forest root specially
264
- if (node.trees) {
265
- if (node.height <= height) {
266
- // If we're at or below the requested height, return the whole forest as one cluster
267
- clusters.push(node.members);
268
- }
269
- else {
270
- // Otherwise, traverse each tree in the forest
262
+ if (node.height <= height || (!node.left && !node.right)) {
263
+ clusters.push(node.members);
264
+ }
265
+ else {
266
+ if (node.trees) {
267
+ // Handle forest nodes
271
268
  for (const tree of node.trees) {
272
269
  traverse(tree);
273
270
  }
274
271
  }
275
- return;
276
- }
277
- // Normal binary tree traversal
278
- if (node.height <= height || !node.left || !node.right) {
279
- clusters.push(node.members);
280
- }
281
- else {
282
- traverse(node.left);
283
- traverse(node.right);
272
+ else {
273
+ if (node.left) {
274
+ traverse(node.left);
275
+ }
276
+ if (node.right) {
277
+ traverse(node.right);
278
+ }
279
+ }
284
280
  }
285
281
  }
286
282
  traverse(root);
287
283
  return clusters;
288
284
  }
289
285
  /**
290
- * Cut dendrogram to get k clusters
286
+ * Cut dendrogram to get exactly k clusters
287
+ * @param root - Root node of the dendrogram
288
+ * @param k - Target number of clusters
289
+ * @returns Array of sets representing exactly k clusters
291
290
  */
292
291
  export function cutDendrogramKClusters(root, k) {
293
292
  if (k <= 0) {
@@ -296,7 +295,7 @@ export function cutDendrogramKClusters(root, k) {
296
295
  if (k === 1) {
297
296
  return [root.members];
298
297
  }
299
- // Find the height that gives us k clusters
298
+ // Binary search for the right height
300
299
  let low = 0;
301
300
  let high = root.height;
302
301
  while (low < high) {
@@ -317,9 +316,12 @@ export function cutDendrogramKClusters(root, k) {
317
316
  /**
318
317
  * Compute modularity-based hierarchical clustering
319
318
  * Uses modularity gain to decide merges
319
+ * @param graph - The input graph to cluster
320
+ * @returns Hierarchical clustering result with dendrogram and cluster assignments
320
321
  */
321
322
  export function modularityHierarchicalClustering(graph) {
322
- const nodes = Array.from(graph.keys());
323
+ const adjacencySet = graphToAdjacencySet(graph);
324
+ const nodes = Array.from(adjacencySet.keys());
323
325
  const n = nodes.length;
324
326
  if (n === 0) {
325
327
  return {
@@ -328,12 +330,15 @@ export function modularityHierarchicalClustering(graph) {
328
330
  clusters: new Map(),
329
331
  };
330
332
  }
331
- // Calculate total edges
332
- let m = 0;
333
- for (const neighbors of graph.values()) {
334
- m += neighbors.size;
333
+ // Compute degrees
334
+ const degrees = new Map();
335
+ let totalEdges = 0;
336
+ for (const [node, neighbors] of adjacencySet) {
337
+ degrees.set(node, neighbors.size);
338
+ totalEdges += neighbors.size;
335
339
  }
336
- m = m / 2; // Each edge counted twice
340
+ // For undirected graphs
341
+ totalEdges = totalEdges / 2;
337
342
  // Initialize clusters
338
343
  const clusters = nodes.map((node, i) => ({
339
344
  id: `leaf-${String(i)}`,
@@ -343,83 +348,65 @@ export function modularityHierarchicalClustering(graph) {
343
348
  }));
344
349
  const dendrogram = [...clusters];
345
350
  let clusterCount = n;
346
- // Track community assignments
347
- const communityMap = new Map();
348
- nodes.forEach((node, i) => communityMap.set(node, i));
349
- // Compute modularity gain for merging communities
350
- function modularityGain(comm1, comm2) {
351
- let edgesBetween = 0;
352
- let degree1 = 0;
353
- let degree2 = 0;
354
- for (const node1 of comm1) {
355
- const neighbors = graph.get(node1);
356
- if (!neighbors) {
357
- continue;
358
- }
359
- degree1 += neighbors.size;
360
- for (const neighbor of neighbors) {
361
- if (comm2.has(neighbor)) {
362
- edgesBetween++;
363
- }
364
- }
365
- }
366
- for (const node2 of comm2) {
367
- const node2Neighbors = graph.get(node2);
368
- if (node2Neighbors) {
369
- degree2 += node2Neighbors.size;
370
- }
371
- }
372
- // Modularity gain formula
373
- const gain = (edgesBetween / m) - ((degree1 * degree2) / (4 * m * m));
374
- return gain;
375
- }
376
- // Merge clusters based on modularity
377
- while (clusters.length > 1) {
351
+ // Active clusters
352
+ const activeClusters = new Set(clusters);
353
+ // Community assignments
354
+ const communities = new Map();
355
+ nodes.forEach((node, i) => communities.set(node, i));
356
+ // Merge clusters based on modularity gain
357
+ while (activeClusters.size > 1) {
378
358
  let maxGain = -Infinity;
379
- let merge1 = 0;
380
- let merge2 = 0;
381
- for (let i = 0; i < clusters.length; i++) {
382
- for (let j = i + 1; j < clusters.length; j++) {
383
- const clusterI = clusters[i];
384
- const clusterJ = clusters[j];
385
- if (!clusterI || !clusterJ) {
359
+ let mergeCluster1 = null;
360
+ let mergeCluster2 = null;
361
+ for (const cluster1 of activeClusters) {
362
+ for (const cluster2 of activeClusters) {
363
+ if (cluster1.id >= cluster2.id) {
386
364
  continue;
387
365
  }
388
- const gain = modularityGain(clusterI.members, clusterJ.members);
366
+ // Calculate modularity gain
367
+ let edgesBetween = 0;
368
+ let degreeProduct = 0;
369
+ for (const node1 of cluster1.members) {
370
+ const neighbors = adjacencySet.get(node1);
371
+ const degree1 = degrees.get(node1) ?? 0;
372
+ for (const node2 of cluster2.members) {
373
+ if (neighbors?.has(node2)) {
374
+ edgesBetween++;
375
+ }
376
+ const degree2 = degrees.get(node2) ?? 0;
377
+ degreeProduct += degree1 * degree2;
378
+ }
379
+ }
380
+ const gain = edgesBetween / totalEdges - degreeProduct / (4 * totalEdges * totalEdges);
389
381
  if (gain > maxGain) {
390
382
  maxGain = gain;
391
- merge1 = i;
392
- merge2 = j;
383
+ mergeCluster1 = cluster1;
384
+ mergeCluster2 = cluster2;
393
385
  }
394
386
  }
395
387
  }
396
- // Create new cluster
397
- const cluster1 = clusters[merge1];
398
- const cluster2 = clusters[merge2];
399
- if (!cluster1 || !cluster2) {
400
- continue;
388
+ if (!mergeCluster1 || !mergeCluster2) {
389
+ break;
401
390
  }
391
+ // Create new cluster
402
392
  const newCluster = {
403
393
  id: `cluster-${String(clusterCount++)}`,
404
- members: new Set([...cluster1.members, ...cluster2.members]),
405
- left: cluster1,
406
- right: cluster2,
394
+ members: new Set([...mergeCluster1.members, ...mergeCluster2.members]),
395
+ left: mergeCluster1,
396
+ right: mergeCluster2,
407
397
  distance: -maxGain, // Use negative gain as distance
408
- height: Math.max(cluster1.height, cluster2.height) + 1,
398
+ height: Math.max(mergeCluster1.height, mergeCluster2.height) + 1,
409
399
  };
410
400
  dendrogram.push(newCluster);
411
- // Update community assignments
412
- for (const node of newCluster.members) {
413
- communityMap.set(node, clusterCount - 1);
414
- }
415
- // Remove merged clusters
416
- clusters.splice(Math.max(merge1, merge2), 1);
417
- clusters.splice(Math.min(merge1, merge2), 1);
418
- clusters.push(newCluster);
401
+ // Remove old and add new
402
+ activeClusters.delete(mergeCluster1);
403
+ activeClusters.delete(mergeCluster2);
404
+ activeClusters.add(newCluster);
419
405
  }
420
- // Generate clusters at different levels
406
+ // Root is the last remaining cluster
407
+ const root = Array.from(activeClusters)[0] ?? dendrogram[dendrogram.length - 1];
408
+ // Generate clusters at different heights
421
409
  const clustersByLevel = new Map();
422
- const root = clusters[0];
423
410
  if (!root) {
424
411
  return {
425
412
  root: { id: "empty", members: new Set(), distance: 0, height: 0 },
@@ -436,4 +423,19 @@ export function modularityHierarchicalClustering(graph) {
436
423
  clusters: clustersByLevel,
437
424
  };
438
425
  }
426
+ /**
427
+ * Agglomerative hierarchical clustering
428
+ * Builds clusters bottom-up by merging closest pairs
429
+ * @param graph - Undirected graph - accepts Graph class or Map<T, Set<T>>
430
+ * @param linkage - Linkage method for cluster distance
431
+ * @returns Hierarchical clustering result
432
+ *
433
+ * Time Complexity: O(n³) naive, O(n² log n) with heap
434
+ * Space Complexity: O(n²)
435
+ */
436
+ export function hierarchicalClustering(graph, linkage = "single") {
437
+ // Convert Graph to adjacency set representation
438
+ const adjacencySet = graphToAdjacencySet(graph);
439
+ return hierarchicalClusteringImpl(adjacencySet, linkage);
440
+ }
439
441
  //# sourceMappingURL=hierarchical.js.map