@graphty/algorithms 1.3.1 → 1.5.0

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 (314) hide show
  1. package/README.md +368 -422
  2. package/dist/algorithms.js +567 -264
  3. package/dist/algorithms.js.map +1 -1
  4. package/dist/src/algorithms/centrality/betweenness.d.ts +10 -0
  5. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  6. package/dist/src/algorithms/centrality/betweenness.js +27 -6
  7. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  8. package/dist/src/algorithms/centrality/closeness.d.ts +11 -2
  9. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  10. package/dist/src/algorithms/centrality/closeness.js +17 -3
  11. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  12. package/dist/src/algorithms/centrality/degree.d.ts +7 -0
  13. package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
  14. package/dist/src/algorithms/centrality/degree.js +9 -6
  15. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  16. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +21 -3
  17. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -1
  18. package/dist/src/algorithms/centrality/delta-pagerank-simple.js +25 -7
  19. package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -1
  20. package/dist/src/algorithms/centrality/delta-pagerank.d.ts +28 -2
  21. package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -1
  22. package/dist/src/algorithms/centrality/delta-pagerank.js +30 -4
  23. package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -1
  24. package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
  25. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
  26. package/dist/src/algorithms/centrality/eigenvector.js +11 -4
  27. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
  28. package/dist/src/algorithms/centrality/hits.d.ts +10 -3
  29. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
  30. package/dist/src/algorithms/centrality/hits.js +11 -4
  31. package/dist/src/algorithms/centrality/hits.js.map +1 -1
  32. package/dist/src/algorithms/centrality/index.d.ts +1 -1
  33. package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
  34. package/dist/src/algorithms/centrality/index.js +1 -1
  35. package/dist/src/algorithms/centrality/index.js.map +1 -1
  36. package/dist/src/algorithms/centrality/katz.d.ts +10 -3
  37. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
  38. package/dist/src/algorithms/centrality/katz.js +13 -8
  39. package/dist/src/algorithms/centrality/katz.js.map +1 -1
  40. package/dist/src/algorithms/centrality/pagerank.d.ts +14 -0
  41. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  42. package/dist/src/algorithms/centrality/pagerank.js +16 -1
  43. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  44. package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
  45. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  46. package/dist/src/algorithms/community/girvan-newman.js +21 -4
  47. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  48. package/dist/src/algorithms/community/index.d.ts.map +1 -1
  49. package/dist/src/algorithms/community/index.js.map +1 -1
  50. package/dist/src/algorithms/community/label-propagation.d.ts +0 -3
  51. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  52. package/dist/src/algorithms/community/label-propagation.js +13 -6
  53. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  54. package/dist/src/algorithms/community/leiden.d.ts +0 -1
  55. package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
  56. package/dist/src/algorithms/community/leiden.js +37 -6
  57. package/dist/src/algorithms/community/leiden.js.map +1 -1
  58. package/dist/src/algorithms/community/louvain-optimized.d.ts +37 -0
  59. package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -1
  60. package/dist/src/algorithms/community/louvain-optimized.js +42 -7
  61. package/dist/src/algorithms/community/louvain-optimized.js.map +1 -1
  62. package/dist/src/algorithms/community/louvain.d.ts +2 -3
  63. package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
  64. package/dist/src/algorithms/community/louvain.js +19 -6
  65. package/dist/src/algorithms/community/louvain.js.map +1 -1
  66. package/dist/src/algorithms/community/modularity-utils.d.ts +0 -4
  67. package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -1
  68. package/dist/src/algorithms/community/modularity-utils.js +1 -5
  69. package/dist/src/algorithms/community/modularity-utils.js.map +1 -1
  70. package/dist/src/algorithms/components/connected.d.ts +23 -0
  71. package/dist/src/algorithms/components/connected.d.ts.map +1 -1
  72. package/dist/src/algorithms/components/connected.js +29 -3
  73. package/dist/src/algorithms/components/connected.js.map +1 -1
  74. package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
  75. package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
  76. package/dist/src/algorithms/matching/bipartite.js +10 -0
  77. package/dist/src/algorithms/matching/bipartite.js.map +1 -1
  78. package/dist/src/algorithms/matching/index.d.ts.map +1 -1
  79. package/dist/src/algorithms/matching/index.js.map +1 -1
  80. package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
  81. package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
  82. package/dist/src/algorithms/matching/isomorphism.js +36 -2
  83. package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
  84. package/dist/src/algorithms/mst/index.d.ts.map +1 -1
  85. package/dist/src/algorithms/mst/index.js.map +1 -1
  86. package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
  87. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
  88. package/dist/src/algorithms/mst/kruskal.js +17 -3
  89. package/dist/src/algorithms/mst/kruskal.js.map +1 -1
  90. package/dist/src/algorithms/mst/prim.d.ts +9 -0
  91. package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
  92. package/dist/src/algorithms/mst/prim.js +9 -0
  93. package/dist/src/algorithms/mst/prim.js.map +1 -1
  94. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
  95. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  96. package/dist/src/algorithms/shortest-path/bellman-ford.js +10 -0
  97. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  98. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +12 -5
  99. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -1
  100. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +19 -17
  101. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -1
  102. package/dist/src/algorithms/shortest-path/dijkstra.d.ts +15 -0
  103. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  104. package/dist/src/algorithms/shortest-path/dijkstra.js +15 -0
  105. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  106. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
  107. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
  108. package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
  109. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
  110. package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
  111. package/dist/src/algorithms/shortest-path/index.js.map +1 -1
  112. package/dist/src/algorithms/traversal/bfs-unified.d.ts +13 -0
  113. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
  114. package/dist/src/algorithms/traversal/bfs-unified.js +37 -0
  115. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
  116. package/dist/src/algorithms/traversal/bfs-variants.d.ts +23 -0
  117. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
  118. package/dist/src/algorithms/traversal/bfs-variants.js +34 -0
  119. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
  120. package/dist/src/algorithms/traversal/bfs.d.ts +1 -1
  121. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  122. package/dist/src/algorithms/traversal/bfs.js +1 -1
  123. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  124. package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
  125. package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
  126. package/dist/src/algorithms/traversal/dfs.js +48 -0
  127. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  128. package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
  129. package/dist/src/algorithms/traversal/index.js.map +1 -1
  130. package/dist/src/benchmark-all-algorithms.d.ts +2 -0
  131. package/dist/src/benchmark-all-algorithms.d.ts.map +1 -1
  132. package/dist/src/benchmark-all-algorithms.js +32 -3
  133. package/dist/src/benchmark-all-algorithms.js.map +1 -1
  134. package/dist/src/clustering/hierarchical.d.ts +8 -1
  135. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  136. package/dist/src/clustering/hierarchical.js +22 -6
  137. package/dist/src/clustering/hierarchical.js.map +1 -1
  138. package/dist/src/clustering/index.d.ts.map +1 -1
  139. package/dist/src/clustering/index.js.map +1 -1
  140. package/dist/src/clustering/k-core.d.ts +2 -5
  141. package/dist/src/clustering/k-core.d.ts.map +1 -1
  142. package/dist/src/clustering/k-core.js +14 -12
  143. package/dist/src/clustering/k-core.js.map +1 -1
  144. package/dist/src/clustering/mcl.d.ts +6 -0
  145. package/dist/src/clustering/mcl.d.ts.map +1 -1
  146. package/dist/src/clustering/mcl.js +34 -3
  147. package/dist/src/clustering/mcl.js.map +1 -1
  148. package/dist/src/clustering/spectral.d.ts +4 -1
  149. package/dist/src/clustering/spectral.d.ts.map +1 -1
  150. package/dist/src/clustering/spectral.js +61 -28
  151. package/dist/src/clustering/spectral.js.map +1 -1
  152. package/dist/src/core/graph.d.ts +45 -0
  153. package/dist/src/core/graph.d.ts.map +1 -1
  154. package/dist/src/core/graph.js +45 -1
  155. package/dist/src/core/graph.js.map +1 -1
  156. package/dist/src/data-structures/index.d.ts.map +1 -1
  157. package/dist/src/data-structures/index.js.map +1 -1
  158. package/dist/src/data-structures/priority-queue.d.ts +18 -1
  159. package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
  160. package/dist/src/data-structures/priority-queue.js +26 -5
  161. package/dist/src/data-structures/priority-queue.js.map +1 -1
  162. package/dist/src/data-structures/union-find.d.ts +21 -0
  163. package/dist/src/data-structures/union-find.d.ts.map +1 -1
  164. package/dist/src/data-structures/union-find.js +21 -0
  165. package/dist/src/data-structures/union-find.js.map +1 -1
  166. package/dist/src/flow/ford-fulkerson.d.ts +4 -2
  167. package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
  168. package/dist/src/flow/ford-fulkerson.js +31 -2
  169. package/dist/src/flow/ford-fulkerson.js.map +1 -1
  170. package/dist/src/flow/min-cut.d.ts +0 -3
  171. package/dist/src/flow/min-cut.d.ts.map +1 -1
  172. package/dist/src/flow/min-cut.js +17 -4
  173. package/dist/src/flow/min-cut.js.map +1 -1
  174. package/dist/src/index.d.ts +2 -1
  175. package/dist/src/index.d.ts.map +1 -1
  176. package/dist/src/index.js +2 -1
  177. package/dist/src/index.js.map +1 -1
  178. package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
  179. package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
  180. package/dist/src/link-prediction/adamic-adar.js +36 -10
  181. package/dist/src/link-prediction/adamic-adar.js.map +1 -1
  182. package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
  183. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  184. package/dist/src/link-prediction/common-neighbors.js +27 -6
  185. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  186. package/dist/src/link-prediction/index.d.ts.map +1 -1
  187. package/dist/src/optimized/bit-packed.d.ts +103 -53
  188. package/dist/src/optimized/bit-packed.d.ts.map +1 -1
  189. package/dist/src/optimized/bit-packed.js +104 -54
  190. package/dist/src/optimized/bit-packed.js.map +1 -1
  191. package/dist/src/optimized/csr-graph.d.ts +87 -24
  192. package/dist/src/optimized/csr-graph.d.ts.map +1 -1
  193. package/dist/src/optimized/csr-graph.js +87 -24
  194. package/dist/src/optimized/csr-graph.js.map +1 -1
  195. package/dist/src/optimized/direction-optimized-bfs.d.ts +37 -16
  196. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
  197. package/dist/src/optimized/direction-optimized-bfs.js +39 -20
  198. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
  199. package/dist/src/optimized/graph-adapter.d.ts +58 -9
  200. package/dist/src/optimized/graph-adapter.d.ts.map +1 -1
  201. package/dist/src/optimized/graph-adapter.js +58 -10
  202. package/dist/src/optimized/graph-adapter.js.map +1 -1
  203. package/dist/src/optimized/index.d.ts +1 -1
  204. package/dist/src/optimized/index.d.ts.map +1 -1
  205. package/dist/src/optimized/index.js +3 -1
  206. package/dist/src/optimized/index.js.map +1 -1
  207. package/dist/src/pathfinding/astar.d.ts +25 -9
  208. package/dist/src/pathfinding/astar.d.ts.map +1 -1
  209. package/dist/src/pathfinding/astar.js +25 -10
  210. package/dist/src/pathfinding/astar.js.map +1 -1
  211. package/dist/src/pathfinding/utils.d.ts +15 -6
  212. package/dist/src/pathfinding/utils.d.ts.map +1 -1
  213. package/dist/src/pathfinding/utils.js +19 -7
  214. package/dist/src/pathfinding/utils.js.map +1 -1
  215. package/dist/src/research/grsbm.d.ts +0 -1
  216. package/dist/src/research/grsbm.d.ts.map +1 -1
  217. package/dist/src/research/grsbm.js +23 -10
  218. package/dist/src/research/grsbm.js.map +1 -1
  219. package/dist/src/research/index.d.ts.map +1 -1
  220. package/dist/src/research/index.js.map +1 -1
  221. package/dist/src/research/sync.d.ts +0 -1
  222. package/dist/src/research/sync.d.ts.map +1 -1
  223. package/dist/src/research/sync.js +29 -8
  224. package/dist/src/research/sync.js.map +1 -1
  225. package/dist/src/research/terahac.d.ts +0 -1
  226. package/dist/src/research/terahac.d.ts.map +1 -1
  227. package/dist/src/research/terahac.js +32 -4
  228. package/dist/src/research/terahac.js.map +1 -1
  229. package/dist/src/utils/graph-converters.d.ts +31 -0
  230. package/dist/src/utils/graph-converters.d.ts.map +1 -1
  231. package/dist/src/utils/graph-converters.js +31 -0
  232. package/dist/src/utils/graph-converters.js.map +1 -1
  233. package/dist/src/utils/graph-utilities.d.ts.map +1 -1
  234. package/dist/src/utils/graph-utilities.js.map +1 -1
  235. package/dist/src/utils/math-utilities.d.ts +11 -0
  236. package/dist/src/utils/math-utilities.d.ts.map +1 -1
  237. package/dist/src/utils/math-utilities.js +14 -5
  238. package/dist/src/utils/math-utilities.js.map +1 -1
  239. package/dist/src/utils/optimization-helpers.d.ts +9 -0
  240. package/dist/src/utils/optimization-helpers.d.ts.map +1 -1
  241. package/dist/src/utils/optimization-helpers.js +12 -0
  242. package/dist/src/utils/optimization-helpers.js.map +1 -1
  243. package/dist/src/utils/priorityQueue.d.ts +24 -0
  244. package/dist/src/utils/priorityQueue.d.ts.map +1 -1
  245. package/dist/src/utils/priorityQueue.js +34 -4
  246. package/dist/src/utils/priorityQueue.js.map +1 -1
  247. package/dist/tsconfig.tsbuildinfo +1 -0
  248. package/package.json +56 -65
  249. package/src/algorithms/centrality/betweenness.ts +34 -17
  250. package/src/algorithms/centrality/closeness.ts +34 -17
  251. package/src/algorithms/centrality/degree.ts +15 -19
  252. package/src/algorithms/centrality/delta-pagerank-simple.ts +36 -28
  253. package/src/algorithms/centrality/delta-pagerank.ts +34 -11
  254. package/src/algorithms/centrality/eigenvector.ts +14 -15
  255. package/src/algorithms/centrality/hits.ts +17 -17
  256. package/src/algorithms/centrality/index.ts +20 -15
  257. package/src/algorithms/centrality/katz.ts +17 -25
  258. package/src/algorithms/centrality/pagerank.ts +26 -17
  259. package/src/algorithms/community/girvan-newman.ts +39 -28
  260. package/src/algorithms/community/index.ts +6 -7
  261. package/src/algorithms/community/label-propagation.ts +20 -28
  262. package/src/algorithms/community/leiden.ts +61 -37
  263. package/src/algorithms/community/louvain-optimized.ts +49 -20
  264. package/src/algorithms/community/louvain.ts +35 -25
  265. package/src/algorithms/community/modularity-utils.ts +5 -17
  266. package/src/algorithms/components/connected.ts +40 -17
  267. package/src/algorithms/matching/bipartite.ts +17 -10
  268. package/src/algorithms/matching/index.ts +4 -4
  269. package/src/algorithms/matching/isomorphism.ts +53 -23
  270. package/src/algorithms/mst/index.ts +3 -4
  271. package/src/algorithms/mst/kruskal.ts +21 -6
  272. package/src/algorithms/mst/prim.ts +13 -4
  273. package/src/algorithms/shortest-path/bellman-ford.ts +16 -15
  274. package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +29 -29
  275. package/src/algorithms/shortest-path/dijkstra.ts +23 -17
  276. package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
  277. package/src/algorithms/shortest-path/index.ts +5 -5
  278. package/src/algorithms/traversal/bfs-unified.ts +64 -60
  279. package/src/algorithms/traversal/bfs-variants.ts +61 -37
  280. package/src/algorithms/traversal/bfs.ts +1 -7
  281. package/src/algorithms/traversal/dfs.ts +61 -42
  282. package/src/algorithms/traversal/index.ts +3 -3
  283. package/src/benchmark-all-algorithms.ts +57 -25
  284. package/src/clustering/hierarchical.ts +40 -34
  285. package/src/clustering/index.ts +8 -8
  286. package/src/clustering/k-core.ts +26 -42
  287. package/src/clustering/mcl.ts +53 -23
  288. package/src/clustering/spectral.ts +91 -57
  289. package/src/core/graph.ts +54 -14
  290. package/src/data-structures/index.ts +2 -2
  291. package/src/data-structures/priority-queue.ts +36 -11
  292. package/src/data-structures/union-find.ts +22 -1
  293. package/src/flow/ford-fulkerson.ts +62 -35
  294. package/src/flow/min-cut.ts +43 -45
  295. package/src/index.ts +3 -2
  296. package/src/link-prediction/adamic-adar.ts +65 -51
  297. package/src/link-prediction/common-neighbors.ts +45 -36
  298. package/src/link-prediction/index.ts +1 -1
  299. package/src/optimized/bit-packed.ts +106 -56
  300. package/src/optimized/csr-graph.ts +93 -30
  301. package/src/optimized/direction-optimized-bfs.ts +44 -25
  302. package/src/optimized/graph-adapter.ts +64 -16
  303. package/src/optimized/index.ts +7 -9
  304. package/src/pathfinding/astar.ts +40 -25
  305. package/src/pathfinding/utils.ts +20 -13
  306. package/src/research/grsbm.ts +31 -25
  307. package/src/research/index.ts +4 -4
  308. package/src/research/sync.ts +40 -24
  309. package/src/research/terahac.ts +54 -20
  310. package/src/utils/graph-converters.ts +42 -14
  311. package/src/utils/graph-utilities.ts +11 -37
  312. package/src/utils/math-utilities.ts +17 -8
  313. package/src/utils/optimization-helpers.ts +27 -21
  314. package/src/utils/priorityQueue.ts +40 -6
