@graphty/algorithms 2.1.2 → 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,5 +1,9 @@
1
1
  import type { Graph } from "../../core/graph.js";
2
- import { UnionFind } from "../../data-structures/union-find.js";
2
+ import { weaklyConnectedComponents as indexedWeaklyConnectedComponents } from "../../indexed/components.js";
3
+ import { depthFirstSearch } from "../../indexed/dfs.js";
4
+ import { labelsToGroups } from "../../indexed/facade.js";
5
+ import { tarjan } from "../../indexed/scc.js";
6
+ import { legacyArcOrder, toTopologySnapshot } from "../../indexed/to-snapshot.js";
3
7
  import type { NodeId } from "../../types/index.js";
4
8
 
5
9
  /**
@@ -21,20 +25,19 @@ export function connectedComponents(graph: Graph): NodeId[][] {
21
25
  );
22
26
  }
23
27
 
24
- const nodes = Array.from(graph.nodes()).map((node) => node.id);
25
-
26
- if (nodes.length === 0) {
27
- return [];
28
- }
29
-
30
- const unionFind = new UnionFind(nodes);
31
-
32
- // Union connected nodes
33
- for (const edge of Array.from(graph.edges())) {
34
- unionFind.union(edge.source, edge.target);
35
- }
28
+ return componentGroups(graph);
29
+ }
36
30
 
37
- return unionFind.getAllComponents();
31
+ /**
32
+ * The weakly connected components as legacy lists them: one group per component in the order of its
33
+ * first node, members in node order.
34
+ * @param graph - The input graph
35
+ * @returns The components
36
+ */
37
+ function componentGroups(graph: Graph): NodeId[][] {
38
+ const s = toTopologySnapshot(graph);
39
+ const { labels, count } = indexedWeaklyConnectedComponents(s);
40
+ return labelsToGroups(s.ids, labels, count);
38
41
  }
39
42
 
