@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,4 +1,15 @@
1
- import { euclideanDistance, SeededRandom } from "../utils/math-utilities.js";
1
+ /**
2
+ * SynC (Synergistic Deep Graph Clustering) over a legacy `Graph`. `syncClustering` delegates to
3
+ * `indexed.syncClustering` over an unweighted snapshot of the graph (SynC never reads a weight);
4
+ * clusters, embeddings, loss, iterations and convergence equal the pre-migration implementation in
5
+ * `sync-legacy.ts`, the floats to rounding. A run that diverges (a large learning rate) amplifies
6
+ * that rounding, so its embeddings and loss can differ from the old code's in the leading digits. A
7
+ * cluster count that is not an integer keeps the old code, which mostly fails on one ("Invalid array
8
+ * length"). Unlike the old code the port never replaces `Math.random`.
9
+ */
10
+ import { syncClustering as indexedSyncClustering } from "../indexed/sync.js";
11
+ import { toTopologySnapshot } from "../indexed/to-snapshot.js";
12
+ import { syncClustering as legacySyncClustering } from "./sync-legacy.js";
2
13
  /**
3
14
  * Synergistic Deep Graph Clustering (SynC) Algorithm
4
15
  *
@@ -12,326 +23,27 @@ import { euclideanDistance, SeededRandom } from "../utils/math-utilities.js";
12
23
  * @returns Clustering result with assignments and embeddings
13
24
  */