@@ -1,6 +1,6 @@
1
- import type {Graph} from "../core/graph.js";
2
- import type {NodeId} from "../types/index.js";
3
- import {SeededRandom} from "../utils/math-utilities.js";
1
+ import type { Graph } from "../core/graph.js";
2
+ import type { NodeId } from "../types/index.js";
3
+ import { SeededRandom } from "../utils/math-utilities.js";
4
4
 
5
5
  /**
6
6
  * Configuration options for the GRSBM (Greedy Recursive Spectral Bisection) algorithm
@@ -80,7 +80,6 @@ export interface ClusterExplanation {
80
80
  * community structure by tracking the reasoning behind each split.
81
81
  *
82
82
  * Based on: "Explainable Community Detection via Hierarchical Spectral Clustering" (2024)
83
- *
84
83
  * @param graph - Input graph to cluster
85
84
  * @param config - Configuration options
86
85
  * @returns Hierarchical clustering result with explanations
@@ -109,7 +108,7 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
109
108
 
110
109
  // Initialize root cluster with all nodes
111
110
  const rootMembers = new Set(nodes.map((node) => node.id));
112
- const initialModularity = calculateModularity(graph, new Map([... rootMembers].map((id) => [id, 0])));
111
+ const initialModularity = calculateModularity(graph, new Map([...rootMembers].map((id) => [id, 0])));
113
112
 
114
113
  const root: GRSBMCluster = {
115
114
  id: "root",
@@ -133,25 +132,18 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
133
132
  }
134
133
 
135
134
  // Check stopping criteria
136
- if (currentCluster.depth >= maxDepth ||
137
- currentCluster.members.size < minClusterSize * 2) {
135
+ if (currentCluster.depth >= maxDepth || currentCluster.members.size < minClusterSize * 2) {
138
136
  continue;
139
137
  }
140
138
 
141
139
  // Attempt spectral bisection
142
- const bisectionResult = spectralBisection(
143
- graph,
144
- currentCluster,
145
- numEigenvectors,
146
- tolerance,
147
- maxIterations,
148
- );
140
+ const bisectionResult = spectralBisection(graph, currentCluster, numEigenvectors, tolerance, maxIterations);
149
141
 
150
142
  if (!bisectionResult) {
151
143
  continue; // Could not split this cluster
152
144
  }
153
145
 
154
- const {leftMembers, rightMembers, spectralScore, keyNodes, spectralValues, reason} = bisectionResult;
146
+ const { leftMembers, rightMembers, spectralScore, keyNodes, spectralValues, reason } = bisectionResult;
155
147
 
156
148
  // Create child clusters
157
149
  const leftCluster: GRSBMCluster = {
@@ -183,7 +175,8 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
183
175
  const modularityImprovement = newModularity - currentCluster.modularity;
184
176
 
185
177
  // Only split if modularity improves or is neutral (allow small splits)
186
- if (modularityImprovement >= -0.01) { // Small tolerance for neutral splits
178
+ if (modularityImprovement >= -0.01) {
179
+ // Small tolerance for neutral splits
187
180
  leftCluster.modularity = newModularity;
188
181
  rightCluster.modularity = newModularity;
189
182
 
@@ -245,11 +238,17 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
245
238
 
246
239
  /**
247
240
  * Perform spectral bisection on a cluster
241
+ * @param graph - The input graph
242
+ * @param cluster - The cluster to bisect
243
+ * @param _numEigenvectors - Number of eigenvectors to compute (reserved for future use)
244
+ * @param tolerance - Convergence tolerance
245
+ * @param maxIterations - Maximum number of iterations
246
+ * @returns Bisection result with left/right members, scores, and explanation, or null if bisection failed
248
247
  */
