@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
@@ -0,0 +1,201 @@
1
+ import {
2
+ type GraphSnapshot,
3
+ INVALID_INDEX,
4
+ makeMask,
5
+ maskSet,
6
+ type NodeMask,
7
+ type NumericVector,
8
+ } from "@graphty/graph-format";
9
+
10
+ import { crossingEdges, edgeCapacities, type MinCutResult } from "./flow.js";
11
+ import { mulberry32 } from "./label-propagation.js";
12
+ import { IndexedMaxHeap } from "./structures/max-heap.js";
13
+ import { IntUnionFind } from "./structures/union-find.js";
14
+
15
+ /** Options of {@link stoerWagner}. @public */
16
+ export interface StoerWagnerOptions {
17
+ /** Per-arc weight override, arcCount long; without it the snapshot's weights (1 when unweighted). */
18
+ readonly weights?: NumericVector | undefined;
19
+ }
20
+
21
+ /** Options of {@link kargerMinCut}. @public */
22
+ export interface KargerOptions {
23
+ /** Independent contraction trials; the lightest cut found wins. Default 100. */
24
+ readonly iterations?: number | undefined;
25
+ /** Seed of the edge orders; one seed gives one result, bit for bit. Default 42. */
26
+ readonly randomSeed?: number | undefined;
27
+ /** Per-arc weight override, arcCount long; without it the snapshot's weights (1 when unweighted). */
28
+ readonly weights?: NumericVector | undefined;
29
+ }
30
+
31
+ /**
32
+ * Global minimum cut by Stoer-Wagner, reading every edge as undirected (a directed snapshot's arcs
33
+ * count in both directions, so two opposite arcs between one pair add up).
34
+ *
35
+ * No graph is contracted (graph-format design 14.2): a representative array and member lists
36
+ * merge the last two nodes of each phase, and each phase's maximum-adjacency order accumulates the
37
+ * weights of the ORIGINAL arcs of the merged members in an {@link IndexedMaxHeap}. A phase starts
38
+ * from the lowest-index remaining representative and takes equal keys lowest index first, and the
39
+ * first phase with the lightest cut wins, which is the order the legacy implementation uses.
40
+ * O(n (m + n) log n).
41
+ * @param s - The snapshot
42
+ * @param options - The weight override
43
+ * @returns The cut value, the side holding the last node of the winning phase, and the cut edges;
44
+ * with fewer than two nodes the value is 0 and every node is on `side`
45
+ * @public
46
+ */
47
+ export function stoerWagner(s: GraphSnapshot, options: StoerWagnerOptions = {}): MinCutResult {
48
+ const n = s.nodeCount;
49
+ const capacity = edgeCapacities(s, options.weights);
50
+ if (n < 2) {
51
+ return { cutValue: 0, side: makeMask(n, true), cutEdges: new Uint32Array(0) };
52
+ }
53
+ // Incident (neighbour, edge) pairs per node, both directions of every edge.
54
+ const { src, dst } = s.edgeList();
55
+ const offset = new Uint32Array(n + 1);
56
+ for (let e = 0; e < s.edgeCount; e++) {
57
+ offset[src[e] + 1]++;
58
+ offset[dst[e] + 1]++;
59
+ }
60
+ for (let i = 0; i < n; i++) {
61
+ offset[i + 1] += offset[i];
62
+ }
63
+ const fill = offset.slice(0, n);
64
+ const neighbour = new Uint32Array(offset[n]);
65
+ const weight = new Float64Array(offset[n]);
66
+ for (let e = 0; e < s.edgeCount; e++) {
67
+ neighbour[fill[src[e]]] = dst[e];
68
+ weight[fill[src[e]]++] = capacity[e];
69
+ neighbour[fill[dst[e]]] = src[e];
70
+ weight[fill[dst[e]]++] = capacity[e];
71
+ }
72
+
73
+ const rep = new Uint32Array(n);
74
+ const next = new Uint32Array(n).fill(INVALID_INDEX); // member lists, one per representative
75
+ const tail = new Uint32Array(n);
76
+ for (let i = 0; i < n; i++) {
77
+ rep[i] = i;
78
+ tail[i] = i;
79
+ }
80
+ const connection = new Float64Array(n);
81
+ const added = new Uint8Array(n);
82
+ const heap = new IndexedMaxHeap(n);
83
+ let best = Infinity;
84
+ let bestSide: NodeMask = makeMask(n);
85
+
86
+ for (let active = n; active > 1; active--) {
87
+ let start = INVALID_INDEX;
88
+ for (let i = 0; i < n; i++) {
89
+ if (rep[i] !== i) {
90
+ continue;
91
+ }
92
+ added[i] = 0;
93
+ connection[i] = 0;
94
+ if (start === INVALID_INDEX) {
95
+ start = i;
96
+ } else {
97
+ heap.push(i, 0);
98
+ }
99
+ }
100
+ let prev = INVALID_INDEX;
101
+ let last = start;
102
+ for (;;) {
103
+ added[last] = 1;
104
+ for (let m = last; m !== INVALID_INDEX; m = next[m]) {
105
+ for (let k = offset[m]; k < offset[m + 1]; k++) {
106
+ const x = rep[neighbour[k]];
107
+ if (added[x] === 0) {
108
+ connection[x] += weight[k];
109
+ heap.pushOrIncrease(x, connection[x]);
110
+ }
111
+ }
112
+ }
113
+ if (heap.isEmpty()) {
114
+ break;
115
+ }
116
+ prev = last;
117
+ last = heap.pop();
118
+ }
119
+ if (connection[last] < best) {
120
+ best = connection[last];
121
+ bestSide = makeMask(n);
122
+ for (let m = last; m !== INVALID_INDEX; m = next[m]) {
123
+ maskSet(bestSide, m, true);
124
+ }
125
+ }
126
+ // Merge the last node of the phase into the one before it.
127
+ for (let m = last; m !== INVALID_INDEX; m = next[m]) {
128
+ rep[m] = prev;
129
+ }
130
+ next[tail[prev]] = last;
131
+ tail[prev] = tail[last];
132
+ }
133
+ return { cutValue: best, side: bestSide, cutEdges: crossingEdges(s, bestSide, false, capacity, false) };
134
+ }
135
+
136
+ /**
137
+ * Global minimum cut by Karger's randomised contraction, reading every edge as undirected. Each
138
+ * trial unions the endpoints of the edges in a seeded random order (an {@link IntUnionFind}, no
139
+ * graph is contracted) until two groups remain, so every edge is equally likely to be contracted
140
+ * next whatever its weight; the trial's cut is the weight of the edges between the two groups.
141
+ * A graph with more than two components gives a cut of 0 with node 0's component on `side`.
142
+ * O(m alpha(n)) per trial.
143
+ * @param s - The snapshot
144
+ * @param options - Trials, seed and weight override
145
+ * @returns The lightest cut found; with fewer than two nodes the value is 0 and `side` is empty
146
+ * @throws RangeError when `randomSeed` is not an integer or `iterations` is not a positive integer
147
+ * @public
148
+ */
149
+ export function kargerMinCut(s: GraphSnapshot, options: KargerOptions = {}): MinCutResult {
150
+ const iterations = options.iterations ?? 100;
151
+ const randomSeed = options.randomSeed ?? 42;
152
+ if (!Number.isInteger(iterations) || iterations < 1) {
153
+ throw new RangeError(`iterations must be a positive integer, got ${iterations}`);
154
+ }
155
+ if (!Number.isInteger(randomSeed)) {
156
+ throw new RangeError(`randomSeed must be a finite integer, got ${randomSeed}`);
157
+ }
158
+ const n = s.nodeCount;
159
+ const capacity = edgeCapacities(s, options.weights);
160
+ if (n < 2) {
161
+ return { cutValue: 0, side: makeMask(n), cutEdges: new Uint32Array(0) };
162
+ }
163
+ const { src, dst } = s.edgeList();
164
+ const rand = mulberry32(randomSeed);
165
+ const order = new Uint32Array(s.edgeCount);
166
+ let best = Infinity;
167
+ let bestSide: NodeMask = makeMask(n);
168
+ for (let trial = 0; trial < iterations; trial++) {
169
+ for (let e = 0; e < order.length; e++) {
170
+ order[e] = e;
171
+ }
172
+ for (let i = order.length - 1; i > 0; i--) {
173
+ const j = Math.floor(rand() * (i + 1));
174
+ const t = order[i];
175
+ order[i] = order[j];
176
+ order[j] = t;
177
+ }
178
+ const uf = new IntUnionFind(n);
179
+ let groups = n;
180
+ for (let i = 0; i < order.length && groups > 2; i++) {
181
+ if (uf.union(src[order[i]], dst[order[i]])) {
182
+ groups--;
183
+ }
184
+ }
185
+ const root = uf.find(0);
186
+ let cut = 0;
187
+ for (let e = 0; e < s.edgeCount; e++) {
188
+ if ((uf.find(src[e]) === root) !== (uf.find(dst[e]) === root)) {
189
+ cut += capacity[e];
190
+ }
191
+ }
192
+ if (cut < best) {
193
+ best = cut;
194
+ bestSide = makeMask(n);
195
+ for (let v = 0; v < n; v++) {
196
+ maskSet(bestSide, v, uf.find(v) === root);
197
+ }
198
+ }
199
+ }
200
+ return { cutValue: best, side: bestSide, cutEdges: crossingEdges(s, bestSide, false, capacity, false) };
201
+ }
@@ -0,0 +1,81 @@
1
+ import { type AdjacencyView, INVALID_INDEX, type NumericVector, type U32 } from "@graphty/graph-format";
2
+
3
+ /** Options of the index-based modularity. @public */
4
+ export interface ModularityOptions {
5
+ /** Resolution gamma: above 1 favours smaller communities; default 1. */
6
+ readonly resolution?: number | undefined;
7
+ /** Per-arc weight override, arcCount long -- the facade passes `expandEdges(s, shadow.data)`. */
8
+ readonly weights?: NumericVector | undefined;
9
+ }
10
+
11
+ /**
12
+ * Newman's modularity of a partition: `sum_c in_c / 2m - gamma * (tot_c / 2m)^2`, where `2m` is
13
+ * twice the total edge weight, `in_c` counts every edge inside community c twice and `tot_c` sums
14
+ * its nodes' weighted degrees. Degrees follow graph-format's `weightedDegree()` convention -- a
15
+ * self-loop counts twice -- so `sum(tot_c) === 2m`. On a directed snapshot the direction is
16
+ * ignored: every arc counts as an undirected edge, and a node's degree is its in-weight plus its
17
+ * out-weight.
18
+ *
19
+ * On an undirected snapshot without self-loops this equals the legacy `calculateModularity`. On
20
+ * a directed snapshot or one with self-loops the two differ, and this one is the NetworkX value:
21
+ * the legacy degree counts a self-loop once and reads only out-neighbours on a directed graph, so
22
+ * its degrees do not sum to 2m.
23
+ *
24
+ * It is NOT the legacy `calculateMCLModularity`, in two ways. That function subtracts the null-model
25
+ * term `d(u) d(v) / 2m` once per edge whatever the partition: on two separate triangles it scores
26
+ * one community and the two triangles both 1/3, where this returns 0 and 1/2. And it ignores edge
27
+ * weights, where this reads the snapshot's weights by default; pass `weights` filled with 1 for
28
+ * the unweighted value.
29
+ * @param s - Any snapshot or adjacency view
30
+ * @param labels - Community of every node; `INVALID_INDEX` leaves a node out of every community
31
+ * @param options - Resolution and the weight override
32
+ * @returns The modularity, 0 when the snapshot has no edge weight
33
+ * @public
34
+ */
35
+ export function modularity(s: AdjacencyView, labels: U32, options: ModularityOptions = {}): number {
36
+ const resolution = options.resolution ?? 1;
37
+ const weights = options.weights ?? s.weights;
38
+ if (labels.length !== s.nodeCount) {
39
+ throw new RangeError(`labels has ${labels.length} entries; the snapshot has ${s.nodeCount} nodes`);
40
+ }
41
+ if (weights !== null && weights.length !== s.arcCount) {
42
+ throw new RangeError(`weights has ${weights.length} entries; the snapshot has ${s.arcCount} arcs`);
43
+ }
44
+ const tot = new Map<number, number>();
45
+ const inside = new Map<number, number>();
46
+ const addTo = (map: Map<number, number>, label: number, w: number): void => {
47
+ if (label !== INVALID_INDEX) {
48
+ map.set(label, (map.get(label) ?? 0) + w);
49
+ }
50
+ };
51
+ let m2 = 0;
52
+ for (let u = 0; u < s.nodeCount; u++) {
53
+ const end = s.rowPtr[u + 1];
54
+ for (let a = s.rowPtr[u]; a < end; a++) {
55
+ const v = s.colIdx[a];
56
+ const w = weights === null ? 1 : weights[a];
57
+ // An undirected edge between two nodes is read from both rows; a self-loop and a
58
+ // directed arc only once, so they count double here.
59
+ const twice = s.directed || v === u ? 2 * w : w;
60
+ m2 += twice;
61
+ if (s.directed) {
62
+ addTo(tot, labels[u], w);
63
+ addTo(tot, labels[v], w);
64
+ } else {
65
+ addTo(tot, labels[u], twice);
66
+ }
67
+ if (labels[u] === labels[v]) {
68
+ addTo(inside, labels[u], twice);
69
+ }
70
+ }
71
+ }
72
+ if (m2 === 0) {
73
+ return 0;
74
+ }
75
+ let q = 0;
76
+ for (const [label, degrees] of tot) {
77
+ const share = degrees / m2;
78
+ q += (inside.get(label) ?? 0) / m2 - resolution * share * share;
79
+ }
80
+ return q;
81
+ }
@@ -1,5 +1,6 @@
1
- import type { GraphSnapshot, NumericVector, U32 } from "@graphty/graph-format";
1
+ import { type GraphSnapshot, INVALID_INDEX, type NumericVector, type U32 } from "@graphty/graph-format";
2
2
 
