@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,7 @@
1
- import {Graph} from "../../core/graph.js";
2
- import type {CommunityResult, LouvainOptions, NodeId} from "../../types/index.js";
1
+ import { Graph } from "../../core/graph.js";
2
+ import type { CommunityResult, LouvainOptions, NodeId } from "../../types/index.js";
3
+ import { louvainOptimized } from "./louvain-optimized.js";
4
+ import { calculateModularity, getNeighborCommunities, getNodeDegree, getTotalEdgeWeight } from "./modularity-utils.js";
3
5
 
4
6
  /**
5
7
  * Louvain community detection algorithm
@@ -9,21 +11,34 @@ import type {CommunityResult, LouvainOptions, NodeId} from "../../types/index.js
9
11
  *
10
12
  * References:
11
13
  * - Blondel, V. D., Guillaume, J. L., Lambiotte, R., & Lefebvre, E. (2008).
12
- * Fast unfolding of communities in large networks.
13
- * Journal of statistical mechanics: theory and experiment, 2008(10), P10008.
14
- *
14
+ * Fast unfolding of communities in large networks.
15
+ * Journal of statistical mechanics: theory and experiment, 2008(10), P10008.
15
16
  * @param graph - The input graph
16
17
  * @param options - Algorithm options
17
18
  * @returns Community detection result with communities, modularity, and iterations
18
19
  */