249
248
  function spectralBisection(
250
249
  graph: Graph,
251
250
  cluster: GRSBMCluster,
252
- numEigenvectors: number,
251
+ _numEigenvectors: number,
253
252
  tolerance: number,
254
253
  maxIterations: number,
255
254
  ): {
@@ -278,9 +277,7 @@ function spectralBisection(
278
277
  }
279
278
 
280
279
  // Find optimal split point
281
- const sortedIndices = eigenvector
282
- .map((value, index) => ({value, index}))
283
- .sort((a, b) => a.value - b.value);
280
+ const sortedIndices = eigenvector.map((value, index) => ({ value, index })).sort((a, b) => a.value - b.value);
284
281
 
285
282
  let bestSplitIndex = Math.floor(n / 2);
286
283
  let bestModularity = -Infinity;
@@ -335,7 +332,7 @@ function spectralBisection(
335
332
 
336
333
  // Identify key nodes (those with extreme spectral values)
337
334
  const extremeThreshold = 0.1;
338
- const sortedValues = [... eigenvector].sort((a, b) => Math.abs(b) - Math.abs(a));
335
+ const sortedValues = [...eigenvector].sort((a, b) => Math.abs(b) - Math.abs(a));
339
336
  const threshold = sortedValues[Math.floor(sortedValues.length * extremeThreshold)] ?? 0;
340
337
 
341
338
  const keyNodes: NodeId[] = [];
@@ -369,6 +366,9 @@ function spectralBisection(
369
366
 
370
367
  /**
371
368
  * Create Laplacian matrix for a subgraph
369
+ * @param graph - The input graph
370
+ * @param nodes - The nodes to include in the subgraph
371
+ * @returns The Laplacian matrix as a 2D array
372
372
  */
373
373
  function createLaplacianMatrix(graph: Graph, nodes: NodeId[]): number[][] {
374
374
  const n = nodes.length;
@@ -383,7 +383,7 @@ function createLaplacianMatrix(graph: Graph, nodes: NodeId[]): number[][] {
383
383
  }
384
384
 
385
385
  // Initialize Laplacian matrix
386
- const laplacian = Array.from({length: n}, () => new Array<number>(n).fill(0));
386
+ const laplacian = Array.from({ length: n }, () => new Array<number>(n).fill(0));
387
387
 
388
388
  // Fill adjacency part and calculate degrees
389
389
  const degrees = new Array<number>(n).fill(0);
@@ -420,6 +420,10 @@ function createLaplacianMatrix(graph: Graph, nodes: NodeId[]): number[][] {
420
420
 
421
421
  /**
422
422
  * Compute the Fiedler vector (second smallest eigenvector) using power iteration
423
+ * @param laplacian - The Laplacian matrix
424
+ * @param tolerance - Convergence tolerance
425
+ * @param maxIterations - Maximum number of iterations
426
+ * @returns The Fiedler vector, or null if computation failed
423
427
  */
424
428
  function computeFiedlerVector(laplacian: number[][], tolerance: number, maxIterations: number): number[] | null {
425
429
  const n = laplacian.length;
@@ -443,7 +447,7 @@ function computeFiedlerVector(laplacian: number[][], tolerance: number, maxItera
443
447
  }
444
448
 
445
449
  // Normalize
446
- const norm = Math.sqrt(vector.reduce((sum: number, val: number) => sum + (val * val), 0));
450
+ const norm = Math.sqrt(vector.reduce((sum: number, val: number) => sum + val * val, 0));
447
451
  if (norm > 0) {
448
452
  for (let i = 0; i < n; i++) {
449
453
  const val = vector[i];
@@ -494,7 +498,7 @@ function computeFiedlerVector(laplacian: number[][], tolerance: number, maxItera
494
498
  }
495
499
 
496
500
  // Normalize
497
- const newNorm = Math.sqrt(newVector.reduce((sum, val) => sum + (val * val), 0));
501
+ const newNorm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
498
502
  if (newNorm < tolerance) {
499
503
  break; // Converged to zero vector
500
504
  }
@@ -528,6 +532,9 @@ function computeFiedlerVector(laplacian: number[][], tolerance: number, maxItera
528
532
 
529
533
  /**
530
534
  * Calculate modularity of a graph partitioning
535
+ * @param graph - The input graph
536
+ * @param assignment - Map from node ID to community ID
537
+ * @returns The modularity score
531
538
  */
532
539
  function calculateModularity(graph: Graph, assignment: Map<NodeId, number>): number {
533
540
  const totalEdges = graph.uniqueEdgeCount;
@@ -577,4 +584,3 @@ function calculateModularity(graph: Graph, assignment: Map<NodeId, number>): num
577
584
 
578
585
  return modularity;
579
586
  }
580
-
@@ -7,11 +7,11 @@
7
7
  */
8
8
 
9
9
  // Synergistic Deep Graph Clustering
10
- export {syncClustering, type SynCConfig, type SynCResult} from "./sync.js";
10
+ export { syncClustering, type SynCConfig, type SynCResult } from "./sync.js";
11
11
 
12
12
  // TeraHAC - Hierarchical Agglomerative Clustering
13
- export {teraHAC, type TeraHACConfig, type TeraHACResult} from "./terahac.js";
14
- export {type ClusterNode as TeraHACClusterNode} from "./terahac.js";
13
+ export { teraHAC, type TeraHACConfig, type TeraHACResult } from "./terahac.js";
14
+ export { type ClusterNode as TeraHACClusterNode } from "./terahac.js";
15
15
 
16
16
  // GRSBM - Greedy Recursive Spectral Bisection with Modularity
17
- export {type ClusterExplanation, grsbm, type GRSBMCluster, type GRSBMConfig, type GRSBMResult} from "./grsbm.js";
17
+ export { type ClusterExplanation, grsbm, type GRSBMCluster, type GRSBMConfig, type GRSBMResult } from "./grsbm.js";
@@ -1,6 +1,6 @@
1
- import type {Graph} from "../core/graph.js";
2
- import type {NodeId} from "../types/index.js";
3
- import {euclideanDistance, SeededRandom} from "../utils/math-utilities.js";
1
+ import type { Graph } from "../core/graph.js";
2
+ import type { NodeId } from "../types/index.js";
3
+ import { euclideanDistance, SeededRandom } from "../utils/math-utilities.js";
4
4
 
5
5
  /**
6
6
  * Configuration options for the SynC (Synergistic Deep Graph Clustering) algorithm
@@ -44,20 +44,12 @@ export interface SynCResult {
44
44
  * embeddings and cluster assignments while preserving graph structure.
45
45
  *
46
46
  * Based on: "Synergistic Deep Graph Clustering" (arXiv:2406.15797, June 2024)
47
- *
48
47
  * @param graph - Input graph to cluster
49
48
  * @param config - Configuration options
50
49
  * @returns Clustering result with assignments and embeddings
51
50
  */
52
51
  export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
53
- const {
54
- numClusters,
55
- maxIterations = 100,
56
- tolerance = 1e-6,
57
- seed = 42,
58
- learningRate = 0.01,
59
- lambda = 0.1,
60
- } = config;
52
+ const { numClusters, maxIterations = 100, tolerance = 1e-6, seed = 42, learningRate = 0.01, lambda = 0.1 } = config;
61
53
 
62
54
  // Set random seed for reproducibility (save original)
63
55
  const originalRandom = Math.random;
@@ -78,7 +70,9 @@ export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
78
70
  }
79
71
 
80
72
  if (numClusters <= 0 || numClusters > nodeCount) {
81
- throw new Error(`Invalid number of clusters: ${String(numClusters)}. Must be between 1 and ${String(nodeCount)}`);
73
+ throw new Error(
74
+ `Invalid number of clusters: ${String(numClusters)}. Must be between 1 and ${String(nodeCount)}`,
75
+ );
82
76
  }
83
77
 
84
78
  // Initialize node embeddings (simplified version using graph features)
@@ -184,6 +178,10 @@ export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
184
178
 
185
179
  /**
186
180
  * Initialize node embedding based on graph structure and features
181
+ * @param graph - The input graph
182
+ * @param nodeId - The node ID to initialize embedding for
183
+ * @param dim - The embedding dimension
184
+ * @returns The initialized embedding vector
187
185
  */
188
186
  function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): number[] {
189
187
  const embedding = new Array<number>(dim).fill(0);
@@ -194,7 +192,7 @@ function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): num
194
192
 
195
193
  // Initialize with small random values influenced by graph structure
196
194
  for (let i = 0; i < dim; i++) {
197
- embedding[i] = ((Math.random() - 0.5) * 0.1) + (normalizedDegree * 0.1);
195
+ embedding[i] = (Math.random() - 0.5) * 0.1 + normalizedDegree * 0.1;
198
196
  }
199
197
 
200
198
  return embedding;
@@ -202,6 +200,9 @@ function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): num
202
200
 
203
201
  /**
204
202
  * Initialize cluster centers using k-means++ style initialization
203
+ * @param embeddings - Map of node IDs to embedding vectors
204
+ * @param numClusters - Number of cluster centers to initialize
205
+ * @returns Array of initialized cluster center vectors
205
206
  */
206
207
  function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters: number): number[][] {
207
208
  const embeddingArray = Array.from(embeddings.values());
@@ -210,7 +211,7 @@ function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters
210
211
  // Choose first center randomly
211
212
  const firstCenter = embeddingArray[Math.floor(Math.random() * embeddingArray.length)];
212
213
  if (firstCenter) {
213
- centers.push([... firstCenter] as number[]);
214
+ centers.push([...firstCenter] as number[]);
214
215
  }
215
216
 
216
217
  // Choose remaining centers using k-means++ initialization
@@ -239,7 +240,7 @@ function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters
239
240
  if (randomValue <= 0) {
240
241
  const newCenter = embeddingArray[i];
241
242
  if (newCenter) {
242
- centers.push([... newCenter] as number[]);
243
+ centers.push([...newCenter] as number[]);
243
244
  }
244
245
 
245
246
  break;
@@ -252,11 +253,16 @@ function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters
252
253
 
253
254
  /**
254
255
  * Update node embeddings using gradient descent
256
+ * @param graph - The input graph
257
+ * @param embeddings - Map of node IDs to embedding vectors
258
+ * @param _clusters - Map of node IDs to cluster assignments (reserved for future use)
259
+ * @param learningRate - Learning rate for gradient descent
260
+ * @param lambda - Regularization parameter
255
261
  */
256
262
  function updateEmbeddings(
257
263
  graph: Graph,
258
264
  embeddings: Map<NodeId, number[]>,
259
- clusters: Map<NodeId, number>,
265
+ _clusters: Map<NodeId, number>,
260
266
  learningRate: number,
261
267
  lambda: number,
262
268
  ): void {
@@ -293,7 +299,7 @@ function updateEmbeddings(
293
299
  const gradVal = gradient[i];
294
300
  const diffVal = diff[i];
295
301
  if (gradVal !== undefined && diffVal !== undefined) {
296
- gradient[i] = gradVal + (lambda * diffVal);
302
+ gradient[i] = gradVal + lambda * diffVal;
297
303
  }
298
304
  }
299
305
  }
@@ -303,7 +309,7 @@ function updateEmbeddings(
303
309
  const gradVal = gradient[i];
304
310
  const nodeVal = nodeEmbedding[i];
305
311
  if (gradVal !== undefined && nodeVal !== undefined) {
306
- gradient[i] = gradVal + (lambda * nodeVal);
312
+ gradient[i] = gradVal + lambda * nodeVal;
307
313
  }
308
314
  }
309
315
  }
@@ -319,7 +325,7 @@ function updateEmbeddings(
319
325
  const embVal = embedding[i];
320
326
  const gradVal = gradient[i];
321
327
  if (embVal !== undefined && gradVal !== undefined) {
322
- embedding[i] = embVal - (learningRate * gradVal);
328
+ embedding[i] = embVal - learningRate * gradVal;
323
329
  }
324
330
  }
325
331
  }
@@ -327,6 +333,10 @@ function updateEmbeddings(
327
333
 
328
334
  /**
329
335
  * Update cluster centers based on current assignments
336
+ * @param embeddings - Map of node IDs to embedding vectors
337
+ * @param clusters - Map of node IDs to cluster assignments
338
+ * @param clusterCenters - Array of cluster center vectors to update
339
+ * @param numClusters - Number of clusters
330
340
  */
331
341
  function updateClusterCenters(
332
342
  embeddings: Map<NodeId, number[]>,
@@ -335,8 +345,9 @@ function updateClusterCenters(
335
345
  numClusters: number,
336
346
  ): void {
337
347
  const dimensions = clusterCenters[0]?.length ?? 0;
338
- const clusterSums: number[][] = Array.from({length: numClusters}, () =>
339
- new Array(dimensions).fill(0) as number[],
348
+ const clusterSums: number[][] = Array.from(
349
+ { length: numClusters },
350
+ () => new Array(dimensions).fill(0) as number[],
340
351
  );
341
352
  const clusterCounts = new Array<number>(numClusters).fill(0);
342
353
 
@@ -382,6 +393,12 @@ function updateClusterCenters(
382
393
 
383
394
  /**
384
395
  * Calculate the total loss function
396
+ * @param graph - The input graph
397
+ * @param embeddings - Map of node IDs to embedding vectors
398
+ * @param clusters - Map of node IDs to cluster assignments
399
+ * @param clusterCenters - Array of cluster center vectors
400
+ * @param lambda - Regularization parameter
401
+ * @returns The total loss value
385
402
  */
386
403
  function calculateLoss(
387
404
  graph: Graph,
@@ -435,6 +452,5 @@ function calculateLoss(
435
452
  }
436
453
  }
437
454
 
438
- return clusteringLoss + (lambda * reconstructionLoss) + (lambda * regularizationLoss);
455
+ return clusteringLoss + lambda * reconstructionLoss + lambda * regularizationLoss;
439
456
  }
440
-
@@ -1,5 +1,5 @@
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
3
 
4
4
  /**
5
5
  * Configuration options for the TeraHAC (Hierarchical Agglomerative Clustering) algorithm
@@ -60,7 +60,6 @@ export interface TeraHACResult {
60
60
  *
61
61
  * Based on: "Scaling Hierarchical Agglomerative Clustering to Trillion-Edge Graphs"
62
62
  * Google Research 2024
63
- *
64
63
  * @param graph - Input graph to cluster
65
64
  * @param config - Configuration options
66
65
  * @returns Hierarchical clustering result
@@ -88,7 +87,9 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
88
87
  }
89
88
 
90
89
  if (nodeCount > maxNodes) {
91
- onWarning(`Graph has ${String(nodeCount)} nodes, which exceeds maxNodes (${String(maxNodes)}). Performance may be degraded.`);
90
+ onWarning(
91
+ `Graph has ${String(nodeCount)} nodes, which exceeds maxNodes (${String(maxNodes)}). Performance may be degraded.`,
92
+ );
92
93
  }
93
94
 
94
95
  // Initialize each node as its own cluster
@@ -122,7 +123,7 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
122
123
  // Perform agglomerative clustering
123
124
  while (clusters.size > 1 && mergeCandidates.length > 0) {
124
125
  // Find closest pair of clusters
125
- const {cluster1Id, cluster2Id, distance} = findClosestPair(mergeCandidates);
126
+ const { cluster1Id, cluster2Id, distance } = findClosestPair(mergeCandidates);
126
127
 
127
128
  // Check stopping criteria
128
129
  if (numClusters && clusters.size <= numClusters) {
@@ -142,7 +143,7 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
142
143
 
143
144
  // Create new merged cluster
144
145
  const newClusterId = (nextClusterId++).toString();
145
- const mergedMembers = new Set([... cluster1.members, ... cluster2.members]);
146
+ const mergedMembers = new Set([...cluster1.members, ...cluster2.members]);
146
147
 
147
148
  const newCluster: ClusterNode = {
148
149
  id: newClusterId,
@@ -182,7 +183,7 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
182
183
 
183
184
  const newRoot: ClusterNode = {
184
185
  id: (nextClusterId++).toString(),
185
- members: new Set([... root.members, ... currentCluster.members]),
186
+ members: new Set([...root.members, ...currentCluster.members]),
186
187
  left: root,
187
188
  right: currentCluster,
188
189
  distance: Infinity,
@@ -216,10 +217,14 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
216
217
 
217
218
  /**
218
219
  * Calculate distance matrix between all pairs of nodes
220
+ * @param graph - The input graph
221
+ * @param nodes - Array of nodes to calculate distances between
222
+ * @param useGraphDistance - Whether to use graph-based shortest path distances
223
+ * @returns 2D distance matrix
219
224
  */
220
- function calculateDistanceMatrix(graph: Graph, nodes: {id: NodeId}[], useGraphDistance: boolean): number[][] {
225
+ function calculateDistanceMatrix(graph: Graph, nodes: { id: NodeId }[], useGraphDistance: boolean): number[][] {
221
226
  const n = nodes.length;
222
- const matrix: number[][] = Array.from({length: n}, () => new Array(n).fill(Infinity) as number[]);
227
+ const matrix: number[][] = Array.from({ length: n }, () => new Array(n).fill(Infinity) as number[]);
223
228
 
224
229
  if (useGraphDistance) {
225
230
  // Use graph-based distances (shortest path)
@@ -286,6 +291,9 @@ function calculateDistanceMatrix(graph: Graph, nodes: {id: NodeId}[], useGraphDi
286
291
 
287
292
  /**
288
293
  * BFS-based shortest path calculation from a source node
294
+ * @param graph - The input graph
295
+ * @param source - The source node ID
296
+ * @returns Map of node IDs to their distances from the source
289
297
  */
290
298
  function bfsShortestPaths(graph: Graph, source: NodeId): Map<NodeId, number> {
291
299
  const distances = new Map<NodeId, number>();
@@ -317,12 +325,15 @@ function bfsShortestPaths(graph: Graph, source: NodeId): Map<NodeId, number> {
317
325
 
318
326
  /**
319
327
  * Initialize merge candidates priority queue
328
+ * @param clusters - Map of cluster IDs to cluster nodes
329
+ * @param distanceMatrix - 2D distance matrix between nodes
330
+ * @returns Array of merge candidates sorted by distance
320
331
  */
321
332
  function initializeMergeCandidates(
322
333
  clusters: Map<string, ClusterNode>,
323
334
  distanceMatrix: number[][],
324
- ): {cluster1Id: string, cluster2Id: string, distance: number}[] {
325
- const candidates: {cluster1Id: string, cluster2Id: string, distance: number}[] = [];
335
+ ): { cluster1Id: string; cluster2Id: string; distance: number }[] {
336
+ const candidates: { cluster1Id: string; cluster2Id: string; distance: number }[] = [];
326
337
  const clusterIds = Array.from(clusters.keys());
327
338
 
328
339
  for (let i = 0; i < clusterIds.length; i++) {
@@ -355,10 +366,14 @@ function initializeMergeCandidates(
355
366
 
356
367
  /**
357
368
  * Find the closest pair of clusters
369
+ * @param mergeCandidates - Array of merge candidates
370
+ * @returns The closest cluster pair with their distance
358
371
  */
359
- function findClosestPair(
360
- mergeCandidates: {cluster1Id: string, cluster2Id: string, distance: number}[],
361
- ): {cluster1Id: string, cluster2Id: string, distance: number} {
372
+ function findClosestPair(mergeCandidates: { cluster1Id: string; cluster2Id: string; distance: number }[]): {
373
+ cluster1Id: string;
374
+ cluster2Id: string;
375
+ distance: number;
376
+ } {
362
377
  // Return the first (closest) valid candidate
363
378
  const candidate = mergeCandidates.shift();
364
379
  if (!candidate) {
@@ -370,9 +385,16 @@ function findClosestPair(
370
385
 
371
386
  /**
372
387
  * Update merge candidates after a merge operation
388
+ * @param mergeCandidates - Array of merge candidates to update
389
+ * @param oldCluster1Id - ID of first merged cluster
390
+ * @param oldCluster2Id - ID of second merged cluster
391
+ * @param newClusterId - ID of the newly created cluster
392
+ * @param clusters - Map of cluster IDs to cluster nodes
393
+ * @param distanceMatrix - 2D distance matrix between nodes
394
+ * @param linkage - Linkage criterion to use
373
395
  */
374
396
  function updateMergeCandidates(
375
- mergeCandidates: {cluster1Id: string, cluster2Id: string, distance: number}[],
397
+ mergeCandidates: { cluster1Id: string; cluster2Id: string; distance: number }[],
376
398
  oldCluster1Id: string,
377
399
  oldCluster2Id: string,
378
400
  newClusterId: string,
@@ -387,8 +409,12 @@ function updateMergeCandidates(
387
409
  continue;
388
410
  }
389
411
 
390
- if (candidate.cluster1Id === oldCluster1Id || candidate.cluster1Id === oldCluster2Id ||
391
- candidate.cluster2Id === oldCluster1Id || candidate.cluster2Id === oldCluster2Id) {
412
+ if (
413
+ candidate.cluster1Id === oldCluster1Id ||
414
+ candidate.cluster1Id === oldCluster2Id ||
415
+ candidate.cluster2Id === oldCluster1Id ||
416
+ candidate.cluster2Id === oldCluster2Id
417
+ ) {
392
418
  mergeCandidates.splice(i, 1);
393
419
  }
394
420
  }
@@ -416,6 +442,11 @@ function updateMergeCandidates(
416
442
 
417
443
  /**
418
444
  * Calculate distance between two clusters based on linkage criterion
445
+ * @param cluster1 - First cluster
446
+ * @param cluster2 - Second cluster
447
+ * @param distanceMatrix - 2D distance matrix between nodes
448
+ * @param linkage - Linkage criterion (single, complete, average, ward)
449
+ * @returns The distance between the two clusters
419
450
  */
420
451
  function calculateClusterDistance(
421
452
  cluster1: ClusterNode,
@@ -451,14 +482,14 @@ function calculateClusterDistance(
451
482
  // Apply linkage criterion
452
483
  switch (linkage) {
453
484
  case "single":
454
- return Math.min(... distances);
485
+ return Math.min(...distances);
455
486
  case "complete":
456
- return Math.max(... distances);
487
+ return Math.max(...distances);
457
488
  case "average":
458
489
  return distances.reduce((sum, d) => sum + d, 0) / distances.length;
459
490
  case "ward":
460
491
  // Simplified Ward linkage (would need cluster centroids for full implementation)
461
- return distances.reduce((sum, d) => sum + (d * d), 0) / distances.length;
492
+ return distances.reduce((sum, d) => sum + d * d, 0) / distances.length;
462
493
  default:
463
494
  return distances.reduce((sum, d) => sum + d, 0) / distances.length;
464
495
  }
@@ -466,6 +497,9 @@ function calculateClusterDistance(
466
497
 
467
498
  /**
468
499
  * Extract flat clustering from dendrogram
500
+ * @param dendrogram - The root of the dendrogram tree
501
+ * @param numClusters - The target number of clusters
502
+ * @returns Map of node IDs to cluster assignments
469
503
  */
470
504
  function extractFlatClustering(dendrogram: ClusterNode, numClusters: number): Map<NodeId, number> {
471
505
  const clusters = new Map<NodeId, number>();