3
+ import { IndexedMinHeap } from "./structures/min-heap.js";
3
4
  import { IntUnionFind } from "./structures/union-find.js";
4
5
 
5
6
  /** Options of the index-based MST. @public */
@@ -60,3 +61,80 @@ export function kruskalMST(s: GraphSnapshot, o: MstOptions = {}): MstResult {
60
61
  }
61
62
  return { edges: accepted.subarray(0, taken), totalWeight };
62
63
  }
64
+
65
+ /** Options of the index-based Prim. @public */
66
+ export interface PrimOptions extends MstOptions {
67
+ /** The node index the (first) tree grows from; default 0. */
68
+ readonly start?: number | undefined;
69
+ /**
70
+ * Grow a tree in every component, rooted at `start` and then at each component's lowest node
71
+ * index, instead of throwing on a disconnected graph. Default false, as legacy `primMST`.
72
+ */
73
+ readonly forest?: boolean | undefined;
74
+ }
75
+
76
+ /** Result of the index-based Prim: the MST result plus each node's discovery arc. @public */
77
+ export interface PrimResult extends MstResult {
78
+ /** The tree arc that reached each node, pointing at it; INVALID_INDEX for every root. */
79
+ readonly predArc: U32;
80
+ }
81
+
82
+ /**
83
+ * Prim's minimum spanning tree, grown from a start node by always taking the cheapest arc from the
84
+ * tree to a node outside it (an indexed heap keyed per node, so of several parallel edges the
85
+ * cheapest, and of equal ones the lowest-indexed, is the one taken). `edges` lists the logical
86
+ * edges in the order they joined the tree and `totalWeight` sums them in that order, as legacy
87
+ * `primMST` does. The start index is checked: one outside the graph throws a RangeError.
88
+ * @param s - An undirected snapshot
89
+ * @param o - Per-arc weight override, start node and the spanning-forest switch
90
+ * @returns The accepted edges, their total weight and the discovery arc per node
91
+ * @throws Error on a directed snapshot, and on a disconnected one unless `forest` is set
92
+ * @public
93
+ */
94
+ export function primMST(s: GraphSnapshot, o: PrimOptions = {}): PrimResult {
95
+ if (s.directed) {
96
+ throw new Error("Prim's algorithm requires an undirected graph");
97
+ }
98
+ const { nodeCount: n, rowPtr, colIdx } = s;
99
+ const weights: NumericVector | null = o.weights ?? s.weights;
100
+ const key = new Float64Array(n).fill(Infinity);
101
+ const predArc = new Uint32Array(n).fill(INVALID_INDEX);
102
+ const inTree = new Uint8Array(n);
103
+ const heap = new IndexedMinHeap(n);
104
+ const accepted = new Uint32Array(Math.max(n - 1, 0));
105
+ let taken = 0;
106
+ let totalWeight = 0;
107
+ const grow = (root: number): void => {
108
+ heap.push(root, 0);
109
+ while (!heap.isEmpty()) {
110
+ const u = heap.pop();
111
+ inTree[u] = 1;
112
+ if (predArc[u] !== INVALID_INDEX) {
113
+ accepted[taken++] = s.arcToEdge[predArc[u]];
114
+ totalWeight += key[u];
115
+ }
116
+ for (let a = rowPtr[u]; a < rowPtr[u + 1]; a++) {
117
+ const v = colIdx[a];
118
+ const w = weights === null ? 1 : weights[a];
119
+ if (inTree[v] === 0 && w < key[v]) {
120
+ key[v] = w;
121
+ predArc[v] = a;
122
+ heap.pushOrDecrease(v, w);
123
+ }
124
+ }
125
+ }
126
+ };
127
+ if (n > 0) {
128
+ grow(o.start ?? 0);
129
+ }
130
+ if (o.forest === true) {
131
+ for (let root = 0; root < n; root++) {
132
+ if (inTree[root] === 0) {
133
+ grow(root);
134
+ }
135
+ }
136
+ } else if (taken < n - 1) {
137
+ throw new Error("Graph is not connected");
138
+ }
139
+ return { edges: accepted.subarray(0, taken), totalWeight, predArc };
140
+ }
@@ -1,15 +1,22 @@
1
- import type { F64, GraphSnapshot, NumericVector } from "@graphty/graph-format";
1
+ import type { F32, F64, GraphSnapshot, NumericVector } from "@graphty/graph-format";
2
2
 
