@graphty/algorithms 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/README.md +139 -50
  2. package/dist/algorithms.js +15260 -10390
  3. package/dist/algorithms.js.map +1 -1
  4. package/dist/algorithms.standalone.js +26675 -21542
  5. package/dist/algorithms.standalone.js.map +1 -1
  6. package/dist/src/algorithms/centrality/betweenness.d.ts +18 -0
  7. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  8. package/dist/src/algorithms/centrality/betweenness.js +40 -1
  9. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  10. package/dist/src/algorithms/centrality/closeness.d.ts +20 -0
  11. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  12. package/dist/src/algorithms/centrality/closeness.js +93 -8
  13. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  14. package/dist/src/algorithms/centrality/degree.d.ts +9 -0
  15. package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
  16. package/dist/src/algorithms/centrality/degree.js +18 -0
  17. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  18. package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -0
  19. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
  20. package/dist/src/algorithms/centrality/eigenvector.js +29 -0
  21. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
  22. package/dist/src/algorithms/centrality/hits.d.ts +9 -0
  23. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
  24. package/dist/src/algorithms/centrality/hits.js +23 -0
  25. package/dist/src/algorithms/centrality/hits.js.map +1 -1
  26. package/dist/src/algorithms/centrality/katz.d.ts +9 -0
  27. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
  28. package/dist/src/algorithms/centrality/katz.js +31 -0
  29. package/dist/src/algorithms/centrality/katz.js.map +1 -1
  30. package/dist/src/algorithms/centrality/pagerank.d.ts +13 -13
  31. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  32. package/dist/src/algorithms/centrality/pagerank.js +47 -10
  33. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  34. package/dist/src/algorithms/community/girvan-newman-legacy.d.ts +27 -0
  35. package/dist/src/algorithms/community/girvan-newman-legacy.d.ts.map +1 -0
  36. package/dist/src/algorithms/community/girvan-newman-legacy.js +335 -0
  37. package/dist/src/algorithms/community/girvan-newman-legacy.js.map +1 -0
  38. package/dist/src/algorithms/community/girvan-newman.d.ts +5 -3
  39. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  40. package/dist/src/algorithms/community/girvan-newman.js +34 -308
  41. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  42. package/dist/src/algorithms/community/label-propagation.d.ts +9 -6
  43. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  44. package/dist/src/algorithms/community/label-propagation.js +24 -110
  45. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  46. package/dist/src/algorithms/components/connected.d.ts.map +1 -1
  47. package/dist/src/algorithms/components/connected.js +28 -77
  48. package/dist/src/algorithms/components/connected.js.map +1 -1
  49. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
  50. package/dist/src/algorithms/mst/kruskal.js +59 -3
  51. package/dist/src/algorithms/mst/kruskal.js.map +1 -1
  52. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  53. package/dist/src/algorithms/shortest-path/bellman-ford.js +46 -0
  54. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  55. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  56. package/dist/src/algorithms/shortest-path/dijkstra.js +27 -0
  57. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  58. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +15 -5
  59. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
  60. package/dist/src/algorithms/shortest-path/floyd-warshall.js +97 -107
  61. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
  62. package/dist/src/algorithms/traversal/bfs-unified.d.ts +2 -12
  63. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
  64. package/dist/src/algorithms/traversal/bfs-unified.js +47 -308
  65. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
  66. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
  67. package/dist/src/algorithms/traversal/bfs-variants.js +5 -25
  68. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
  69. package/dist/src/algorithms/traversal/bfs.d.ts +0 -3
  70. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  71. package/dist/src/algorithms/traversal/bfs.js +0 -4
  72. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  73. package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
  74. package/dist/src/algorithms/traversal/dfs.js +33 -154
  75. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  76. package/dist/src/clustering/hierarchical-legacy.d.ts +56 -0
  77. package/dist/src/clustering/hierarchical-legacy.d.ts.map +1 -0
  78. package/dist/src/clustering/hierarchical-legacy.js +441 -0
  79. package/dist/src/clustering/hierarchical-legacy.js.map +1 -0
  80. package/dist/src/clustering/hierarchical.d.ts +7 -39
  81. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  82. package/dist/src/clustering/hierarchical.js +56 -417
  83. package/dist/src/clustering/hierarchical.js.map +1 -1
  84. package/dist/src/clustering/k-core-legacy.d.ts +30 -0
  85. package/dist/src/clustering/k-core-legacy.d.ts.map +1 -0
  86. package/dist/src/clustering/k-core-legacy.js +191 -0
  87. package/dist/src/clustering/k-core-legacy.js.map +1 -0
  88. package/dist/src/clustering/k-core.d.ts +8 -7
  89. package/dist/src/clustering/k-core.d.ts.map +1 -1
  90. package/dist/src/clustering/k-core.js +33 -174
  91. package/dist/src/clustering/k-core.js.map +1 -1
  92. package/dist/src/clustering/mcl-legacy.d.ts +42 -0
  93. package/dist/src/clustering/mcl-legacy.d.ts.map +1 -0
  94. package/dist/src/clustering/mcl-legacy.js +386 -0
  95. package/dist/src/clustering/mcl-legacy.js.map +1 -0
  96. package/dist/src/clustering/mcl.d.ts +9 -32
  97. package/dist/src/clustering/mcl.d.ts.map +1 -1
  98. package/dist/src/clustering/mcl.js +48 -374
  99. package/dist/src/clustering/mcl.js.map +1 -1
  100. package/dist/src/index.d.ts +29 -3
  101. package/dist/src/index.d.ts.map +1 -1
  102. package/dist/src/index.js +2 -1
  103. package/dist/src/index.js.map +1 -1
  104. package/dist/src/indexed/accelerator.d.ts +141 -11
  105. package/dist/src/indexed/accelerator.d.ts.map +1 -1
  106. package/dist/src/indexed/accelerator.js +319 -5
  107. package/dist/src/indexed/accelerator.js.map +1 -1
  108. package/dist/src/indexed/all-pairs.d.ts +59 -0
  109. package/dist/src/indexed/all-pairs.d.ts.map +1 -0
  110. package/dist/src/indexed/all-pairs.js +274 -0
  111. package/dist/src/indexed/all-pairs.js.map +1 -0
  112. package/dist/src/indexed/bellman-ford.d.ts +25 -0
  113. package/dist/src/indexed/bellman-ford.d.ts.map +1 -0
  114. package/dist/src/indexed/bellman-ford.js +59 -0
  115. package/dist/src/indexed/bellman-ford.js.map +1 -0
  116. package/dist/src/indexed/betweenness.d.ts +85 -0
  117. package/dist/src/indexed/betweenness.d.ts.map +1 -0
  118. package/dist/src/indexed/betweenness.js +188 -0
  119. package/dist/src/indexed/betweenness.js.map +1 -0
  120. package/dist/src/indexed/bfs.d.ts +69 -2
  121. package/dist/src/indexed/bfs.d.ts.map +1 -1
  122. package/dist/src/indexed/bfs.js +148 -1
  123. package/dist/src/indexed/bfs.js.map +1 -1
  124. package/dist/src/indexed/bipartite.d.ts +37 -0
  125. package/dist/src/indexed/bipartite.d.ts.map +1 -0
  126. package/dist/src/indexed/bipartite.js +54 -0
  127. package/dist/src/indexed/bipartite.js.map +1 -0
  128. package/dist/src/indexed/closeness.d.ts +49 -0
  129. package/dist/src/indexed/closeness.d.ts.map +1 -0
  130. package/dist/src/indexed/closeness.js +127 -0
  131. package/dist/src/indexed/closeness.js.map +1 -0
  132. package/dist/src/indexed/common-neighbors.d.ts +14 -1
  133. package/dist/src/indexed/common-neighbors.d.ts.map +1 -1
  134. package/dist/src/indexed/common-neighbors.js +24 -14
  135. package/dist/src/indexed/common-neighbors.js.map +1 -1
  136. package/dist/src/indexed/degree.d.ts +19 -0
  137. package/dist/src/indexed/degree.d.ts.map +1 -0
  138. package/dist/src/indexed/degree.js +45 -0
  139. package/dist/src/indexed/degree.js.map +1 -0
  140. package/dist/src/indexed/delta-pagerank.d.ts +145 -0
  141. package/dist/src/indexed/delta-pagerank.d.ts.map +1 -0
  142. package/dist/src/indexed/delta-pagerank.js +428 -0
  143. package/dist/src/indexed/delta-pagerank.js.map +1 -0
  144. package/dist/src/indexed/dfs.d.ts +55 -0
  145. package/dist/src/indexed/dfs.d.ts.map +1 -0
  146. package/dist/src/indexed/dfs.js +146 -0
  147. package/dist/src/indexed/dfs.js.map +1 -0
  148. package/dist/src/indexed/eigenvector.d.ts +48 -0
  149. package/dist/src/indexed/eigenvector.d.ts.map +1 -0
  150. package/dist/src/indexed/eigenvector.js +162 -0
  151. package/dist/src/indexed/eigenvector.js.map +1 -0
  152. package/dist/src/indexed/facade.d.ts +109 -0
  153. package/dist/src/indexed/facade.d.ts.map +1 -0
  154. package/dist/src/indexed/facade.js +204 -0
  155. package/dist/src/indexed/facade.js.map +1 -0
  156. package/dist/src/indexed/flow.d.ts +120 -0
  157. package/dist/src/indexed/flow.d.ts.map +1 -0
  158. package/dist/src/indexed/flow.js +377 -0
  159. package/dist/src/indexed/flow.js.map +1 -0
  160. package/dist/src/indexed/girvan-newman.d.ts +42 -0
  161. package/dist/src/indexed/girvan-newman.d.ts.map +1 -0
  162. package/dist/src/indexed/girvan-newman.js +90 -0
  163. package/dist/src/indexed/girvan-newman.js.map +1 -0
  164. package/dist/src/indexed/grsbm.d.ts +85 -0
  165. package/dist/src/indexed/grsbm.d.ts.map +1 -0
  166. package/dist/src/indexed/grsbm.js +287 -0
  167. package/dist/src/indexed/grsbm.js.map +1 -0
  168. package/dist/src/indexed/hierarchical.d.ts +69 -0
  169. package/dist/src/indexed/hierarchical.d.ts.map +1 -0
  170. package/dist/src/indexed/hierarchical.js +245 -0
  171. package/dist/src/indexed/hierarchical.js.map +1 -0
  172. package/dist/src/indexed/index.d.ts +29 -3
  173. package/dist/src/indexed/index.d.ts.map +1 -1
  174. package/dist/src/indexed/index.js +29 -3
  175. package/dist/src/indexed/index.js.map +1 -1
  176. package/dist/src/indexed/isomorphism.d.ts +42 -0
  177. package/dist/src/indexed/isomorphism.d.ts.map +1 -0
  178. package/dist/src/indexed/isomorphism.js +269 -0
  179. package/dist/src/indexed/isomorphism.js.map +1 -0
  180. package/dist/src/indexed/label-propagation.d.ts +134 -0
  181. package/dist/src/indexed/label-propagation.d.ts.map +1 -0
  182. package/dist/src/indexed/label-propagation.js +486 -0
  183. package/dist/src/indexed/label-propagation.js.map +1 -0
  184. package/dist/src/indexed/leiden.d.ts +52 -0
  185. package/dist/src/indexed/leiden.d.ts.map +1 -0
  186. package/dist/src/indexed/leiden.js +384 -0
  187. package/dist/src/indexed/leiden.js.map +1 -0
  188. package/dist/src/indexed/link-prediction.d.ts +147 -0
  189. package/dist/src/indexed/link-prediction.d.ts.map +1 -0
  190. package/dist/src/indexed/link-prediction.js +317 -0
  191. package/dist/src/indexed/link-prediction.js.map +1 -0
  192. package/dist/src/indexed/markov.d.ts +54 -0
  193. package/dist/src/indexed/markov.d.ts.map +1 -0
  194. package/dist/src/indexed/markov.js +284 -0
  195. package/dist/src/indexed/markov.js.map +1 -0
  196. package/dist/src/indexed/matching.d.ts +53 -0
  197. package/dist/src/indexed/matching.d.ts.map +1 -0
  198. package/dist/src/indexed/matching.js +139 -0
  199. package/dist/src/indexed/matching.js.map +1 -0
  200. package/dist/src/indexed/min-cut.d.ts +48 -0
  201. package/dist/src/indexed/min-cut.d.ts.map +1 -0
  202. package/dist/src/indexed/min-cut.js +175 -0
  203. package/dist/src/indexed/min-cut.js.map +1 -0
  204. package/dist/src/indexed/modularity.d.ts +34 -0
  205. package/dist/src/indexed/modularity.d.ts.map +1 -0
  206. package/dist/src/indexed/modularity.js +74 -0
  207. package/dist/src/indexed/modularity.js.map +1 -0
  208. package/dist/src/indexed/mst.d.ts +29 -1
  209. package/dist/src/indexed/mst.d.ts.map +1 -1
  210. package/dist/src/indexed/mst.js +62 -0
  211. package/dist/src/indexed/mst.js.map +1 -1
  212. package/dist/src/indexed/pagerank.d.ts +29 -6
  213. package/dist/src/indexed/pagerank.d.ts.map +1 -1
  214. package/dist/src/indexed/pagerank.js +107 -11
  215. package/dist/src/indexed/pagerank.js.map +1 -1
  216. package/dist/src/indexed/point-to-point.d.ts +53 -0
  217. package/dist/src/indexed/point-to-point.d.ts.map +1 -0
  218. package/dist/src/indexed/point-to-point.js +173 -0
  219. package/dist/src/indexed/point-to-point.js.map +1 -0
  220. package/dist/src/indexed/scc.d.ts +53 -0
  221. package/dist/src/indexed/scc.d.ts.map +1 -0
  222. package/dist/src/indexed/scc.js +110 -0
  223. package/dist/src/indexed/scc.js.map +1 -0
  224. package/dist/src/indexed/spectral.d.ts +61 -0
  225. package/dist/src/indexed/spectral.d.ts.map +1 -0
  226. package/dist/src/indexed/spectral.js +476 -0
  227. package/dist/src/indexed/spectral.js.map +1 -0
  228. package/dist/src/indexed/structures/bit-set.d.ts +45 -0
  229. package/dist/src/indexed/structures/bit-set.d.ts.map +1 -0
  230. package/dist/src/indexed/structures/bit-set.js +57 -0
  231. package/dist/src/indexed/structures/bit-set.js.map +1 -0
  232. package/dist/src/indexed/structures/max-heap.d.ts +52 -0
  233. package/dist/src/indexed/structures/max-heap.d.ts.map +1 -0
  234. package/dist/src/indexed/structures/max-heap.js +66 -0
  235. package/dist/src/indexed/structures/max-heap.js.map +1 -0
  236. package/dist/src/indexed/structures/min-heap.d.ts +30 -1
  237. package/dist/src/indexed/structures/min-heap.d.ts.map +1 -1
  238. package/dist/src/indexed/structures/min-heap.js +46 -6
  239. package/dist/src/indexed/structures/min-heap.js.map +1 -1
  240. package/dist/src/indexed/structures/ring-queue.d.ts +41 -0
  241. package/dist/src/indexed/structures/ring-queue.d.ts.map +1 -0
  242. package/dist/src/indexed/structures/ring-queue.js +64 -0
  243. package/dist/src/indexed/structures/ring-queue.js.map +1 -0
  244. package/dist/src/indexed/sync.d.ts +50 -0
  245. package/dist/src/indexed/sync.d.ts.map +1 -0
  246. package/dist/src/indexed/sync.js +175 -0
  247. package/dist/src/indexed/sync.js.map +1 -0
  248. package/dist/src/indexed/terahac.d.ts +58 -0
  249. package/dist/src/indexed/terahac.d.ts.map +1 -0
  250. package/dist/src/indexed/terahac.js +244 -0
  251. package/dist/src/indexed/terahac.js.map +1 -0
  252. package/dist/src/indexed/to-snapshot.d.ts +45 -2
  253. package/dist/src/indexed/to-snapshot.d.ts.map +1 -1
  254. package/dist/src/indexed/to-snapshot.js +140 -6
  255. package/dist/src/indexed/to-snapshot.js.map +1 -1
  256. package/dist/src/link-prediction/common-neighbors-legacy.d.ts +71 -0
  257. package/dist/src/link-prediction/common-neighbors-legacy.d.ts.map +1 -0
  258. package/dist/src/link-prediction/common-neighbors-legacy.js +172 -0
  259. package/dist/src/link-prediction/common-neighbors-legacy.js.map +1 -0
  260. package/dist/src/link-prediction/common-neighbors.d.ts +11 -19
  261. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  262. package/dist/src/link-prediction/common-neighbors.js +56 -123
  263. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  264. package/dist/src/optimized/direction-optimized-bfs.d.ts +6 -0
  265. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
  266. package/dist/src/optimized/direction-optimized-bfs.js +6 -0
  267. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
  268. package/dist/src/research/grsbm-legacy.d.ts +82 -0
  269. package/dist/src/research/grsbm-legacy.d.ts.map +1 -0
  270. package/dist/src/research/grsbm-legacy.js +416 -0
  271. package/dist/src/research/grsbm-legacy.js.map +1 -0
  272. package/dist/src/research/grsbm.d.ts +9 -66
  273. package/dist/src/research/grsbm.d.ts.map +1 -1
  274. package/dist/src/research/grsbm.js +67 -391
  275. package/dist/src/research/grsbm.js.map +1 -1
  276. package/dist/src/research/sync-legacy.d.ts +48 -0
  277. package/dist/src/research/sync-legacy.d.ts.map +1 -0
  278. package/dist/src/research/sync-legacy.js +337 -0
  279. package/dist/src/research/sync-legacy.js.map +1 -0
  280. package/dist/src/research/sync.d.ts +10 -32
  281. package/dist/src/research/sync.d.ts.map +1 -1
  282. package/dist/src/research/sync.js +28 -316
  283. package/dist/src/research/sync.js.map +1 -1
  284. package/package.json +4 -3
  285. package/src/algorithms/centrality/betweenness.ts +51 -1
  286. package/src/algorithms/centrality/closeness.ts +112 -12
  287. package/src/algorithms/centrality/degree.ts +19 -0
  288. package/src/algorithms/centrality/eigenvector.ts +34 -0
  289. package/src/algorithms/centrality/hits.ts +24 -0
  290. package/src/algorithms/centrality/katz.ts +32 -0
  291. package/src/algorithms/centrality/pagerank.ts +53 -13
  292. package/src/algorithms/community/girvan-newman-legacy.ts +414 -0
  293. package/src/algorithms/community/girvan-newman.ts +36 -386
  294. package/src/algorithms/community/label-propagation.ts +24 -133
  295. package/src/algorithms/components/connected.ts +28 -95
  296. package/src/algorithms/mst/kruskal.ts +65 -4
  297. package/src/algorithms/shortest-path/bellman-ford.ts +49 -0
  298. package/src/algorithms/shortest-path/dijkstra.ts +29 -0
  299. package/src/algorithms/shortest-path/floyd-warshall.ts +106 -136
  300. package/src/algorithms/traversal/bfs-unified.ts +52 -366
  301. package/src/algorithms/traversal/bfs-variants.ts +5 -28
  302. package/src/algorithms/traversal/bfs.ts +0 -4
  303. package/src/algorithms/traversal/dfs.ts +39 -182
  304. package/src/clustering/hierarchical-legacy.ts +551 -0
  305. package/src/clustering/hierarchical.ts +64 -520
  306. package/src/clustering/k-core-legacy.ts +229 -0
  307. package/src/clustering/k-core.ts +38 -209
  308. package/src/clustering/mcl-legacy.ts +498 -0
  309. package/src/clustering/mcl.ts +46 -479
  310. package/src/index.ts +90 -3
  311. package/src/indexed/accelerator.ts +502 -16
  312. package/src/indexed/all-pairs.ts +342 -0
  313. package/src/indexed/bellman-ford.ts +72 -0
  314. package/src/indexed/betweenness.ts +258 -0
  315. package/src/indexed/bfs.ts +189 -3
  316. package/src/indexed/bipartite.ts +77 -0
  317. package/src/indexed/closeness.ts +160 -0
  318. package/src/indexed/common-neighbors.ts +31 -14
  319. package/src/indexed/degree.ts +55 -0
  320. package/src/indexed/delta-pagerank.ts +513 -0
  321. package/src/indexed/dfs.ts +189 -0
  322. package/src/indexed/eigenvector.ts +202 -0
  323. package/src/indexed/facade.ts +229 -0
  324. package/src/indexed/flow.ts +500 -0
  325. package/src/indexed/girvan-newman.ts +120 -0
  326. package/src/indexed/grsbm.ts +390 -0
  327. package/src/indexed/hierarchical.ts +295 -0
  328. package/src/indexed/index.ts +94 -3
  329. package/src/indexed/isomorphism.ts +305 -0
  330. package/src/indexed/label-propagation.ts +582 -0
  331. package/src/indexed/leiden.ts +436 -0
  332. package/src/indexed/link-prediction.ts +399 -0
  333. package/src/indexed/markov.ts +324 -0
  334. package/src/indexed/matching.ts +192 -0
  335. package/src/indexed/min-cut.ts +201 -0
  336. package/src/indexed/modularity.ts +81 -0
  337. package/src/indexed/mst.ts +79 -1
  338. package/src/indexed/pagerank.ts +128 -15
  339. package/src/indexed/point-to-point.ts +220 -0
  340. package/src/indexed/scc.ts +132 -0
  341. package/src/indexed/spectral.ts +550 -0
  342. package/src/indexed/structures/bit-set.ts +65 -0
  343. package/src/indexed/structures/max-heap.ts +74 -0
  344. package/src/indexed/structures/min-heap.ts +51 -6
  345. package/src/indexed/structures/ring-queue.ts +70 -0
  346. package/src/indexed/sync.ts +229 -0
  347. package/src/indexed/terahac.ts +290 -0
  348. package/src/indexed/to-snapshot.ts +156 -6
  349. package/src/link-prediction/common-neighbors-legacy.ts +252 -0
  350. package/src/link-prediction/common-neighbors.ts +65 -168
  351. package/src/optimized/direction-optimized-bfs.ts +6 -1
  352. package/src/research/grsbm-legacy.ts +586 -0
  353. package/src/research/grsbm.ts +76 -555
  354. package/src/research/sync-legacy.ts +456 -0
  355. package/src/research/sync.ts +30 -431
