@graphty/algorithms 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/README.md +139 -50
  2. package/dist/algorithms.js +15260 -10390
  3. package/dist/algorithms.js.map +1 -1
  4. package/dist/algorithms.standalone.js +26675 -21542
  5. package/dist/algorithms.standalone.js.map +1 -1
  6. package/dist/src/algorithms/centrality/betweenness.d.ts +18 -0
  7. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  8. package/dist/src/algorithms/centrality/betweenness.js +40 -1
  9. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  10. package/dist/src/algorithms/centrality/closeness.d.ts +20 -0
  11. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  12. package/dist/src/algorithms/centrality/closeness.js +93 -8
  13. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  14. package/dist/src/algorithms/centrality/degree.d.ts +9 -0
  15. package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
  16. package/dist/src/algorithms/centrality/degree.js +18 -0
  17. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  18. package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -0
  19. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
  20. package/dist/src/algorithms/centrality/eigenvector.js +29 -0
  21. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
  22. package/dist/src/algorithms/centrality/hits.d.ts +9 -0
  23. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
  24. package/dist/src/algorithms/centrality/hits.js +23 -0
  25. package/dist/src/algorithms/centrality/hits.js.map +1 -1
  26. package/dist/src/algorithms/centrality/katz.d.ts +9 -0
  27. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
  28. package/dist/src/algorithms/centrality/katz.js +31 -0
  29. package/dist/src/algorithms/centrality/katz.js.map +1 -1
  30. package/dist/src/algorithms/centrality/pagerank.d.ts +13 -13
  31. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  32. package/dist/src/algorithms/centrality/pagerank.js +47 -10
  33. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  34. package/dist/src/algorithms/community/girvan-newman-legacy.d.ts +27 -0
  35. package/dist/src/algorithms/community/girvan-newman-legacy.d.ts.map +1 -0
  36. package/dist/src/algorithms/community/girvan-newman-legacy.js +335 -0
  37. package/dist/src/algorithms/community/girvan-newman-legacy.js.map +1 -0
  38. package/dist/src/algorithms/community/girvan-newman.d.ts +5 -3
  39. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  40. package/dist/src/algorithms/community/girvan-newman.js +34 -308
  41. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  42. package/dist/src/algorithms/community/label-propagation.d.ts +9 -6
  43. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  44. package/dist/src/algorithms/community/label-propagation.js +24 -110
  45. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  46. package/dist/src/algorithms/components/connected.d.ts.map +1 -1
  47. package/dist/src/algorithms/components/connected.js +28 -77
  48. package/dist/src/algorithms/components/connected.js.map +1 -1
  49. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
  50. package/dist/src/algorithms/mst/kruskal.js +59 -3
  51. package/dist/src/algorithms/mst/kruskal.js.map +1 -1
  52. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  53. package/dist/src/algorithms/shortest-path/bellman-ford.js +46 -0
  54. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  55. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  56. package/dist/src/algorithms/shortest-path/dijkstra.js +27 -0
  57. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  58. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +15 -5
  59. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
  60. package/dist/src/algorithms/shortest-path/floyd-warshall.js +97 -107
  61. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
  62. package/dist/src/algorithms/traversal/bfs-unified.d.ts +2 -12
  63. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
  64. package/dist/src/algorithms/traversal/bfs-unified.js +47 -308
  65. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
  66. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
  67. package/dist/src/algorithms/traversal/bfs-variants.js +5 -25
  68. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
  69. package/dist/src/algorithms/traversal/bfs.d.ts +0 -3
  70. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  71. package/dist/src/algorithms/traversal/bfs.js +0 -4
  72. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  73. package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
  74. package/dist/src/algorithms/traversal/dfs.js +33 -154
  75. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  76. package/dist/src/clustering/hierarchical-legacy.d.ts +56 -0
  77. package/dist/src/clustering/hierarchical-legacy.d.ts.map +1 -0
  78. package/dist/src/clustering/hierarchical-legacy.js +441 -0
  79. package/dist/src/clustering/hierarchical-legacy.js.map +1 -0
  80. package/dist/src/clustering/hierarchical.d.ts +7 -39
  81. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  82. package/dist/src/clustering/hierarchical.js +56 -417
  83. package/dist/src/clustering/hierarchical.js.map +1 -1
  84. package/dist/src/clustering/k-core-legacy.d.ts +30 -0
  85. package/dist/src/clustering/k-core-legacy.d.ts.map +1 -0
  86. package/dist/src/clustering/k-core-legacy.js +191 -0
  87. package/dist/src/clustering/k-core-legacy.js.map +1 -0
  88. package/dist/src/clustering/k-core.d.ts +8 -7
  89. package/dist/src/clustering/k-core.d.ts.map +1 -1
  90. package/dist/src/clustering/k-core.js +33 -174
  91. package/dist/src/clustering/k-core.js.map +1 -1
  92. package/dist/src/clustering/mcl-legacy.d.ts +42 -0
  93. package/dist/src/clustering/mcl-legacy.d.ts.map +1 -0
  94. package/dist/src/clustering/mcl-legacy.js +386 -0
  95. package/dist/src/clustering/mcl-legacy.js.map +1 -0
  96. package/dist/src/clustering/mcl.d.ts +9 -32
  97. package/dist/src/clustering/mcl.d.ts.map +1 -1
  98. package/dist/src/clustering/mcl.js +48 -374
  99. package/dist/src/clustering/mcl.js.map +1 -1
  100. package/dist/src/index.d.ts +29 -3
  101. package/dist/src/index.d.ts.map +1 -1
  102. package/dist/src/index.js +2 -1
  103. package/dist/src/index.js.map +1 -1
  104. package/dist/src/indexed/accelerator.d.ts +141 -11
  105. package/dist/src/indexed/accelerator.d.ts.map +1 -1
  106. package/dist/src/indexed/accelerator.js +319 -5
  107. package/dist/src/indexed/accelerator.js.map +1 -1
  108. package/dist/src/indexed/all-pairs.d.ts +59 -0
  109. package/dist/src/indexed/all-pairs.d.ts.map +1 -0
  110. package/dist/src/indexed/all-pairs.js +274 -0
  111. package/dist/src/indexed/all-pairs.js.map +1 -0
  112. package/dist/src/indexed/bellman-ford.d.ts +25 -0
  113. package/dist/src/indexed/bellman-ford.d.ts.map +1 -0
  114. package/dist/src/indexed/bellman-ford.js +59 -0
  115. package/dist/src/indexed/bellman-ford.js.map +1 -0
  116. package/dist/src/indexed/betweenness.d.ts +85 -0
  117. package/dist/src/indexed/betweenness.d.ts.map +1 -0
  118. package/dist/src/indexed/betweenness.js +188 -0
  119. package/dist/src/indexed/betweenness.js.map +1 -0
  120. package/dist/src/indexed/bfs.d.ts +69 -2
  121. package/dist/src/indexed/bfs.d.ts.map +1 -1
  122. package/dist/src/indexed/bfs.js +148 -1
  123. package/dist/src/indexed/bfs.js.map +1 -1
  124. package/dist/src/indexed/bipartite.d.ts +37 -0
  125. package/dist/src/indexed/bipartite.d.ts.map +1 -0
  126. package/dist/src/indexed/bipartite.js +54 -0
  127. package/dist/src/indexed/bipartite.js.map +1 -0
  128. package/dist/src/indexed/closeness.d.ts +49 -0
  129. package/dist/src/indexed/closeness.d.ts.map +1 -0
  130. package/dist/src/indexed/closeness.js +127 -0
  131. package/dist/src/indexed/closeness.js.map +1 -0
  132. package/dist/src/indexed/common-neighbors.d.ts +14 -1
  133. package/dist/src/indexed/common-neighbors.d.ts.map +1 -1
  134. package/dist/src/indexed/common-neighbors.js +24 -14
  135. package/dist/src/indexed/common-neighbors.js.map +1 -1
  136. package/dist/src/indexed/degree.d.ts +19 -0
  137. package/dist/src/indexed/degree.d.ts.map +1 -0
  138. package/dist/src/indexed/degree.js +45 -0
  139. package/dist/src/indexed/degree.js.map +1 -0
  140. package/dist/src/indexed/delta-pagerank.d.ts +145 -0
  141. package/dist/src/indexed/delta-pagerank.d.ts.map +1 -0
  142. package/dist/src/indexed/delta-pagerank.js +428 -0
  143. package/dist/src/indexed/delta-pagerank.js.map +1 -0
  144. package/dist/src/indexed/dfs.d.ts +55 -0
  145. package/dist/src/indexed/dfs.d.ts.map +1 -0
  146. package/dist/src/indexed/dfs.js +146 -0
  147. package/dist/src/indexed/dfs.js.map +1 -0
  148. package/dist/src/indexed/eigenvector.d.ts +48 -0
  149. package/dist/src/indexed/eigenvector.d.ts.map +1 -0
  150. package/dist/src/indexed/eigenvector.js +162 -0
  151. package/dist/src/indexed/eigenvector.js.map +1 -0
  152. package/dist/src/indexed/facade.d.ts +109 -0
  153. package/dist/src/indexed/facade.d.ts.map +1 -0
  154. package/dist/src/indexed/facade.js +204 -0
  155. package/dist/src/indexed/facade.js.map +1 -0
  156. package/dist/src/indexed/flow.d.ts +120 -0
  157. package/dist/src/indexed/flow.d.ts.map +1 -0
  158. package/dist/src/indexed/flow.js +377 -0
  159. package/dist/src/indexed/flow.js.map +1 -0
  160. package/dist/src/indexed/girvan-newman.d.ts +42 -0
  161. package/dist/src/indexed/girvan-newman.d.ts.map +1 -0
  162. package/dist/src/indexed/girvan-newman.js +90 -0
  163. package/dist/src/indexed/girvan-newman.js.map +1 -0
  164. package/dist/src/indexed/grsbm.d.ts +85 -0
  165. package/dist/src/indexed/grsbm.d.ts.map +1 -0
  166. package/dist/src/indexed/grsbm.js +287 -0
  167. package/dist/src/indexed/grsbm.js.map +1 -0
  168. package/dist/src/indexed/hierarchical.d.ts +69 -0
  169. package/dist/src/indexed/hierarchical.d.ts.map +1 -0
  170. package/dist/src/indexed/hierarchical.js +245 -0
  171. package/dist/src/indexed/hierarchical.js.map +1 -0
  172. package/dist/src/indexed/index.d.ts +29 -3
  173. package/dist/src/indexed/index.d.ts.map +1 -1
  174. package/dist/src/indexed/index.js +29 -3
  175. package/dist/src/indexed/index.js.map +1 -1
  176. package/dist/src/indexed/isomorphism.d.ts +42 -0
  177. package/dist/src/indexed/isomorphism.d.ts.map +1 -0
  178. package/dist/src/indexed/isomorphism.js +269 -0
  179. package/dist/src/indexed/isomorphism.js.map +1 -0
  180. package/dist/src/indexed/label-propagation.d.ts +134 -0
  181. package/dist/src/indexed/label-propagation.d.ts.map +1 -0
  182. package/dist/src/indexed/label-propagation.js +486 -0
  183. package/dist/src/indexed/label-propagation.js.map +1 -0
  184. package/dist/src/indexed/leiden.d.ts +52 -0
  185. package/dist/src/indexed/leiden.d.ts.map +1 -0
  186. package/dist/src/indexed/leiden.js +384 -0
  187. package/dist/src/indexed/leiden.js.map +1 -0
  188. package/dist/src/indexed/link-prediction.d.ts +147 -0
  189. package/dist/src/indexed/link-prediction.d.ts.map +1 -0
  190. package/dist/src/indexed/link-prediction.js +317 -0
  191. package/dist/src/indexed/link-prediction.js.map +1 -0
  192. package/dist/src/indexed/markov.d.ts +54 -0
  193. package/dist/src/indexed/markov.d.ts.map +1 -0
  194. package/dist/src/indexed/markov.js +284 -0
  195. package/dist/src/indexed/markov.js.map +1 -0
  196. package/dist/src/indexed/matching.d.ts +53 -0
  197. package/dist/src/indexed/matching.d.ts.map +1 -0
  198. package/dist/src/indexed/matching.js +139 -0
  199. package/dist/src/indexed/matching.js.map +1 -0
  200. package/dist/src/indexed/min-cut.d.ts +48 -0
  201. package/dist/src/indexed/min-cut.d.ts.map +1 -0
  202. package/dist/src/indexed/min-cut.js +175 -0
  203. package/dist/src/indexed/min-cut.js.map +1 -0
  204. package/dist/src/indexed/modularity.d.ts +34 -0
  205. package/dist/src/indexed/modularity.d.ts.map +1 -0
  206. package/dist/src/indexed/modularity.js +74 -0
  207. package/dist/src/indexed/modularity.js.map +1 -0
  208. package/dist/src/indexed/mst.d.ts +29 -1
  209. package/dist/src/indexed/mst.d.ts.map +1 -1
  210. package/dist/src/indexed/mst.js +62 -0
  211. package/dist/src/indexed/mst.js.map +1 -1
  212. package/dist/src/indexed/pagerank.d.ts +29 -6
  213. package/dist/src/indexed/pagerank.d.ts.map +1 -1
  214. package/dist/src/indexed/pagerank.js +107 -11
  215. package/dist/src/indexed/pagerank.js.map +1 -1
  216. package/dist/src/indexed/point-to-point.d.ts +53 -0
  217. package/dist/src/indexed/point-to-point.d.ts.map +1 -0
  218. package/dist/src/indexed/point-to-point.js +173 -0
  219. package/dist/src/indexed/point-to-point.js.map +1 -0
  220. package/dist/src/indexed/scc.d.ts +53 -0
  221. package/dist/src/indexed/scc.d.ts.map +1 -0
  222. package/dist/src/indexed/scc.js +110 -0
  223. package/dist/src/indexed/scc.js.map +1 -0
  224. package/dist/src/indexed/spectral.d.ts +61 -0
  225. package/dist/src/indexed/spectral.d.ts.map +1 -0
  226. package/dist/src/indexed/spectral.js +476 -0
  227. package/dist/src/indexed/spectral.js.map +1 -0
  228. package/dist/src/indexed/structures/bit-set.d.ts +45 -0
  229. package/dist/src/indexed/structures/bit-set.d.ts.map +1 -0
  230. package/dist/src/indexed/structures/bit-set.js +57 -0
  231. package/dist/src/indexed/structures/bit-set.js.map +1 -0
  232. package/dist/src/indexed/structures/max-heap.d.ts +52 -0
  233. package/dist/src/indexed/structures/max-heap.d.ts.map +1 -0
  234. package/dist/src/indexed/structures/max-heap.js +66 -0
  235. package/dist/src/indexed/structures/max-heap.js.map +1 -0
  236. package/dist/src/indexed/structures/min-heap.d.ts +30 -1
  237. package/dist/src/indexed/structures/min-heap.d.ts.map +1 -1
  238. package/dist/src/indexed/structures/min-heap.js +46 -6
  239. package/dist/src/indexed/structures/min-heap.js.map +1 -1
  240. package/dist/src/indexed/structures/ring-queue.d.ts +41 -0
  241. package/dist/src/indexed/structures/ring-queue.d.ts.map +1 -0
  242. package/dist/src/indexed/structures/ring-queue.js +64 -0
  243. package/dist/src/indexed/structures/ring-queue.js.map +1 -0
  244. package/dist/src/indexed/sync.d.ts +50 -0
  245. package/dist/src/indexed/sync.d.ts.map +1 -0
  246. package/dist/src/indexed/sync.js +175 -0
  247. package/dist/src/indexed/sync.js.map +1 -0
  248. package/dist/src/indexed/terahac.d.ts +58 -0
  249. package/dist/src/indexed/terahac.d.ts.map +1 -0
  250. package/dist/src/indexed/terahac.js +244 -0
  251. package/dist/src/indexed/terahac.js.map +1 -0
  252. package/dist/src/indexed/to-snapshot.d.ts +45 -2
  253. package/dist/src/indexed/to-snapshot.d.ts.map +1 -1
  254. package/dist/src/indexed/to-snapshot.js +140 -6
  255. package/dist/src/indexed/to-snapshot.js.map +1 -1
  256. package/dist/src/link-prediction/common-neighbors-legacy.d.ts +71 -0
  257. package/dist/src/link-prediction/common-neighbors-legacy.d.ts.map +1 -0
  258. package/dist/src/link-prediction/common-neighbors-legacy.js +172 -0
  259. package/dist/src/link-prediction/common-neighbors-legacy.js.map +1 -0
  260. package/dist/src/link-prediction/common-neighbors.d.ts +11 -19
  261. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  262. package/dist/src/link-prediction/common-neighbors.js +56 -123
  263. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  264. package/dist/src/optimized/direction-optimized-bfs.d.ts +6 -0
  265. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
  266. package/dist/src/optimized/direction-optimized-bfs.js +6 -0
  267. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
  268. package/dist/src/research/grsbm-legacy.d.ts +82 -0
  269. package/dist/src/research/grsbm-legacy.d.ts.map +1 -0
  270. package/dist/src/research/grsbm-legacy.js +416 -0
  271. package/dist/src/research/grsbm-legacy.js.map +1 -0
  272. package/dist/src/research/grsbm.d.ts +9 -66
  273. package/dist/src/research/grsbm.d.ts.map +1 -1
  274. package/dist/src/research/grsbm.js +67 -391
  275. package/dist/src/research/grsbm.js.map +1 -1
  276. package/dist/src/research/sync-legacy.d.ts +48 -0
  277. package/dist/src/research/sync-legacy.d.ts.map +1 -0
  278. package/dist/src/research/sync-legacy.js +337 -0
  279. package/dist/src/research/sync-legacy.js.map +1 -0
  280. package/dist/src/research/sync.d.ts +10 -32
  281. package/dist/src/research/sync.d.ts.map +1 -1
  282. package/dist/src/research/sync.js +28 -316
  283. package/dist/src/research/sync.js.map +1 -1
  284. package/package.json +4 -3
  285. package/src/algorithms/centrality/betweenness.ts +51 -1
  286. package/src/algorithms/centrality/closeness.ts +112 -12
  287. package/src/algorithms/centrality/degree.ts +19 -0
  288. package/src/algorithms/centrality/eigenvector.ts +34 -0
  289. package/src/algorithms/centrality/hits.ts +24 -0
  290. package/src/algorithms/centrality/katz.ts +32 -0
  291. package/src/algorithms/centrality/pagerank.ts +53 -13
  292. package/src/algorithms/community/girvan-newman-legacy.ts +414 -0
  293. package/src/algorithms/community/girvan-newman.ts +36 -386
  294. package/src/algorithms/community/label-propagation.ts +24 -133
  295. package/src/algorithms/components/connected.ts +28 -95
  296. package/src/algorithms/mst/kruskal.ts +65 -4
  297. package/src/algorithms/shortest-path/bellman-ford.ts +49 -0
  298. package/src/algorithms/shortest-path/dijkstra.ts +29 -0
  299. package/src/algorithms/shortest-path/floyd-warshall.ts +106 -136
  300. package/src/algorithms/traversal/bfs-unified.ts +52 -366
  301. package/src/algorithms/traversal/bfs-variants.ts +5 -28
  302. package/src/algorithms/traversal/bfs.ts +0 -4
  303. package/src/algorithms/traversal/dfs.ts +39 -182
  304. package/src/clustering/hierarchical-legacy.ts +551 -0
  305. package/src/clustering/hierarchical.ts +64 -520
  306. package/src/clustering/k-core-legacy.ts +229 -0
  307. package/src/clustering/k-core.ts +38 -209
  308. package/src/clustering/mcl-legacy.ts +498 -0
  309. package/src/clustering/mcl.ts +46 -479
  310. package/src/index.ts +90 -3
  311. package/src/indexed/accelerator.ts +502 -16
  312. package/src/indexed/all-pairs.ts +342 -0
  313. package/src/indexed/bellman-ford.ts +72 -0
  314. package/src/indexed/betweenness.ts +258 -0
  315. package/src/indexed/bfs.ts +189 -3
  316. package/src/indexed/bipartite.ts +77 -0
  317. package/src/indexed/closeness.ts +160 -0
  318. package/src/indexed/common-neighbors.ts +31 -14
  319. package/src/indexed/degree.ts +55 -0
  320. package/src/indexed/delta-pagerank.ts +513 -0
  321. package/src/indexed/dfs.ts +189 -0
  322. package/src/indexed/eigenvector.ts +202 -0
  323. package/src/indexed/facade.ts +229 -0
  324. package/src/indexed/flow.ts +500 -0
  325. package/src/indexed/girvan-newman.ts +120 -0
  326. package/src/indexed/grsbm.ts +390 -0
  327. package/src/indexed/hierarchical.ts +295 -0
  328. package/src/indexed/index.ts +94 -3
  329. package/src/indexed/isomorphism.ts +305 -0
  330. package/src/indexed/label-propagation.ts +582 -0
  331. package/src/indexed/leiden.ts +436 -0
  332. package/src/indexed/link-prediction.ts +399 -0
  333. package/src/indexed/markov.ts +324 -0
  334. package/src/indexed/matching.ts +192 -0
  335. package/src/indexed/min-cut.ts +201 -0
  336. package/src/indexed/modularity.ts +81 -0
  337. package/src/indexed/mst.ts +79 -1
  338. package/src/indexed/pagerank.ts +128 -15
  339. package/src/indexed/point-to-point.ts +220 -0
  340. package/src/indexed/scc.ts +132 -0
  341. package/src/indexed/spectral.ts +550 -0
  342. package/src/indexed/structures/bit-set.ts +65 -0
  343. package/src/indexed/structures/max-heap.ts +74 -0
  344. package/src/indexed/structures/min-heap.ts +51 -6
  345. package/src/indexed/structures/ring-queue.ts +70 -0
  346. package/src/indexed/sync.ts +229 -0
  347. package/src/indexed/terahac.ts +290 -0
  348. package/src/indexed/to-snapshot.ts +156 -6
  349. package/src/link-prediction/common-neighbors-legacy.ts +252 -0
  350. package/src/link-prediction/common-neighbors.ts +65 -168
  351. package/src/optimized/direction-optimized-bfs.ts +6 -1
  352. package/src/research/grsbm-legacy.ts +586 -0
  353. package/src/research/grsbm.ts +76 -555
  354. package/src/research/sync-legacy.ts +456 -0
  355. package/src/research/sync.ts +30 -431