3
- /** Options of the index-based PageRank (graph-format design 14.2 Port 3, line 3892). @public */
3
+ /** Options of the index-based PageRank (graph-format design 14.2 Port 3). @public */
4
4
  export interface PageRankOptions {
5
5
  /** Probability of following a link; default 0.85. */
6
6
  readonly dampingFactor?: number | undefined;
7
7
  /** Iteration cap; default 100. */
8
8
  readonly maxIterations?: number | undefined;
9
- /** L1 convergence tolerance; default 1e-6. */
9
+ /** Convergence tolerance on the per-iteration change; default 1e-6. */
10
10
  readonly tolerance?: number | undefined;
11
11
  /** Use the snapshot's arc weights; default false. */
12
12
  readonly weighted?: boolean | undefined;
13
+ /** Starting rank per node index, normalised to sum 1 (when the sum is positive); default uniform. */
14
+ readonly initialRanks?: F32 | F64 | undefined;
15
+ /**
16
+ * How the per-iteration change is measured against `tolerance`: `"l1"` (default) sums it over
17
+ * all nodes, `"max"` takes the largest single-node change, the legacy `pageRank` rule.
18
+ */
19
+ readonly convergenceNorm?: "l1" | "max" | undefined;
13
20
  }
14
21
 
15
22
  /** Result of the index-based PageRank. @public */
