@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,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
  * Markov Clustering (MCL) algorithm implementation
@@ -31,11 +31,11 @@ export interface MCLResult {
31
31
 
32
32
  /**
33
33
  * Perform Markov Clustering on a graph
34
+ * @param graph - The input graph to cluster
35
+ * @param options - MCL algorithm options (expansion, inflation, iterations, etc.)
36
+ * @returns MCL clustering result with communities, attractors, and convergence info
34
37
  */
35
- export function markovClustering(
36
- graph: Graph,
37
- options: MCLOptions = {},
38
- ): MCLResult {
38
+ export function markovClustering(graph: Graph, options: MCLOptions = {}): MCLResult {
39
39
  const {
40
40
  expansion = 2,
41
41
  inflation = 2,
@@ -65,7 +65,7 @@ export function markovClustering(
65
65
  let iteration = 0;
66
66
 
67
67
  for (iteration = 0; iteration < maxIterations; iteration++) {
68
- const oldMatrix = matrix.map((row) => [... row]);
68
+ const oldMatrix = matrix.map((row) => [...row]);
69
69
 
70
70
  // Expansion step (matrix multiplication)
71
71
  matrix = matrixPower(matrix, expansion);
@@ -84,7 +84,7 @@ export function markovClustering(
84
84
  }
85
85
 
86
86
  // Extract clusters from final matrix
87
- const {communities, attractors} = extractClusters(matrix, nodeIds);
87
+ const { communities, attractors } = extractClusters(matrix, nodeIds);
88
88
 
89
89
  return {
90
90
  communities,
@@ -96,10 +96,14 @@ export function markovClustering(
96
96
 
97
97
  /**
98
98
  * Build initial transition matrix from graph
99
+ * @param graph - The input graph
100
+ * @param nodeIds - Array of node IDs in the graph
101
+ * @param selfLoops - Whether to add self-loops to the matrix
102
+ * @returns Column-normalized transition matrix
99
103
  */
100
104
  function buildTransitionMatrix(graph: Graph, nodeIds: NodeId[], selfLoops: boolean): number[][] {
101
105
  const n = nodeIds.length;
102
- const matrix = Array.from({length: n}, (): number[] => Array(n).fill(0) as number[]);
106
+ const matrix = Array.from({ length: n }, (): number[] => Array(n).fill(0) as number[]);
103
107
  const nodeToIndex = new Map<NodeId, number>();
104
108
 
105
109
  nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
@@ -168,6 +172,9 @@ function buildTransitionMatrix(graph: Graph, nodeIds: NodeId[], selfLoops: boole
168
172
 
169
173
  /**
170
174
  * Raise matrix to a power (for expansion step)
175
+ * @param matrix - Input square matrix
176
+ * @param power - Power to raise the matrix to
177
+ * @returns Matrix raised to the specified power
171
178
  */
172
179
  function matrixPower(matrix: number[][], power: number): number[][] {
173
180
  if (power === 1) {
@@ -187,13 +194,16 @@ function matrixPower(matrix: number[][], power: number): number[][] {
187
194
 
188
195
  /**
189
196
  * Multiply two matrices
197
+ * @param a - First matrix
198
+ * @param b - Second matrix
199
+ * @returns Product matrix a * b
190
200
  */
191
201
  function matrixMultiply(a: number[][], b: number[][]): number[][] {
192
202
  const n = a.length;
193
203
  const m = b[0]?.length ?? 0;
194
204
  const p = b.length;
195
205
 
196
- const result: number[][] = Array.from({length: n}, () => Array(m).fill(0) as number[]);
206
+ const result: number[][] = Array.from({ length: n }, () => Array(m).fill(0) as number[]);
197
207
 
198
208
  for (let i = 0; i < n; i++) {
199
209
  for (let j = 0; j < m; j++) {
@@ -207,7 +217,7 @@ function matrixMultiply(a: number[][], b: number[][]): number[][] {
207
217
 
208
218
  const prevVal = resultRow[j];
209
219
  if (prevVal !== undefined) {
210
- resultRow[j] = prevVal + (aVal * bVal);
220
+ resultRow[j] = prevVal + aVal * bVal;
211
221
  }
212
222
  }
213
223
  }
@@ -218,10 +228,13 @@ function matrixMultiply(a: number[][], b: number[][]): number[][] {
218
228
 
219
229
  /**
220
230
  * Inflation step: element-wise powering and column normalization
231
+ * @param matrix - Input matrix to inflate
232
+ * @param inflation - Inflation parameter for element-wise powering
233
+ * @returns Inflated and column-normalized matrix
221
234
  */
222
235
  function inflate(matrix: number[][], inflation: number): number[][] {
223
236
  const n = matrix.length;
224
- const result: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
237
+ const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
225
238
 
226
239
  // Element-wise powering
227
240
  for (let i = 0; i < n; i++) {
@@ -266,10 +279,13 @@ function inflate(matrix: number[][], inflation: number): number[][] {
266
279
 
267
280
  /**
268
281
  * Pruning step: remove small values
282
+ * @param matrix - Input matrix to prune
283
+ * @param threshold - Values below this threshold are set to zero
284
+ * @returns Pruned and re-normalized matrix
269
285
  */
270
286
  function prune(matrix: number[][], threshold: number): number[][] {
271
287
  const n = matrix.length;
272
- const result: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
288
+ const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
273
289
 
274
290
  for (let i = 0; i < n; i++) {
275
291
  for (let j = 0; j < n; j++) {
@@ -323,6 +339,10 @@ function prune(matrix: number[][], threshold: number): number[][] {
323
339
 
324
340
  /**
325
341
  * Check if the algorithm has converged
342
+ * @param oldMatrix - Matrix from previous iteration
343
+ * @param newMatrix - Matrix from current iteration
344
+ * @param tolerance - Convergence tolerance threshold
345
+ * @returns True if the maximum difference between matrices is below tolerance
326
346
  */
327
347
  function hasConverged(oldMatrix: number[][], newMatrix: number[][], tolerance: number): boolean {
328
348
  const n = oldMatrix.length;
@@ -348,8 +368,14 @@ function hasConverged(oldMatrix: number[][], newMatrix: number[][], tolerance: n
348
368
 
349
369
  /**
350
370
  * Extract clusters from the final matrix
371
+ * @param matrix - Final converged MCL matrix
372
+ * @param nodeIds - Array of node IDs corresponding to matrix indices
373
+ * @returns Object containing communities and attractor nodes
351
374
  */
352
- function extractClusters(matrix: number[][], nodeIds: NodeId[]): {
375
+ function extractClusters(
376
+ matrix: number[][],
377
+ nodeIds: NodeId[],
378
+ ): {
353
379
  communities: NodeId[][];
354
380
  attractors: Set<NodeId>;
355
381
  } {
@@ -391,10 +417,14 @@ function extractClusters(matrix: number[][], nodeIds: NodeId[]): {
391
417
  }
392
418
 
393
419
  if (clusterNodes.length > 0) {
394
- communities.push(clusterNodes.map((idx) => {
395
- const nodeId = nodeIds[idx];
396
- return nodeId;
397
- }).filter((node): node is NodeId => node !== undefined));
420
+ communities.push(
421
+ clusterNodes
422
+ .map((idx) => {
423
+ const nodeId = nodeIds[idx];
424
+ return nodeId;
425
+ })
426
+ .filter((node): node is NodeId => node !== undefined),
427
+ );
398
428
  clusterIndex++;
399
429
  }
400
430
  }
@@ -411,16 +441,16 @@ function extractClusters(matrix: number[][], nodeIds: NodeId[]): {
411
441
  }
412
442
  }
413
443
 
414
- return {communities, attractors};
444
+ return { communities, attractors };
415
445
  }
416
446
 
417
447
  /**
418
448
  * Calculate modularity of MCL clustering result
449
+ * @param graph - The original graph that was clustered
450
+ * @param communities - Array of communities (each community is an array of node IDs)
451
+ * @returns Modularity score between -0.5 and 1.0
419
452
  */
420
- export function calculateMCLModularity(
421
- graph: Graph,
422
- communities: NodeId[][],
423
- ): number {
453
+ export function calculateMCLModularity(graph: Graph, communities: NodeId[][]): number {
424
454
  const m = graph.totalEdgeCount;
425
455
  if (m === 0) {
426
456
  return 0;
@@ -1,6 +1,6 @@
1
- import type {Graph} from "../core/graph.js";
2
- import type {NodeId} from "../types/index.js";
3
- import {euclideanDistance} from "../utils/math-utilities.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";
4
4
 
5
5
  /**
6
6
  * Spectral Clustering implementation
@@ -29,24 +29,19 @@ export interface SpectralClusteringResult {
29
29
  /**
30
30
  * Perform spectral clustering on a graph
31
31
  *
32
- * @warning This implementation uses simplified power iteration for eigenvector
32
+ * Note: This implementation uses simplified power iteration for eigenvector
33
33
  * computation with approximate eigenvalues. For production use cases requiring
34
34
  * precise clustering, consider using a proper linear algebra library like ml-matrix.
35
35
  *
36
36
  * The approximate eigenvalues (0.1, 0.2 for second and third eigenvectors) work
37
37
  * well for most graph structures but may produce suboptimal results for graphs
38
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
39
42
  */
40
- export function spectralClustering(
41
- graph: Graph,
42
- options: SpectralClusteringOptions,
43
- ): SpectralClusteringResult {
44
- const {
45
- k,
46
- laplacianType = "normalized",
47
- maxIterations = 100,
48
- tolerance = 1e-4,
49
- } = options;
43
+ export function spectralClustering(graph: Graph, options: SpectralClusteringOptions): SpectralClusteringResult {
44
+ const { k, laplacianType = "normalized", maxIterations = 100, tolerance = 1e-4 } = options;
50
45
 
51
46
  // Input validation
52
47
  if (k < 1 || !Number.isInteger(k)) {
@@ -62,7 +57,7 @@ export function spectralClustering(
62
57
  const communities: NodeId[][] = nodeIds.map((id) => [id]);
63
58
  const clusterAssignments = new Map<NodeId, number>();
64
59
  nodeIds.forEach((id, index) => clusterAssignments.set(id, index));
65
- return {communities, clusterAssignments};
60
+ return { communities, clusterAssignments };
66
61
  }
67
62
 
68
63
  // Build adjacency matrix
@@ -97,7 +92,7 @@ export function spectralClustering(
97
92
  const kmeans = kMeansClustering(dataPoints, k, maxIterations, tolerance);
98
93
 
99
94
  // Build communities
100
- const communities: NodeId[][] = Array.from({length: k}, () => []);
95
+ const communities: NodeId[][] = Array.from({ length: k }, () => []);
101
96
  const clusterAssignments = new Map<NodeId, number>();
102
97
 
103
98
  for (let i = 0; i < nodeIds.length; i++) {
@@ -139,10 +134,13 @@ export function spectralClustering(
139
134
 
140
135
  /**
141
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
142
140
  */
143
141
  function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
144
142
  const n = nodeIds.length;
145
- 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[]);
146
144
  const nodeToIndex = new Map<NodeId, number>();
147
145
 
148
146
  nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
@@ -180,10 +178,13 @@ function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
180
178
 
181
179
  /**
182
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
183
184
  */
184
185
  function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
185
186
  const n = adjacency.length;
186
- 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[]);
187
188
 
188
189
  // Calculate degree matrix
189
190
  const degrees = Array(n).fill(0) as number[];
@@ -239,7 +240,14 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
239
240
  const adjacencyVal = adjacencyRow[j];
240
241
  const di = degrees[i];
241
242
  const dj = degrees[j];
242
- 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
+ ) {
243
251
  const laplacianRow = laplacian[i];
244
252
  if (laplacianRow) {
245
253
  laplacianRow[j] = -adjacencyVal / Math.sqrt(di * dj);
@@ -283,15 +291,21 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
283
291
  /**
284
292
  * Find k smallest eigenvectors using simplified eigendecomposition
285
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
286
297
  */
287
- function findSmallestEigenvectors(matrix: number[][], k: number): {
298
+ function findSmallestEigenvectors(
299
+ matrix: number[][],
300
+ k: number,
301
+ ): {
288
302
  eigenvalues: number[];
289
303
  eigenvectors: number[][];
290
304
  } {
291
305
  const n = matrix.length;
292
306
 
293
307
  if (n === 0 || k === 0) {
294
- return {eigenvalues: [], eigenvectors: []};
308
+ return { eigenvalues: [], eigenvectors: [] };
295
309
  }
296
310
 
297
311
  // For very small matrices, use the full power iteration approach
@@ -314,10 +328,12 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
314
328
 
315
329
  for (let eigIdx = 0; eigIdx < k; eigIdx++) {
316
330
  // Initialize random vector
317
- 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);
318
334
 
319
335
  // Normalize initial vector
320
- 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));
321
337
  if (initNorm > 0) {
322
338
  vector = vector.map((val) => val / initNorm);
323
339
  }
@@ -329,8 +345,8 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
329
345
  continue;
330
346
  }
331
347
 
332
- const dot = vector.reduce((sum, val, idx) => sum + (val * (ejVector[idx] ?? 0)), 0);
333
- 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));
334
350
  }
335
351
 
336
352
  // Power iteration
@@ -345,7 +361,7 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
345
361
  if (matrixVal !== undefined && vecVal !== undefined) {
346
362
  const nvVal = newVector[i];
347
363
  if (nvVal !== undefined) {
348
- newVector[i] = nvVal + (matrixVal * vecVal);
364
+ newVector[i] = nvVal + matrixVal * vecVal;
349
365
  }
350
366
  }
351
367
  }
@@ -358,20 +374,20 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
358
374
  continue;
359
375
  }
360
376
 
361
- 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);
362
378
  for (let i = 0; i < n; i++) {
363
379
  const ejVal = ejVector[i];
364
380
  if (ejVal !== undefined) {
365
381
  const nvVal = newVector[i];
366
382
  if (nvVal !== undefined) {
367
- newVector[i] = nvVal - (dot * ejVal);
383
+ newVector[i] = nvVal - dot * ejVal;
368
384
  }
369
385
  }
370
386
  }
371
387
  }
372
388
 
373
389
  // Normalize
374
- 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));
375
391
  if (norm > 1e-10) {
376
392
  vector = newVector.map((val) => val / norm);
377
393
  } else {
@@ -390,29 +406,30 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
390
406
  if (matrixVal !== undefined && vecVal !== undefined) {
391
407
  const avVal = Av[i];
392
408
  if (avVal !== undefined) {
393
- Av[i] = avVal + (matrixVal * vecVal);
409
+ Av[i] = avVal + matrixVal * vecVal;
394
410
  }
395
411
  }
396
412
  }
397
413
  }
398
414
  eigenvalue = vector.reduce((sum, val, idx) => {
399
415
  const avVal = Av[idx];
400
- return sum + (val * (avVal ?? 0));
416
+ return sum + val * (avVal ?? 0);
401
417
  }, 0);
402
418
 
403
419
  eigenvectors.push(vector);
404
420
  eigenvalues.push(eigenvalue);
405
421
  }
406
422
 
407
- return {eigenvalues, eigenvectors};
423
+ return { eigenvalues, eigenvectors };
408
424
  }
409
425
 
410
426
  /**
411
427
  * Normalize matrix rows to unit length
428
+ * @param matrix - Matrix to normalize in place
412
429
  */
413
430
  function normalizeRows(matrix: number[][]): void {
414
431
  for (const row of matrix) {
415
- 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));
416
433
  if (norm > 0) {
417
434
  for (let j = 0; j < row.length; j++) {
418
435
  const val = row[j];
@@ -426,25 +443,30 @@ function normalizeRows(matrix: number[][]): void {
426
443
 
427
444
  /**
428
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
429
451
  */
430
452
  function kMeansClustering(
431
453
  data: number[][],
432
454
  k: number,
433
455
  maxIterations: number,
434
456
  tolerance = 1e-4,
435
- ): {assignments: number[], centroids: number[][]} {
457
+ ): { assignments: number[]; centroids: number[][] } {
436
458
  const n = data.length;
437
459
  const d = data[0]?.length ?? 0;
438
460
 
439
461
  // Handle edge cases
440
462
  if (n === 0 || k === 0) {
441
- return {assignments: [], centroids: []};
463
+ return { assignments: [], centroids: [] };
442
464
  }
443
465
 
444
466
  if (k >= n) {
445
467
  // Each point is its own cluster
446
468
  return {
447
- assignments: Array.from({length: n}, (_, i) => i),
469
+ assignments: Array.from({ length: n }, (_, i) => i),
448
470
  centroids: data.slice(0, n),
449
471
  };
450
472
  }
@@ -457,7 +479,7 @@ function kMeansClustering(
457
479
  const idx = Math.floor(Math.random() * n);
458
480
  if (!selectedIndices.has(idx) && data[idx]) {
459
481
  selectedIndices.add(idx);
460
- centroids.push([... data[idx]]);
482
+ centroids.push([...data[idx]]);
461
483
  }
462
484
  }
463
485
 
@@ -509,14 +531,14 @@ function kMeansClustering(
509
531
  break;
510
532
  }
511
533
 
512
- oldAssignments = [... assignments];
534
+ oldAssignments = [...assignments];
513
535
 
514
536
  // Store old centroids for tolerance-based convergence check
515
- const oldCentroids = centroids.map((c) => [... c]);
537
+ const oldCentroids = centroids.map((c) => [...c]);
516
538
 
517
539
  // Update centroids
518
540
  const counts = Array(k).fill(0) as number[];
519
- 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[]);
520
542
 
521
543
  for (let i = 0; i < n; i++) {
522
544
  const cluster = assignments[i] ?? 0;
@@ -581,13 +603,21 @@ function kMeansClustering(
581
603
  }
582
604
  }
583
605
 
584
- return {assignments, centroids};
606
+ return { assignments, centroids };
585
607
  }
586
608
 
587
609
  /**
588
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
589
615
  */
590
- function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: number): {
616
+ function computeSmallestEigenvectorsSimple(
617
+ matrix: number[][],
618
+ k: number,
619
+ n: number,
620
+ ): {
591
621
  eigenvalues: number[];
592
622
  eigenvectors: number[][];
593
623
  } {
@@ -603,11 +633,13 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
603
633
  if (k >= 2) {
604
634
  // Use power iteration on I - L/lambda_max to find second smallest
605
635
  const maxEig = 2; // For normalized Laplacian, max eigenvalue <= 2
606
- 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);
607
639
 
608
640
  // Make orthogonal to first eigenvector
609
- const dot1 = vector.reduce((sum, val) => sum + (val / Math.sqrt(n)), 0);
610
- 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));
611
643
 
612
644
  // Power iteration on shifted matrix
613
645
  for (let iter = 0; iter < 100; iter++) {
@@ -624,18 +656,18 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
624
656
  for (let j = 0; j < n; j++) {
625
657
  const matrixVal = matrix[i]?.[j] ?? 0;
626
658
  const vecVal = vector[j] ?? 0;
627
- newVector[i] = (newVector[i] ?? 0) - ((matrixVal * vecVal) / maxEig);
659
+ newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / maxEig;
628
660
  }
629
661
  }
630
662
 
631
663
  // Orthogonalize against first eigenvector
632
- 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);
633
665
  for (let i = 0; i < n; i++) {
634
- newVector[i] = (newVector[i] ?? 0) - (dot / Math.sqrt(n));
666
+ newVector[i] = (newVector[i] ?? 0) - dot / Math.sqrt(n);
635
667
  }
636
668
 
637
669
  // Normalize
638
- 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));
639
671
  if (norm > 1e-10) {
640
672
  vector = newVector.map((val) => val / norm);
641
673
  }
@@ -647,12 +679,14 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
647
679
 
648
680
  // For k = 3, add another eigenvector
649
681
  if (k >= 3) {
650
- 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);
651
685
 
652
686
  // Orthogonalize against previous eigenvectors
653
687
  for (const prev of eigenvectors) {
654
- const dot = vector.reduce((sum, val, idx) => sum + (val * (prev[idx] ?? 0)), 0);
655
- 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));
656
690
  }
657
691
 
658
692
  // Similar power iteration
@@ -669,20 +703,20 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
669
703
  for (let j = 0; j < n; j++) {
670
704
  const matrixVal = matrix[i]?.[j] ?? 0;
671
705
  const vecVal = vector[j] ?? 0;
672
- newVector[i] = (newVector[i] ?? 0) - ((matrixVal * vecVal) / 2);
706
+ newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / 2;
673
707
  }
674
708
  }
675
709
 
676
710
  // Orthogonalize
677
711
  for (const prev of eigenvectors) {
678
- 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);
679
713
  for (let i = 0; i < n; i++) {
680
- newVector[i] = (newVector[i] ?? 0) - (dot * (prev[i] ?? 0));
714
+ newVector[i] = (newVector[i] ?? 0) - dot * (prev[i] ?? 0);
681
715
  }
682
716
  }
683
717
 
684
718
  // Normalize
685
- 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));
686
720
  if (norm > 1e-10) {
687
721
  vector = newVector.map((val) => val / norm);
688
722
  }
@@ -692,5 +726,5 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
692
726
  eigenvalues.push(0.2); // Approximate
693
727
  }
694
728
 
695
- return {eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k)};
729
+ return { eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k) };
696
730
  }