40
43
  /**
@@ -127,12 +130,10 @@ export function getConnectedComponent(graph: Graph, nodeId: NodeId): NodeId[] {
127
130
  throw new Error("Connected components algorithm requires an undirected graph");
128
131
  }
129
132
 
130
- const visited = new Set<NodeId>();
131
- const component: NodeId[] = [];
132
-
133
- dfsComponent(graph, nodeId, visited, component);
134
-
135
- return component;
133
+ // The members in depth-first discovery order, neighbours in insertion order.
134
+ const s = toTopologySnapshot(graph);
135
+ const { order } = depthFirstSearch(s, s.ids.indexOf(nodeId), { arcOrder: legacyArcOrder(graph, s) });
136
+ return Array.from(order, (i) => s.ids.idOf(i));
136
137
  }
137
138
 
138
139
  /**
@@ -145,68 +146,13 @@ export function stronglyConnectedComponents(graph: Graph): NodeId[][] {
145
146
  throw new Error("Strongly connected components require a directed graph");
146
147
  }
147
148
 
148
- const nodes = Array.from(graph.nodes()).map((node) => node.id);
149
- const components: NodeId[][] = [];
150
- const indices = new Map<NodeId, number>();
151
- const lowLinks = new Map<NodeId, number>();
152
- const onStack = new Set<NodeId>();
153
- const stack: NodeId[] = [];
154
- let index = 0;
155
-
156
- function tarjanSCC(nodeId: NodeId): void {
157
- // Set the depth index for this node
158
- indices.set(nodeId, index);
159
- lowLinks.set(nodeId, index);
160
- index++;
161
- stack.push(nodeId);
162
- onStack.add(nodeId);
163
-
164
- // Consider successors
165
- for (const neighbor of Array.from(graph.neighbors(nodeId))) {
166
- if (!indices.has(neighbor)) {
167
- // Successor has not yet been visited; recurse on it
168
- tarjanSCC(neighbor);
169
- const nodeLL = lowLinks.get(nodeId) ?? 0;
170
- const neighborLL = lowLinks.get(neighbor) ?? 0;
171
- lowLinks.set(nodeId, Math.min(nodeLL, neighborLL));
172
- } else if (onStack.has(neighbor)) {
173
- // Successor is in stack and hence in the current SCC
174
- const nodeLL = lowLinks.get(nodeId) ?? 0;
175
- const neighborIndex = indices.get(neighbor) ?? 0;
176
- lowLinks.set(nodeId, Math.min(nodeLL, neighborIndex));
177
- }
178
- }
179
-
180
- // If nodeId is a root node, pop the stack and create an SCC
181
- const nodeIndex = indices.get(nodeId) ?? 0;
182
- const nodeLowLink = lowLinks.get(nodeId) ?? 0;
183
-
184
- if (nodeLowLink === nodeIndex) {
185
- const component: NodeId[] = [];
186
- let w: NodeId;
187
-
188
- do {
189
- const popped = stack.pop();
190
- if (popped === undefined) {
191
- break;
192
- }
193
-
194
- w = popped;
195
-
196
- onStack.delete(w);
197
- component.push(w);
198
- } while (w !== nodeId);
199
-
200
- components.push(component);
201
- }
202
- }
203
-
204
- for (const nodeId of nodes) {
205
- if (!indices.has(nodeId)) {
206
- tarjanSCC(nodeId);
207
- }
149
+ // Tarjan's pop order lists each component as one run, in component order.
150
+ const s = toTopologySnapshot(graph);
151
+ const { labels, count, popped } = tarjan(s, { arcOrder: legacyArcOrder(graph, s) });
152
+ const components: NodeId[][] = Array.from({ length: count }, () => []);
153
+ for (const i of popped) {
154
+ components[labels[i]].push(s.ids.idOf(i));
208
155
  }
209
-
210
156
  return components;
211
157
  }
212
158
 
@@ -237,20 +183,7 @@ export function weaklyConnectedComponents(graph: Graph): NodeId[][] {
237
183
  );
238
184
  }
239
185
 
240
- const nodes = Array.from(graph.nodes()).map((node) => node.id);
241
-
242
- if (nodes.length === 0) {
243
- return [];
244
- }
245
-
246
- const unionFind = new UnionFind(nodes);
247
-
248
- // Union nodes connected by edges (ignore direction)
249
- for (const edge of Array.from(graph.edges())) {
250
- unionFind.union(edge.source, edge.target);
251
- }
252
-
253
- return unionFind.getAllComponents();
186
+ return componentGroups(graph);
254
187
  }
255
188
 
256
189
  /**
@@ -1,5 +1,8 @@
1
1
  import { Graph } from "../../core/graph.js";
2
2
  import { UnionFind } from "../../data-structures/union-find.js";
3
+ import { exactArcWeights } from "../../indexed/facade.js";
4
+ import { kruskalMST as indexedKruskalMST } from "../../indexed/mst.js";
5
+ import { toSnapshotOrNull } from "../../indexed/to-snapshot.js";
3
6
  import type { Edge } from "../../types/index.js";
4
7
 
5
8
  export interface MSTResult {
@@ -20,14 +23,72 @@ export function kruskalMST(graph: Graph): MSTResult {
20
23
  throw new Error("Kruskal's algorithm requires an undirected graph");
21
24
  }
22
25
 
26
+ // A NaN weight makes the legacy sort order depend on the sort's own internals, and legacy drops
27
+ // an edge whose key string another edge already spells (1-2 and "1"-"2"): keep that walk for both.
28
+ const s = legacyDropsAnEdge(graph) ? null : toSnapshotOrNull(graph);
29
+ if (s === null) {
30
+ return kruskalMSTLegacy(graph);
31
+ }
32
+
33
+ const { edges: accepted, totalWeight } = indexedKruskalMST(s, { weights: exactArcWeights(s) });
34
+ const { src, dst } = s.edgeList();
35
+ const edges: Edge[] = [];
36
+ for (const e of accepted) {
37
+ // The graph's own edge object, as legacy returned it.
38
+ const edge = graph.getEdge(s.ids.idOf(src[e]), s.ids.idOf(dst[e]));
39
+ if (edge !== undefined) {
40
+ edges.push(edge);
41
+ }
42
+ }
43
+
44
+ if (edges.length !== graph.nodeCount - 1) {
45
+ throw new Error("Graph is not connected");
46
+ }
47
+
48
+ return { edges, totalWeight };
49
+ }
50
+
51
+ /**
52
+ * The key legacy Kruskal dedups edges by: both ids as strings, lesser first.
53
+ * @param edge - The edge
54
+ * @returns Its key
55
+ */
56
+ function legacyEdgeKey(edge: Edge): string {
57
+ return edge.source < edge.target
58
+ ? `${String(edge.source)}-${String(edge.target)}`
59
+ : `${String(edge.target)}-${String(edge.source)}`;
60
+ }
61
+
62
+ /**
63
+ * Whether legacy Kruskal would ignore one of the graph's edges.
64
+ * @param graph - The undirected graph
65
+ * @returns True if two of its edges share a legacy key, so legacy Kruskal ignores one of them
66
+ */
67
+ function legacyDropsAnEdge(graph: Graph): boolean {
68
+ const keys = new Set<string>();
69
+ for (const edge of graph.edges()) {
70
+ const key = legacyEdgeKey(edge);
71
+ if (keys.has(key)) {
72
+ return true;
73
+ }
74
+ keys.add(key);
75
+ }
76
+ return false;
77
+ }
78
+
79
+ /**
80
+ * The legacy Kruskal behind {@link kruskalMST}, for a graph with a NaN weight or two edges whose
81
+ * keys collide.
82
+ * @param graph - The undirected graph to compute the MST for
83
+ * @returns The minimum spanning tree as a set of edges and the total weight
84
+ * @throws Error if the graph is not connected
85
+ */
86
+ function kruskalMSTLegacy(graph: Graph): MSTResult {
23
87
  const edges: Edge[] = [];
24
88
  const visitedEdges = new Set<string>();
25
89
 
26
90
  for (const edge of Array.from(graph.edges())) {
27
- const edgeKey =
28
- edge.source < edge.target
29
- ? `${String(edge.source)}-${String(edge.target)}`
30
- : `${String(edge.target)}-${String(edge.source)}`;
91
+ const edgeKey = legacyEdgeKey(edge);
31
92
 
32
93
  if (!visitedEdges.has(edgeKey)) {
33
94
  visitedEdges.add(edgeKey);
@@ -1,4 +1,7 @@
1
1
  import type { Graph } from "../../core/graph.js";
2
+ import { bellmanFord as indexedBellmanFord } from "../../indexed/bellman-ford.js";
3
+ import { exactArcWeights } from "../../indexed/facade.js";
4
+ import { toSnapshotOrNull } from "../../indexed/to-snapshot.js";
2
5
  import type { NodeId, ShortestPathResult } from "../../types/index.js";
3
6
  import { reconstructPath } from "../../utils/graph-utilities.js";
4
7
 
@@ -210,6 +213,52 @@ export function bellmanFordPath(graph: Graph, source: NodeId, target: NodeId): S
210
213
  * @returns True if the graph contains a negative cycle
211
214
  */
212
215
  export function hasNegativeCycle(graph: Graph): boolean {
216
+ // A -Infinity weight: legacy's answer depends on where its |V| - 1 rounds stopped, so keep that walk.
217
+ const s = hasMinusInfinityWeight(graph) ? null : toSnapshotOrNull(graph);
218
+ if (s === null) {
219
+ return hasNegativeCycleLegacy(graph);
220
+ }
221
+
222
+ // One search from each node no earlier search reached, as a cycle reachable from a reached node
223
+ // was reachable from that search's source too.
224
+ const weights = exactArcWeights(s);
225
+ const reached = new Uint8Array(s.nodeCount);
226
+ for (let source = 0; source < s.nodeCount; source++) {
227
+ if (reached[source] === 0) {
228
+ const { dist, hasNegativeCycle: found } = indexedBellmanFord(s, source, { weights });
229
+ if (found) {
230
+ return true;
231
+ }
232
+ for (let v = 0; v < s.nodeCount; v++) {
233
+ if (dist[v] !== Infinity) {
234
+ reached[v] = 1;
235
+ }
236
+ }
237
+ }
238
+ }
239
+ return false;
240
+ }
241
+
242
+ /**
243
+ * Whether any edge of the graph weighs -Infinity.
244
+ * @param graph - The graph to scan
245
+ * @returns True if any edge weighs -Infinity
246
+ */
247
+ function hasMinusInfinityWeight(graph: Graph): boolean {
248
+ for (const edge of graph.edges()) {
249
+ if (edge.weight === -Infinity) {
250
+ return true;
251
+ }
252
+ }
253
+ return false;
254
+ }
255
+
256
+ /**
257
+ * The legacy check behind {@link hasNegativeCycle}, for a graph with a NaN or -Infinity weight.
258
+ * @param graph - The graph to check for negative cycles
259
+ * @returns True if the graph contains a negative cycle
260
+ */
261
+ function hasNegativeCycleLegacy(graph: Graph): boolean {
213
262
  const nodes = Array.from(graph.nodes());
214
263
 
215
264
  if (nodes.length === 0) {
@@ -1,5 +1,8 @@
1
1
  import type { Graph } from "../../core/graph.js";
2
2
  import { PriorityQueue } from "../../data-structures/priority-queue.js";
3
+ import { dijkstra as indexedDijkstra } from "../../indexed/dijkstra.js";
4
+ import { exactArcWeights } from "../../indexed/facade.js";
5
+ import { toSnapshotOrNull } from "../../indexed/to-snapshot.js";
3
6
  import type { DijkstraOptions, NodeId, ShortestPathResult } from "../../types/index.js";
4
7
  import { reconstructPath } from "../../utils/graph-utilities.js";
5
8
  import { BidirectionalDijkstra } from "./bidirectional-dijkstra.js";
@@ -174,6 +177,32 @@ export function singleSourceShortestPath(graph: Graph, source: NodeId, cutoff?:
174
177
  throw new Error(`Source node ${String(source)} not found in graph`);
175
178
  }
176
179
 
180
+ const s = toSnapshotOrNull(graph);
181
+ const weights = s === null ? undefined : (exactArcWeights(s) ?? s.weights ?? undefined);
182
+ // Dijkstra's answer with a negative or NaN weight depends on the order the legacy walk settled
183
+ // nodes in, which the port does not share: those graphs keep the legacy walk.
184
+ if (s === null || weights?.some((w) => w < 0) === true) {
185
+ return singleSourceShortestPathLegacy(graph, source, cutoff);
186
+ }
187
+
188
+ const { dist } = indexedDijkstra(s, s.ids.indexOf(source), { cutoff, weights });
189
+ const result = new Map<NodeId, number>();
190
+ for (let i = 0; i < s.nodeCount; i++) {
191
+ if (dist[i] < Infinity && (cutoff === undefined || dist[i] <= cutoff)) {
192
+ result.set(s.ids.idOf(i), dist[i]);
193
+ }
194
+ }
195
+ return result;
196
+ }
197
+
198
+ /**
199
+ * The legacy walk behind {@link singleSourceShortestPath}, for graphs with a negative or NaN weight.
200
+ * @param graph - The graph to search
201
+ * @param source - The starting node for the search, known to be in the graph
202
+ * @param cutoff - Optional maximum distance to search
203
+ * @returns A map of node IDs to their distances from the source
204
+ */
205
+ function singleSourceShortestPathLegacy(graph: Graph, source: NodeId, cutoff?: number): Map<NodeId, number> {
177
206
  const distances = new Map<NodeId, number>();
178
207
  const visited = new Set<NodeId>();
179
208
  const pq = new PriorityQueue<NodeId>();
@@ -1,5 +1,10 @@
1
- import { Graph } from "../../core/graph.js";
2
- import type { NodeId } from "../../types/index.js";
1
+ import { INVALID_INDEX } from "@graphty/graph-format";
2
+
3
+ import type { Graph } from "../../core/graph.js";
4
+ import { allPairsShortestPath } from "../../indexed/all-pairs.js";
5
+ import { exactArcWeights } from "../../indexed/facade.js";
6
+ import { toSnapshot } from "../../indexed/to-snapshot.js";
7
+ import type { Edge, NodeId } from "../../types/index.js";
3
8
 
4
9
  export interface FloydWarshallResult {
5
10
  distances: Map<NodeId, Map<NodeId, number>>;
@@ -8,106 +13,91 @@ export interface FloydWarshallResult {
8
13
  }
9
14
 
10
15
  /**
11
- * Computes all-pairs shortest paths using the Floyd-Warshall algorithm
16
+ * The graph without its +Infinity edges, which the port refuses and which never shortened a path
17
+ * (`x + Infinity < d` is false), and those edges. A NaN or -Infinity weight has no such reading, so it
18
+ * throws.
19
+ * @param graph - The legacy graph
20
+ * @param name - The calling function, for the error message
21
+ * @returns The graph to run on (the input itself when every weight is finite) and the removed edges,
22
+ * each in both orientations on an undirected graph
23
+ * @throws RangeError on a NaN or -Infinity edge weight
24
+ */
25
+ function withoutInfinite(graph: Graph, name: string): { finite: Graph; removed: Edge[] } {
26
+ const removed: Edge[] = [];
27
+ for (const e of graph.edges()) {
28
+ const w = e.weight ?? 1;
29
+ if (Number.isNaN(w) || w === -Infinity) {
30
+ throw new RangeError(
31
+ `${name}: the edge ${String(e.source)} -> ${String(e.target)} has weight ${String(w)}; weights must be numbers or +Infinity (no edge)`,
32
+ );
33
+ }
34
+ if (w === Infinity) {
35
+ removed.push(e);
36
+ }
37
+ }
38
+ if (removed.length === 0) {
39
+ return { finite: graph, removed };
40
+ }
41
+ const finite = graph.clone();
42
+ for (const e of removed) {
43
+ finite.removeEdge(e.source, e.target);
44
+ }
45
+ const reversed = graph.isDirected ? [] : removed.map((e) => ({ ...e, source: e.target, target: e.source }));
46
+ return { finite, removed: [...removed, ...reversed] };
47
+ }
48
+
49
+ /**
50
+ * Computes all-pairs shortest paths using the Floyd-Warshall algorithm: the port's sweep in node order
51
+ * over the exact f64 weights, with no node bound. A self-loop never changes a node's distance to itself
52
+ * (0); its predecessor on the diagonal is the node. Under a negative cycle (a negative undirected edge is
53
+ * one) every distance is NaN and every predecessor null. An infinite edge weight counts as no edge
54
+ * (its predecessor entry is its source while nothing reaches the target); a NaN weight leaves its own
55
+ * entry NaN and shortens nothing.
12
56
  * @param graph - The graph to compute shortest paths for
13
57
  * @returns The distances, predecessors, and negative cycle detection result
58
+ * @throws RangeError on a NaN or -Infinity edge weight
14
59
  */
15
60
  export function floydWarshall(graph: Graph): FloydWarshallResult {
16
- const nodes = Array.from(graph.nodes()).map((n) => n.id);
61
+ const { finite, removed } = withoutInfinite(graph, "floydWarshall");
62
+ const s = toSnapshot(finite);
63
+ const { dist, n, predArc, hasNegativeCycle } = allPairsShortestPath(s, {
64
+ weights: exactArcWeights(s),
65
+ method: "floyd-warshall",
66
+ paths: true,
67
+ maxNodes: Infinity,
68
+ });
17
69
  const distances = new Map<NodeId, Map<NodeId, number>>();
18
70
  const predecessors = new Map<NodeId, Map<NodeId, NodeId | null>>();
19
-
20
- for (const node of nodes) {
21
- distances.set(node, new Map());
22
- predecessors.set(node, new Map());
23
-
24
- const nodeDistances = distances.get(node);
25
- const nodePredecessors = predecessors.get(node);
26
-
27
- if (!nodeDistances || !nodePredecessors) {
28
- continue;
29
- }
30
-
31
- for (const other of nodes) {
32
- if (node === other) {
33
- nodeDistances.set(other, 0);
34
- nodePredecessors.set(other, null);
35
- } else {
36
- nodeDistances.set(other, Infinity);
37
- nodePredecessors.set(other, null);
38
- }
71
+ for (let i = 0; i < n; i++) {
72
+ const row = new Map<NodeId, number>();
73
+ const predRow = new Map<NodeId, NodeId | null>();
74
+ for (let j = 0; j < n; j++) {
75
+ const id = s.ids.idOf(j);
76
+ row.set(id, dist[i * n + j]);
77
+ const arc = predArc?.[i * n + j];
78
+ predRow.set(
79
+ id,
80
+ hasNegativeCycle || arc === undefined || i === j || dist[i * n + j] === Infinity
81
+ ? null
82
+ : s.ids.idOf(s.arcSource(arc)),
83
+ );
39
84
  }
85
+ distances.set(s.ids.idOf(i), row);
86
+ predecessors.set(s.ids.idOf(i), predRow);
40
87
  }
41
-
42
- for (const edge of Array.from(graph.edges())) {
43
- const weight = edge.weight ?? 1;
44
- const sourceDistances = distances.get(edge.source);
45
- const sourcePredecessors = predecessors.get(edge.source);
46
-
47
- if (sourceDistances && sourcePredecessors) {
48
- sourceDistances.set(edge.target, weight);
49
- sourcePredecessors.set(edge.target, edge.source);
50
- }
51
-
52
- if (!graph.isDirected) {
53
- const targetDistances = distances.get(edge.target);
54
- const targetPredecessors = predecessors.get(edge.target);
55
-
56
- if (targetDistances && targetPredecessors) {
57
- targetDistances.set(edge.source, weight);
58
- targetPredecessors.set(edge.source, edge.target);
88
+ if (!hasNegativeCycle) {
89
+ for (const e of graph.edges()) {
90
+ if (e.source === e.target) {
91
+ predecessors.get(e.source)?.set(e.source, e.source);
59
92
  }
60
93
  }
61
- }
62
-
63
- for (const k of nodes) {
64
- for (const i of nodes) {
65
- for (const j of nodes) {
66
- const distancesI = distances.get(i);
67
- const distancesK = distances.get(k);
68
- const predecessorsI = predecessors.get(i);
69
- const predecessorsK = predecessors.get(k);
70
-
71
- if (!distancesI || !distancesK || !predecessorsI || !predecessorsK) {
72
- continue;
73
- }
74
-
75
- const distIK = distancesI.get(k);
76
- const distKJ = distancesK.get(j);
77
- const distIJ = distancesI.get(j);
78
-
79
- if (distIK === undefined || distKJ === undefined || distIJ === undefined) {
80
- continue;
81
- }
82
-
83
- if (distIK + distKJ < distIJ) {
84
- distancesI.set(j, distIK + distKJ);
85
- const predKJ = predecessorsK.get(j);
86
- predecessorsI.set(j, predKJ ?? null);
87
- }
94
+ for (const { source, target } of removed) {
95
+ if (distances.get(source)?.get(target) === Infinity) {
96
+ predecessors.get(source)?.set(target, source);
88
97
  }
89
98
  }
90
99
  }
91
-
92
- let hasNegativeCycle = false;
93
- for (const node of nodes) {
94
- const nodeDistances = distances.get(node);
95
- if (!nodeDistances) {
96
- continue;
97
- }
98
-
99
- const selfDistance = nodeDistances.get(node);
100
- if (selfDistance !== undefined && selfDistance < 0) {
101
- hasNegativeCycle = true;
102
- break;
103
- }
104
- }
105
-
106
- return {
107
- distances,
108
- predecessors,
109
- hasNegativeCycle,
110
- };
100
+ return { distances, predecessors, hasNegativeCycle };
111
101
  }
112
102
 
113
103
  /**
@@ -115,73 +105,53 @@ export function floydWarshall(graph: Graph): FloydWarshallResult {
115
105
  * @param graph - The graph to search
116
106
  * @param source - The starting node for the path
117
107
  * @param target - The destination node for the path
118
- * @returns The path and distance or null if no path exists
108
+ * @returns The path and distance, or null if either node is missing, no path exists, or the graph
109
+ * has a negative cycle
110
+ * @throws RangeError on a NaN or -Infinity edge weight
119
111
  */
120
112
  export function floydWarshallPath(
121
113
  graph: Graph,
122
114
  source: NodeId,
123
115
  target: NodeId,
124
116
  ): { path: NodeId[]; distance: number } | null {
125
- const result = floydWarshall(graph);
126
-
127
- if (!graph.hasNode(source) || !graph.hasNode(target)) {
128
- return null;
129
- }
130
-
131
- const sourceDistances = result.distances.get(source);
132
- if (!sourceDistances) {
117
+ const s = toSnapshot(withoutInfinite(graph, "floydWarshallPath").finite);
118
+ const i = s.ids.indexOf(source);
119
+ const j = s.ids.indexOf(target);
120
+ if (i === INVALID_INDEX || j === INVALID_INDEX) {
133
121
  return null;
134
122
  }
135
-
136
- const distance = sourceDistances.get(target);
137
- if (distance === undefined || distance === Infinity) {
123
+ const result = allPairsShortestPath(s, {
124
+ weights: exactArcWeights(s),
125
+ method: "floyd-warshall",
126
+ paths: true,
127
+ maxNodes: Infinity,
128
+ });
129
+ const distance = result.dist[i * result.n + j];
130
+ if (result.hasNegativeCycle || distance === Infinity) {
138
131
  return null;
139
132
  }
140
-
141
- const path: NodeId[] = [];
142
- let current: NodeId | null = target;
143
-
144
- while (current !== null && current !== source) {
145
- path.unshift(current);
146
-
147
- const sourcePredecessors = result.predecessors.get(source);
148
- if (!sourcePredecessors) {
149
- return null;
150
- }
151
-
152
- current = sourcePredecessors.get(current) ?? null;
153
-
154
- if (path.length > graph.nodeCount) {
155
- return null;
156
- }
157
- }
158
-
159
- if (current === source) {
160
- path.unshift(source);
161
- return { path, distance };
162
- }
163
-
164
- return null;
133
+ return { path: Array.from(result.pathTo(i, j), (k) => s.ids.idOf(k)), distance };
165
134
  }
166
135
 
167
136
  /**
168
- * Computes the transitive closure of a graph using Floyd-Warshall
137
+ * Computes the transitive closure of a graph: one breadth-first search per node, edge weights
138
+ * ignored except that a +Infinity edge counts as no edge.
169
139
  * @param graph - The graph to compute transitive closure for
170
- * @returns A map of each node to the set of nodes reachable from it
140
+ * @returns A map of each node to the set of nodes reachable from it, itself included, in node order
141
+ * @throws RangeError on a NaN or -Infinity edge weight
171
142
  */
172
143
  export function transitiveClosure(graph: Graph): Map<NodeId, Set<NodeId>> {
173
- const result = floydWarshall(graph);
144
+ const s = toSnapshot(withoutInfinite(graph, "transitiveClosure").finite);
145
+ const { dist, n } = allPairsShortestPath(s, { weighted: false, maxNodes: Infinity });
174
146
  const closure = new Map<NodeId, Set<NodeId>>();
175
-
176
- for (const [source, distances] of Array.from(result.distances)) {
147
+ for (let i = 0; i < n; i++) {
177
148
  const reachable = new Set<NodeId>();
178
- for (const [target, distance] of Array.from(distances)) {
179
- if (distance < Infinity) {
180
- reachable.add(target);
149
+ for (let j = 0; j < n; j++) {
150
+ if (dist[i * n + j] < Infinity) {
151
+ reachable.add(s.ids.idOf(j));
181
152
  }
182
153
  }
183
- closure.set(source, reachable);
154
+ closure.set(s.ids.idOf(i), reachable);
184
155
  }
185
-
186
156
  return closure;
187
157
  }