@@ -18,29 +25,74 @@ export interface PageRankResult {
18
25
  readonly scores: F64;
19
26
  /** Iterations actually run. */
20
27
  readonly iterations: number;
21
- /** Whether the L1 delta fell below the tolerance. */
28
+ /** Whether the change fell below the tolerance. */
22
29
  readonly converged: boolean;
23
30
  }
24
31
 
25
32
  /**
26
- * PageRank by pull over `reverse()`, with the dangling mass redistributed uniformly.
27
- * @param s - A DIRECTED snapshot
33
+ * PageRank by pull over `reverse()`, with the dangling mass redistributed uniformly. On an
34
+ * undirected snapshot every edge carries rank both ways (a self-loop once), which is PageRank on
35
+ * the directed graph holding both arcs of every edge.
36
+ * @param s - The snapshot
28
37
  * @param o - Algorithm options
29
38
  * @returns The scores, the iteration count and the convergence flag
30
39
  * @public
31
40
  */
32
41
  export function pageRank(s: GraphSnapshot, o: PageRankOptions = {}): PageRankResult {
33
- if (!s.directed) {
34
- throw new Error("PageRank requires a directed graph");
35
- }
42
+ return run(s, null, o);
43
+ }
44
+
45
+ /**
46
+ * Personalized PageRank, as the legacy function computes it: the personalization vector,
47
+ * normalised to sum 1, replaces the uniform `1 / n` in the teleport, and each node receives
48
+ * `d * dangling / n` of the dangling mass scaled by its personalization. That keeps only a
49
+ * `1 / n` share of the dangling mass, so on a graph with a dangling node the scores sum to less
50
+ * than 1 (networkx spreads all of it). An all-zero vector gives plain PageRank, as the legacy
51
+ * function does for an empty list of personal nodes.
52
+ * @param s - The snapshot
53
+ * @param personalization - One finite, non-negative mass per node index
54
+ * @param o - Algorithm options
55
+ * @returns The scores, the iteration count and the convergence flag
56
+ * @public
57
+ */
58
+ export function personalizedPageRank(
59
+ s: GraphSnapshot,
60
+ personalization: F32 | F64,
61
+ o: PageRankOptions = {},
62
+ ): PageRankResult {
63
+ return run(s, distribution(personalization, s.nodeCount, "personalization"), o);
64
+ }
65
+
66
+ /**
67
+ * The shared power iteration.
68
+ * @param s - The snapshot
69
+ * @param p - The normalised personalization, or null for the uniform teleport
70
+ * @param o - Algorithm options
71
+ * @returns The scores, the iteration count and the convergence flag
72
+ */
73
+ function run(s: GraphSnapshot, p: F64 | null, o: PageRankOptions): PageRankResult {
36
74
  const n = s.nodeCount;
37
75
  const d = o.dampingFactor ?? 0.85;
38
76
  const maxIter = o.maxIterations ?? 100;
39
77
  const tol = o.tolerance ?? 1e-6;
78
+ const useMax = o.convergenceNorm === "max";
79
+ if (n === 0) {
80
+ return { scores: new Float64Array(0), iterations: 0, converged: true };
81
+ }
40
82
  const rev = s.reverse();
41
83
  const weighted = o.weighted === true && rev.weights !== null;
42
- const outW: NumericVector = weighted ? s.weightedOutDegree() : s.outDegree();
43
- let rank = new Float64Array(n).fill(1 / n);
84
+ // The f64 shadow toSnapshot keeps when a weight is not f32-exact, else the f32 arc weights.
85
+ const shadow = weighted ? s.edges.byRole("weight") : null;
86
+ const exact = shadow?.dtype === "f64" ? shadow.data : null;
87
+ let revW: NumericVector | null = weighted ? rev.weights : null;
88
+ let outW: NumericVector = weighted ? s.weightedOutDegree() : s.outDegree();
89
+ if (exact !== null) {
90
+ revW = gather(exact, rev.arcToEdge);
91
+ outW = rowSums(s, gather(exact, s.arcToEdge));
92
+ }
93
+ let rank =
94
+ (o.initialRanks === undefined ? null : distribution(o.initialRanks, n, "initialRanks")) ??
95
+ new Float64Array(n).fill(o.initialRanks === undefined ? 1 / n : 0);
44
96
  let next = new Float64Array(n);
45
97
  let it = 0;
46
98
  let converged = false;
@@ -51,7 +103,9 @@ export function pageRank(s: GraphSnapshot, o: PageRankOptions = {}): PageRankRes
51
103
  dangling += rank[u];
52
104
  }