@@ -13,13 +13,28 @@
13
13
 
14
14
  import type { F32, F64, GraphSnapshot, NumericVector, U32 } from "@graphty/graph-format";
15
15
 
16
- import type { BfsOptions } from "./bfs.js";
16
+ import { ConvergenceError } from "../errors.js";
17
+ import { APSP_DEFAULT_MAX_NODES, type ApspOptions } from "./all-pairs.js";
18
+ import type { BellmanFordResult } from "./bellman-ford.js";
19
+ import { type BetweennessOptions, type EdgeBetweennessOptions, resolveSources } from "./betweenness.js";
20
+ import type { ArcOrderOption, BfsOptions } from "./bfs.js";
21
+ import type { ClosenessOptions } from "./closeness.js";
22
+ import type { LabelResult } from "./components.js";
23
+ import type { DfsOptions, DfsResult } from "./dfs.js";
17
24
  import { type SsspOptions, type SsspResult, walkPredArcs, walkPredEdges } from "./dijkstra.js";
25
+ import { type EigenvectorOptions, minMaxRescale } from "./eigenvector.js";
26
+ import type { MaxFlowOptions, MaxFlowResult, MinCutResult } from "./flow.js";
27
+ import type { GirvanNewmanOptions, GirvanNewmanResult } from "./girvan-newman.js";
18
28
  import type { HitsOptions } from "./hits.js";
