@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
@@ -1,5 +1,6 @@
1
- import type {Graph} from "../core/graph.js";
2
- import type {NodeId} from "../types/index.js";
1
+ import type { Graph } from "../core/graph.js";
2
+ import type { NodeId } from "../types/index.js";
3
+ import { euclideanDistance } from "../utils/math-utilities.js";
3
4
 
4
5
  /**
5
6
  * Spectral Clustering implementation
@@ -27,16 +28,25 @@ export interface SpectralClusteringResult {
27
28
 
28
29
  /**
29
30
  * Perform spectral clustering on a graph
31
+ *
32
+ * Note: This implementation uses simplified power iteration for eigenvector
33
+ * computation with approximate eigenvalues. For production use cases requiring
34
+ * precise clustering, consider using a proper linear algebra library like ml-matrix.
35
+ *
36
+ * The approximate eigenvalues (0.1, 0.2 for second and third eigenvectors) work
37
+ * well for most graph structures but may produce suboptimal results for graphs
38
+ * with unusual spectral properties.
39
+ * @param graph - The input graph to cluster
40
+ * @param options - Spectral clustering options (k, laplacianType, etc.)
41
+ * @returns Spectral clustering result with communities and cluster assignments
30
42
  */
31
- export function spectralClustering(
32
- graph: Graph,
33
- options: SpectralClusteringOptions,
34
- ): SpectralClusteringResult {
35
- const {
36
- k,
37
- laplacianType = "normalized",
38
- maxIterations = 100,
39
- } = options;
43
+ export function spectralClustering(graph: Graph, options: SpectralClusteringOptions): SpectralClusteringResult {
44
+ const { k, laplacianType = "normalized", maxIterations = 100, tolerance = 1e-4 } = options;
45
+
46
+ // Input validation
47
+ if (k < 1 || !Number.isInteger(k)) {
48
+ throw new Error("k must be a positive integer");
49
+ }
40
50
 
41
51
  const nodes = Array.from(graph.nodes());
42
52
  const nodeIds = nodes.map((node) => node.id);
@@ -47,7 +57,7 @@ export function spectralClustering(
47
57
  const communities: NodeId[][] = nodeIds.map((id) => [id]);
48
58
  const clusterAssignments = new Map<NodeId, number>();
49
59
  nodeIds.forEach((id, index) => clusterAssignments.set(id, index));
50
- return {communities, clusterAssignments};
60
+ return { communities, clusterAssignments };
51
61
  }
52
62
 
53
63
  // Build adjacency matrix
@@ -79,10 +89,10 @@ export function spectralClustering(
79
89
  normalizeRows(dataPoints);
80
90
  }
81
91
 
82
- const kmeans = kMeansClustering(dataPoints, k, maxIterations);
92
+ const kmeans = kMeansClustering(dataPoints, k, maxIterations, tolerance);
83
93
 
84
94
  // Build communities
85
- const communities: NodeId[][] = Array.from({length: k}, () => []);
95
+ const communities: NodeId[][] = Array.from({ length: k }, () => []);
86
96
  const clusterAssignments = new Map<NodeId, number>();
87
97
 
88
98
  for (let i = 0; i < nodeIds.length; i++) {
@@ -124,10 +134,13 @@ export function spectralClustering(
124
134
 
125
135
  /**
126
136
  * Build adjacency matrix from graph
137
+ * @param graph - The input graph
138
+ * @param nodeIds - Array of node IDs defining matrix index order
139
+ * @returns Square adjacency matrix with edge weights
127
140
  */
128
141
  function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
129
142
  const n = nodeIds.length;
130
- const matrix: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
143
+ const matrix: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
131
144
  const nodeToIndex = new Map<NodeId, number>();
132
145
 
133
146
  nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
@@ -165,10 +178,13 @@ function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
165
178
 
166
179
  /**
167
180
  * Build Laplacian matrix from adjacency matrix
181
+ * @param adjacency - Square adjacency matrix
182
+ * @param type - Type of Laplacian (unnormalized, normalized, or randomWalk)
183
+ * @returns The computed Laplacian matrix
168
184
  */
169
185
  function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
170
186
  const n = adjacency.length;
171
- const laplacian: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
187
+ const laplacian: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
172
188
 
173
189
  // Calculate degree matrix
174
190
  const degrees = Array(n).fill(0) as number[];
@@ -224,7 +240,14 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
224
240
  const adjacencyVal = adjacencyRow[j];
225
241
  const di = degrees[i];
226
242
  const dj = degrees[j];
227
- if (adjacencyVal !== undefined && adjacencyVal > 0 && di !== undefined && dj !== undefined && di > 0 && dj > 0) {
243
+ if (
244
+ adjacencyVal !== undefined &&
245
+ adjacencyVal > 0 &&
246
+ di !== undefined &&
247
+ dj !== undefined &&
248
+ di > 0 &&
249
+ dj > 0
250
+ ) {
228
251
  const laplacianRow = laplacian[i];
229
252
  if (laplacianRow) {
230
253
  laplacianRow[j] = -adjacencyVal / Math.sqrt(di * dj);
@@ -268,15 +291,21 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
268
291
  /**
269
292
  * Find k smallest eigenvectors using simplified eigendecomposition
270
293
  * This is a simplified implementation - in practice, you'd use LAPACK or similar
294
+ * @param matrix - The Laplacian matrix for eigendecomposition
295
+ * @param k - Number of smallest eigenvectors to find
296
+ * @returns Object containing eigenvalues and corresponding eigenvectors
271
297
  */
272
- function findSmallestEigenvectors(matrix: number[][], k: number): {
298
+ function findSmallestEigenvectors(
299
+ matrix: number[][],
300
+ k: number,
301
+ ): {
273
302
  eigenvalues: number[];
274
303
  eigenvectors: number[][];
275
304
  } {
276
305
  const n = matrix.length;
277
306
 
278
307
  if (n === 0 || k === 0) {
279
- return {eigenvalues: [], eigenvectors: []};
308
+ return { eigenvalues: [], eigenvectors: [] };
280
309
  }
281
310
 
282
311
  // For very small matrices, use the full power iteration approach
@@ -299,10 +328,12 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
299
328
 
300
329
  for (let eigIdx = 0; eigIdx < k; eigIdx++) {
301
330
  // Initialize random vector
302
- let vector = Array(n).fill(0).map(() => Math.random() - 0.5);
331
+ let vector = Array(n)
332
+ .fill(0)
333
+ .map(() => Math.random() - 0.5);
303
334
 
304
335
  // Normalize initial vector
305
- const initNorm = Math.sqrt(vector.reduce((sum, val) => sum + (val * val), 0));
336
+ const initNorm = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
306
337
  if (initNorm > 0) {
307
338
  vector = vector.map((val) => val / initNorm);
308
339
  }
@@ -314,8 +345,8 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
314
345
  continue;
315
346
  }
316
347
 
317
- const dot = vector.reduce((sum, val, idx) => sum + (val * (ejVector[idx] ?? 0)), 0);
318
- vector = vector.map((val, idx) => val - (dot * (ejVector[idx] ?? 0)));
348
+ const dot = vector.reduce((sum, val, idx) => sum + val * (ejVector[idx] ?? 0), 0);
349
+ vector = vector.map((val, idx) => val - dot * (ejVector[idx] ?? 0));
319
350
  }
320
351
 
321
352
  // Power iteration
@@ -330,7 +361,7 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
330
361
  if (matrixVal !== undefined && vecVal !== undefined) {
331
362
  const nvVal = newVector[i];
332
363
  if (nvVal !== undefined) {
333
- newVector[i] = nvVal + (matrixVal * vecVal);
364
+ newVector[i] = nvVal + matrixVal * vecVal;
334
365
  }
335
366
  }
336
367
  }
@@ -343,20 +374,20 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
343
374
  continue;
344
375
  }
345
376
 
346
- const dot = newVector.reduce((sum, val, idx) => sum + (val * (ejVector[idx] ?? 0)), 0);
377
+ const dot = newVector.reduce((sum, val, idx) => sum + val * (ejVector[idx] ?? 0), 0);
347
378
  for (let i = 0; i < n; i++) {
348
379
  const ejVal = ejVector[i];
349
380
  if (ejVal !== undefined) {
350
381
  const nvVal = newVector[i];
351
382
  if (nvVal !== undefined) {
352
- newVector[i] = nvVal - (dot * ejVal);
383
+ newVector[i] = nvVal - dot * ejVal;
353
384
  }
354
385
  }
355
386
  }
356
387
  }
357
388
 
358
389
  // Normalize
359
- const norm = Math.sqrt(newVector.reduce((sum, val) => sum + ((val * val)), 0));
390
+ const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
360
391
  if (norm > 1e-10) {
361
392
  vector = newVector.map((val) => val / norm);
362
393
  } else {
@@ -375,29 +406,30 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
375
406
  if (matrixVal !== undefined && vecVal !== undefined) {
376
407
  const avVal = Av[i];
377
408
  if (avVal !== undefined) {
378
- Av[i] = avVal + (matrixVal * vecVal);
409
+ Av[i] = avVal + matrixVal * vecVal;
379
410
  }
380
411
  }
381
412
  }
382
413
  }
383
414
  eigenvalue = vector.reduce((sum, val, idx) => {
384
415
  const avVal = Av[idx];
385
- return sum + (val * (avVal ?? 0));
416
+ return sum + val * (avVal ?? 0);
386
417
  }, 0);
387
418
 
388
419
  eigenvectors.push(vector);
389
420
  eigenvalues.push(eigenvalue);
390
421
  }
391
422
 
392
- return {eigenvalues, eigenvectors};
423
+ return { eigenvalues, eigenvectors };
393
424
  }
394
425
 
395
426
  /**
396
427
  * Normalize matrix rows to unit length
428
+ * @param matrix - Matrix to normalize in place
397
429
  */
398
430
  function normalizeRows(matrix: number[][]): void {
399
431
  for (const row of matrix) {
400
- const norm = Math.sqrt(row.reduce((sum, val) => sum + (val * val), 0));
432
+ const norm = Math.sqrt(row.reduce((sum, val) => sum + val * val, 0));
401
433
  if (norm > 0) {
402
434
  for (let j = 0; j < row.length; j++) {
403
435
  const val = row[j];
@@ -411,24 +443,30 @@ function normalizeRows(matrix: number[][]): void {
411
443
 
412
444
  /**
413
445
  * K-means clustering algorithm
446
+ * @param data - Array of data points, each point is an array of coordinates
447
+ * @param k - Number of clusters to form
448
+ * @param maxIterations - Maximum number of iterations
449
+ * @param tolerance - Convergence tolerance for centroid movement
450
+ * @returns Object containing cluster assignments and final centroids
414
451
  */
415
452
  function kMeansClustering(
416
453
  data: number[][],
417
454
  k: number,
418
455
  maxIterations: number,
419
- ): {assignments: number[], centroids: number[][]} {
456
+ tolerance = 1e-4,
457
+ ): { assignments: number[]; centroids: number[][] } {
420
458
  const n = data.length;
421
459
  const d = data[0]?.length ?? 0;
422
460
 
423
461
  // Handle edge cases
424
462
  if (n === 0 || k === 0) {
425
- return {assignments: [], centroids: []};
463
+ return { assignments: [], centroids: [] };
426
464
  }
427
465
 
428
466
  if (k >= n) {
429
467
  // Each point is its own cluster
430
468
  return {
431
- assignments: Array.from({length: n}, (_, i) => i),
469
+ assignments: Array.from({ length: n }, (_, i) => i),
432
470
  centroids: data.slice(0, n),
433
471
  };
434
472
  }
@@ -441,7 +479,7 @@ function kMeansClustering(
441
479
  const idx = Math.floor(Math.random() * n);
442
480
  if (!selectedIndices.has(idx) && data[idx]) {
443
481
  selectedIndices.add(idx);
444
- centroids.push([... data[idx]]);
482
+ centroids.push([...data[idx]]);
445
483
  }
446
484
  }
447
485
 
@@ -480,24 +518,27 @@ function kMeansClustering(
480
518
  assignments[i] = bestCluster;
481
519
  }
482
520
 
483
- // Check for convergence
484
- let changed = false;
521
+ // Check for convergence based on assignment changes
522
+ let assignmentsChanged = false;
485
523
  for (let i = 0; i < n; i++) {
486
524
  if (assignments[i] !== oldAssignments[i]) {
487
- changed = true;
525
+ assignmentsChanged = true;
488
526
  break;
489
527
  }
490
528
  }
491
529
 
492
- if (!changed) {
530
+ if (!assignmentsChanged) {
493
531
  break;
494
532
  }
495
533
 
496
- oldAssignments = [... assignments];
534
+ oldAssignments = [...assignments];
535
+
536
+ // Store old centroids for tolerance-based convergence check
537
+ const oldCentroids = centroids.map((c) => [...c]);
497
538
 
498
539
  // Update centroids
499
540
  const counts = Array(k).fill(0) as number[];
500
- const sums: number[][] = Array.from({length: k}, () => Array(d).fill(0) as number[]);
541
+ const sums: number[][] = Array.from({ length: k }, () => Array(d).fill(0) as number[]);
501
542
 
502
543
  for (let i = 0; i < n; i++) {
503
544
  const cluster = assignments[i] ?? 0;
@@ -534,36 +575,49 @@ function kMeansClustering(
534
575
  for (let j = 0; j < d; j++) {
535
576
  const sumVal = sumsRow[j];
536
577
  if (sumVal !== undefined) {
537
- const countVal = counts[i];
538
- if (countVal !== undefined) {
539
- centroidsRow[j] = sumVal / countVal;
578
+ const countValInner = counts[i];
579
+ if (countValInner !== undefined) {
580
+ centroidsRow[j] = sumVal / countValInner;
540
581
  }
541
582
  }
542
583
  }
543
584
  }
544
585
  }
545
586
  }
546
- }
547
587
 
548
- return {assignments, centroids};
549
- }
588
+ // Check for tolerance-based convergence (centroid movement)
589
+ let maxCentroidShift = 0;
590
+ for (let i = 0; i < k; i++) {
591
+ const oldCentroid = oldCentroids[i];
592
+ const newCentroid = centroids[i];
593
+ if (oldCentroid && newCentroid) {
594
+ const shift = euclideanDistance(oldCentroid, newCentroid);
595
+ if (shift > maxCentroidShift) {
596
+ maxCentroidShift = shift;
597
+ }
598
+ }
599
+ }
550
600
 
551
- /**
552
- * Calculate Euclidean distance between two vectors
553
- */
554
- function euclideanDistance(a: number[], b: number[]): number {
555
- let sum = 0;
556
- for (let i = 0; i < a.length; i++) {
557
- const diff = (a[i] ?? 0) - (b[i] ?? 0);
558
- sum += diff * diff;
601
+ if (maxCentroidShift < tolerance) {
602
+ break;
603
+ }
559
604
  }
560
- return Math.sqrt(sum);
605
+
606
+ return { assignments, centroids };
561
607
  }
562
608
 
563
609
  /**
564
610
  * Compute smallest eigenvectors for small k (optimized for k=2, k=3)
611
+ * @param matrix - The Laplacian matrix
612
+ * @param k - Number of eigenvectors to compute (1-3)
613
+ * @param n - Size of the matrix
614
+ * @returns Object containing eigenvalues and eigenvectors
565
615
  */
566
- function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: number): {
616
+ function computeSmallestEigenvectorsSimple(
617
+ matrix: number[][],
618
+ k: number,
619
+ n: number,
620
+ ): {
567
621
  eigenvalues: number[];
568
622
  eigenvectors: number[][];
569
623
  } {
@@ -579,11 +633,13 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
579
633
  if (k >= 2) {
580
634
  // Use power iteration on I - L/lambda_max to find second smallest
581
635
  const maxEig = 2; // For normalized Laplacian, max eigenvalue <= 2
582
- let vector = Array(n).fill(0).map(() => Math.random() - 0.5);
636
+ let vector = Array(n)
637
+ .fill(0)
638
+ .map(() => Math.random() - 0.5);
583
639
 
584
640
  // Make orthogonal to first eigenvector
585
- const dot1 = vector.reduce((sum, val) => sum + (val / Math.sqrt(n)), 0);
586
- vector = vector.map((val) => val - (dot1 / Math.sqrt(n)));
641
+ const dot1 = vector.reduce((sum, val) => sum + val / Math.sqrt(n), 0);
642
+ vector = vector.map((val) => val - dot1 / Math.sqrt(n));
587
643
 
588
644
  // Power iteration on shifted matrix
589
645
  for (let iter = 0; iter < 100; iter++) {
@@ -600,18 +656,18 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
600
656
  for (let j = 0; j < n; j++) {
601
657
  const matrixVal = matrix[i]?.[j] ?? 0;
602
658
  const vecVal = vector[j] ?? 0;
603
- newVector[i] = (newVector[i] ?? 0) - ((matrixVal * vecVal) / maxEig);
659
+ newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / maxEig;
604
660
  }
605
661
  }
606
662
 
607
663
  // Orthogonalize against first eigenvector
608
- const dot = newVector.reduce((sum, val) => sum + (val / Math.sqrt(n)), 0);
664
+ const dot = newVector.reduce((sum, val) => sum + val / Math.sqrt(n), 0);
609
665
  for (let i = 0; i < n; i++) {
610
- newVector[i] = (newVector[i] ?? 0) - (dot / Math.sqrt(n));
666
+ newVector[i] = (newVector[i] ?? 0) - dot / Math.sqrt(n);
611
667
  }
612
668
 
613
669
  // Normalize
614
- const norm = Math.sqrt(newVector.reduce((sum, val) => sum + (val * val), 0));
670
+ const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
615
671
  if (norm > 1e-10) {
616
672
  vector = newVector.map((val) => val / norm);
617
673
  }
@@ -623,12 +679,14 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
623
679
 
624
680
  // For k = 3, add another eigenvector
625
681
  if (k >= 3) {
626
- let vector = Array(n).fill(0).map(() => Math.random() - 0.5);
682
+ let vector = Array(n)
683
+ .fill(0)
684
+ .map(() => Math.random() - 0.5);
627
685
 
628
686
  // Orthogonalize against previous eigenvectors
629
687
  for (const prev of eigenvectors) {
630
- const dot = vector.reduce((sum, val, idx) => sum + (val * (prev[idx] ?? 0)), 0);
631
- vector = vector.map((val, idx) => val - (dot * (prev[idx] ?? 0)));
688
+ const dot = vector.reduce((sum, val, idx) => sum + val * (prev[idx] ?? 0), 0);
689
+ vector = vector.map((val, idx) => val - dot * (prev[idx] ?? 0));
632
690
  }
633
691
 
634
692
  // Similar power iteration
@@ -645,20 +703,20 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
645
703
  for (let j = 0; j < n; j++) {
646
704
  const matrixVal = matrix[i]?.[j] ?? 0;
647
705
  const vecVal = vector[j] ?? 0;
648
- newVector[i] = (newVector[i] ?? 0) - ((matrixVal * vecVal) / 2);
706
+ newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / 2;
649
707
  }
650
708
  }
651
709
 
652
710
  // Orthogonalize
653
711
  for (const prev of eigenvectors) {
654
- const dot = newVector.reduce((sum, val, idx) => sum + (val * (prev[idx] ?? 0)), 0);
712
+ const dot = newVector.reduce((sum, val, idx) => sum + val * (prev[idx] ?? 0), 0);
655
713
  for (let i = 0; i < n; i++) {
656
- newVector[i] = (newVector[i] ?? 0) - (dot * (prev[i] ?? 0));
714
+ newVector[i] = (newVector[i] ?? 0) - dot * (prev[i] ?? 0);
657
715
  }
658
716
  }
659
717
 
660
718
  // Normalize
661
- const norm = Math.sqrt(newVector.reduce((sum, val) => sum + (val * val), 0));
719
+ const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
662
720
  if (norm > 1e-10) {
663
721
  vector = newVector.map((val) => val / norm);
664
722
  }
@@ -668,5 +726,5 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
668
726
  eigenvalues.push(0.2); // Approximate
669
727
  }
670
728
 
671
- return {eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k)};
729
+ return { eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k) };
672
730
  }