53
105
  }
54
- const base = (1 - d) / n + (d * dangling) / n;
106
+ // Teleport (1 - d) spread by p or uniformly; the dangling mass d * dangling spread uniformly,
107
+ // and with p scaled by p[v] on top of the uniform 1 / n, as the legacy function does.
108
+ const spread = 1 - d + d * dangling * (p === null ? 1 : 1 / n);
55
109
  let delta = 0;
56
110
  for (let v = 0; v < n; v++) {
57
111
  let acc = 0;
@@ -60,14 +114,73 @@ export function pageRank(s: GraphSnapshot, o: PageRankOptions = {}): PageRankRes
60
114
  const u = rev.colIdx[a];
61
115
  const ow = outW[u];
62
116
  if (ow > 0) {
63
- acc += (rank[u] * (weighted && rev.weights !== null ? rev.weights[a] : 1)) / ow;
117
+ acc += (rank[u] * (revW === null ? 1 : revW[a])) / ow;
64
118
  }
65
119
  }
66
- next[v] = base + d * acc;
67
- delta += Math.abs(next[v] - rank[v]);
120
+ next[v] = spread * (p === null ? 1 / n : p[v]) + d * acc;
121
+ const change = Math.abs(next[v] - rank[v]);
122
+ delta = useMax ? Math.max(delta, change) : delta + change;
68
123
  }
