@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,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
@@ -15,6 +15,8 @@ export interface TeraHACConfig {
15
15
  maxNodes?: number;
16
16
  /** Use graph structure for distance calculation */
17
17
  useGraphDistance?: boolean;
18
+ /** Custom warning handler (default: console.warn) */
19
+ onWarning?: (message: string) => void;
18
20
  }
19
21
 
20
22
  /**
@@ -58,7 +60,6 @@ export interface TeraHACResult {
58
60
  *
59
61
  * Based on: "Scaling Hierarchical Agglomerative Clustering to Trillion-Edge Graphs"
60
62
  * Google Research 2024
61
- *
62
63
  * @param graph - Input graph to cluster
63
64
  * @param config - Configuration options
64
65
  * @returns Hierarchical clustering result
@@ -70,8 +71,14 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
70
71
  distanceThreshold,
71
72
  maxNodes = 10000,
72
73
  useGraphDistance = true,
74
+ onWarning = console.warn,
73
75
  } = config;
74
76
 
77
+ // Input validation
78
+ if (numClusters !== undefined && (numClusters < 1 || !Number.isInteger(numClusters))) {
79
+ throw new Error("numClusters must be a positive integer");
80
+ }
81
+
75
82
  const nodes = Array.from(graph.nodes());
76
83
  const nodeCount = nodes.length;
77
84
 
@@ -80,7 +87,9 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
80
87
  }
81
88
 
82
89
  if (nodeCount > maxNodes) {
83
- console.warn(`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
+ );
84
93
  }
85
94
 
86
95
  // Initialize each node as its own cluster
@@ -112,9 +121,9 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
112
121
  let dendrogram: ClusterNode | undefined;
113
122
 
114
123
  // Perform agglomerative clustering
115
- while (clusters.size > 1) {
124
+ while (clusters.size > 1 && mergeCandidates.length > 0) {
116
125
  // Find closest pair of clusters
117
- const {cluster1Id, cluster2Id, distance} = findClosestPair(mergeCandidates);
126
+ const { cluster1Id, cluster2Id, distance } = findClosestPair(mergeCandidates);
118
127
 
119
128
  // Check stopping criteria
120
129
  if (numClusters && clusters.size <= numClusters) {
@@ -134,7 +143,7 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
134
143
 
135
144
  // Create new merged cluster
136
145
  const newClusterId = (nextClusterId++).toString();
137
- const mergedMembers = new Set([... cluster1.members, ... cluster2.members]);
146
+ const mergedMembers = new Set([...cluster1.members, ...cluster2.members]);
138
147
 
139
148
  const newCluster: ClusterNode = {
140
149
  id: newClusterId,
@@ -174,7 +183,7 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
174
183
 
175
184
  const newRoot: ClusterNode = {
176
185
  id: (nextClusterId++).toString(),
177
- members: new Set([... root.members, ... currentCluster.members]),
186
+ members: new Set([...root.members, ...currentCluster.members]),
178
187
  left: root,
179
188
  right: currentCluster,
180
189
  distance: Infinity,
@@ -208,10 +217,14 @@ export function teraHAC(graph: Graph, config: TeraHACConfig = {}): TeraHACResult
208
217
 
209
218
  /**
210
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
211
224
  */