@@ -1,6 +1,9 @@
1
- import { Graph } from "../../core/graph.js";
2
- import type { CommunityResult, ComponentResult, GirvanNewmanOptions, NodeId } from "../../types/index.js";
3
- import { connectedComponents } from "../components/connected.js";
1
+ import type { Graph } from "../../core/graph.js";
2
+ import { labelsToGroups } from "../../indexed/facade.js";
3
+ import { girvanNewman as indexedGirvanNewman } from "../../indexed/girvan-newman.js";
4
+ import { needsLegacyCode, toSnapshot } from "../../indexed/to-snapshot.js";
5
+ import type { CommunityResult, GirvanNewmanOptions } from "../../types/index.js";
6
+ import { girvanNewman as legacyGirvanNewman } from "./girvan-newman-legacy.js";
4
7
 
5
8
  /**
6
9
  * Girvan-Newman community detection algorithm
@@ -10,399 +13,46 @@ import { connectedComponents } from "../components/connected.js";
10
13
  * splits into disconnected components.
11
14
  *
12
15
  * This is a divisive hierarchical clustering algorithm that produces a
13
- * dendrogram of community structures.
16
+ * dendrogram of community structures. It delegates to `indexed.girvanNewman`; the result equals
17
+ * the pre-migration implementation in `girvan-newman-legacy.ts`.
14
18
  *
15
19
  * References:
16
20
  * - Girvan, M., & Newman, M. E. J. (2002). Community structure in social
17
21
  * and biological networks. Proceedings of the National Academy of Sciences,
18
22
  * 99(12), 7821-7826.
19
- * @param graph - The input graph
23
+ * @param graph - The input graph (must be undirected)
20
24
  * @param options - Algorithm options
21
25
  * @returns Array of community detection results representing the dendrogram
26
+ * @throws Error on a directed graph
22
27
  */