69
124
  [rank, next] = [next, rank];
70
125
  converged = delta < tol;
71
126
  }
72
127
  return { scores: rank, iterations: it, converged };
73
128
  }
129
+
130
+ /**
131
+ * One value per arc, read through the arc's logical edge.
132
+ * @param perEdge - One value per logical edge
133
+ * @param arcToEdge - The logical edge of every arc
134
+ * @returns One value per arc
135
+ */
136
+ function gather(perEdge: F64, arcToEdge: Uint32Array): F64 {
137
+ const out = new Float64Array(arcToEdge.length);
138
+ for (let a = 0; a < out.length; a++) {
139
+ out[a] = perEdge[arcToEdge[a]];
140
+ }
141
+ return out;
142
+ }
143
+
144
+ /**
145
+ * The sum of each node's out-arc values.
146
+ * @param s - The snapshot
147
+ * @param perArc - One value per forward arc
148
+ * @returns One sum per node
149
+ */
150
+ function rowSums(s: GraphSnapshot, perArc: F64): F64 {
151
+ const out = new Float64Array(s.nodeCount);
152
+ for (let u = 0; u < s.nodeCount; u++) {
153
+ for (let a = s.rowPtr[u]; a < s.rowPtr[u + 1]; a++) {
154
+ out[u] += perArc[a];
155
+ }
156
+ }
157
+ return out;
158
+ }
159
+
160
+ /**
161
+ * Check a per-node mass vector and normalise it to sum 1.
162
+ * @param v - The caller's vector
163
+ * @param n - The node count
164
+ * @param name - The option's name, for the message
165
+ * @returns A normalised copy, or null when every entry is 0
166
+ */
167
+ function distribution(v: F32 | F64, n: number, name: string): F64 | null {
168
+ if (v.length !== n) {
169
+ throw new Error(`PageRank: ${name} has ${String(v.length)} entries for ${String(n)} nodes`);
170
+ }
171
+ let total = 0;
172
+ for (const mass of v) {
173
+ if (!Number.isFinite(mass) || mass < 0) {
174
+ throw new Error(`PageRank: ${name} must be finite and non-negative, got ${String(mass)}`);
175
+ }
176
+ total += mass;
177
+ }
178
+ if (total === 0) {
179
+ return null;
180
+ }
181
+ const out = new Float64Array(n);
182
+ for (let u = 0; u < n; u++) {
183
+ out[u] = v[u] / total;
184
+ }
185
+ return out;
186
+ }