212
- function calculateDistanceMatrix(graph: Graph, nodes: {id: NodeId}[], useGraphDistance: boolean): number[][] {
225
+ function calculateDistanceMatrix(graph: Graph, nodes: { id: NodeId }[], useGraphDistance: boolean): number[][] {
213
226
  const n = nodes.length;
214
- 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[]);
215
228
 
216
229
  if (useGraphDistance) {
217
230
  // Use graph-based distances (shortest path)
@@ -278,6 +291,9 @@ function calculateDistanceMatrix(graph: Graph, nodes: {id: NodeId}[], useGraphDi
278
291
 
279
292
  /**
280
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
281
297
  */
282
298
  function bfsShortestPaths(graph: Graph, source: NodeId): Map<NodeId, number> {
283
299
  const distances = new Map<NodeId, number>();
@@ -309,12 +325,15 @@ function bfsShortestPaths(graph: Graph, source: NodeId): Map<NodeId, number> {
309
325
 
310
326
  /**
311
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
312
331
  */
313
332
  function initializeMergeCandidates(
314
333
  clusters: Map<string, ClusterNode>,
315
334
  distanceMatrix: number[][],
316
- ): {cluster1Id: string, cluster2Id: string, distance: number}[] {
317
- const candidates: {cluster1Id: string, cluster2Id: string, distance: number}[] = [];
335
+ ): { cluster1Id: string; cluster2Id: string; distance: number }[] {
336
+ const candidates: { cluster1Id: string; cluster2Id: string; distance: number }[] = [];
318
337
  const clusterIds = Array.from(clusters.keys());
319
338
 
320
339
  for (let i = 0; i < clusterIds.length; i++) {
@@ -347,10 +366,14 @@ function initializeMergeCandidates(
347
366
 
348
367
  /**
349
368
  * Find the closest pair of clusters
369
+ * @param mergeCandidates - Array of merge candidates
370
+ * @returns The closest cluster pair with their distance
350
371
  */
351
- function findClosestPair(
352
- mergeCandidates: {cluster1Id: string, cluster2Id: string, distance: number}[],
353
- ): {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
+ } {
354
377
  // Return the first (closest) valid candidate
355
378
  const candidate = mergeCandidates.shift();
356
379
  if (!candidate) {
@@ -362,9 +385,16 @@ function findClosestPair(
362
385
 
363
386
  /**
364
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
365
395
  */
366
396
  function updateMergeCandidates(
367
- mergeCandidates: {cluster1Id: string, cluster2Id: string, distance: number}[],
397
+ mergeCandidates: { cluster1Id: string; cluster2Id: string; distance: number }[],
368
398
  oldCluster1Id: string,
369
399
  oldCluster2Id: string,
370
400
  newClusterId: string,
@@ -379,8 +409,12 @@ function updateMergeCandidates(
379
409
  continue;
380
410
  }
381
411
 
382
- if (candidate.cluster1Id === oldCluster1Id || candidate.cluster1Id === oldCluster2Id ||
383
- 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
+ ) {
384
418
  mergeCandidates.splice(i, 1);
385
419
  }
386
420
  }
@@ -408,6 +442,11 @@ function updateMergeCandidates(
408
442
 
409
443
  /**
410
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
411
450
  */
412
451
  function calculateClusterDistance(
413
452
  cluster1: ClusterNode,
@@ -443,14 +482,14 @@ function calculateClusterDistance(
443
482
  // Apply linkage criterion
444
483
  switch (linkage) {
445
484
  case "single":
446
- return Math.min(... distances);
485
+ return Math.min(...distances);
447
486
  case "complete":
448
- return Math.max(... distances);
487
+ return Math.max(...distances);
449
488
  case "average":
450
489
  return distances.reduce((sum, d) => sum + d, 0) / distances.length;
451
490
  case "ward":
452
491
  // Simplified Ward linkage (would need cluster centroids for full implementation)
453
- return distances.reduce((sum, d) => sum + (d * d), 0) / distances.length;
492
+ return distances.reduce((sum, d) => sum + d * d, 0) / distances.length;
454
493
  default:
455
494
  return distances.reduce((sum, d) => sum + d, 0) / distances.length;
456
495
  }
@@ -458,6 +497,9 @@ function calculateClusterDistance(
458
497
 
459
498
  /**
460
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
461
503
  */
462
504
  function extractFlatClustering(dendrogram: ClusterNode, numClusters: number): Map<NodeId, number> {
463
505
  const clusters = new Map<NodeId, number>();
@@ -72,6 +72,12 @@ export interface TraversalOptions {
72
72
 
73
73
  export interface DijkstraOptions {
74
74
  target?: NodeId;
75
+ /**
76
+ * Use bidirectional search optimization for point-to-point queries.
77
+ * Defaults to true for graphs with >10 nodes, false for smaller graphs.
78
+ * Set explicitly to override automatic heuristic.
79
+ */
80
+ bidirectional?: boolean;
75
81
  }
76
82
 
77
83
  export interface BellmanFordResult {
@@ -98,9 +104,11 @@ export interface LouvainOptions {
98
104
  resolution?: number; // Resolution parameter (default: 1.0)
99
105
  maxIterations?: number; // Maximum iterations (default: 100)
100
106
  tolerance?: number; // Improvement tolerance (default: 1e-6)
107
+ useOptimized?: boolean; // Use optimized implementation for large graphs (default: true for graphs > 50 nodes)
101
108
  }
102
109
 
103
110
  export interface GirvanNewmanOptions {
104
111
  maxCommunities?: number; // Stop when this many communities reached
105
112
  minCommunitySize?: number; // Minimum size for valid community
113
+ maxIterations?: number; // Maximum iterations to prevent infinite loops
106
114
  }
@@ -0,0 +1,2 @@
1
+ // Algorithm utility functions will be added here
2
+ export {};
@@ -0,0 +1,269 @@
1
+ import { Graph } from "../core/graph.js";
2
+ import type { NodeId } from "../types/index.js";
3
+
4
+ /**
5
+ * Convert Graph class to Map representation
6
+ * Used by community detection and flow algorithms
7
+ * @param graph - The Graph instance to convert
8
+ * @returns A nested Map where outer keys are source nodes and inner maps contain target nodes with edge weights
9
+ */
10
+ export function graphToMap(graph: Graph): Map<string, Map<string, number>> {
11
+ const map = new Map<string, Map<string, number>>();
12
+
13
+ // Initialize all nodes
14
+ for (const node of graph.nodes()) {
15
+ map.set(String(node.id), new Map());
16
+ }
17
+
18
+ // Add edges with weights
19
+ for (const edge of graph.edges()) {
20
+ const source = String(edge.source);
21
+ const target = String(edge.target);
22
+ const weight = edge.weight ?? 1;
23
+
24
+ const sourceMap = map.get(source);
25
+ if (sourceMap) {
26
+ sourceMap.set(target, weight);
27
+ }
28
+
29
+ // For undirected graphs, add reverse edge
30
+ if (!graph.isDirected) {
31
+ const targetMap = map.get(target);
32
+ if (targetMap) {
33
+ targetMap.set(source, weight);
34
+ }
35
+ }
36
+ }
37
+
38
+ return map;
39
+ }
40
+
41
+ /**
42
+ * Convert Map representation to Graph class
43
+ * @param map - The nested Map representation of the graph
44
+ * @param isDirected - Whether the resulting graph should be directed (defaults to true)
45
+ * @returns A new Graph instance with nodes and edges from the map
46
+ */
47
+ export function mapToGraph(map: Map<string, Map<string, number>>, isDirected = true): Graph {
48
+ const graph = new Graph({ directed: isDirected });
49
+
50
+ // Add all nodes first
51
+ for (const node of map.keys()) {
52
+ graph.addNode(node);
53
+ }
54
+
55
+ // Add edges
56
+ const processedEdges = new Set<string>();
57
+
58
+ for (const [source, neighbors] of map) {
59
+ for (const [target, weight] of neighbors) {
60
+ const edgeKey = isDirected ? `${source}->${target}` : [source, target].sort().join("-");
61
+
62
+ // Skip if we've already processed this edge (for undirected graphs)
63
+ if (!isDirected && processedEdges.has(edgeKey)) {
64
+ continue;
65
+ }
66
+
67
+ graph.addEdge(source, target, weight);
68
+ processedEdges.add(edgeKey);
69
+ }
70
+ }
71
+
72
+ return graph;
73
+ }
74
+
75
+ /**
76
+ * Adapter for using Graph algorithms with Map representation
77
+ * Implements a subset of Graph interface methods needed by algorithms
78
+ */
79
+ export class GraphAdapter {
80
+ private map: Map<string, Map<string, number>>;
81
+ public readonly isDirected: boolean;
82
+
83
+ /**
84
+ * Creates a new GraphAdapter instance wrapping a Map representation.
85
+ * @param map - The nested Map representation of the graph
86
+ * @param isDirected - Whether the graph should be treated as directed (defaults to true)
87
+ */
88
+ constructor(map: Map<string, Map<string, number>>, isDirected = true) {
89
+ this.map = map;
90
+ this.isDirected = isDirected;
91
+ }
92
+
93
+ /**
94
+ * Get all nodes
95
+ * @returns Array of node objects with their ids
96
+ */
97
+ nodes(): { id: NodeId }[] {
98
+ return Array.from(this.map.keys()).map((id) => ({ id }));
99
+ }
100
+
101
+ /**
102
+ * Get node count
103
+ * @returns The total number of nodes in the graph
104
+ */
105
+ get nodeCount(): number {
106
+ return this.map.size;
107
+ }
108
+
109
+ /**
110
+ * Check if node exists
111
+ * @param nodeId - The id of the node to check
112
+ * @returns True if the node exists, false otherwise
113
+ */
114
+ hasNode(nodeId: NodeId): boolean {
115
+ return this.map.has(String(nodeId));
116
+ }
117
+
118
+ /**
119
+ * Get neighbors of a node
120
+ * @param nodeId - The id of the node to get neighbors for
121
+ * @returns Array of neighbor node ids
122
+ */
123
+ neighbors(nodeId: NodeId): NodeId[] {
124
+ const neighbors = this.map.get(String(nodeId));
125
+ if (!neighbors) {
126
+ return [];
127
+ }
128
+
129
+ return Array.from(neighbors.keys());
130
+ }
131
+
132
+ /**
133
+ * Get edge between two nodes
134
+ * @param source - The source node id
135
+ * @param target - The target node id
136
+ * @returns The edge object with source, target and weight, or null if no edge exists
137
+ */
138
+ getEdge(source: NodeId, target: NodeId): { source: NodeId; target: NodeId; weight?: number } | null {
139
+ const sourceNeighbors = this.map.get(String(source));
140
+ if (!sourceNeighbors) {
141
+ return null;
142
+ }
143
+
144
+ const weight = sourceNeighbors.get(String(target));
145
+ if (weight === undefined) {
146
+ return null;
147
+ }
148
+
149
+ return { source, target, weight };
150
+ }
151
+
152
+ /**
153
+ * Check if edge exists
154
+ * @param source - The source node id
155
+ * @param target - The target node id
156
+ * @returns True if an edge exists between source and target, false otherwise
157
+ */
158
+ hasEdge(source: NodeId, target: NodeId): boolean {
159
+ const sourceNeighbors = this.map.get(String(source));
160
+ if (!sourceNeighbors) {
161
+ return false;
162
+ }
163
+
164
+ return sourceNeighbors.has(String(target));
165
+ }
166
+
167
+ /**
168
+ * Get all edges
169
+ * @returns Array of edge objects with source, target and optional weight
170
+ */
171
+ edges(): { source: NodeId; target: NodeId; weight?: number }[] {
172
+ const edges: { source: NodeId; target: NodeId; weight?: number }[] = [];
173
+ const processedEdges = new Set<string>();
174
+
175
+ for (const [source, neighbors] of this.map) {
176
+ for (const [target, weight] of neighbors) {
177
+ const edgeKey = this.isDirected ? `${source}->${target}` : [source, target].sort().join("-");
178
+
179
+ // Skip if we've already processed this edge (for undirected graphs)
180
+ if (!this.isDirected && processedEdges.has(edgeKey)) {
181
+ continue;
182
+ }
183
+
184
+ edges.push({ source, target, weight });
185
+ processedEdges.add(edgeKey);
186
+ }
187
+ }
188
+
189
+ return edges;
190
+ }
191
+
192
+ /**
193
+ * Get edge count
194
+ * @returns The total number of edges in the graph
195
+ */
196
+ get edgeCount(): number {
197
+ if (this.isDirected) {
198
+ let count = 0;
199
+ for (const neighbors of this.map.values()) {
200
+ count += neighbors.size;
201
+ }
202
+ return count;
203
+ }
204
+
205
+ // For undirected graphs, count each edge once
206
+ let count = 0;
207
+ const counted = new Set<string>();
208
+
209
+ for (const [source, neighbors] of this.map) {
210
+ for (const target of neighbors.keys()) {
211
+ const edgeKey = [source, target].sort().join("-");
212
+ if (!counted.has(edgeKey)) {
213
+ counted.add(edgeKey);
214
+ count++;
215
+ }
216
+ }
217
+ }
218
+ return count;
219
+ }
220
+
221
+ /**
222
+ * Get node degree
223
+ * @param nodeId - The id of the node to get degree for
224
+ * @returns The number of edges connected to the node
225
+ */
226
+ degree(nodeId: NodeId): number {
227
+ const neighbors = this.map.get(String(nodeId));
228
+ return neighbors ? neighbors.size : 0;
229
+ }
230
+
231
+ /**
232
+ * Get in-degree (for directed graphs)
233
+ * @param nodeId - The id of the node to get in-degree for
234
+ * @returns The number of incoming edges to the node
235
+ */
236
+ inDegree(nodeId: NodeId): number {
237
+ if (!this.isDirected) {
238
+ return this.degree(nodeId);
239
+ }
240
+
241
+ let count = 0;
242
+ const targetId = String(nodeId);
243
+
244
+ for (const neighbors of this.map.values()) {
245
+ if (neighbors.has(targetId)) {
246
+ count++;
247
+ }
248
+ }
249
+
250
+ return count;
251
+ }
252
+
253
+ /**
254
+ * Get out-degree (for directed graphs)
255
+ * @param nodeId - The id of the node to get out-degree for
256
+ * @returns The number of outgoing edges from the node
257
+ */
258
+ outDegree(nodeId: NodeId): number {
259
+ return this.degree(nodeId);
260
+ }
261
+
262
+ /**
263
+ * Get underlying map representation
264
+ * @returns The internal Map representation of the graph
265
+ */
266
+ getMap(): Map<string, Map<string, number>> {
267
+ return this.map;
268
+ }
269
+ }
@@ -0,0 +1,173 @@
1
+ import { Graph } from "../core/graph.js";
2
+ import type { NodeId } from "../types/index.js";
3
+
4
+ /**
5
+ * Reconstructs a path from source to target using a predecessor map
6
+ * @param target - The target node
7
+ * @param predecessor - Map of node to its predecessor in the path
8
+ * @returns Array of nodes from source to target, or empty array if no path exists
9
+ */
10
+ export function reconstructPath<T>(target: T, predecessor: Map<T, T | null>): T[] {
11
+ const path: T[] = [];
12
+ let current: T | null = target;
13
+
14
+ // Build path backwards from target to source
15
+ while (current !== null) {
16
+ path.unshift(current);
17
+ const pred = predecessor.get(current);
18
+ if (pred === undefined) {
19
+ // No path exists
20
+ return [];
21
+ }
22
+
23
+ current = pred;
24
+ }
25
+
26
+ return path;
27
+ }
28
+
29
+ /**
30
+ * Find common neighbors between two nodes
31
+ * @param graph - The graph
32
+ * @param source - First node
33
+ * @param target - Second node
34
+ * @param directed - Whether to consider edge direction
35
+ * @returns Set of common neighbor node IDs
36
+ */
37
+ export function getCommonNeighbors(graph: Graph, source: NodeId, target: NodeId, directed = false): Set<NodeId> {
38
+ const sourceNeighbors = new Set(directed ? graph.outNeighbors(source) : graph.neighbors(source));
39
+ const targetNeighbors = new Set(directed ? graph.outNeighbors(target) : graph.neighbors(target));
40
+
41
+ const common = new Set<NodeId>();
42
+ for (const neighbor of sourceNeighbors) {
43
+ if (targetNeighbors.has(neighbor)) {
44
+ common.add(neighbor);
45
+ }
46
+ }
47
+
48
+ return common;
49
+ }
50
+
51
+ /**
52
+ * Find common neighbors for link prediction in directed graphs
53
+ * This finds nodes that form a path from source to target (source->X->target)
54
+ * @param graph - The graph
55
+ * @param source - First node
56
+ * @param target - Second node
57
+ * @returns Set of intermediate node IDs
58
+ */
59
+ export function getIntermediateNodes(graph: Graph, source: NodeId, target: NodeId): Set<NodeId> {
60
+ const sourceOutNeighbors = new Set(graph.outNeighbors(source));
61
+ const targetInNeighbors = new Set(graph.inNeighbors(target));
62
+
63
+ const common = new Set<NodeId>();
64
+ for (const neighbor of sourceOutNeighbors) {
65
+ if (targetInNeighbors.has(neighbor)) {
66
+ common.add(neighbor);
67
+ }
68
+ }
69
+
70
+ return common;
71
+ }
72
+
73
+ /**
74
+ * Generate a consistent edge key for undirected graphs
75
+ * @param source - Source node
76
+ * @param target - Target node
77
+ * @param isDirected - Whether the graph is directed
78
+ * @returns A consistent edge identifier
79
+ */
80
+ export function getEdgeKey(source: NodeId, target: NodeId, isDirected: boolean): string {
81
+ if (isDirected) {
82
+ return `${String(source)}->${String(target)}`;
83
+ }
84
+
85
+ // For undirected graphs, sort to ensure consistency
86
+ const [first, second] = source < target ? [source, target] : [target, source];
87
+ return `${String(first)}-${String(second)}`;
88
+ }
89
+
90
+ /**
91
+ * Calculate total edge weight in a graph
92
+ * @param graph - The graph
93
+ * @returns Total weight of all edges
94
+ */
95
+ export function getTotalEdgeWeight(graph: Graph): number {
96
+ let totalWeight = 0;
97
+ for (const edge of graph.edges()) {
98
+ totalWeight += edge.weight ?? 1;
99
+ }
100
+ return graph.isDirected ? totalWeight : totalWeight / 2;
101
+ }
102
+
103
+ /**
104
+ * Get node degree with optional mode for directed graphs
105
+ * @param graph - The graph
106
+ * @param nodeId - The node ID
107
+ * @param mode - Degree mode for directed graphs
108
+ * @returns Node degree
109
+ */
110
+ export function getNodeDegree(graph: Graph, nodeId: NodeId, mode: "in" | "out" | "total" = "total"): number {
111
+ if (!graph.isDirected || mode === "total") {
112
+ return graph.degree(nodeId);
113
+ }
114
+
115
+ if (mode === "in") {
116
+ return graph.inDegree(nodeId);
117
+ }
118
+
119
+ return graph.outDegree(nodeId);
120
+ }
121
+
122
+ /**
123
+ * Convert directed graph to undirected
124
+ * @param graph - The directed graph
125
+ * @returns A new undirected graph
126
+ */
127
+ export function makeUndirected(graph: Graph): Graph {
128
+ if (!graph.isDirected) {
129
+ return graph;
130
+ }
131
+
132
+ const undirected = new Graph({ directed: false });
133
+
134
+ // Add all nodes
135
+ for (const node of graph.nodes()) {
136
+ undirected.addNode(node.id, node.data);
137
+ }
138
+
139
+ // Add edges (will automatically be made bidirectional)
140
+ for (const edge of graph.edges()) {
141
+ // For undirected, we only add each edge once
142
+ if (!undirected.hasEdge(edge.source, edge.target)) {
143
+ undirected.addEdge(edge.source, edge.target, edge.weight, edge.data);
144
+ }
145
+ }
146
+
147
+ return undirected;
148
+ }
149
+
150
+ /**
151
+ * Renumber communities consecutively starting from 0
152
+ * @param communities - Map of node to community ID
153
+ * @returns Map with renumbered community IDs
154
+ */
155
+ export function renumberCommunities<T>(communities: Map<T, number>): Map<T, number> {
156
+ const uniqueCommunities = new Set(communities.values());
157
+ const remapping = new Map<number, number>();
158
+ let newId = 0;
159
+
160
+ for (const community of uniqueCommunities) {
161
+ remapping.set(community, newId++);
162
+ }
163
+
164
+ const renumbered = new Map<T, number>();
165
+ for (const [node, community] of communities) {
166
+ const newCommunity = remapping.get(community);
167
+ if (newCommunity !== undefined) {
168
+ renumbered.set(node, newCommunity);
169
+ }
170
+ }
171
+
172
+ return renumbered;
173
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./algorithm-utilities.js";
2
+ export * from "./graph-utilities.js";
3
+ export * from "./math-utilities.js";
4
+ export * from "./matrix-utilities.js";