19
29
  import * as indexed from "./index.js";
20
30
  import type { KatzOptions } from "./katz.js";
31
+ import type { LabelPropagationOptions } from "./label-propagation.js";
32
+ import type { LeidenOptions, LeidenResult } from "./leiden.js";
33
+ import type { LinkPredictionOptions, LinkPredictionResult } from "./link-prediction.js";
21
34
  import type { LouvainOptions } from "./louvain.js";
22
- import type { MstOptions } from "./mst.js";
35
+ import type { BipartiteMatchingOptions, BipartiteMatchingResult } from "./matching.js";
36
+ import type { KargerOptions, StoerWagnerOptions } from "./min-cut.js";
37
+ import type { MstOptions, PrimOptions, PrimResult } from "./mst.js";
23
38
  import type { PageRankOptions } from "./pagerank.js";
24
39
 
25
40
  // ============================================================ result shapes (design 9.2 lines 2909-2922)
@@ -73,6 +88,10 @@ export interface ApspResultLike {
73
88
  readonly dist: NumericVector;
74
89
  readonly n: number;
75
90
  }
91
+ /** ApspResultLike plus the negative-cycle flag. @public */
92
+ export interface ApspCycleResultLike extends ApspResultLike {
93
+ readonly hasNegativeCycle: boolean;
94
+ }
76
95
  /** Per-node coreness. @public */