14
25
  export function syncClustering(graph, config) {
15
- const { numClusters, maxIterations = 100, tolerance = 1e-6, seed = 42, learningRate = 0.01, lambda = 0.1 } = config;
16
- // Set random seed for reproducibility (save original)
17
- const originalRandom = Math.random;
18
- const rng = SeededRandom.createGenerator(seed);
19
- Math.random = rng;
20
- const nodes = Array.from(graph.nodes());
21
- const nodeCount = nodes.length;
22
- if (nodeCount === 0) {
23
- return {
24
- clusters: new Map(),
25
- loss: 0,
26
- iterations: 0,
27
- embeddings: new Map(),
28
- converged: true,
29
- };
26
+ if (!Number.isInteger(config.numClusters)) {
27
+ return legacySyncClustering(graph, config);
30
28
  }
31
- if (numClusters <= 0 || numClusters > nodeCount) {
32
- throw new Error(`Invalid number of clusters: ${String(numClusters)}. Must be between 1 and ${String(nodeCount)}`);
33
- }
34
- // Initialize node embeddings (simplified version using graph features)
35
- const embeddingDim = Math.min(64, nodeCount);
29
+ const s = toTopologySnapshot(graph);
30
+ const n = s.nodeCount;
31
+ const r = indexedSyncClustering(s, config);
32
+ const clusters = new Map();
36
33
  const embeddings = new Map();
37
- // Initialize embeddings based on node features
38
- for (const node of nodes) {
39
- const embedding = initializeNodeEmbedding(graph, node.id, embeddingDim);
40
- embeddings.set(node.id, embedding);
41
- }
42
- // Initialize cluster centers
43
- const clusterCenters = initializeClusterCenters(embeddings, numClusters);
44
- let previousLoss = Infinity;
45
- let iterations = 0;
46
- let converged = false;
47
- for (iterations = 0; iterations < maxIterations; iterations++) {
48
- // E-step: Assign nodes to clusters
49
- const clusters = new Map();
50
- for (const node of nodes) {
51
- const nodeEmbedding = embeddings.get(node.id);
52
- if (!nodeEmbedding) {
53
- continue;
54
- }
55
- let bestCluster = 0;
56
- let minDistance = Infinity;
57
- for (let k = 0; k < numClusters; k++) {
58
- const center = clusterCenters[k];
59
- if (!center) {
60
- continue;
61
- }
62
- const distance = euclideanDistance(nodeEmbedding, center);
63
- if (distance < minDistance) {
64
- minDistance = distance;
65
- bestCluster = k;
66
- }
67
- }
68
- clusters.set(node.id, bestCluster);
69
- }
70
- // M-step: Update embeddings and cluster centers
71
- updateEmbeddings(graph, embeddings, clusters, learningRate, lambda);
72
- updateClusterCenters(embeddings, clusters, clusterCenters, numClusters);
73
- // Calculate current loss
74
- const currentLoss = calculateLoss(graph, embeddings, clusters, clusterCenters, lambda);
75
- // Check for convergence
76
- if (Math.abs(previousLoss - currentLoss) < tolerance) {
77
- converged = true;
78
- break;
79
- }
80
- previousLoss = currentLoss;
34
+ for (let i = 0; i < n; i++) {
35
+ const id = s.ids.idOf(i);
36
+ clusters.set(id, r.labels[i]);
37
+ embeddings.set(id, Array.from(r.embeddings.subarray(i * r.dimensions, (i + 1) * r.dimensions)));
81
38
  }
82
- // Final cluster assignment
83
- const finalClusters = new Map();
84
- for (const node of nodes) {
85
- const nodeEmbedding = embeddings.get(node.id);
86
- if (!nodeEmbedding) {
87
- continue;
88
- }
89
- let bestCluster = 0;
90
- let minDistance = Infinity;
91
- for (let k = 0; k < numClusters; k++) {
92
- const center = clusterCenters[k];
93
- if (!center) {
94
- continue;
95
- }
96
- const distance = euclideanDistance(nodeEmbedding, center);
97
- if (distance < minDistance) {
98
- minDistance = distance;
99
- bestCluster = k;
100
- }
101
- }
102
- finalClusters.set(node.id, bestCluster);
103
- }
104
- // Restore original random function
105
- Math.random = originalRandom;
106
39
  return {
107
- clusters: finalClusters,
108
- loss: previousLoss,
109
- iterations: iterations + 1,
40
+ clusters,
41
+ // The old code reported the loss of the round before the converging one.
42
+ loss: r.converged && r.iterations > 1 ? r.previousLoss : r.loss,
43
+ // ...and one round more than it ran when it stopped at the cap.
44
+ iterations: r.iterations + (r.converged ? 0 : 1),
110
45
  embeddings,
111
- converged,
46
+ converged: r.converged,
112
47
  };
113
48
  }
114
- /**
115
- * Initialize node embedding based on graph structure and features
116
- * @param graph - The input graph
117
- * @param nodeId - The node ID to initialize embedding for
118
- * @param dim - The embedding dimension
119
- * @returns The initialized embedding vector
120
- */
121
- function initializeNodeEmbedding(graph, nodeId, dim) {
122
- const embedding = new Array(dim).fill(0);
123
- // Use node degree as a base feature
124
- const degree = graph.degree(nodeId);
125
- const normalizedDegree = degree / Math.max(1, graph.nodeCount - 1);
126
- // Initialize with small random values influenced by graph structure
127
- for (let i = 0; i < dim; i++) {
128
- embedding[i] = (Math.random() - 0.5) * 0.1 + normalizedDegree * 0.1;
129
- }
130
- return embedding;
131
- }
132
- /**
133
- * Initialize cluster centers using k-means++ style initialization
134
- * @param embeddings - Map of node IDs to embedding vectors
135
- * @param numClusters - Number of cluster centers to initialize
136
- * @returns Array of initialized cluster center vectors
137
- */
138
- function initializeClusterCenters(embeddings, numClusters) {
139
- const embeddingArray = Array.from(embeddings.values());
140
- const centers = [];
141
- // Choose first center randomly
142
- const firstCenter = embeddingArray[Math.floor(Math.random() * embeddingArray.length)];
143
- if (firstCenter) {
144
- centers.push([...firstCenter]);
145
- }
146
- // Choose remaining centers using k-means++ initialization
147
- for (let k = 1; k < numClusters; k++) {
148
- const distances = [];
149
- let totalDistance = 0;
150
- for (const embedding of embeddingArray) {
151
- let minDistance = Infinity;
152
- for (const center of centers) {
153
- const distance = euclideanDistance(embedding, center);
154
- minDistance = Math.min(minDistance, distance);
155
- }
156
- distances.push(minDistance * minDistance);
157
- totalDistance += minDistance * minDistance;
158
- }
159
- // Choose next center with probability proportional to squared distance
160
- let randomValue = Math.random() * totalDistance;
161
- for (let i = 0; i < embeddingArray.length; i++) {
162
- const distanceValue = distances[i];
163
- if (distanceValue !== undefined) {
164
- randomValue -= distanceValue;
165
- }
166
- if (randomValue <= 0) {
167
- const newCenter = embeddingArray[i];
168
- if (newCenter) {
169
- centers.push([...newCenter]);
170
- }
171
- break;
172
- }
173
- }
174
- }
175
- return centers;
176
- }
177
- /**
178
- * Update node embeddings using gradient descent
179
- * @param graph - The input graph
180
- * @param embeddings - Map of node IDs to embedding vectors
181
- * @param _clusters - Map of node IDs to cluster assignments (reserved for future use)
182
- * @param learningRate - Learning rate for gradient descent
183
- * @param lambda - Regularization parameter
184
- */
185
- function updateEmbeddings(graph, embeddings, _clusters, learningRate, lambda) {
186
- const gradients = new Map();
187
- // Initialize gradients
188
- for (const [nodeId, embedding] of embeddings) {
189
- gradients.set(nodeId, new Array(embedding.length).fill(0));
190
- }
191
- // Calculate gradients based on graph structure (simplified)
192
- for (const node of graph.nodes()) {
193
- const nodeId = node.id;
194
- const nodeEmbedding = embeddings.get(nodeId);
195
- if (!nodeEmbedding) {
196
- continue;
197
- }
198
- const gradient = gradients.get(nodeId);
199
- if (!gradient) {
200
- continue;
201
- }
202
- // Neighbor reconstruction loss gradient
203
- for (const neighborId of graph.neighbors(nodeId)) {
204
- const neighborEmbedding = embeddings.get(neighborId);
205
- if (!neighborEmbedding) {
206
- continue;
207
- }
208
- const diff = nodeEmbedding.map((val, i) => val - (neighborEmbedding[i] ?? 0));
209
- for (let i = 0; i < gradient.length; i++) {
210
- const gradVal = gradient[i];
211
- const diffVal = diff[i];
212
- if (gradVal !== undefined && diffVal !== undefined) {
213
- gradient[i] = gradVal + lambda * diffVal;
214
- }
215
- }
216
- }
217
- // Regularization gradient
218
- for (let i = 0; i < gradient.length; i++) {
219
- const gradVal = gradient[i];
220
- const nodeVal = nodeEmbedding[i];
221
- if (gradVal !== undefined && nodeVal !== undefined) {
222
- gradient[i] = gradVal + lambda * nodeVal;
223
- }
224
- }
225
- }
226
- // Update embeddings
227
- for (const [nodeId, embedding] of embeddings) {
228
- const gradient = gradients.get(nodeId);
229
- if (!gradient) {
230
- continue;
231
- }
232
- for (let i = 0; i < embedding.length; i++) {
233
- const embVal = embedding[i];
234
- const gradVal = gradient[i];
235
- if (embVal !== undefined && gradVal !== undefined) {
236
- embedding[i] = embVal - learningRate * gradVal;
237
- }
238
- }
239
- }
240
- }
241
- /**
242
- * Update cluster centers based on current assignments
243
- * @param embeddings - Map of node IDs to embedding vectors
244
- * @param clusters - Map of node IDs to cluster assignments
245
- * @param clusterCenters - Array of cluster center vectors to update
246
- * @param numClusters - Number of clusters
247
- */
248
- function updateClusterCenters(embeddings, clusters, clusterCenters, numClusters) {
249
- const dimensions = clusterCenters[0]?.length ?? 0;
250
- const clusterSums = Array.from({ length: numClusters }, () => new Array(dimensions).fill(0));
251
- const clusterCounts = new Array(numClusters).fill(0);
252
- // Sum embeddings for each cluster
253
- for (const [nodeId, clusterIdx] of clusters) {
254
- const embedding = embeddings.get(nodeId);
255
- if (!embedding) {
256
- continue;
257
- }
258
- for (let i = 0; i < embedding.length; i++) {
259
- const clusterSum = clusterSums[clusterIdx];
260
- if (!clusterSum) {
261
- continue;
262
- }
263
- const sumVal = clusterSum[i];
264
- const embVal = embedding[i];
265
- if (sumVal !== undefined && embVal !== undefined) {
266
- clusterSum[i] = sumVal + embVal;
267
- }
268
- }
269
- if (clusterCounts[clusterIdx] !== undefined) {
270
- clusterCounts[clusterIdx]++;
271
- }
272
- }
273
- // Update cluster centers (average of assigned embeddings)
274
- for (let k = 0; k < numClusters; k++) {
275
- const count = clusterCounts[k];
276
- const center = clusterCenters[k];
277
- const sum = clusterSums[k];
278
- if (count !== undefined && count > 0 && center && sum) {
279
- for (let i = 0; i < center.length; i++) {
280
- const sumVal = sum[i];
281
- if (sumVal !== undefined) {
282
- center[i] = sumVal / count;
283
- }
284
- }
285
- }
286
- }
287
- }
288
- /**
289
- * Calculate the total loss function
290
- * @param graph - The input graph
291
- * @param embeddings - Map of node IDs to embedding vectors
292
- * @param clusters - Map of node IDs to cluster assignments
293
- * @param clusterCenters - Array of cluster center vectors
294
- * @param lambda - Regularization parameter
295
- * @returns The total loss value
296
- */
297
- function calculateLoss(graph, embeddings, clusters, clusterCenters, lambda) {
298
- let clusteringLoss = 0;
299
- let reconstructionLoss = 0;
300
- let regularizationLoss = 0;
301
- // Clustering loss (distance to cluster centers)
302
- for (const [nodeId, clusterIdx] of clusters) {
303
- const embedding = embeddings.get(nodeId);
304
- if (!embedding) {
305
- continue;
306
- }
307
- const center = clusterCenters[clusterIdx];
308
- if (!center) {
309
- continue;
310
- }
311
- clusteringLoss += euclideanDistance(embedding, center) ** 2;
312
- }
313
- // Graph reconstruction loss
314
- for (const node of graph.nodes()) {
315
- const nodeId = node.id;
316
- const nodeEmbedding = embeddings.get(nodeId);
317
- if (!nodeEmbedding) {
318
- continue;
319
- }
320
- for (const neighborId of graph.neighbors(nodeId)) {
321
- const neighborEmbedding = embeddings.get(neighborId);
322
- if (!neighborEmbedding) {
323
- continue;
324
- }
325
- const distance = euclideanDistance(nodeEmbedding, neighborEmbedding);
326
- reconstructionLoss += distance ** 2;
327
- }
328
- }
329
- // Regularization loss
330
- for (const embedding of embeddings.values()) {
331
- for (const value of embedding) {
332
- regularizationLoss += value ** 2;
333
- }
334
- }
335
- return clusteringLoss + lambda * reconstructionLoss + lambda * regularizationLoss;
336
- }
337
49
  //# sourceMappingURL=sync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../src/research/sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../src/research/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,cAAc,IAAI,oBAAoB,EAAoC,MAAM,kBAAkB,CAAC;AAI5G;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,KAAY,EAAE,MAAkB;IAC3D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QACxC,OAAO,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC;IACtB,MAAM,CAAC,GAAG,qBAAqB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACpG,CAAC;IACD,OAAO;QACH,QAAQ;QACR,yEAAyE;QACzE,IAAI,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;QAC/D,gEAAgE;QAChE,UAAU,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,UAAU;QACV,SAAS,EAAE,CAAC,CAAC,SAAS;KACzB,CAAC;AACN,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphty/algorithms",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "description": "Graph algorithms library for browser environments implemented in TypeScript",
5
5
  "author": "Adam Powers <apowers@ato.ms>",
6
6
  "main": "dist/algorithms.js",
@@ -89,11 +89,12 @@
89
89
  "typescript-eslint": "^8.34.1",
90
90
  "vite": "^7.0.5",
91
91
  "vitepress": "^1.6.3",
92
- "vitest": "4.1.11"
92
+ "vitest": "4.1.11",
93
+ "@graphty/graph-samples": "^0.1.8"
93
94
  },
94
95
  "dependencies": {
95
96
  "typedfastbitset": "^0.6.1",
96
- "@graphty/graph-format": "^1.1.2"
97
+ "@graphty/graph-format": "^1.2.0"
97
98
  },
98
99
  "peerDependencies": {
99
100
  "@graphty/graph-format": "^1.0.0"
@@ -1,4 +1,10 @@
1
1
  import type { Graph } from "../../core/graph.js";
2
+ import {
3
+ betweennessCentrality as indexedBetweenness,
4
+ edgeBetweennessCentrality as indexedEdgeBetweenness,
5
+ } from "../../indexed/betweenness.js";
6
+ import { edgeScoresToPairMap, scoresToRecord } from "../../indexed/facade.js";
7
+ import { needsLegacyCode, toSnapshot } from "../../indexed/to-snapshot.js";
2
8
  import type { NodeId } from "../../types/index.js";
3
9
  import { bfsWithPathCounting } from "../traversal/bfs-variants.js";
4
10
 
@@ -44,7 +50,8 @@ function rejectIndexOptions(options: BetweennessCentralityOptions, fn: string):
44
50
  if (options.sources !== undefined || options.k !== undefined) {
45
51
  throw new Error(
46
52
  `${fn}: 'sources' and 'k' are node INDICES and are meaningful only against a GraphSnapshot; ` +
47
- "call accelerated(accelerator).betweennessCentrality(snapshot, options) instead",
53
+ "for sampled betweenness call indexed.betweennessCentrality(snapshot, options), or " +
54
+ "accelerated(accelerator).betweennessCentrality(snapshot, options) to use an AlgorithmAccelerator",
48
55
  );
49
56
  }
50
57
  }
@@ -226,6 +233,27 @@ function normalizeCentrality(centrality: Record<string, number>, nodeCount: numb
226
233
  export function betweennessCentrality(
227
234
  graph: Graph,
228
235
  options: BetweennessCentralityOptions = {},
236
+ ): Record<string, number> {
237
+ rejectIndexOptions(options, "betweennessCentrality");
238
+ if (needsLegacyCode(graph)) {
239
+ return legacyBetweennessCentrality(graph, options);
240
+ }
241
+ const s = toSnapshot(graph);
242
+ // `endpoints` is not passed on: the legacy accumulation accepts it and ignores it.
243
+ return scoresToRecord(s.ids, indexedBetweenness(s, { normalized: options.normalized }).scores);
244
+ }
245
+
246
+ /**
247
+ * The implementation {@link betweennessCentrality} delegates away from, kept as its
248
+ * differential-test oracle. Deleted at the removal release.
249
+ * @param graph - The input graph to analyze
250
+ * @param options - Algorithm configuration options
251
+ * @returns Centrality scores for each node keyed by node ID
252
+ * @internal
253
+ */
254
+ export function legacyBetweennessCentrality(
255
+ graph: Graph,
256
+ options: BetweennessCentralityOptions = {},
229
257
  ): Record<string, number> {
230
258
  rejectIndexOptions(options, "betweennessCentrality");
231
259
  const nodes = Array.from(graph.nodes()).map((node) => node.id);
@@ -291,6 +319,28 @@ export function nodeBetweennessCentrality(
291
319
  export function edgeBetweennessCentrality(
292
320
  graph: Graph,
293
321
  options: BetweennessCentralityOptions = {},
322
+ ): Map<string, number> {
323
+ rejectIndexOptions(options, "edgeBetweennessCentrality");
324
+ // On an undirected graph the legacy Map adds each reversed "v-u" key in the order its searches
325
+ // first cross the edge that way, an order the port's per-edge scores do not carry.
326
+ if (!graph.isDirected || needsLegacyCode(graph)) {
327
+ return legacyEdgeBetweennessCentrality(graph, options);
328
+ }
329
+ const s = toSnapshot(graph);
330
+ return edgeScoresToPairMap(s, indexedEdgeBetweenness(s, { normalized: options.normalized }).scores);
331
+ }
332
+
333
+ /**
334
+ * The implementation {@link edgeBetweennessCentrality} delegates away from, kept as its
335
+ * differential-test oracle and for an undirected graph. Deleted at the removal release.
336
+ * @param graph - The input graph to analyze
337
+ * @param options - Algorithm configuration options
338
+ * @returns Edge centrality scores keyed by edge string representation
339
+ * @internal
340
+ */
341
+ export function legacyEdgeBetweennessCentrality(
342
+ graph: Graph,
343
+ options: BetweennessCentralityOptions = {},
294
344
  ): Map<string, number> {
295
345
  rejectIndexOptions(options, "edgeBetweennessCentrality");
296
346
  const nodes = Array.from(graph.nodes()).map((node) => node.id);
@@ -1,4 +1,13 @@
1
+ import type { GraphSnapshot } from "@graphty/graph-format";
2
+
1
3
  import type { Graph } from "../../core/graph.js";
4
+ import {
5
+ closenessCentrality as indexedCloseness,
6
+ type ClosenessOptions,
7
+ nodeClosenessCentrality as indexedNodeCloseness,
8
+ } from "../../indexed/closeness.js";
9
+ import { exactArcWeights, scoresToRecord } from "../../indexed/facade.js";
10
+ import { needsLegacyCode, toSnapshot } from "../../indexed/to-snapshot.js";
2
11
  import type { NodeId } from "../../types/index.js";
3
12
  import { bfsDistancesOnly, bfsWeightedDistances } from "../traversal/bfs-variants.js";
4
13
 
@@ -112,16 +121,56 @@ function calculateClosenessFromDistances(
112
121
  * Space Complexity: O(V)
113
122
  */
114
123
  export function closenessCentrality(graph: Graph, options: ClosenessCentralityOptions = {}): Record<string, number> {
115
- const nodes = Array.from(graph.nodes()).map((node) => node.id);
116
- const centrality: Record<string, number> = {};
117
-
118
- for (const sourceNode of nodes) {
119
- centrality[String(sourceNode)] = nodeClosenessCentrality(graph, sourceNode, options);
124
+ if (needsLegacyCode(graph)) {
125
+ return perNodeRecord(graph, (id) => legacyNodeClosenessCentrality(graph, id, options));
120
126
  }
127
+ const s = toSnapshot(graph);
128
+ return scoresToRecord(s.ids, indexedCloseness(s, portOptions(options)).scores);
129
+ }
130
+
131
+ /**
132
+ * The port's options for a legacy call; `optimized` picked a legacy search engine and has no
133
+ * counterpart.
134
+ * @param options - The legacy options
135
+ * @param s - Given only for a weighted call, which then reads this snapshot's exact f64 weights
136
+ * @returns The port's options
137
+ */
138
+ function portOptions(options: ClosenessCentralityOptions, s?: GraphSnapshot): ClosenessOptions {
139
+ return {
140
+ normalized: options.normalized,
141
+ harmonic: options.harmonic,
142
+ cutoff: options.cutoff,
143
+ weighted: s !== undefined,
144
+ weights: s === undefined ? undefined : exactArcWeights(s),
145
+ };
146
+ }
121
147
 
148
+ /**
149
+ * The legacy whole-graph loop: one per-node score per node, keyed by `String(id)` in node order.
150
+ * @param graph - The input graph
151
+ * @param score - The per-node score
152
+ * @returns The keyed scores
153
+ */
154
+ function perNodeRecord(graph: Graph, score: (id: NodeId) => number): Record<string, number> {
155
+ const centrality: Record<string, number> = {};
156
+ for (const node of graph.nodes()) {
157
+ centrality[String(node.id)] = score(node.id);
158
+ }
122
159
  return centrality;
123
160
  }
124
161
 
162
+ /**
163
+ * Whether a weighted search must stay on the legacy code: with a negative weight the distance a
164
+ * node keeps depends on the order equal keys leave the priority queue, which the port does not
165
+ * reproduce.
166
+ * @param s - The snapshot
167
+ * @returns True when some weight is negative
168
+ */
169
+ function hasNegativeWeight(s: GraphSnapshot): boolean {
170
+ const weights = exactArcWeights(s) ?? s.weights;
171
+ return weights?.some((w) => w < 0) ?? false;
172
+ }
173
+
125
174
  /**
126
175
  * Calculate closeness centrality for a specific node
127
176
  * @param graph - The input graph to analyze
@@ -133,6 +182,30 @@ export function nodeClosenessCentrality(graph: Graph, node: NodeId, options: Clo
133
182
  if (!graph.hasNode(node)) {
134
183
  throw new Error(`Node ${String(node)} not found in graph`);
135
184
  }
185
+ if (needsLegacyCode(graph)) {
186
+ return legacyNodeClosenessCentrality(graph, node, options);
187
+ }
188
+ const s = toSnapshot(graph);
189
+ return indexedNodeCloseness(s, s.ids.indexOf(node), portOptions(options));
190
+ }
191
+
192
+ /**
193
+ * The implementation {@link nodeClosenessCentrality} delegates away from, kept as its
194
+ * differential-test oracle. Deleted at the removal release.
195
+ * @param graph - The input graph to analyze
196
+ * @param node - The node to calculate centrality for
197
+ * @param options - Algorithm configuration options
198
+ * @returns The closeness centrality score for the node
199
+ * @internal
200
+ */
201
+ export function legacyNodeClosenessCentrality(
202
+ graph: Graph,
203
+ node: NodeId,
204
+ options: ClosenessCentralityOptions = {},
205
+ ): number {
206
+ if (!graph.hasNode(node)) {
207
+ throw new Error(`Node ${String(node)} not found in graph`);
208
+ }
136
209
 
137
210
  // Use optimized BFS variant for unweighted graphs
138
211
  const distances = bfsDistancesOnly(
@@ -156,14 +229,14 @@ export function weightedClosenessCentrality(
156
229
  graph: Graph,
157
230
  options: ClosenessCentralityOptions = {},
158
231
  ): Record<string, number> {
159
- const nodes = Array.from(graph.nodes()).map((node) => node.id);
160
- const centrality: Record<string, number> = {};
161
-
162
- for (const sourceNode of nodes) {
163
- centrality[String(sourceNode)] = nodeWeightedClosenessCentrality(graph, sourceNode, options);
232
+ if (needsLegacyCode(graph)) {
233
+ return perNodeRecord(graph, (id) => legacyNodeWeightedClosenessCentrality(graph, id, options));
164
234
  }
165
-
166
- return centrality;
235
+ const s = toSnapshot(graph);
236
+ if (hasNegativeWeight(s)) {
237
+ return perNodeRecord(graph, (id) => legacyNodeWeightedClosenessCentrality(graph, id, options));
238
+ }
239
+ return scoresToRecord(s.ids, indexedCloseness(s, portOptions(options, s)).scores);
167
240
  }
168
241
 
169
242
  /**
@@ -181,6 +254,33 @@ export function nodeWeightedClosenessCentrality(
181
254
  if (!graph.hasNode(node)) {
182
255
  throw new Error(`Node ${String(node)} not found in graph`);
183
256
  }
257
+ if (needsLegacyCode(graph)) {
258
+ return legacyNodeWeightedClosenessCentrality(graph, node, options);
259
+ }
260
+ const s = toSnapshot(graph);
261
+ if (hasNegativeWeight(s)) {
262
+ return legacyNodeWeightedClosenessCentrality(graph, node, options);
263
+ }
264
+ return indexedNodeCloseness(s, s.ids.indexOf(node), portOptions(options, s));
265
+ }
266
+
267
+ /**
268
+ * The implementation {@link nodeWeightedClosenessCentrality} delegates away from, kept as its
269
+ * differential-test oracle and for a graph with a negative weight. Deleted at the removal release.
270
+ * @param graph - The input graph to analyze
271
+ * @param node - The node to calculate centrality for
272
+ * @param options - Algorithm configuration options
273
+ * @returns The weighted closeness centrality score for the node
274
+ * @internal
275
+ */
276
+ export function legacyNodeWeightedClosenessCentrality(
277
+ graph: Graph,
278
+ node: NodeId,
279
+ options: ClosenessCentralityOptions = {},
280
+ ): number {
281
+ if (!graph.hasNode(node)) {
282
+ throw new Error(`Node ${String(node)} not found in graph`);
283
+ }
184
284
 
185
285
  // Use optimized weighted BFS variant (simplified Dijkstra)
186
286
  const distances = bfsWeightedDistances(