19
- export function louvain(
20
- graph: Graph,
21
- options: LouvainOptions = {},
22
- ): CommunityResult {
20
+ export function louvain(graph: Graph, options: LouvainOptions = {}): CommunityResult {
23
21
  const resolution = options.resolution ?? 1.0;
24
22
  const maxIterations = options.maxIterations ?? 100;
25
23
  const tolerance = options.tolerance ?? 1e-6;
26
24
 
25
+ // Use optimized version by default for larger graphs
26
+ // Only disable if explicitly requested or graph is very small
27
+ const shouldUseOptimized = options.useOptimized !== false && graph.nodeCount > 50;
28
+
29
+ if (shouldUseOptimized) {
30
+ return louvainOptimized(graph, {
31
+ resolution,
32
+ maxIterations,
33
+ tolerance,
34
+ // Enable all optimizations by default
35
+ pruneLeaves: true,
36
+ importanceOrdering: true,
37
+ thresholdCycling: true,
38
+ pruningThreshold: 0.01,
39
+ });
40
+ }
41
+
27
42
  // Initialize: each node in its own community
28
43
  const communities = initializeCommunities(graph);
29
44
  let modularity = calculateModularity(graph, communities, resolution);
@@ -38,7 +53,7 @@ export function louvain(
38
53
  const newModularity = calculateModularity(graph, communities, resolution);
39
54
 
40
55
  // Check for convergence
41
- if ((newModularity - modularity) < tolerance) {
56
+ if (newModularity - modularity < tolerance) {
42
57
  break;
43
58
  }
44
59
 
@@ -56,6 +71,8 @@ export function louvain(
56
71
 
57
72
  /**
58
73
  * Initialize communities: each node in its own community
74
+ * @param graph - The input graph
75
+ * @returns Map of node IDs to their initial community IDs
59
76
  */
60
77
  function initializeCommunities(graph: Graph): Map<NodeId, number> {
61
78
  const communities = new Map<NodeId, number>();
@@ -72,12 +89,12 @@ function initializeCommunities(graph: Graph): Map<NodeId, number> {
72
89
  * Phase 1 of Louvain algorithm: Local optimization
73
90
  * For each node, try moving to neighboring communities and keep the move
74
91
  * that provides the best modularity gain.
92
+ * @param graph - The input graph
93
+ * @param communities - Map of node IDs to community IDs
94
+ * @param resolution - Resolution parameter for modularity calculation
95
+ * @returns True if any improvement was made, false otherwise
75
96
  */
76
- function louvainPhase1(
77
- graph: Graph,
78
- communities: Map<NodeId, number>,
79
- resolution: number,
80
- ): boolean {
97
+ function louvainPhase1(graph: Graph, communities: Map<NodeId, number>, resolution: number): boolean {
81
98
  let globalImprovement = false;
82
99
  let localImprovement = true;
83
100
 
@@ -94,7 +111,11 @@ function louvainPhase1(
94
111
 
95
112
  // Calculate current modularity contribution
96
113
  const currentModularity = nodeModularityContribution(
97
- graph, nodeId, currentCommunity, communities, resolution,
114
+ graph,
115
+ nodeId,
116
+ currentCommunity,
117
+ communities,
118
+ resolution,
98
119
  );
99
120
 
100
121
  let bestCommunity = currentCommunity;
@@ -109,7 +130,11 @@ function louvainPhase1(
109
130
  }
110
131
 
111
132
  const newModularity = nodeModularityContribution(
112
- graph, nodeId, neighborCommunity, communities, resolution,
133
+ graph,
134
+ nodeId,
135
+ neighborCommunity,
136
+ communities,
137
+ resolution,
113
138
  );
114
139
 
115
140
  if (newModularity > bestModularity) {
@@ -132,6 +157,12 @@ function louvainPhase1(
132
157
 
133
158
  /**
134
159
  * Calculate modularity contribution of a node to a specific community
160
+ * @param graph - The input graph
161
+ * @param nodeId - The node ID to evaluate
162
+ * @param community - The community to measure contribution to
163
+ * @param communities - Map of all node IDs to community IDs
164
+ * @param resolution - Resolution parameter for modularity calculation
165
+ * @returns The modularity contribution of the node to the specified community
135
166
  */
136
167
  function nodeModularityContribution(
137
168
  graph: Graph,
@@ -168,90 +199,16 @@ function nodeModularityContribution(
168
199
  }
169
200
 
170
201
  // Modularity formula: Q = (1/2m) * Σ[A_ij - (k_i * k_j)/(2m)] * δ(c_i, c_j)
171
- const modularityIncrease = (internalLinks - ((resolution * nodeDegree * communityDegree) / (2 * totalEdgeWeight))) / totalEdgeWeight;
202
+ const modularityIncrease =
203
+ (internalLinks - (resolution * nodeDegree * communityDegree) / (2 * totalEdgeWeight)) / totalEdgeWeight;
172
204
 
173
205
  return modularityIncrease;
174
206
  }
175
207
 
176
- /**
177
- * Get communities of neighboring nodes
178
- */
179
- function getNeighborCommunities(
180
- graph: Graph,
181
- nodeId: NodeId,
182
- communities: Map<NodeId, number>,
183
- ): Set<number> {
184
- const neighborCommunities = new Set<number>();
185
-
186
- for (const neighbor of graph.neighbors(nodeId)) {
187
- const community = communities.get(neighbor);
188
- if (community !== undefined) {
189
- neighborCommunities.add(community);
190
- }
191
- }
192
-
193
- return neighborCommunities;
194
- }
195
-
196
- /**
197
- * Calculate total modularity of the current community assignment
198
- */
199
- function calculateModularity(
200
- graph: Graph,
201
- communities: Map<NodeId, number>,
202
- resolution: number,
203
- ): number {
204
- const totalEdgeWeight = getTotalEdgeWeight(graph);
205
- if (totalEdgeWeight === 0) {
206
- return 0;
207
- }
208
-
209
- let modularity = 0;
210
-
211
- // For undirected graphs, we need to be careful not to double-count edges
212
- const countedEdges = new Set<string>();
213
-
214
- // Calculate modularity: Q = (1/2m) * Σ[A_ij - γ(k_i * k_j)/(2m)] * δ(c_i, c_j)
215
- for (const nodeI of graph.nodes()) {
216
- for (const nodeJ of graph.nodes()) {
217
- // Skip if already counted this pair in undirected graph
218
- if (!graph.isDirected) {
219
- const nodeIStr = String(nodeI.id);
220
- const nodeJStr = String(nodeJ.id);
221
- const edgeKey = nodeIStr <= nodeJStr ? `${nodeIStr}-${nodeJStr}` : `${nodeJStr}-${nodeIStr}`;
222
- if (countedEdges.has(edgeKey)) {
223
- continue;
224
- }
225
-
226
- countedEdges.add(edgeKey);
227
- }
228
-
229
- if (communities.get(nodeI.id) === communities.get(nodeJ.id)) {
230
- const edge = graph.getEdge(nodeI.id, nodeJ.id);
231
- const reverseEdge = !graph.isDirected ? graph.getEdge(nodeJ.id, nodeI.id) : null;
232
-
233
- let edgeWeight = 0;
234
- if (edge) {
235
- edgeWeight += edge.weight ?? 1;
236
- }
237
-
238
- if (reverseEdge && nodeI.id !== nodeJ.id) {
239
- edgeWeight += reverseEdge.weight ?? 1;
240
- }
241
-
242
- const degreeI = getNodeDegree(graph, nodeI.id);
243
- const degreeJ = getNodeDegree(graph, nodeJ.id);
244
-
245
- modularity += edgeWeight - ((resolution * degreeI * degreeJ) / (2 * totalEdgeWeight));
246
- }
247
- }
248
- }
249
-
250
- return modularity / (2 * totalEdgeWeight);
251
- }
252
-
253
208
  /**
254
209
  * Extract final community structure
210
+ * @param communities - Map of node IDs to community IDs
211
+ * @returns Array of communities (each is an array of node IDs)
255
212
  */
256
213
  function extractCommunities(communities: Map<NodeId, number>): NodeId[][] {
257
214
  const communityMap = new Map<number, NodeId[]>();
@@ -269,30 +226,3 @@ function extractCommunities(communities: Map<NodeId, number>): NodeId[][] {
269
226
 
270
227
  return Array.from(communityMap.values());
271
228
  }
272
-
273
- /**
274
- * Calculate total edge weight in the graph
275
- */
276
- function getTotalEdgeWeight(graph: Graph): number {
277
- let totalWeight = 0;
278
-
279
- for (const edge of graph.edges()) {
280
- totalWeight += edge.weight ?? 1;
281
- }
282
-
283
- return totalWeight;
284
- }
285
-
286
- /**
287
- * Get the total degree (sum of edge weights) for a node
288
- */
289
- function getNodeDegree(graph: Graph, nodeId: NodeId): number {
290
- let degree = 0;
291
-
292
- for (const neighbor of graph.neighbors(nodeId)) {
293
- const edge = graph.getEdge(nodeId, neighbor);
294
- degree += edge?.weight ?? 1;
295
- }
296
-
297
- return degree;
298
- }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Shared modularity calculation utilities for community detection algorithms
3
+ *
4
+ * Used by: Louvain, Leiden, Girvan-Newman
5
+ */
6
+ import type { Graph } from "../../core/graph.js";
7
+ import type { NodeId } from "../../types/index.js";
8
+
9
+ /**
10
+ * Calculate total edge weight in the graph
11
+ *
12
+ * For undirected graphs, each edge is counted once.
13
+ * For directed graphs, each directed edge is counted once.
14
+ * @param graph - The input graph
15
+ * @returns Total sum of edge weights (default weight is 1)
16
+ */
17
+ export function getTotalEdgeWeight(graph: Graph): number {
18
+ let totalWeight = 0;
19
+
20
+ for (const edge of graph.edges()) {
21
+ totalWeight += edge.weight ?? 1;
22
+ }
23
+
24
+ return totalWeight;
25
+ }
26
+
27
+ /**
28
+ * Get the total degree (sum of edge weights) for a node
29
+ *
30
+ * Computes the weighted degree by summing all edge weights incident to the node.
31
+ * @param graph - The input graph
32
+ * @param nodeId - The node ID to compute degree for
33
+ * @returns The weighted degree of the node
34
+ */
35
+ export function getNodeDegree(graph: Graph, nodeId: NodeId): number {
36
+ let degree = 0;
37
+
38
+ for (const neighbor of graph.neighbors(nodeId)) {
39
+ const edge = graph.getEdge(nodeId, neighbor);
40
+ degree += edge?.weight ?? 1;
41
+ }
42
+
43
+ return degree;
44
+ }
45
+
46
+ /**
47
+ * Get communities of neighboring nodes
48
+ *
49
+ * Returns the set of community IDs that neighbors of the given node belong to.
50
+ * @param graph - The input graph
51
+ * @param nodeId - The node to get neighbor communities for
52
+ * @param communities - Map from node IDs to community IDs
53
+ * @returns Set of community IDs that neighbors belong to
54
+ */
55
+ export function getNeighborCommunities(graph: Graph, nodeId: NodeId, communities: Map<NodeId, number>): Set<number> {
56
+ const neighborCommunities = new Set<number>();
57
+
58
+ for (const neighbor of graph.neighbors(nodeId)) {
59
+ const community = communities.get(neighbor);
60
+ if (community !== undefined) {
61
+ neighborCommunities.add(community);
62
+ }
63
+ }
64
+
65
+ return neighborCommunities;
66
+ }
67
+
68
+ /**
69
+ * Calculate modularity of a partition
70
+ *
71
+ * Modularity measures the quality of a community partition. It compares the
72
+ * number of edges within communities to what would be expected in a random graph.
73
+ *
74
+ * Formula: Q = (1/2m) * Σ[A_ij - γ(k_i * k_j)/(2m)] * δ(c_i, c_j)
75
+ * where:
76
+ * - m = total edge weight
77
+ * - A_ij = adjacency matrix entry (edge weight between i and j)
78
+ * - k_i, k_j = degrees of nodes i and j
79
+ * - γ = resolution parameter (higher values favor smaller communities)
80
+ * - δ = Kronecker delta (1 if same community, 0 otherwise)
81
+ * @param graph - The input graph
82
+ * @param communities - Map from node IDs to community IDs
83
+ * @param resolution - Resolution parameter (default: 1.0)
84
+ * @returns Modularity score (range typically -0.5 to 1.0)
85
+ */
86
+ export function calculateModularity(graph: Graph, communities: Map<NodeId, number>, resolution = 1.0): number {
87
+ const totalEdgeWeight = getTotalEdgeWeight(graph);
88
+ if (totalEdgeWeight === 0) {
89
+ return 0;
90
+ }
91
+
92
+ let modularity = 0;
93
+
94
+ // For undirected graphs, we need to be careful not to double-count edges
95
+ const countedEdges = new Set<string>();
96
+
97
+ // Calculate modularity: Q = (1/2m) * Σ[A_ij - γ(k_i * k_j)/(2m)] * δ(c_i, c_j)
98
+ for (const nodeI of graph.nodes()) {
99
+ for (const nodeJ of graph.nodes()) {
100
+ // Skip if already counted this pair in undirected graph
101
+ if (!graph.isDirected) {
102
+ const nodeIStr = String(nodeI.id);
103
+ const nodeJStr = String(nodeJ.id);
104
+ const edgeKey = nodeIStr <= nodeJStr ? `${nodeIStr}-${nodeJStr}` : `${nodeJStr}-${nodeIStr}`;
105
+ if (countedEdges.has(edgeKey)) {
106
+ continue;
107
+ }
108
+
109
+ countedEdges.add(edgeKey);
110
+ }
111
+
112
+ if (communities.get(nodeI.id) === communities.get(nodeJ.id)) {
113
+ const edge = graph.getEdge(nodeI.id, nodeJ.id);
114
+ const reverseEdge = !graph.isDirected ? graph.getEdge(nodeJ.id, nodeI.id) : null;
115
+
116
+ let edgeWeight = 0;
117
+ if (edge) {
118
+ edgeWeight += edge.weight ?? 1;
119
+ }
120
+
121
+ if (reverseEdge && nodeI.id !== nodeJ.id) {
122
+ edgeWeight += reverseEdge.weight ?? 1;
123
+ }
124
+
125
+ const degreeI = getNodeDegree(graph, nodeI.id);
126
+ const degreeJ = getNodeDegree(graph, nodeJ.id);
127
+
128
+ modularity += edgeWeight - (resolution * degreeI * degreeJ) / (2 * totalEdgeWeight);
129
+ }
130
+ }
131
+ }
132
+
133
+ return modularity / (2 * totalEdgeWeight);
134
+ }
@@ -1,6 +1,6 @@
1
- import type {Graph} from "../../core/graph.js";
2
- import {UnionFind} from "../../data-structures/union-find.js";
3
- import type {NodeId} from "../../types/index.js";
1
+ import type { Graph } from "../../core/graph.js";
2
+ import { UnionFind } from "../../data-structures/union-find.js";
3
+ import type { NodeId } from "../../types/index.js";
4
4
 
5
5
  /**
6
6
  * Connected components algorithms
@@ -11,10 +11,14 @@ import type {NodeId} from "../../types/index.js";
11
11
 
12
12
  /**
13
13
  * Find connected components in an undirected graph using Union-Find
14
+ * @param graph - The input graph (must be undirected)
15
+ * @returns Array of connected components, each containing an array of node IDs
14
16
  */
15
17
  export function connectedComponents(graph: Graph): NodeId[][] {
16
18
  if (graph.isDirected) {
17
- throw new Error("Connected components algorithm requires an undirected graph. Use stronglyConnectedComponents for directed graphs.");
19
+ throw new Error(
20
+ "Connected components algorithm requires an undirected graph. Use stronglyConnectedComponents for directed graphs.",
21
+ );
18
22
  }
19
23
 
20
24
  const nodes = Array.from(graph.nodes()).map((node) => node.id);
@@ -35,6 +39,8 @@ export function connectedComponents(graph: Graph): NodeId[][] {
35
39
 
36
40
  /**
37
41
  * Find connected components using DFS (alternative implementation)
42
+ * @param graph - The input graph (must be undirected)
43
+ * @returns Array of connected components, each containing an array of node IDs
38
44
  */
39
45
  export function connectedComponentsDFS(graph: Graph): NodeId[][] {
40
46
  if (graph.isDirected) {
@@ -57,13 +63,12 @@ export function connectedComponentsDFS(graph: Graph): NodeId[][] {
57
63
 
58
64
  /**
59
65
  * DFS helper for connected components
66
+ * @param graph - The input graph
67
+ * @param nodeId - The starting node ID
68
+ * @param visited - Set of already visited node IDs
69
+ * @param component - Array to accumulate component members
60
70
  */
61
- function dfsComponent(
62
- graph: Graph,
63
- nodeId: NodeId,
64
- visited: Set<NodeId>,
65
- component: NodeId[],
66
- ): void {
71
+ function dfsComponent(graph: Graph, nodeId: NodeId, visited: Set<NodeId>, component: NodeId[]): void {
67
72
  visited.add(nodeId);
68
73
  component.push(nodeId);
69
74
 
@@ -76,6 +81,8 @@ function dfsComponent(
76
81
 
77
82
  /**
78
83
  * Find the number of connected components
84
+ * @param graph - The input graph (must be undirected)
85
+ * @returns The number of connected components in the graph
79
86
  */
80
87
  export function numberOfConnectedComponents(graph: Graph): number {
81
88
  return connectedComponents(graph).length;
@@ -83,6 +90,8 @@ export function numberOfConnectedComponents(graph: Graph): number {
83
90
 
84
91
  /**
85
92
  * Check if the graph is connected (has exactly one connected component)
93
+ * @param graph - The input graph (must be undirected)
94
+ * @returns True if the graph is connected, false otherwise
86
95
  */
87
96
  export function isConnected(graph: Graph): boolean {
88
97
  return numberOfConnectedComponents(graph) <= 1;
@@ -90,6 +99,8 @@ export function isConnected(graph: Graph): boolean {
90
99
 
91
100
  /**
92
101
  * Find the largest connected component
102
+ * @param graph - The input graph (must be undirected)
103
+ * @returns Array of node IDs in the largest connected component
93
104
  */
94
105
  export function largestConnectedComponent(graph: Graph): NodeId[] {
95
106
  const components = connectedComponents(graph);
@@ -98,13 +109,14 @@ export function largestConnectedComponent(graph: Graph): NodeId[] {
98
109
  return [];
99
110
  }
100
111
 
101
- return components.reduce((largest, current) =>
102
- current.length > largest.length ? current : largest,
103
- );
112
+ return components.reduce((largest, current) => (current.length > largest.length ? current : largest));
104
113
  }
105
114
 
106
115
  /**
107
116
  * Get the connected component containing a specific node
117
+ * @param graph - The input graph (must be undirected)
118
+ * @param nodeId - The node ID to find the component for
119
+ * @returns Array of node IDs in the same component as the specified node
108
120
  */
109
121
  export function getConnectedComponent(graph: Graph, nodeId: NodeId): NodeId[] {
110
122
  if (!graph.hasNode(nodeId)) {
@@ -125,6 +137,8 @@ export function getConnectedComponent(graph: Graph, nodeId: NodeId): NodeId[] {
125
137
 
126
138
  /**
127
139
  * Find strongly connected components using Tarjan's algorithm
140
+ * @param graph - The input graph (must be directed)
141
+ * @returns Array of strongly connected components, each containing an array of node IDs
128
142
  */
129
143
  export function stronglyConnectedComponents(graph: Graph): NodeId[][] {
130
144
  if (!graph.isDirected) {
@@ -198,6 +212,8 @@ export function stronglyConnectedComponents(graph: Graph): NodeId[][] {
198
212
 
199
213
  /**
200
214
  * Check if a directed graph is strongly connected
215
+ * @param graph - The input graph (must be directed)
216
+ * @returns True if the graph is strongly connected, false otherwise
201
217
  */
202
218
  export function isStronglyConnected(graph: Graph): boolean {
203
219
  if (!graph.isDirected) {
@@ -211,10 +227,14 @@ export function isStronglyConnected(graph: Graph): boolean {
211
227
  /**
212
228
  * Find weakly connected components in a directed graph
213
229
  * (treat the graph as undirected for connectivity)
230
+ * @param graph - The input graph (must be directed)
231
+ * @returns Array of weakly connected components, each containing an array of node IDs
214
232
  */
215
233
  export function weaklyConnectedComponents(graph: Graph): NodeId[][] {
216
234
  if (!graph.isDirected) {
217
- throw new Error("Weakly connected components are for directed graphs. Use connectedComponents for undirected graphs.");
235
+ throw new Error(
236
+ "Weakly connected components are for directed graphs. Use connectedComponents for undirected graphs.",
237
+ );
218
238
  }
219
239
 
220
240
  const nodes = Array.from(graph.nodes()).map((node) => node.id);
@@ -235,6 +255,8 @@ export function weaklyConnectedComponents(graph: Graph): NodeId[][] {
235
255
 
236
256
  /**
237
257
  * Check if a directed graph is weakly connected
258
+ * @param graph - The input graph (must be directed)
259
+ * @returns True if the graph is weakly connected, false otherwise
238
260
  */
239
261
  export function isWeaklyConnected(graph: Graph): boolean {
240
262
  if (!graph.isDirected) {
@@ -246,6 +268,8 @@ export function isWeaklyConnected(graph: Graph): boolean {
246
268
 
247
269
  /**
248
270
  * Find condensation graph (quotient graph of strongly connected components)
271
+ * @param graph - The input graph (must be directed)
272
+ * @returns Object containing the condensed graph, component map, and component arrays
249
273
  */
250
274
  export function condensationGraph(graph: Graph): {
251
275
  condensedGraph: Graph;
@@ -258,7 +282,7 @@ export function condensationGraph(graph: Graph): {
258
282
 
259
283
  const components = stronglyConnectedComponents(graph);
260
284
  const componentMap = new Map<NodeId, number>();
261
- const condensedGraph = new (graph.constructor as new (config: {directed: boolean}) => Graph)({directed: true});
285
+ const condensedGraph = new (graph.constructor as new (config: { directed: boolean }) => Graph)({ directed: true });
262
286
 
263
287
  // Map each node to its component index
264
288
  for (let i = 0; i < components.length; i++) {
@@ -279,8 +303,7 @@ export function condensationGraph(graph: Graph): {
279
303
  const sourceComponent = componentMap.get(edge.source);
280
304
  const targetComponent = componentMap.get(edge.target);
281
305
 
282
- if (sourceComponent !== undefined && targetComponent !== undefined &&
283
- sourceComponent !== targetComponent) {
306
+ if (sourceComponent !== undefined && targetComponent !== undefined && sourceComponent !== targetComponent) {
284
307
  const edgeKey = `${String(sourceComponent)}-${String(targetComponent)}`;
285
308
 
286
309
  if (!addedEdges.has(edgeKey)) {
@@ -1,5 +1,6 @@
1
- import type {Graph} from "../../core/graph.js";
2
- import type {NodeId} from "../../types/index.js";
1
+ import type { Graph } from "../../core/graph.js";
2
+ import type { NodeId } from "../../types/index.js";
3
+ import { bfsColoringWithPartitions } from "../traversal/bfs-variants.js";
3
4
 
4
5
  /**
5
6
  * Maximum Bipartite Matching implementation using Hopcroft-Karp algorithm
@@ -24,13 +25,17 @@ export interface BipartiteMatchingOptions {
24
25
  /**
25
26
  * Find maximum matching in a bipartite graph using augmenting path algorithm
26
27
  * (simplified implementation that's more reliable than Hopcroft-Karp for this context)
28
+ * @param graph - The bipartite graph to find maximum matching for
29
+ * @param options - Optional configuration including left and right node partitions
30
+ * @returns The maximum matching as a map from left nodes to right nodes and the matching size
31
+ * @throws Error if the graph is not bipartite
27
32
  */
28
33
  export function maximumBipartiteMatching(
29
34
  graph: Graph,
30
35
  options: BipartiteMatchingOptions = {},
31
36
  ): BipartiteMatchingResult {
32
37
  // If partitions not provided, try to infer them
33
- let {leftNodes, rightNodes} = options;
38
+ let { leftNodes, rightNodes } = options;
34
39
 
35
40
  if (!leftNodes || !rightNodes) {
36
41
  const partition = bipartitePartition(graph);
@@ -86,66 +91,32 @@ export function maximumBipartiteMatching(
86
91
 
87
92
  /**
88
93
  * Check if graph is bipartite and return the partition
94
+ * @param graph - The graph to check for bipartiteness
95
+ * @returns An object with left and right node sets if bipartite, or null if not bipartite
89
96
  */
90
- export function bipartitePartition(graph: Graph): {left: Set<NodeId>, right: Set<NodeId>} | null {
91
- const color = new Map<NodeId, boolean>();
92
- const left = new Set<NodeId>();
93
- const right = new Set<NodeId>();
97
+ export function bipartitePartition(graph: Graph): { left: Set<NodeId>; right: Set<NodeId> } | null {
98
+ const result = bfsColoringWithPartitions(graph);
94
99
 
95
- const nodes = Array.from(graph.nodes());
96
-
97
- for (const startNode of nodes) {
98
- if (color.has(startNode.id)) {
99
- continue;
100
- }
101
-
102
- // BFS to color the component
103
- const queue = [startNode.id];
104
- color.set(startNode.id, true);
105
- left.add(startNode.id);
106
-
107
- while (queue.length > 0) {
108
- const u = queue.shift();
109
- if (u === undefined) {
110
- continue;
111
- }
112
-
113
- const uColor = color.get(u);
114
- if (uColor === undefined) {
115
- continue;
116
- }
117
-
118
- const neighbors = Array.from(graph.neighbors(u));
119
- for (const v of neighbors) {
120
- if (!color.has(v)) {
121
- color.set(v, !uColor);
122
- if (!uColor) {
123
- left.add(v);
124
- } else {
125
- right.add(v);
126
- }
127
-
128
- queue.push(v);
129
- } else if (color.get(v) === uColor) {
130
- // Same color as neighbor - not bipartite
131
- return null;
132
- }
133
- }
134
- }
100
+ if (!result.isBipartite || !result.partitions) {
101
+ return null;
135
102
  }
136
103
 
137
- return {left, right};
104
+ return {
105
+ left: result.partitions[0],
106
+ right: result.partitions[1],
107
+ };
138
108
  }
139
109
 
140
110
  /**
141
111
  * Simple greedy bipartite matching algorithm
142
112
  * Useful for comparison or when Hopcroft-Karp is overkill
113
+ * @param graph - The bipartite graph to find matching for
114
+ * @param options - Optional configuration including left and right node partitions
115
+ * @returns The matching as a map from left nodes to right nodes and the matching size
116
+ * @throws Error if the graph is not bipartite
143
117
  */
144
- export function greedyBipartiteMatching(
145
- graph: Graph,
146
- options: BipartiteMatchingOptions = {},
147
- ): BipartiteMatchingResult {
148
- let {leftNodes, rightNodes} = options;
118
+ export function greedyBipartiteMatching(graph: Graph, options: BipartiteMatchingOptions = {}): BipartiteMatchingResult {
119
+ let { leftNodes, rightNodes } = options;
149
120
 
150
121
  if (!leftNodes || !rightNodes) {
151
122
  const partition = bipartitePartition(graph);
@@ -2,7 +2,7 @@
2
2
  * Graph matching algorithms
3
3
  */
4
4
 
5
- export type {BipartiteMatchingOptions, BipartiteMatchingResult} from "./bipartite.js";
6
- export {bipartitePartition, greedyBipartiteMatching, maximumBipartiteMatching} from "./bipartite.js";
7
- export type {IsomorphismOptions, IsomorphismResult} from "./isomorphism.js";
8
- export {findAllIsomorphisms, isGraphIsomorphic} from "./isomorphism.js";
5
+ export type { BipartiteMatchingOptions, BipartiteMatchingResult } from "./bipartite.js";
6
+ export { bipartitePartition, greedyBipartiteMatching, maximumBipartiteMatching } from "./bipartite.js";
7
+ export type { IsomorphismOptions, IsomorphismResult } from "./isomorphism.js";
8
+ export { findAllIsomorphisms, isGraphIsomorphic } from "./isomorphism.js";