77
96
  export interface CorenessResultLike {
78
97
  readonly coreness: U32;
@@ -98,18 +117,24 @@ export interface CommunityResultLike extends LabelResultLike {
98
117
  */
99
118
  export interface AlgorithmAccelerator {
100
119
  readonly kind: string;
101
- pageRank?(s: GraphSnapshot, options?: PageRankOptions): Promise<PageRankResultLike>;
120
+ pageRank?(s: GraphSnapshot, options?: PageRankOptionsLike): Promise<PageRankResultLike>;
102
121
  personalizedPageRank?(
103
122
  s: GraphSnapshot,
104
123
  personalization: F32 | F64,
105
- options?: PageRankOptions,
124
+ options?: PageRankOptionsLike,
106
125
  ): Promise<PageRankResultLike>;
107
126
  hits?(s: GraphSnapshot, options?: HitsOptionsLike): Promise<HitsResultLike>;
108
127
  eigenvectorCentrality?(s: GraphSnapshot, options?: HitsOptionsLike): Promise<ScoresResultLike>;
109
- katzCentrality?(s: GraphSnapshot, options?: HitsOptionsLike): Promise<ScoresResultLike>;
128
+ katzCentrality?(s: GraphSnapshot, options?: KatzOptionsLike): Promise<ScoresResultLike>;
110
129
  connectedComponents?(s: GraphSnapshot): Promise<LabelResultLike>;
111
130
  weaklyConnectedComponents?(s: GraphSnapshot): Promise<LabelResultLike>;
112
- breadthFirstSearch?(s: GraphSnapshot, source: number, options?: BfsOptions): Promise<BfsResultLike>;
131
+ // Only maxDepth: a GPU BFS expands whole levels in index order, so it can neither stop early at a
132
+ // target nor try arcs in a caller's order.
133
+ breadthFirstSearch?(
134
+ s: GraphSnapshot,
135
+ source: number,
136
+ options?: Pick<BfsOptions, "maxDepth">,
137
+ ): Promise<BfsResultLike>;
113
138
  sssp?(s: GraphSnapshot, source: number, options?: SsspOptions): Promise<SsspResultLike>;
114
139
  bellmanFord?(s: GraphSnapshot, source: number, options?: SsspOptions): Promise<BellmanFordResultLike>;
115
140
  closenessCentrality?(s: GraphSnapshot, options?: HitsOptionsLike): Promise<ScoresResultLike>;
@@ -141,6 +166,28 @@ export interface HitsOptionsLike {
141
166
  readonly weighted?: boolean | undefined;
142
167
  }
143
168
 
169
+ /**
170
+ * Katz options as the accelerator sees them: the power-iteration trio plus the two coefficients
171
+ * that define the recurrence `x = alpha * A^T x + beta`.
172
+ * @public
173
+ */
174
+ export interface KatzOptionsLike extends HitsOptionsLike {
175
+ readonly alpha?: number | undefined;
176
+ readonly beta?: number | undefined;
177
+ }
178
+
179
+ /**
180
+ * PageRank options as the accelerator sees them: the port's `PageRankOptions` without
181
+ * `initialRanks` and `convergenceNorm`, which the dispatcher keeps on the CPU port.
182
+ * @public
183
+ */
184
+ export interface PageRankOptionsLike {
185
+ readonly dampingFactor?: number | undefined;
186
+ readonly maxIterations?: number | undefined;
187
+ readonly tolerance?: number | undefined;
188
+ readonly weighted?: boolean | undefined;
189
+ }
190
+
144
191
  /**
145
192
  * Betweenness options as the accelerator sees them: node INDICES, which is the only form that
146
193
  * means anything on a snapshot (plan decision PD-5).
@@ -162,17 +209,96 @@ export interface BetweennessAcceleratorOptions {
162
209
  *
163
210
  * The list GROWS with the A2 ports -- each port PR adds its method (plan departure DEP-8A-E).
164
211
  *
165
- * The four newest methods take their port's own option type, which is WIDER than the
166
- * `HitsOptionsLike` the accelerator side still declares: an accelerator therefore never sees Katz's
167
- * `alpha` / `beta` or Louvain's `resolution`, and one that is handed them would answer a different
168
- * question than the CPU port. Narrowing `AlgorithmAccelerator` is a change to the interface the GPU
169
- * package implements, so it belongs to the pull request that lands a GPU Louvain or Katz.
212
+ * `louvain` and `kCoreDecomposition` pass their port's options through unchanged; Louvain's
213
+ * `resolution` is wider than the `HitsOptionsLike` the accelerator side declares, and narrowing that
214
+ * member belongs to the pull request that lands a GPU Louvain.
215
+ *
216
+ * `katzCentrality` and `hits` hand the accelerator only what it reads -- `{ alpha, beta,
217
+ * maxIterations, tolerance, weighted }` and `{ maxIterations, tolerance, weighted }` -- with
218
+ * `weighted` resolved to the port's default (false), since an accelerator may default it the
219
+ * other way, and give its result the port's scale: an accelerator may end its iterate on any
220
+ * positive scale (webgpu-graph-algorithms divides Katz by its L2 norm and HITS by its sum), so
221
+ * Katz is min-max rescaled to [0, 1] as the port rescales its own, and each HITS vector is
222
+ * rescaled to unit length, or to a largest entry of 1 under `normalized: false`, as the port does.
223
+ * Katz runs the CPU port under `normalized: false` (the raw sums cannot be recovered from a
224
+ * rescaled vector), with `alpha` 0, and when every node has the same in-degree (or in-weight):
225
+ * there every score is equal, and the port leaves an equal vector unscaled. The iteration counts
226
+ * of the two paths differ.
227
+ *
228
+ * `labelPropagation` passes its options through the same way, except that a call with `randomSeed`
229
+ * set runs the CPU port: the partition depends on the seed, and a GPU kernel has none to honour. An
230
+ * accelerator's result carries no `iterations` or `converged`; call `indexed.labelPropagation`
231
+ * directly for those. webgpu-graph-algorithms does not implement `labelPropagation` yet, so with its
232
+ * accelerator this method runs the CPU port.
233
+ *
234
+ * `breadthFirstSearch` with a `target` or an `arcOrder` runs the CPU port: a GPU BFS expands whole
235
+ * levels and has no early stop and no neighbour order, so it would give a different result.
236
+ *
237
+ * `pageRank` and `personalizedPageRank` run the CPU port when `initialRanks` or
238
+ * `convergenceNorm: "max"` is set: the accelerator members take neither. The accelerator is
239
+ * handed `{ dampingFactor, maxIterations, tolerance, weighted }` with `weighted` resolved to the
240
+ * port's default (false), since an accelerator may default it the other way. Its stopping rule
241
+ * may differ from the port's (webgpu-graph-algorithms stops at an L1 change below
242
+ * `tolerance * n`, as networkx does), so the two paths can stop at different iterations.
243
+ * `personalizedPageRank` also runs the CPU port when the personalization is all zero (the port
244
+ * then answers plain PageRank, as the legacy function does for no personal nodes) or when a node
245
+ * is dangling: the port spreads dangling mass as legacy does, `d * dangling / n` scaled by the
246
+ * personalization, and the accelerator spreads `d * dangling` by the personalization, as networkx
247
+ * does. The two agree when no node is dangling.
248
+ *
249
+ * `eigenvectorCentrality` goes to the accelerator only for the question its kernel answers the
250
+ * same way: an undirected snapshot with no parallel edges and no bipartite component, unweighted,
251
+ * from the uniform start. The kernel iterates on `A` where the port iterates on `A + I`, and walks
252
+ * every parallel arc where the port counts a neighbour once; on a bipartite component the
253
+ * iteration on `A` oscillates forever. A directed snapshot always runs the port, since a
254
+ * periodic strongly connected component oscillates the same way and is not cheap to rule out.
255
+ * The accelerator is called with `{ maxIterations, tolerance, weighted: false }` and its
256
+ * unit-length vector is rescaled to [0, 1] exactly as the port rescales its own unless
257
+ * `normalized: false`. A result with `converged: false` raises `ConvergenceError`, as the port
258
+ * does. The iteration counts of the two paths differ.
259
+ *
260
+ * `allPairsShortestPath` is the reverse case: the accelerator member reads none of the port's
261
+ * options and has no negative-cycle flag, so the dispatcher calls it, without options, only when it
262
+ * answers the port's question -- no options that change the result, at most the port's default
263
+ * 5,792 nodes, and non-negative finite snapshot weights -- and runs the CPU port otherwise. That is
264
+ * routing decided from the inputs, not a fallback: an error the accelerator raises still propagates.
265
+ * Same question is not same bits: both read the snapshot's f32 arc weights, but a GPU member sums
266
+ * them in f32 and returns an f32 `dist`, where the port sums in f64. The two agree exactly while
267
+ * every path sum is an integer below 2^24, and otherwise may differ by f32 rounding.
268
+ *
269
+ * `bellmanFord` goes to the accelerator unless the call carries a `weights` override that is not a
270
+ * `Float32Array`: an accelerator narrows the override to f32, so an f64 one -- the exact weights a
271
+ * legacy facade passes -- would be answered for different weights. Its result gets the same
272
+ * `pathTo` / `pathEdges` decoration as `sssp`.
273
+ *
274
+ * The three path centralities route the same way. `betweennessCentrality` and
275
+ * `edgeBetweennessCentrality` go to the accelerator unless the snapshot is a multigraph (the port
276
+ * counts a pair's parallel edges as one path, the WebGPU kernel as several), `endpoints` is set, or an
277
+ * `alive` edge mask is given. The accelerator is always handed the sources the port would run -- the
278
+ * caller's, the port's `k` draw, or every node -- so it never substitutes a draw or a sampling default of
279
+ * its own. `closenessCentrality` goes only for the plain score -- no `normalized`,
280
+ * `harmonic`, `cutoff` or `weights` override -- and hands the accelerator an explicit `weighted`,
281
+ * because the WebGPU member otherwise defaults it from the snapshot where the port defaults it off.
282
+ *
283
+ * `depthFirstSearch`, `stronglyConnectedComponents`, `leiden`, `girvanNewman`, `maxFlow`,
284
+ * `minSTCut`, `stoerWagner`, `kargerMinCut`, `commonNeighborsPrediction`, `adamicAdarPrediction`,
285
+ * `primMST` and `maximumBipartiteMatching` always run the CPU port: `AlgorithmAccelerator` declares no member for
286
+ * them, since no GPU kernel exists. They are here so graphty-element runs every algorithm through
287
+ * one object, and each gains an accelerator branch when a kernel lands. A port's throw becomes a
288
+ * rejection.
170
289
  * @public
171
290
  */
172
291
  export interface AcceleratedAlgorithms {
173
292
  readonly accelerator: AlgorithmAccelerator | null;
174
293
  pageRank(s: GraphSnapshot, options?: PageRankOptions): Promise<PageRankResultLike>;
294
+ personalizedPageRank(
295
+ s: GraphSnapshot,
296
+ personalization: F32 | F64,
297
+ options?: PageRankOptions,
298
+ ): Promise<PageRankResultLike>;
299
+ eigenvectorCentrality(s: GraphSnapshot, options?: EigenvectorOptions): Promise<ScoresResultLike>;
175
300
  sssp(s: GraphSnapshot, source: number, options?: SsspOptions): Promise<SsspResult>;
301
+ bellmanFord(s: GraphSnapshot, source: number, options?: SsspOptions): Promise<BellmanFordResult>;
176
302
  breadthFirstSearch(s: GraphSnapshot, source: number, options?: BfsOptions): Promise<BfsResultLike>;
177
303
  connectedComponents(s: GraphSnapshot): Promise<LabelResultLike>;
178
304
  weaklyConnectedComponents(s: GraphSnapshot): Promise<LabelResultLike>;
@@ -181,6 +307,291 @@ export interface AcceleratedAlgorithms {
181
307
  katzCentrality(s: GraphSnapshot, options?: KatzOptions): Promise<ScoresResultLike>;
182
308
  hits(s: GraphSnapshot, options?: HitsOptions): Promise<HitsResultLike>;
183
309
  louvain(s: GraphSnapshot, options?: LouvainOptions): Promise<CommunityResultLike>;
310
+ labelPropagation(s: GraphSnapshot, options?: LabelPropagationOptions): Promise<LabelResultLike>;
311
+ allPairsShortestPath(s: GraphSnapshot, options?: ApspOptions): Promise<ApspCycleResultLike>;
312
+ betweennessCentrality(s: GraphSnapshot, options?: BetweennessOptions): Promise<ScoresResultLike>;
313
+ edgeBetweennessCentrality(s: GraphSnapshot, options?: EdgeBetweennessOptions): Promise<EdgeScoresResultLike>;
314
+ closenessCentrality(s: GraphSnapshot, options?: ClosenessOptions): Promise<ScoresResultLike>;
315
+ depthFirstSearch(s: GraphSnapshot, start: number, options?: DfsOptions): Promise<DfsResult>;
316
+ stronglyConnectedComponents(s: GraphSnapshot, options?: ArcOrderOption): Promise<LabelResult>;
317
+ leiden(s: GraphSnapshot, options?: LeidenOptions): Promise<LeidenResult>;
318
+ girvanNewman(s: GraphSnapshot, options?: GirvanNewmanOptions): Promise<GirvanNewmanResult>;
319
+ maxFlow(s: GraphSnapshot, source: number, sink: number, options?: MaxFlowOptions): Promise<MaxFlowResult>;
320
+ minSTCut(s: GraphSnapshot, source: number, sink: number, options?: MaxFlowOptions): Promise<MinCutResult>;
321
+ stoerWagner(s: GraphSnapshot, options?: StoerWagnerOptions): Promise<MinCutResult>;
322
+ kargerMinCut(s: GraphSnapshot, options?: KargerOptions): Promise<MinCutResult>;
323
+ commonNeighborsPrediction(s: GraphSnapshot, options?: LinkPredictionOptions): Promise<LinkPredictionResult>;
324
+ adamicAdarPrediction(s: GraphSnapshot, options?: LinkPredictionOptions): Promise<LinkPredictionResult>;
325
+ primMST(s: GraphSnapshot, options?: PrimOptions): Promise<PrimResult>;
326
+ maximumBipartiteMatching(s: GraphSnapshot, options?: BipartiteMatchingOptions): Promise<BipartiteMatchingResult>;
327
+ }
328
+
329
+ /**
330
+ * Run a CPU port inside a promise, so a throw becomes a rejection.
331
+ * @param run - The port call
332
+ * @returns Its result
333
+ */
334
+ function onCpu<T>(run: () => T): Promise<T> {
335
+ return new Promise((resolve) => {
336
+ resolve(run());
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Betweenness options with the sources the port would run spelled out, and no `k`. `endpoints` is never
342
+ * forwarded: a call that sets it runs the port.
343
+ * @param s - The snapshot
344
+ * @param options - The caller's port options
345
+ * @returns The options for the accelerator
346
+ */
347
+ function explicitSources(
348
+ s: GraphSnapshot,
349
+ options: BetweennessOptions | EdgeBetweennessOptions | undefined,
350
+ ): BetweennessAcceleratorOptions {
351
+ return {
352
+ normalized: options?.normalized,
353
+ sources: resolveSources(s.nodeCount, options?.sources, options?.k),
354
+ };
355
+ }
356
+
357
+ /**
358
+ * Whether the accelerator's closeness member answers the port's question: only the plain
359
+ * `1 / sum(distance)` score over the snapshot's own weights.
360
+ * @param options - The caller's port options
361
+ * @returns True when the call may go to the accelerator
362
+ */
363
+ function acceleratorAnswersCloseness(options: ClosenessOptions | undefined): boolean {
364
+ return (
365
+ options?.normalized !== true &&
366
+ options?.harmonic !== true &&
367
+ options?.cutoff === undefined &&
368
+ options?.weights === undefined
369
+ );
370
+ }
371
+
372
+ /**
373
+ * Whether the accelerator's all-pairs member, called with the snapshot alone, answers the question
374
+ * the CPU port would (at its own precision; see `AcceleratedAlgorithms`): no option that changes the answer, a size the port accepts by default, and
375
+ * weights under which no negative cycle can exist, so `hasNegativeCycle: false` is true.
376
+ * @param s - The snapshot
377
+ * @param options - The caller's port options
378
+ * @returns True when the call may go to the accelerator
379
+ */
380
+ function acceleratorAnswersApsp(s: GraphSnapshot, options: ApspOptions | undefined): boolean {
381
+ return (
382
+ options?.weights === undefined &&
383
+ (options?.method ?? "auto") === "auto" &&
384
+ options?.maxNodes === undefined &&
385
+ options?.paths !== true &&
386
+ options?.weighted !== false &&
387
+ s.nodeCount <= APSP_DEFAULT_MAX_NODES &&
388
+ s.flags.nonNegativeWeights &&
389
+ s.flags.finiteWeights
390
+ );
391
+ }
392
+
393
+ /**
394
+ * Whether a PageRank call asks for something the accelerator members do not take.
395
+ * @param options - The caller's port options
396
+ * @returns True when the call must run the CPU port
397
+ */
398
+ function pageRankNeedsCpu(options: PageRankOptions | undefined): boolean {
399
+ return options?.initialRanks !== undefined || options?.convergenceNorm === "max";
400
+ }
401
+
402
+ /**
403
+ * The PageRank options an accelerator is handed: only the members it takes, with `weighted`
404
+ * resolved to the port's default.
405
+ * @param options - The caller's port options
406
+ * @returns The accelerator's options
407
+ */
408
+ function pageRankOptionsLike(options: PageRankOptions | undefined): PageRankOptionsLike {
409
+ return {
410
+ dampingFactor: options?.dampingFactor,
411
+ maxIterations: options?.maxIterations,
412
+ tolerance: options?.tolerance,
413
+ weighted: options?.weighted === true,
414
+ };
415
+ }
416
+
417
+ /**
418
+ * Whether a personalized PageRank call must run the port: an all-zero personalization (plain
419
+ * PageRank in the port, an error in the accelerator) or a dangling node (the two spread its mass
420
+ * differently).
421
+ * @param s - The snapshot
422
+ * @param personalization - The caller's personalization
423
+ * @param options - The caller's port options
424
+ * @returns True when the call must run the CPU port
425
+ */
426
+ function personalizedNeedsCpu(
427
+ s: GraphSnapshot,
428
+ personalization: F32 | F64,
429
+ options: PageRankOptions | undefined,
430
+ ): boolean {
431
+ const outW = options?.weighted === true && s.weights !== null ? s.weightedOutDegree() : s.outDegree();
432
+ return pageRankNeedsCpu(options) || !personalization.some((mass) => mass > 0) || outW.some((w) => w === 0);
433
+ }
434
+
435
+ /**
436
+ * Whether the accelerator's eigenvector member answers the port's question: an undirected
437
+ * snapshot with no parallel edges and no bipartite component, from the uniform start.
438
+ * @param s - The snapshot
439
+ * @param options - The caller's port options
440
+ * @returns True when the call may go to the accelerator
441
+ */
442
+ function acceleratorAnswersEigenvector(s: GraphSnapshot, options: EigenvectorOptions | undefined): boolean {
443
+ return options?.startVector === undefined && !s.directed && !s.flags.multigraph && !hasBipartiteComponent(s);
444
+ }
445
+
446
+ /**
447
+ * Whether some connected component with at least one edge is bipartite, by two-colouring each
448
+ * component breadth first. A self-loop is an odd cycle.
449
+ * @param s - An undirected snapshot
450
+ * @returns True when a component with an edge has no odd cycle
451
+ */
452
+ function hasBipartiteComponent(s: GraphSnapshot): boolean {
453
+ const n = s.nodeCount;
454
+ const colour = new Int8Array(n).fill(-1);
455
+ const queue = new Uint32Array(n);
456
+ for (let root = 0; root < n; root++) {
457
+ if (colour[root] !== -1 || s.rowPtr[root] === s.rowPtr[root + 1]) {
458
+ continue;
459
+ }
460
+ colour[root] = 0;
461
+ let tail = 0;
462
+ queue[tail++] = root;
463
+ let odd = false;
464
+ for (let head = 0; head < tail; head++) {
465
+ const u = queue[head];
466
+ for (let a = s.rowPtr[u]; a < s.rowPtr[u + 1]; a++) {
467
+ const v = s.colIdx[a];
468
+ if (colour[v] === -1) {
469
+ colour[v] = 1 - colour[u];
470
+ queue[tail++] = v;
471
+ } else if (colour[v] === colour[u]) {
472
+ odd = true;
473
+ }
474
+ }
475
+ }
476
+ if (!odd) {
477
+ return true;
478
+ }
479
+ }
480
+ return false;
481
+ }
482
+
483
+ /**
484
+ * Give an accelerator's eigenvector result the port's contract: an unconverged run throws, and the
485
+ * unit-length vector is rescaled to [0, 1] unless `normalized: false`.
486
+ * @param like - The accelerator's result
487
+ * @param options - The caller's port options
488
+ * @returns The result, rescaled into a new vector when asked
489
+ */
490
+ function finishEigenvector(like: ScoresResultLike, options: EigenvectorOptions | undefined): ScoresResultLike {
491
+ if (!like.converged) {
492
+ throw new ConvergenceError("eigenvectorCentrality", options?.maxIterations ?? 100, options?.tolerance ?? 1e-6);
493
+ }
494
+ if (options?.normalized === false) {
495
+ return like;
496
+ }
497
+ const scores = Float64Array.from(like.scores);
498
+ minMaxRescale(scores);
499
+ return { scores, iterations: like.iterations, converged: true };
500
+ }
501
+
502
+ /**
503
+ * Whether the accelerator's Katz member answers the port's question: the min-max rescaled score
504
+ * of a vector that is not constant. Raw sums (`normalized: false`) are lost to the accelerator's
505
+ * own rescaling, and a constant vector -- `alpha` 0, or the same in-degree (in-weight) everywhere
506
+ * -- is one the port leaves unscaled.
507
+ * @param s - The snapshot
508
+ * @param options - The caller's port options
509
+ * @returns True when the call may go to the accelerator
510
+ */
511
+ function acceleratorAnswersKatz(s: GraphSnapshot, options: KatzOptions | undefined): boolean {
512
+ if (options?.normalized === false || options?.alpha === 0) {
513
+ return false;
514
+ }
515
+ const rev = s.reverse();
516
+ const weights = options?.weighted === true ? rev.weights : null;
517
+ let first: number | undefined;
518
+ for (let v = 0; v < s.nodeCount; v++) {
519
+ let inWeight = 0;
520
+ for (let a = rev.rowPtr[v]; a < rev.rowPtr[v + 1]; a++) {
521
+ inWeight += weights === null ? 1 : weights[a];
522
+ }
523
+ first ??= inWeight;
524
+ if (inWeight !== first) {
525
+ return true;
526
+ }
527
+ }
528
+ return false;
529
+ }
530
+
531
+ /**
532
+ * Rescale a vector in place to [0, 1] by min-max, leaving a constant vector alone -- the Katz
533
+ * port's own rescaling.
534
+ * @param x - The vector
535
+ */
536
+ function katzRescale(x: F64): void {
537
+ let min = Infinity;
538
+ let max = -Infinity;
539
+ for (const value of x) {
540
+ min = Math.min(min, value);
541
+ max = Math.max(max, value);
542
+ }
543
+ const range = max - min;
544
+ if (range > 0) {
545
+ for (let v = 0; v < x.length; v++) {
546
+ x[v] = (x[v] - min) / range;
547
+ }
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Give an accelerator's Katz result the port's scale: min-max rescaled to [0, 1], into a new vector.
553
+ * @param like - The accelerator's result
554
+ * @returns The rescaled result
555
+ */
556
+ function finishKatz(like: ScoresResultLike): ScoresResultLike {
557
+ const scores = Float64Array.from(like.scores);
558
+ katzRescale(scores);
559
+ return { scores, iterations: like.iterations, converged: like.converged };
560
+ }
561
+
562
+ /**
563
+ * Rescale a vector in place to unit L2 length, or to a largest entry of 1; a vector with no
564
+ * positive scale is left alone, as the port leaves it.
565
+ * @param x - The vector
566
+ * @param norm - Which scale the port gives it
567
+ */
568
+ function hitsRescale(x: F64, norm: "l2" | "max"): void {
569
+ let scale = 0;
570
+ for (const value of x) {
571
+ scale = norm === "l2" ? scale + value * value : Math.max(scale, value);
572
+ }
573
+ scale = norm === "l2" ? Math.sqrt(scale) : scale;
574
+ if (scale > 0) {
575
+ for (let v = 0; v < x.length; v++) {
576
+ x[v] /= scale;
577
+ }
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Give an accelerator's HITS result the port's scale: each vector at unit length, or at a largest
583
+ * entry of 1 under `normalized: false`, into new vectors.
584
+ * @param like - The accelerator's result
585
+ * @param options - The caller's port options
586
+ * @returns The rescaled result
587
+ */
588
+ function finishHits(like: HitsResultLike, options: HitsOptions | undefined): HitsResultLike {
589
+ const norm = options?.normalized === false ? "max" : "l2";
590
+ const hubs = Float64Array.from(like.hubs);
591
+ const authorities = Float64Array.from(like.authorities);
592
+ hitsRescale(hubs, norm);
593
+ hitsRescale(authorities, norm);
594
+ return { hubs, authorities, iterations: like.iterations, converged: like.converged };
184
595
  }
185
596
 
186
597
  /**
@@ -212,13 +623,40 @@ export function accelerated(acc: AlgorithmAccelerator | null | undefined): Accel
212
623
  return {
213
624
  accelerator: acc ?? null,
214
625
  pageRank: (s, options) =>
215
- acc?.pageRank !== undefined ? acc.pageRank(s, options) : Promise.resolve(indexed.pageRank(s, options)),
626
+ acc?.pageRank !== undefined && !pageRankNeedsCpu(options)
627
+ ? acc.pageRank(s, pageRankOptionsLike(options))
628
+ : Promise.resolve(indexed.pageRank(s, options)),
629
+ personalizedPageRank: (s, personalization, options) =>
630
+ acc?.personalizedPageRank !== undefined && !personalizedNeedsCpu(s, personalization, options)
631
+ ? acc.personalizedPageRank(s, personalization, pageRankOptionsLike(options))
632
+ : Promise.resolve(indexed.personalizedPageRank(s, personalization, options)),
633
+ eigenvectorCentrality: (s, options) =>
634
+ acc?.eigenvectorCentrality !== undefined && acceleratorAnswersEigenvector(s, options)
635
+ ? acc
636
+ .eigenvectorCentrality(s, {
637
+ maxIterations: options?.maxIterations,
638
+ tolerance: options?.tolerance,
639
+ weighted: false,
640
+ })
641
+ .then((like) => finishEigenvector(like, options))
642
+ : // The executor turns the port's ConvergenceError into a rejection, as on the accelerator path.
643
+ new Promise((resolve) => {
644
+ resolve(indexed.eigenvectorCentrality(s, options));
645
+ }),
216
646
  sssp: (s, source, options) =>
217
647
  acc?.sssp !== undefined
218
648
  ? acc.sssp(s, source, options).then((like) => decorateSssp(s, source, like))
219
649
  : Promise.resolve(indexed.dijkstra(s, source, options)),
650
+ bellmanFord: (s, source, options) =>
651
+ acc?.bellmanFord !== undefined &&
652
+ (options?.weights === undefined || options.weights instanceof Float32Array)
653
+ ? acc.bellmanFord(s, source, options).then((like) => ({
654
+ ...decorateSssp(s, source, like),
655
+ hasNegativeCycle: like.hasNegativeCycle,
656
+ }))
657
+ : Promise.resolve(indexed.bellmanFord(s, source, options)),
220
658
  breadthFirstSearch: (s, source, options) =>
221
- acc?.breadthFirstSearch !== undefined
659
+ acc?.breadthFirstSearch !== undefined && options?.target === undefined && options?.arcOrder === undefined
222
660
  ? acc.breadthFirstSearch(s, source, options)
223
661
  : Promise.resolve(indexed.breadthFirstSearch(s, source, options)),
224
662
  connectedComponents: (s) =>
@@ -238,12 +676,60 @@ export function accelerated(acc: AlgorithmAccelerator | null | undefined): Accel
238
676
  ? acc.kCoreDecomposition(s)
239
677
  : Promise.resolve(indexed.kCoreDecomposition(s)),
240
678
  katzCentrality: (s, options) =>
241
- acc?.katzCentrality !== undefined
242
- ? acc.katzCentrality(s, options)
679
+ acc?.katzCentrality !== undefined && acceleratorAnswersKatz(s, options)
680
+ ? acc
681
+ .katzCentrality(s, {
682
+ alpha: options?.alpha,
683
+ beta: options?.beta,
684
+ maxIterations: options?.maxIterations,
685
+ tolerance: options?.tolerance,
686
+ weighted: options?.weighted === true,
687
+ })
688
+ .then(finishKatz)
243
689
  : Promise.resolve(indexed.katzCentrality(s, options)),
244
690
  hits: (s, options) =>
245
- acc?.hits !== undefined ? acc.hits(s, options) : Promise.resolve(indexed.hits(s, options)),
691
+ acc?.hits !== undefined
692
+ ? acc
693
+ .hits(s, {
694
+ maxIterations: options?.maxIterations,
695
+ tolerance: options?.tolerance,
696
+ weighted: options?.weighted === true,
697
+ })
698
+ .then((like) => finishHits(like, options))
699
+ : Promise.resolve(indexed.hits(s, options)),
246
700
  louvain: (s, options) =>
247
701
  acc?.louvain !== undefined ? acc.louvain(s, options) : Promise.resolve(indexed.louvain(s, options)),
702
+ allPairsShortestPath: (s, options) =>
703
+ acc?.allPairsShortestPath !== undefined && acceleratorAnswersApsp(s, options)
704
+ ? acc.allPairsShortestPath(s).then(({ dist, n }) => ({ dist, n, hasNegativeCycle: false }))
705
+ : Promise.resolve(indexed.allPairsShortestPath(s, options)),
706
+ betweennessCentrality: (s, options) =>
707
+ acc?.betweennessCentrality !== undefined && !s.flags.multigraph && options?.endpoints !== true
708
+ ? acc.betweennessCentrality(s, explicitSources(s, options))
709
+ : Promise.resolve(indexed.betweennessCentrality(s, options)),
710
+ edgeBetweennessCentrality: (s, options) =>
711
+ acc?.edgeBetweennessCentrality !== undefined && !s.flags.multigraph && options?.alive === undefined
712
+ ? acc.edgeBetweennessCentrality(s, explicitSources(s, options))
713
+ : Promise.resolve(indexed.edgeBetweennessCentrality(s, options)),
714
+ closenessCentrality: (s, options) =>
715
+ acc?.closenessCentrality !== undefined && acceleratorAnswersCloseness(options)
716
+ ? acc.closenessCentrality(s, { weighted: options?.weighted === true })
717
+ : Promise.resolve(indexed.closenessCentrality(s, options)),
718
+ labelPropagation: (s, options) =>
719
+ acc?.labelPropagation !== undefined && options?.randomSeed === undefined
720
+ ? acc.labelPropagation(s, options)
721
+ : Promise.resolve(indexed.labelPropagation(s, options)),
722
+ depthFirstSearch: (s, start, options) => onCpu(() => indexed.depthFirstSearch(s, start, options)),
723
+ stronglyConnectedComponents: (s, options) => onCpu(() => indexed.stronglyConnectedComponents(s, options)),
724
+ leiden: (s, options) => onCpu(() => indexed.leiden(s, options)),
725
+ girvanNewman: (s, options) => onCpu(() => indexed.girvanNewman(s, options)),
726
+ maxFlow: (s, source, sink, options) => onCpu(() => indexed.maxFlow(s, source, sink, options)),
727
+ minSTCut: (s, source, sink, options) => onCpu(() => indexed.minSTCut(s, source, sink, options)),
728
+ stoerWagner: (s, options) => onCpu(() => indexed.stoerWagner(s, options)),
729
+ kargerMinCut: (s, options) => onCpu(() => indexed.kargerMinCut(s, options)),
730
+ commonNeighborsPrediction: (s, options) => onCpu(() => indexed.commonNeighborsPrediction(s, options)),
731
+ adamicAdarPrediction: (s, options) => onCpu(() => indexed.adamicAdarPrediction(s, options)),
732
+ primMST: (s, options) => onCpu(() => indexed.primMST(s, options)),
733
+ maximumBipartiteMatching: (s, options) => onCpu(() => indexed.maximumBipartiteMatching(s, options)),
248
734
  };
249
735
  }