23
28
  export function girvanNewman(graph: Graph, options: GirvanNewmanOptions = {}): CommunityResult[] {
24
- const { maxCommunities } = options;
25
- const minCommunitySize = options.minCommunitySize ?? 1;
26
- const maxIterations = options.maxIterations ?? 100; // Prevent infinite loops
27
-
28
- const dendrogram: CommunityResult[] = [];
29
- const workingGraph = graph.clone();
30
- let iterations = 0;
31
-
32
- // Initial state: one large component
33
- let components = getConnectedComponentsResult(workingGraph);
34
- dendrogram.push({
35
- communities: components.components.filter((community) => community.length >= minCommunitySize),
36
- modularity: calculateModularity(graph, components.componentMap),
37
- });
38
-
39
- while (Array.from(workingGraph.edges()).length > 0 && iterations < maxIterations) {
40
- iterations++;
41
- // Calculate edge betweenness centrality
42
- const { betweenness: edgeBetweenness, edgeEndpoints } = calculateEdgeBetweenness(workingGraph);
43
-
44
- if (edgeBetweenness.size === 0) {
45
- break;
46
- }
47
-
48
- // Find edges with maximum betweenness
49
- const maxBetweenness = Math.max(...edgeBetweenness.values());
50
- const edgesToRemove: { source: NodeId; target: NodeId }[] = [];
51
-
52
- for (const [edgeKey, centrality] of edgeBetweenness) {
53
- if (Math.abs(centrality - maxBetweenness) < 1e-10) {
54
- // Use original edge endpoints to preserve node ID types (number vs string)
55
- const endpoints = edgeEndpoints.get(edgeKey);
56
- if (endpoints) {
57
- edgesToRemove.push(endpoints);
58
- }
59
- }
60
- }
61
-
62
- // Remove edges with highest betweenness
63
- for (const { source, target } of edgesToRemove) {
64
- workingGraph.removeEdge(source, target);
65
- }
66
-
67
- // Find new connected components (communities)
68
- components = getConnectedComponentsResult(workingGraph);
69
-
70
- // Filter communities by minimum size
71
- const validCommunities = components.components.filter((community) => community.length >= minCommunitySize);
72
-
73
- // Calculate modularity for the new community structure
74
- const modularity = calculateModularity(graph, components.componentMap);
75
-
76
- dendrogram.push({
77
- communities: validCommunities,
78
- modularity,
79
- });
80
-
81
- // Stop if desired number of communities reached
82
- if (maxCommunities && validCommunities.length >= maxCommunities) {
83
- break;
84
- }
85
-
86
- // Stop if no more meaningful communities can be formed
87
- if (validCommunities.length === workingGraph.nodeCount) {
88
- break;
89
- }
90
- }
91
-
92
- return dendrogram;
93
- }
94
-
95
- /**
96
- * Edge betweenness result containing centrality values and original edge endpoints
97
- */
98
- interface EdgeBetweennessResult {
99
- betweenness: Map<string, number>;
100
- edgeEndpoints: Map<string, { source: NodeId; target: NodeId }>;
101
- }
102
-
103
- /**
104
- * Calculate edge betweenness centrality for all edges in the graph
105
- *
106
- * Edge betweenness is the fraction of shortest paths that pass through the edge.
107
- * We adapt node betweenness centrality calculation to work with edges.
108
- * @param graph - The input graph to analyze
109
- * @returns Object with betweenness map and edge endpoints map (preserving original ID types)
110
- */
111
- function calculateEdgeBetweenness(graph: Graph): EdgeBetweennessResult {
112
- const edgeBetweenness = new Map<string, number>();
113
- const edgeEndpoints = new Map<string, { source: NodeId; target: NodeId }>();
114
-
115
- // Initialize all edges with 0 betweenness and store original endpoints
116
- for (const edge of graph.edges()) {
117
- const edgeKey = getEdgeKey(edge.source, edge.target);
118
- edgeBetweenness.set(edgeKey, 0);
119
- edgeEndpoints.set(edgeKey, { source: edge.source, target: edge.target });
120
- }
121
-
122
- // For each node as source, calculate shortest paths and accumulate edge betweenness
123
- for (const sourceNode of graph.nodes()) {
124
- const source = sourceNode.id;
125
-
126
- // Run BFS to find all shortest paths from source
127
- const { distances, predecessors, pathCounts } = findAllShortestPaths(graph, source);
128
-
129
- // Calculate dependency for each node and accumulate edge betweenness
130
- const dependency = new Map<NodeId, number>();
131
-
132
- // Initialize dependency
133
- for (const node of graph.nodes()) {
134
- dependency.set(node.id, 0);
135
- }
136
-
137
- // Process nodes in order of decreasing distance
138
- const sortedNodes = Array.from(distances.keys())
139
- .filter((node) => {
140
- const distance = distances.get(node);
141
- return distance !== undefined && distance < Infinity;
142
- })
143
- .sort((a, b) => {
144
- const distanceA = distances.get(a);
145
- const distanceB = distances.get(b);
146
- if (distanceA === undefined || distanceB === undefined) {
147
- return 0;
148
- }
149
-
150
- return distanceB - distanceA;
151
- });
152
-
153
- for (const node of sortedNodes) {
154
- if (node === source) {
155
- continue;
156
- }
157
-
158
- const nodeDependency = dependency.get(node);
159
- const nodePathCount = pathCounts.get(node);
160
-
161
- if (nodeDependency === undefined || nodePathCount === undefined) {
162
- continue;
163
- }
164
-
165
- // Distribute dependency to predecessors
166
- const nodePredecessors = predecessors.get(node) ?? [];
167
- for (const predecessor of nodePredecessors) {
168
- const predPathCount = pathCounts.get(predecessor);
169
- if (predPathCount === undefined) {
170
- continue;
171
- }
172
-
173
- const edgeDependency = (predPathCount / nodePathCount) * (1 + nodeDependency);
174
-
175
- // Update predecessor dependency
176
- const currentDependency = dependency.get(predecessor);
177
- if (currentDependency !== undefined) {
178
- dependency.set(predecessor, currentDependency + edgeDependency);
179
- }
180
-
181
- // Update edge betweenness
182
- const edgeKey = getEdgeKey(predecessor, node);
183
- const currentBetweenness = edgeBetweenness.get(edgeKey) ?? 0;
184
- edgeBetweenness.set(edgeKey, currentBetweenness + edgeDependency);
185
- }
186
- }
29
+ // A directed graph throws from the old code's component step. A self-loop counts once in the
30
+ // old modularity's node degree and twice in the port's; ids spelled alike share the old code's
31
+ // edge keys.
32
+ if (graph.isDirected || needsLegacyCode(graph)) {
33
+ return legacyGirvanNewman(graph, options);
187
34
  }
188
-
189
- // Normalize: divide by 2 for undirected graphs (each edge counted twice)
190
- if (!graph.isDirected) {
191
- for (const [edgeKey, betweenness] of edgeBetweenness) {
192
- edgeBetweenness.set(edgeKey, betweenness / 2);
193
- }
35
+ const s = toSnapshot(graph);
36
+ if (s.flags.hasSelfLoops) {
37
+ return legacyGirvanNewman(graph, options);
194
38
  }
195
-
196
- return { betweenness: edgeBetweenness, edgeEndpoints };
197
- }
198
-
199
- /**
200
- * Find all shortest paths from a source node using optimized BFS
201
- * @param graph - The input graph to search
202
- * @param source - The source node to compute shortest paths from
203
- * @returns Object containing distances, predecessors, and path counts
204
- */
205
- function findAllShortestPaths(
206
- graph: Graph,
207
- source: NodeId,
208
- ): {
209
- distances: Map<NodeId, number>;
210
- predecessors: Map<NodeId, NodeId[]>;
211
- pathCounts: Map<NodeId, number>;
212
- } {
213
- const distances = new Map<NodeId, number>();
214
- const predecessors = new Map<NodeId, NodeId[]>();
215
- const pathCounts = new Map<NodeId, number>();
216
- const queue: NodeId[] = [];
217
- const visited = new Set<NodeId>();
218
-
219
- // Only initialize for reachable nodes
220
- distances.set(source, 0);
221
- predecessors.set(source, []);
222
- pathCounts.set(source, 1);
223
- queue.push(source);
224
-
225
- // Optimized BFS with early termination
226
- let queueIndex = 0;
227
- while (queueIndex < queue.length) {
228
- const current = queue[queueIndex++];
229
- if (current === undefined) {
230
- continue;
231
- }
232
-
233
- if (visited.has(current)) {
234
- continue;
235
- }
236
-
237
- visited.add(current);
238
-
239
- const currentDistance = distances.get(current);
240
- if (currentDistance === undefined) {
241
- continue;
242
- }
243
-
244
- for (const neighbor of graph.neighbors(current)) {
245
- // Skip if already processed
246
- if (visited.has(neighbor)) {
247
- continue;
248
- }
249
-
250
- const edgeWeight = 1; // Unweighted for community detection
251
- const newDistance = currentDistance + edgeWeight;
252
- const neighborDistance = distances.get(neighbor) ?? Infinity;
253
-
254
- if (newDistance < neighborDistance) {
255
- // Found shorter path
256
- distances.set(neighbor, newDistance);
257
- predecessors.set(neighbor, [current]);
258
- const currentPathCount = pathCounts.get(current);
259
- if (currentPathCount !== undefined) {
260
- pathCounts.set(neighbor, currentPathCount);
261
- }
262
-
263
- queue.push(neighbor);
264
- } else if (newDistance === neighborDistance) {
265
- // Found alternative shortest path
266
- const neighborPredecessors = predecessors.get(neighbor);
267
- if (neighborPredecessors) {
268
- neighborPredecessors.push(current);
269
- const currentPathCount = pathCounts.get(current);
270
- if (currentPathCount !== undefined) {
271
- pathCounts.set(neighbor, (pathCounts.get(neighbor) ?? 0) + currentPathCount);
272
- }
273
- }
274
- }
275
- }
276
- }
277
-
278
- return { distances, predecessors, pathCounts };
279
- }
280
-
281
- /**
282
- * Generate a consistent edge key for undirected graphs
283
- * @param source - The source node ID
284
- * @param target - The target node ID
285
- * @returns A consistent string key for the edge (ordered alphabetically)
286
- */
287
- function getEdgeKey(source: NodeId, target: NodeId): string {
288
- // For undirected graphs, ensure consistent ordering
289
- const sourceStr = String(source);
290
- const targetStr = String(target);
291
-
292
- if (sourceStr <= targetStr) {
293
- return `${sourceStr}|${targetStr}`;
294
- }
295
-
296
- return `${targetStr}|${sourceStr}`;
297
- }
298
-
299
- /**
300
- * Calculate modularity for a given community structure
301
- *
302
- * Newman's Q, written per community:
303
- *
304
- * Q = sum over communities c of [ w_in(c) / m - ( K_c / (2m) )^2 ]
305
- *
306
- * where m is the total edge weight with each undirected edge counted once, w_in(c) is the summed
307
- * weight of the edges with both endpoints inside c, and K_c is the summed weighted degree of c's
308
- * nodes.
309
- *
310
- * WHY NOT A SUM OVER EDGES. The textbook double sum runs over every ordered PAIR of nodes inside
311
- * a community, not only over the pairs an edge happens to join. Collecting the null-model term
312
- * only where an edge exists leaves the penalty far too small, and the shortfall grows with
313
- * community size -- so the uncut whole graph, whose modularity is 0 by definition, outscores
314
- * every real cut and a caller reading the dendrogram the standard way is handed one community
315
- * containing everything. The per-community form above counts every pair exactly once and still
316
- * costs one pass over the edges rather than a pass over every pair.
317
- * @param graph - The original graph
318
- * @param communityMap - Map from node IDs to community indices
319
- * @returns The modularity score of the partition
320
- */
321
- function calculateModularity(graph: Graph, communityMap: Map<NodeId, number>): number {
322
- const totalEdgeWeight = getTotalEdgeWeight(graph);
323
- if (totalEdgeWeight === 0) {
324
- return 0;
325
- }
326
-
327
- // Summed weighted degree of each community's nodes.
328
- const degreeSum = new Map<number, number>();
329
- for (const node of graph.nodes()) {
330
- const community = communityMap.get(node.id);
331
- if (community === undefined) {
332
- continue;
333
- }
334
-
335
- degreeSum.set(community, (degreeSum.get(community) ?? 0) + getNodeDegree(graph, node.id));
336
- }
337
-
338
- // Summed weight of the edges that stay inside a community.
339
- const internalWeight = new Map<number, number>();
340
- for (const edge of graph.edges()) {
341
- const communityI = communityMap.get(edge.source);
342
- const communityJ = communityMap.get(edge.target);
343
-
344
- if (communityI === undefined || communityI !== communityJ) {
345
- continue;
346
- }
347
-
348
- internalWeight.set(communityI, (internalWeight.get(communityI) ?? 0) + (edge.weight ?? 1));
349
- }
350
-
351
- let modularity = 0;
352
- for (const [community, degrees] of degreeSum) {
353
- const share = degrees / (2 * totalEdgeWeight);
354
- modularity += (internalWeight.get(community) ?? 0) / totalEdgeWeight - share * share;
355
- }
356
-
357
- return modularity;
358
- }
359
-
360
- /**
361
- * Calculate total edge weight in the graph
362
- * @param graph - The input graph
363
- * @returns Total sum of all edge weights
364
- */
365
- function getTotalEdgeWeight(graph: Graph): number {
366
- let totalWeight = 0;
367
-
368
- for (const edge of graph.edges()) {
369
- totalWeight += edge.weight ?? 1;
370
- }
371
-
372
- return totalWeight;
373
- }
374
-
375
- /**
376
- * Get the total degree (sum of edge weights) for a node
377
- * @param graph - The input graph
378
- * @param nodeId - The node ID to compute degree for
379
- * @returns The weighted degree of the node
380
- */
381
- function getNodeDegree(graph: Graph, nodeId: NodeId): number {
382
- let degree = 0;
383
-
384
- for (const neighbor of graph.neighbors(nodeId)) {
385
- const edge = graph.getEdge(nodeId, neighbor);
386
- degree += edge?.weight ?? 1;
387
- }
388
-
389
- return degree;
390
- }
391
-
392
- /**
393
- * Convert connected components result to ComponentResult format
394
- * @param graph - The input graph
395
- * @returns Object containing components array and node-to-component map
396
- */
397
- function getConnectedComponentsResult(graph: Graph): ComponentResult {
398
- const components = connectedComponents(graph);
399
- const componentMap = new Map<NodeId, number>();
400
-
401
- components.forEach((component, index) => {
402
- component.forEach((nodeId) => {
403
- componentMap.set(nodeId, index);
404
- });
39
+ const minCommunitySize = options.minCommunitySize ?? 1;
40
+ let { maxCommunities, maxIterations } = options;
41
+ // The old code stopped after the first round on any truthy maxCommunities it had reached, which
42
+ // a negative one always has; the port ignores a count below 1.
43
+ if (maxCommunities !== undefined && maxCommunities < 0) {
44
+ maxCommunities = undefined;
45
+ maxIterations = Math.min(maxIterations ?? 100, 1);
46
+ }
47
+ const r = indexedGirvanNewman(s, { maxCommunities, minCommunitySize, maxIterations });
48
+ return r.levels.map((labels, i) => {
49
+ let count = 0;
50
+ for (const label of labels) {
51
+ count = Math.max(count, label + 1);
52
+ }
53
+ return {
54
+ communities: labelsToGroups(s.ids, labels, count).filter((c) => c.length >= minCommunitySize),
55
+ modularity: r.modularity[i],
56
+ };
405
57
  });
406
-
407
- return { components, componentMap };
408
58
  }
@@ -10,6 +10,8 @@
10
10
  */
11
11
 
12
12
  import type { Graph } from "../../core/graph.js";
13
+ import { labelPropagation as indexedLabelPropagation } from "../../indexed/label-propagation.js";
14
+ import { toSnapshot } from "../../indexed/to-snapshot.js";
13
15
  import { graphToMap } from "../../utils/graph-converters.js";
14
16
  import { SeededRandom, shuffle } from "../../utils/math-utilities.js";
15
17
 
@@ -24,130 +26,6 @@ export interface LabelPropagationResult {
24
26
  converged: boolean;
25
27
  }
26
28
 
27
- /**
28
- * Internal implementation of Label Propagation Algorithm
29
- * @param graph - Map representation of the graph (node to neighbors with weights)
30
- * @param options - Algorithm configuration options
31
- * @returns Community assignments, iteration count, and convergence status
32
- */
33
- function labelPropagationImpl(
34
- graph: Map<string, Map<string, number>>,
35
- options: LabelPropagationOptions = {},
36
- ): LabelPropagationResult {
37
- const { maxIterations = 100, randomSeed = 42 } = options;
38
-
39
- if (graph.size === 0) {
40
- return {
41
- communities: new Map(),
42
- iterations: 0,
43
- converged: true,
44
- };
45
- }
46
-
47
- // Initialize random number generator
48
- const random = SeededRandom.createGenerator(randomSeed);
49
-
50
- // Initialize labels - each node gets its own label
51
- const labels = new Map<string, number>();
52
- const nodes = Array.from(graph.keys());
53
- nodes.forEach((node, i) => labels.set(node, i));
54
-
55
- let iterations = 0;
56
- let converged = false;
57
-
58
- // Main loop
59
- while (iterations < maxIterations && !converged) {
60
- iterations++;
61
- converged = true;
62
-
63
- // Create random order for node updates
64
- const nodeOrder = [...nodes];
65
- shuffle(nodeOrder, random);
66
-
67
- // Update each node's label
68
- for (const node of nodeOrder) {
69
- const neighbors = graph.get(node);
70
- if (!neighbors || neighbors.size === 0) {
71
- continue;
72
- }
73
-
74
- // Count label frequencies among neighbors
75
- const labelCounts = new Map<number, number>();
76
- let maxCount = 0;
77
- const candidateLabels: number[] = [];
78
-
79
- for (const [neighbor, weight] of neighbors) {
80
- const neighborLabel = labels.get(neighbor);
81
- if (neighborLabel === undefined) {
82
- continue;
83
- }
84
-
85
- const count = (labelCounts.get(neighborLabel) ?? 0) + weight;
86
- labelCounts.set(neighborLabel, count);
87
-
88
- if (count > maxCount) {
89
- maxCount = count;
90
- candidateLabels.length = 0;
91
- candidateLabels.push(neighborLabel);
92
- } else if (count === maxCount) {
93
- candidateLabels.push(neighborLabel);
94
- }
95
- }
96
-
97
- // Choose label (break ties randomly)
98
- const currentLabel = labels.get(node);
99
- if (currentLabel === undefined) {
100
- continue;
101
- }
102
-
103
- let newLabel = currentLabel;
104
-
105
- if (candidateLabels.length > 0) {
106
- // Include current label if it has max count
107
- if (labelCounts.get(currentLabel) === maxCount) {
108
- candidateLabels.push(currentLabel);
109
- }
110
-
111
- // Random selection from candidates
112
- const index = Math.floor(random() * candidateLabels.length);
113
- const selectedLabel = candidateLabels[index];
114
- if (selectedLabel !== undefined) {
115
- newLabel = selectedLabel;
116
- }
117
- }
118
-
119
- // Update label if changed
120
- if (newLabel !== currentLabel) {
121
- labels.set(node, newLabel);
122
- converged = false;
123
- }
124
- }
125
- }
126
-
127
- // Renumber communities consecutively
128
- const uniqueLabels = new Set(labels.values());
129
- const labelMap = new Map<number, number>();
130
- let communityId = 0;
131
-
132
- for (const label of uniqueLabels) {
133
- labelMap.set(label, communityId++);
134
- }
135
-
136
- const communities = new Map<string, number>();
137
- for (const [node, label] of labels) {
138
- const mappedLabel = labelMap.get(label);
139
- if (mappedLabel !== undefined) {
140
- communities.set(node, mappedLabel);
141
- }
142
- }
143
-
144
- return {
145
- communities,
146
- iterations,
147
- converged,
148
- };
149
- }
150
-
151
29
  /**
152
30
  * Internal implementation of Asynchronous Label Propagation
153
31
  * Updates all nodes simultaneously (can lead to oscillations)
@@ -366,19 +244,32 @@ function labelPropagationSemiSupervisedImpl(
366
244
  }
367
245
 
368
246
  /**
369
- * Label Propagation Algorithm
370
- * Each node adopts the most frequent label among its neighbors
371
- * @param graph - Undirected graph (can be weighted)
247
+ * Label Propagation Algorithm (fast label propagation: a seeded work queue of nodes whose label
248
+ * may no longer be dominant). Each node adopts the label with the largest summed edge weight among
249
+ * its neighbours, ties drawn uniformly; on a directed graph in- and out-neighbours both vote, and a
250
+ * self-loop does not vote. Communities are numbered in order of first appearance in node order.
251
+ * @param graph - The graph (can be weighted)
372
252
  * @param options - Algorithm options
373
- * @returns Community assignments
253
+ * @returns Community assignments keyed by `String(id)`
254
+ * @throws RangeError when an edge weight is negative, NaN or infinite
374
255
  *
375
- * Time Complexity: O(m) per iteration, typically O(km) for k iterations
376
- * Space Complexity: O(n)
256
+ * Time Complexity: O(m) per full pass, at most maxIterations passes
257
+ * Space Complexity: O(n + m)
377
258
  */
378
259
  export function labelPropagation(graph: Graph, options: LabelPropagationOptions = {}): LabelPropagationResult {
379
- // Convert Graph to Map representation
380
- const graphMap = graphToMap(graph);
381
- return labelPropagationImpl(graphMap, options);
260
+ const s = toSnapshot(graph);
261
+ const n = s.nodeCount;
262
+ // This function always took any number: its loop ran while iterations < maxIterations, and any
263
+ // seed started a stream. The port wants integers, so round here the way that loop counted.
264
+ const max = options.maxIterations ?? 100;
265
+ const cap = max > 0 ? Math.min(Math.ceil(max), Math.floor(Number.MAX_SAFE_INTEGER / Math.max(n, 1))) : 0;
266
+ const seed = options.randomSeed ?? 42;
267
+ const { labels, iterations, converged } = indexedLabelPropagation(s, {
268
+ maxIterations: cap,
269
+ randomSeed: Number.isFinite(seed) ? Math.floor(seed) : 0,
270
+ });
271
+ // With no pass run on a non-empty graph, nothing was checked, so nothing converged.
272
+ return { communities: s.ids.toStringMap(labels), iterations, converged: converged && (cap > 0 || n === 0) };
382
273
  }
383
274
 
384
275
  /**