@graphty/algorithms 2.1.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/README.md +139 -50
  2. package/dist/algorithms.js +15260 -10390
  3. package/dist/algorithms.js.map +1 -1
  4. package/dist/algorithms.standalone.js +26675 -21542
  5. package/dist/algorithms.standalone.js.map +1 -1
  6. package/dist/src/algorithms/centrality/betweenness.d.ts +18 -0
  7. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  8. package/dist/src/algorithms/centrality/betweenness.js +40 -1
  9. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  10. package/dist/src/algorithms/centrality/closeness.d.ts +20 -0
  11. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  12. package/dist/src/algorithms/centrality/closeness.js +93 -8
  13. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  14. package/dist/src/algorithms/centrality/degree.d.ts +9 -0
  15. package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
  16. package/dist/src/algorithms/centrality/degree.js +18 -0
  17. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  18. package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -0
  19. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
  20. package/dist/src/algorithms/centrality/eigenvector.js +29 -0
  21. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
  22. package/dist/src/algorithms/centrality/hits.d.ts +9 -0
  23. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
  24. package/dist/src/algorithms/centrality/hits.js +23 -0
  25. package/dist/src/algorithms/centrality/hits.js.map +1 -1
  26. package/dist/src/algorithms/centrality/katz.d.ts +9 -0
  27. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
  28. package/dist/src/algorithms/centrality/katz.js +31 -0
  29. package/dist/src/algorithms/centrality/katz.js.map +1 -1
  30. package/dist/src/algorithms/centrality/pagerank.d.ts +13 -13
  31. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  32. package/dist/src/algorithms/centrality/pagerank.js +47 -10
  33. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  34. package/dist/src/algorithms/community/girvan-newman-legacy.d.ts +27 -0
  35. package/dist/src/algorithms/community/girvan-newman-legacy.d.ts.map +1 -0
  36. package/dist/src/algorithms/community/girvan-newman-legacy.js +335 -0
  37. package/dist/src/algorithms/community/girvan-newman-legacy.js.map +1 -0
  38. package/dist/src/algorithms/community/girvan-newman.d.ts +5 -3
  39. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  40. package/dist/src/algorithms/community/girvan-newman.js +34 -308
  41. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  42. package/dist/src/algorithms/community/label-propagation.d.ts +9 -6
  43. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  44. package/dist/src/algorithms/community/label-propagation.js +24 -110
  45. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  46. package/dist/src/algorithms/components/connected.d.ts.map +1 -1
  47. package/dist/src/algorithms/components/connected.js +28 -77
  48. package/dist/src/algorithms/components/connected.js.map +1 -1
  49. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
  50. package/dist/src/algorithms/mst/kruskal.js +59 -3
  51. package/dist/src/algorithms/mst/kruskal.js.map +1 -1
  52. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  53. package/dist/src/algorithms/shortest-path/bellman-ford.js +46 -0
  54. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  55. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  56. package/dist/src/algorithms/shortest-path/dijkstra.js +27 -0
  57. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  58. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +15 -5
  59. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
  60. package/dist/src/algorithms/shortest-path/floyd-warshall.js +97 -107
  61. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
  62. package/dist/src/algorithms/traversal/bfs-unified.d.ts +2 -12
  63. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
  64. package/dist/src/algorithms/traversal/bfs-unified.js +47 -308
  65. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
  66. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
  67. package/dist/src/algorithms/traversal/bfs-variants.js +5 -25
  68. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
  69. package/dist/src/algorithms/traversal/bfs.d.ts +0 -3
  70. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  71. package/dist/src/algorithms/traversal/bfs.js +0 -4
  72. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  73. package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
  74. package/dist/src/algorithms/traversal/dfs.js +33 -154
  75. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  76. package/dist/src/clustering/hierarchical-legacy.d.ts +56 -0
  77. package/dist/src/clustering/hierarchical-legacy.d.ts.map +1 -0
  78. package/dist/src/clustering/hierarchical-legacy.js +441 -0
  79. package/dist/src/clustering/hierarchical-legacy.js.map +1 -0
  80. package/dist/src/clustering/hierarchical.d.ts +7 -39
  81. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  82. package/dist/src/clustering/hierarchical.js +56 -417
  83. package/dist/src/clustering/hierarchical.js.map +1 -1
  84. package/dist/src/clustering/k-core-legacy.d.ts +30 -0
  85. package/dist/src/clustering/k-core-legacy.d.ts.map +1 -0
  86. package/dist/src/clustering/k-core-legacy.js +191 -0
  87. package/dist/src/clustering/k-core-legacy.js.map +1 -0
  88. package/dist/src/clustering/k-core.d.ts +8 -7
  89. package/dist/src/clustering/k-core.d.ts.map +1 -1
  90. package/dist/src/clustering/k-core.js +33 -174
  91. package/dist/src/clustering/k-core.js.map +1 -1
  92. package/dist/src/clustering/mcl-legacy.d.ts +42 -0
  93. package/dist/src/clustering/mcl-legacy.d.ts.map +1 -0
  94. package/dist/src/clustering/mcl-legacy.js +386 -0
  95. package/dist/src/clustering/mcl-legacy.js.map +1 -0
  96. package/dist/src/clustering/mcl.d.ts +9 -32
  97. package/dist/src/clustering/mcl.d.ts.map +1 -1
  98. package/dist/src/clustering/mcl.js +48 -374
  99. package/dist/src/clustering/mcl.js.map +1 -1
  100. package/dist/src/index.d.ts +29 -3
  101. package/dist/src/index.d.ts.map +1 -1
  102. package/dist/src/index.js +2 -1
  103. package/dist/src/index.js.map +1 -1
  104. package/dist/src/indexed/accelerator.d.ts +141 -11
  105. package/dist/src/indexed/accelerator.d.ts.map +1 -1
  106. package/dist/src/indexed/accelerator.js +319 -5
  107. package/dist/src/indexed/accelerator.js.map +1 -1
  108. package/dist/src/indexed/all-pairs.d.ts +59 -0
  109. package/dist/src/indexed/all-pairs.d.ts.map +1 -0
  110. package/dist/src/indexed/all-pairs.js +274 -0
  111. package/dist/src/indexed/all-pairs.js.map +1 -0
  112. package/dist/src/indexed/bellman-ford.d.ts +25 -0
  113. package/dist/src/indexed/bellman-ford.d.ts.map +1 -0
  114. package/dist/src/indexed/bellman-ford.js +59 -0
  115. package/dist/src/indexed/bellman-ford.js.map +1 -0
  116. package/dist/src/indexed/betweenness.d.ts +85 -0
  117. package/dist/src/indexed/betweenness.d.ts.map +1 -0
  118. package/dist/src/indexed/betweenness.js +188 -0
  119. package/dist/src/indexed/betweenness.js.map +1 -0
  120. package/dist/src/indexed/bfs.d.ts +69 -2
  121. package/dist/src/indexed/bfs.d.ts.map +1 -1
  122. package/dist/src/indexed/bfs.js +148 -1
  123. package/dist/src/indexed/bfs.js.map +1 -1
  124. package/dist/src/indexed/bipartite.d.ts +37 -0
  125. package/dist/src/indexed/bipartite.d.ts.map +1 -0
  126. package/dist/src/indexed/bipartite.js +54 -0
  127. package/dist/src/indexed/bipartite.js.map +1 -0
  128. package/dist/src/indexed/closeness.d.ts +49 -0
  129. package/dist/src/indexed/closeness.d.ts.map +1 -0
  130. package/dist/src/indexed/closeness.js +127 -0
  131. package/dist/src/indexed/closeness.js.map +1 -0
  132. package/dist/src/indexed/common-neighbors.d.ts +14 -1
  133. package/dist/src/indexed/common-neighbors.d.ts.map +1 -1
  134. package/dist/src/indexed/common-neighbors.js +24 -14
  135. package/dist/src/indexed/common-neighbors.js.map +1 -1
  136. package/dist/src/indexed/degree.d.ts +19 -0
  137. package/dist/src/indexed/degree.d.ts.map +1 -0
  138. package/dist/src/indexed/degree.js +45 -0
  139. package/dist/src/indexed/degree.js.map +1 -0
  140. package/dist/src/indexed/delta-pagerank.d.ts +145 -0
  141. package/dist/src/indexed/delta-pagerank.d.ts.map +1 -0
  142. package/dist/src/indexed/delta-pagerank.js +428 -0
  143. package/dist/src/indexed/delta-pagerank.js.map +1 -0
  144. package/dist/src/indexed/dfs.d.ts +55 -0
  145. package/dist/src/indexed/dfs.d.ts.map +1 -0
  146. package/dist/src/indexed/dfs.js +146 -0
  147. package/dist/src/indexed/dfs.js.map +1 -0
  148. package/dist/src/indexed/eigenvector.d.ts +48 -0
  149. package/dist/src/indexed/eigenvector.d.ts.map +1 -0
  150. package/dist/src/indexed/eigenvector.js +162 -0
  151. package/dist/src/indexed/eigenvector.js.map +1 -0
  152. package/dist/src/indexed/facade.d.ts +109 -0
  153. package/dist/src/indexed/facade.d.ts.map +1 -0
  154. package/dist/src/indexed/facade.js +204 -0
  155. package/dist/src/indexed/facade.js.map +1 -0
  156. package/dist/src/indexed/flow.d.ts +120 -0
  157. package/dist/src/indexed/flow.d.ts.map +1 -0
  158. package/dist/src/indexed/flow.js +377 -0
  159. package/dist/src/indexed/flow.js.map +1 -0
  160. package/dist/src/indexed/girvan-newman.d.ts +42 -0
  161. package/dist/src/indexed/girvan-newman.d.ts.map +1 -0
  162. package/dist/src/indexed/girvan-newman.js +90 -0
  163. package/dist/src/indexed/girvan-newman.js.map +1 -0
  164. package/dist/src/indexed/grsbm.d.ts +85 -0
  165. package/dist/src/indexed/grsbm.d.ts.map +1 -0
  166. package/dist/src/indexed/grsbm.js +287 -0
  167. package/dist/src/indexed/grsbm.js.map +1 -0
  168. package/dist/src/indexed/hierarchical.d.ts +69 -0
  169. package/dist/src/indexed/hierarchical.d.ts.map +1 -0
  170. package/dist/src/indexed/hierarchical.js +245 -0
  171. package/dist/src/indexed/hierarchical.js.map +1 -0
  172. package/dist/src/indexed/index.d.ts +29 -3
  173. package/dist/src/indexed/index.d.ts.map +1 -1
  174. package/dist/src/indexed/index.js +29 -3
  175. package/dist/src/indexed/index.js.map +1 -1
  176. package/dist/src/indexed/isomorphism.d.ts +42 -0
  177. package/dist/src/indexed/isomorphism.d.ts.map +1 -0
  178. package/dist/src/indexed/isomorphism.js +269 -0
  179. package/dist/src/indexed/isomorphism.js.map +1 -0
  180. package/dist/src/indexed/label-propagation.d.ts +134 -0
  181. package/dist/src/indexed/label-propagation.d.ts.map +1 -0
  182. package/dist/src/indexed/label-propagation.js +486 -0
  183. package/dist/src/indexed/label-propagation.js.map +1 -0
  184. package/dist/src/indexed/leiden.d.ts +52 -0
  185. package/dist/src/indexed/leiden.d.ts.map +1 -0
  186. package/dist/src/indexed/leiden.js +384 -0
  187. package/dist/src/indexed/leiden.js.map +1 -0
  188. package/dist/src/indexed/link-prediction.d.ts +147 -0
  189. package/dist/src/indexed/link-prediction.d.ts.map +1 -0
  190. package/dist/src/indexed/link-prediction.js +317 -0
  191. package/dist/src/indexed/link-prediction.js.map +1 -0
  192. package/dist/src/indexed/markov.d.ts +54 -0
  193. package/dist/src/indexed/markov.d.ts.map +1 -0
  194. package/dist/src/indexed/markov.js +284 -0
  195. package/dist/src/indexed/markov.js.map +1 -0
  196. package/dist/src/indexed/matching.d.ts +53 -0
  197. package/dist/src/indexed/matching.d.ts.map +1 -0
  198. package/dist/src/indexed/matching.js +139 -0
  199. package/dist/src/indexed/matching.js.map +1 -0
  200. package/dist/src/indexed/min-cut.d.ts +48 -0
  201. package/dist/src/indexed/min-cut.d.ts.map +1 -0
  202. package/dist/src/indexed/min-cut.js +175 -0
  203. package/dist/src/indexed/min-cut.js.map +1 -0
  204. package/dist/src/indexed/modularity.d.ts +34 -0
  205. package/dist/src/indexed/modularity.d.ts.map +1 -0
  206. package/dist/src/indexed/modularity.js +74 -0
  207. package/dist/src/indexed/modularity.js.map +1 -0
  208. package/dist/src/indexed/mst.d.ts +29 -1
  209. package/dist/src/indexed/mst.d.ts.map +1 -1
  210. package/dist/src/indexed/mst.js +62 -0
  211. package/dist/src/indexed/mst.js.map +1 -1
  212. package/dist/src/indexed/pagerank.d.ts +29 -6
  213. package/dist/src/indexed/pagerank.d.ts.map +1 -1
  214. package/dist/src/indexed/pagerank.js +107 -11
  215. package/dist/src/indexed/pagerank.js.map +1 -1
  216. package/dist/src/indexed/point-to-point.d.ts +53 -0
  217. package/dist/src/indexed/point-to-point.d.ts.map +1 -0
  218. package/dist/src/indexed/point-to-point.js +173 -0
  219. package/dist/src/indexed/point-to-point.js.map +1 -0
  220. package/dist/src/indexed/scc.d.ts +53 -0
  221. package/dist/src/indexed/scc.d.ts.map +1 -0
  222. package/dist/src/indexed/scc.js +110 -0
  223. package/dist/src/indexed/scc.js.map +1 -0
  224. package/dist/src/indexed/spectral.d.ts +61 -0
  225. package/dist/src/indexed/spectral.d.ts.map +1 -0
  226. package/dist/src/indexed/spectral.js +476 -0
  227. package/dist/src/indexed/spectral.js.map +1 -0
  228. package/dist/src/indexed/structures/bit-set.d.ts +45 -0
  229. package/dist/src/indexed/structures/bit-set.d.ts.map +1 -0
  230. package/dist/src/indexed/structures/bit-set.js +57 -0
  231. package/dist/src/indexed/structures/bit-set.js.map +1 -0
  232. package/dist/src/indexed/structures/max-heap.d.ts +52 -0
  233. package/dist/src/indexed/structures/max-heap.d.ts.map +1 -0
  234. package/dist/src/indexed/structures/max-heap.js +66 -0
  235. package/dist/src/indexed/structures/max-heap.js.map +1 -0
  236. package/dist/src/indexed/structures/min-heap.d.ts +30 -1
  237. package/dist/src/indexed/structures/min-heap.d.ts.map +1 -1
  238. package/dist/src/indexed/structures/min-heap.js +46 -6
  239. package/dist/src/indexed/structures/min-heap.js.map +1 -1
  240. package/dist/src/indexed/structures/ring-queue.d.ts +41 -0
  241. package/dist/src/indexed/structures/ring-queue.d.ts.map +1 -0
  242. package/dist/src/indexed/structures/ring-queue.js +64 -0
  243. package/dist/src/indexed/structures/ring-queue.js.map +1 -0
  244. package/dist/src/indexed/sync.d.ts +50 -0
  245. package/dist/src/indexed/sync.d.ts.map +1 -0
  246. package/dist/src/indexed/sync.js +175 -0
  247. package/dist/src/indexed/sync.js.map +1 -0
  248. package/dist/src/indexed/terahac.d.ts +58 -0
  249. package/dist/src/indexed/terahac.d.ts.map +1 -0
  250. package/dist/src/indexed/terahac.js +244 -0
  251. package/dist/src/indexed/terahac.js.map +1 -0
  252. package/dist/src/indexed/to-snapshot.d.ts +45 -2
  253. package/dist/src/indexed/to-snapshot.d.ts.map +1 -1
  254. package/dist/src/indexed/to-snapshot.js +140 -6
  255. package/dist/src/indexed/to-snapshot.js.map +1 -1
  256. package/dist/src/link-prediction/common-neighbors-legacy.d.ts +71 -0
  257. package/dist/src/link-prediction/common-neighbors-legacy.d.ts.map +1 -0
  258. package/dist/src/link-prediction/common-neighbors-legacy.js +172 -0
  259. package/dist/src/link-prediction/common-neighbors-legacy.js.map +1 -0
  260. package/dist/src/link-prediction/common-neighbors.d.ts +11 -19
  261. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  262. package/dist/src/link-prediction/common-neighbors.js +56 -123
  263. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  264. package/dist/src/optimized/direction-optimized-bfs.d.ts +6 -0
  265. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
  266. package/dist/src/optimized/direction-optimized-bfs.js +6 -0
  267. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
  268. package/dist/src/research/grsbm-legacy.d.ts +82 -0
  269. package/dist/src/research/grsbm-legacy.d.ts.map +1 -0
  270. package/dist/src/research/grsbm-legacy.js +416 -0
  271. package/dist/src/research/grsbm-legacy.js.map +1 -0
  272. package/dist/src/research/grsbm.d.ts +9 -66
  273. package/dist/src/research/grsbm.d.ts.map +1 -1
  274. package/dist/src/research/grsbm.js +67 -391
  275. package/dist/src/research/grsbm.js.map +1 -1
  276. package/dist/src/research/sync-legacy.d.ts +48 -0
  277. package/dist/src/research/sync-legacy.d.ts.map +1 -0
  278. package/dist/src/research/sync-legacy.js +337 -0
  279. package/dist/src/research/sync-legacy.js.map +1 -0
  280. package/dist/src/research/sync.d.ts +10 -32
  281. package/dist/src/research/sync.d.ts.map +1 -1
  282. package/dist/src/research/sync.js +28 -316
  283. package/dist/src/research/sync.js.map +1 -1
  284. package/package.json +4 -3
  285. package/src/algorithms/centrality/betweenness.ts +51 -1
  286. package/src/algorithms/centrality/closeness.ts +112 -12
  287. package/src/algorithms/centrality/degree.ts +19 -0
  288. package/src/algorithms/centrality/eigenvector.ts +34 -0
  289. package/src/algorithms/centrality/hits.ts +24 -0
  290. package/src/algorithms/centrality/katz.ts +32 -0
  291. package/src/algorithms/centrality/pagerank.ts +53 -13
  292. package/src/algorithms/community/girvan-newman-legacy.ts +414 -0
  293. package/src/algorithms/community/girvan-newman.ts +36 -386
  294. package/src/algorithms/community/label-propagation.ts +24 -133
  295. package/src/algorithms/components/connected.ts +28 -95
  296. package/src/algorithms/mst/kruskal.ts +65 -4
  297. package/src/algorithms/shortest-path/bellman-ford.ts +49 -0
  298. package/src/algorithms/shortest-path/dijkstra.ts +29 -0
  299. package/src/algorithms/shortest-path/floyd-warshall.ts +106 -136
  300. package/src/algorithms/traversal/bfs-unified.ts +52 -366
  301. package/src/algorithms/traversal/bfs-variants.ts +5 -28
  302. package/src/algorithms/traversal/bfs.ts +0 -4
  303. package/src/algorithms/traversal/dfs.ts +39 -182
  304. package/src/clustering/hierarchical-legacy.ts +551 -0
  305. package/src/clustering/hierarchical.ts +64 -520
  306. package/src/clustering/k-core-legacy.ts +229 -0
  307. package/src/clustering/k-core.ts +38 -209
  308. package/src/clustering/mcl-legacy.ts +498 -0
  309. package/src/clustering/mcl.ts +46 -479
  310. package/src/index.ts +90 -3
  311. package/src/indexed/accelerator.ts +502 -16
  312. package/src/indexed/all-pairs.ts +342 -0
  313. package/src/indexed/bellman-ford.ts +72 -0
  314. package/src/indexed/betweenness.ts +258 -0
  315. package/src/indexed/bfs.ts +189 -3
  316. package/src/indexed/bipartite.ts +77 -0
  317. package/src/indexed/closeness.ts +160 -0
  318. package/src/indexed/common-neighbors.ts +31 -14
  319. package/src/indexed/degree.ts +55 -0
  320. package/src/indexed/delta-pagerank.ts +513 -0
  321. package/src/indexed/dfs.ts +189 -0
  322. package/src/indexed/eigenvector.ts +202 -0
  323. package/src/indexed/facade.ts +229 -0
  324. package/src/indexed/flow.ts +500 -0
  325. package/src/indexed/girvan-newman.ts +120 -0
  326. package/src/indexed/grsbm.ts +390 -0
  327. package/src/indexed/hierarchical.ts +295 -0
  328. package/src/indexed/index.ts +94 -3
  329. package/src/indexed/isomorphism.ts +305 -0
  330. package/src/indexed/label-propagation.ts +582 -0
  331. package/src/indexed/leiden.ts +436 -0
  332. package/src/indexed/link-prediction.ts +399 -0
  333. package/src/indexed/markov.ts +324 -0
  334. package/src/indexed/matching.ts +192 -0
  335. package/src/indexed/min-cut.ts +201 -0
  336. package/src/indexed/modularity.ts +81 -0
  337. package/src/indexed/mst.ts +79 -1
  338. package/src/indexed/pagerank.ts +128 -15
  339. package/src/indexed/point-to-point.ts +220 -0
  340. package/src/indexed/scc.ts +132 -0
  341. package/src/indexed/spectral.ts +550 -0
  342. package/src/indexed/structures/bit-set.ts +65 -0
  343. package/src/indexed/structures/max-heap.ts +74 -0
  344. package/src/indexed/structures/min-heap.ts +51 -6
  345. package/src/indexed/structures/ring-queue.ts +70 -0
  346. package/src/indexed/sync.ts +229 -0
  347. package/src/indexed/terahac.ts +290 -0
  348. package/src/indexed/to-snapshot.ts +156 -6
  349. package/src/link-prediction/common-neighbors-legacy.ts +252 -0
  350. package/src/link-prediction/common-neighbors.ts +65 -168
  351. package/src/optimized/direction-optimized-bfs.ts +6 -1
  352. package/src/research/grsbm-legacy.ts +586 -0
  353. package/src/research/grsbm.ts +76 -555
  354. package/src/research/sync-legacy.ts +456 -0
  355. package/src/research/sync.ts +30 -431
@@ -0,0 +1,324 @@
1
+ import type { AdjacencyView, NumericVector } from "@graphty/graph-format";
2
+
3
+ import { type LabelResult, withGroups } from "./components.js";
4
+
5
+ /** Options of the index-based Markov clustering, with the legacy `markovClustering` defaults. @public */
6
+ export interface MarkovOptions {
7
+ /** Matrix power of each expansion step, an integer of at least 1; default 2. */
8
+ readonly expansion?: number | undefined;
9
+ /** Element-wise power of each inflation step, above 0; default 2. */
10
+ readonly inflation?: number | undefined;
11
+ /** Cap on expansion-inflation rounds; default 100. */
12
+ readonly maxIterations?: number | undefined;
13
+ /** Stop when no matrix entry moved by more than this in a round; default 1e-6. */
14
+ readonly tolerance?: number | undefined;
15
+ /** Entries below this are dropped after each inflation; default 1e-5. */
16
+ readonly pruningThreshold?: number | undefined;
17
+ /** Give every node a self-loop of weight 1 before the first round; default true. */
18
+ readonly selfLoops?: boolean | undefined;
19
+ /** Per-arc weight override, arcCount long -- the facade passes `expandEdges(s, shadow.data)`. */
20
+ readonly weights?: NumericVector | undefined;
21
+ }
22
+
23
+ /** Result of the index-based Markov clustering. @public */
24
+ export interface MarkovResult extends LabelResult {
25
+ /** Nodes whose own flow survived (a positive diagonal entry), in index order. */
26
+ readonly attractors: Uint32Array;
27
+ /** Expansion-inflation rounds run. */
28
+ readonly iterations: number;
29
+ /** True when the last round moved no entry by more than `tolerance`. */
30
+ readonly converged: boolean;
31
+ }
32
+
33
+ /** A column-stochastic sparse matrix: column j holds rows `row[ptr[j]] .. row[ptr[j+1] - 1]`, ascending. */
34
+ interface Csc {
35
+ readonly ptr: Uint32Array;
36
+ readonly row: Uint32Array;
37
+ readonly val: Float64Array;
38
+ }
39
+
40
+ /**
41
+ * Divide every column by its sum, summed in ascending row order as the legacy dense loop sums it.
42
+ * @param m - The matrix, changed in place
43
+ */
44
+ function normaliseColumns(m: Csc): void {
45
+ for (let j = 0; j + 1 < m.ptr.length; j++) {
46
+ let sum = 0;
47
+ for (let p = m.ptr[j]; p < m.ptr[j + 1]; p++) {
48
+ sum += m.val[p];
49
+ }
50
+ if (sum > 0) {
51
+ for (let p = m.ptr[j]; p < m.ptr[j + 1]; p++) {
52
+ m.val[p] /= sum;
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * The product `a * b`. For every entry the products are added in ascending k, the order the legacy
60
+ * dense triple loop adds them in; the zero products it adds as well leave a sum unchanged, so the
61
+ * result is bit-identical to the dense one.
62
+ * @param a - Left factor
63
+ * @param b - Right factor
64
+ * @param acc - n-long scratch, all zero on entry and on exit
65
+ * @param mark - n-long scratch, all zero on entry and on exit
66
+ * @returns The product
67
+ */
68
+ function multiply(a: Csc, b: Csc, acc: Float64Array, mark: Uint8Array): Csc {
69
+ const n = acc.length;
70
+ const ptr = new Uint32Array(n + 1);
71
+ const rows: number[] = [];
72
+ const vals: number[] = [];
73
+ const touched: number[] = [];
74
+ for (let j = 0; j < n; j++) {
75
+ touched.length = 0;
76
+ for (let q = b.ptr[j]; q < b.ptr[j + 1]; q++) {
77
+ const k = b.row[q];
78
+ const bkj = b.val[q];
79
+ for (let p = a.ptr[k]; p < a.ptr[k + 1]; p++) {
80
+ const i = a.row[p];
81
+ if (mark[i] === 0) {
82
+ mark[i] = 1;
83
+ touched.push(i);
84
+ }
85
+ acc[i] += a.val[p] * bkj;
86
+ }
87
+ }
88
+ touched.sort((x, y) => x - y);
89
+ for (const i of touched) {
90
+ rows.push(i);
91
+ vals.push(acc[i]);
92
+ acc[i] = 0;
93
+ mark[i] = 0;
94
+ }
95
+ ptr[j + 1] = rows.length;
96
+ }
97
+ return { ptr, row: Uint32Array.from(rows), val: Float64Array.from(vals) };
98
+ }
99
+
100
+ /**
101
+ * Raise every entry to `inflation`, renormalise, drop the entries below `threshold` and renormalise
102
+ * again: the legacy inflate and prune steps.
103
+ * @param m - The expanded matrix
104
+ * @param inflation - The element-wise power
105
+ * @param threshold - The pruning threshold
106
+ * @returns The new matrix
107
+ */
108
+ function inflateAndPrune(m: Csc, inflation: number, threshold: number): Csc {
109
+ const powered: Csc = { ptr: m.ptr, row: m.row, val: m.val.map((v) => Math.pow(v, inflation)) };
110
+ normaliseColumns(powered);
111
+ const n = m.ptr.length - 1;
112
+ const ptr = new Uint32Array(n + 1);
113
+ const rows: number[] = [];
114
+ const vals: number[] = [];
115
+ for (let j = 0; j < n; j++) {
116
+ for (let p = powered.ptr[j]; p < powered.ptr[j + 1]; p++) {
117
+ if (powered.val[p] >= threshold) {
118
+ rows.push(powered.row[p]);
119
+ vals.push(powered.val[p]);
120
+ }
121
+ }
122
+ ptr[j + 1] = rows.length;
123
+ }
124
+ const out = { ptr, row: Uint32Array.from(rows), val: Float64Array.from(vals) };
125
+ normaliseColumns(out);
126
+ return out;
127
+ }
128
+
129
+ /**
130
+ * Whether no entry differs by more than `tolerance` between two matrices, an absent entry being 0.
131
+ * @param a - The previous matrix
132
+ * @param b - The new matrix
133
+ * @param tolerance - The tolerance
134
+ * @returns True when every entry is within tolerance
135
+ */
136
+ function withinTolerance(a: Csc, b: Csc, tolerance: number): boolean {
137
+ for (let j = 0; j + 1 < a.ptr.length; j++) {
138
+ let p = a.ptr[j];
139
+ let q = b.ptr[j];
140
+ const pe = a.ptr[j + 1];
141
+ const qe = b.ptr[j + 1];
142
+ while (p < pe || q < qe) {
143
+ let diff: number;
144
+ if (q >= qe || (p < pe && a.row[p] < b.row[q])) {
145
+ diff = a.val[p++];
146
+ } else if (p >= pe || b.row[q] < a.row[p]) {
147
+ diff = b.val[q++];
148
+ } else {
149
+ diff = a.val[p++] - b.val[q++];
150
+ }
151
+ if (Math.abs(diff) > tolerance) {
152
+ return false;
153
+ }
154
+ }
155
+ }
156
+ return true;
157
+ }
158
+
159
+ /**
160
+ * The starting matrix: entry (i, j) is the weight of the arc i -> j (parallel arcs summed), the
161
+ * diagonal set to 1 when `selfLoops`, every column normalised.
162
+ * @param s - The adjacency
163
+ * @param weights - Per-arc weights, or null for all ones
164
+ * @param selfLoops - Whether to set the diagonal to 1
165
+ * @returns The column-stochastic matrix
166
+ */
167
+ function transitionMatrix(s: AdjacencyView, weights: NumericVector | null, selfLoops: boolean): Csc {
168
+ const n = s.nodeCount;
169
+ // Column j of the matrix is the set of arcs INTO j: a counting sort of the arcs by target, which
170
+ // keeps each column's rows ascending because the rows are visited in order.
171
+ const ptr = new Uint32Array(n + 1);
172
+ for (let a = 0; a < s.arcCount; a++) {
173
+ ptr[s.colIdx[a] + 1]++;
174
+ }
175
+ if (selfLoops) {
176
+ for (let j = 0; j < n; j++) {
177
+ ptr[j + 1]++;
178
+ }
179
+ }
180
+ for (let j = 0; j < n; j++) {
181
+ ptr[j + 1] += ptr[j];
182
+ }
183
+ const fill = ptr.slice(0, n);
184
+ const row = new Uint32Array(ptr[n]);
185
+ const val = new Float64Array(ptr[n]);
186
+ const put = (i: number, j: number, w: number, replace: boolean): void => {
187
+ const last = fill[j] - 1;
188
+ if (last >= ptr[j] && row[last] === i) {
189
+ val[last] = replace ? w : val[last] + w;
190
+ } else {
191
+ row[fill[j]] = i;
192
+ val[fill[j]++] = w;
193
+ }
194
+ };
195
+ for (let i = 0; i < n; i++) {
196
+ // The diagonal goes in its place in row order: the arcs i -> j with j < i were placed while
197
+ // visiting earlier rows, so for column i this is where row i belongs.
198
+ for (let a = s.rowPtr[i]; a < s.rowPtr[i + 1]; a++) {
199
+ const j = s.colIdx[a];
200
+ const w = weights === null ? 1 : weights[a];
201
+ if (!(w >= 0) || w === Infinity) {
202
+ throw new RangeError(`arc ${a} has weight ${w}; Markov clustering needs finite, non-negative weights`);
203
+ }
204
+ put(i, j, w, false);
205
+ }
206
+ if (selfLoops) {
207
+ put(i, i, 1, true);
208
+ }
209
+ }
210
+ // Compact: merged parallel arcs and a self-loop folded into the diagonal left gaps.
211
+ const outPtr = new Uint32Array(n + 1);
212
+ const rows: number[] = [];
213
+ const vals: number[] = [];
214
+ for (let j = 0; j < n; j++) {
215
+ for (let p = ptr[j]; p < fill[j]; p++) {
216
+ rows.push(row[p]);
217
+ vals.push(val[p]);
218
+ }
219
+ outPtr[j + 1] = rows.length;
220
+ }
221
+ const m = { ptr: outPtr, row: Uint32Array.from(rows), val: Float64Array.from(vals) };
222
+ normaliseColumns(m);
223
+ return m;
224
+ }
225
+
226
+ /**
227
+ * Markov clustering (MCL; van Dongen, 2000), the index-based port of the legacy `markovClustering`.
228
+ *
229
+ * The flow matrix starts as the column-normalised adjacency: entry (i, j) is the weight of the arc
230
+ * i -> j, so column j spreads j's flow over the nodes with an arc into it (on an undirected
231
+ * snapshot, its neighbours). With `selfLoops` every diagonal entry is first set to 1, replacing a
232
+ * self-loop's weight. Each round raises the matrix to the `expansion` power, raises every entry to
233
+ * the `inflation` power, renormalises the columns, drops the entries below `pruningThreshold` and
234
+ * renormalises again, until no entry moves by more than `tolerance` or `maxIterations` rounds ran.
235
+ * Node j then joins the attractor with the largest entry in its column, the lowest index on a tie;
236
+ * a node whose column emptied is a community of its own.
237
+ *
238
+ * The matrix is kept sparse, and every sum is taken in the order the legacy dense loops take it, so
239
+ * the rounds are bit-identical to the legacy function's and so are the communities, their order and
240
+ * the attractors. Communities are labelled in the legacy function's order: the flow clusters by
241
+ * their lowest member, then the nodes whose column emptied. Two differences: parallel arcs are
242
+ * summed (a legacy graph has none), and `iterations` counts the rounds run, where the legacy
243
+ * function reports one more than `maxIterations` when it stops at the cap. A negative, NaN or
244
+ * infinite weight throws.
245
+ * @param s - Any snapshot or adjacency view
246
+ * @param options - MCL parameters and the weight override
247
+ * @returns The partition, the attractors, the rounds run and whether they converged
248
+ * @public
249
+ */
250
+ export function markovClustering(s: AdjacencyView, options: MarkovOptions = {}): MarkovResult {
251
+ const expansion = options.expansion ?? 2;
252
+ const inflation = options.inflation ?? 2;
253
+ const maxIterations = options.maxIterations ?? 100;
254
+ const tolerance = options.tolerance ?? 1e-6;
255
+ const pruningThreshold = options.pruningThreshold ?? 1e-5;
256
+ if (!Number.isInteger(expansion) || expansion < 1) {
257
+ throw new RangeError(`expansion must be an integer of at least 1, got ${expansion}`);
258
+ }
259
+ if (!(inflation > 0) || inflation === Infinity) {
260
+ throw new RangeError(`inflation must be finite and above 0, got ${inflation}`);
261
+ }
262
+ if (!Number.isInteger(maxIterations) || maxIterations < 0) {
263
+ throw new RangeError(`maxIterations must be a non-negative integer, got ${maxIterations}`);
264
+ }
265
+ if (!(tolerance >= 0) || !(pruningThreshold >= 0)) {
266
+ throw new RangeError("tolerance and pruningThreshold must be non-negative");
267
+ }
268
+ const weights = options.weights ?? s.weights;
269
+ if (weights !== null && weights.length !== s.arcCount) {
270
+ throw new RangeError(`weights has ${weights.length} entries; the snapshot has ${s.arcCount} arcs`);
271
+ }
272
+ const n = s.nodeCount;
273
+ let m = transitionMatrix(s, weights, options.selfLoops ?? true);
274
+ const acc = new Float64Array(n);
275
+ const mark = new Uint8Array(n);
276
+ let converged = n === 0;
277
+ let iterations = 0;
278
+ while (!converged && iterations < maxIterations) {
279
+ let expanded = m;
280
+ for (let t = 1; t < expansion; t++) {
281
+ expanded = multiply(expanded, m, acc, mark);
282
+ }
283
+ const next = inflateAndPrune(expanded, inflation, pruningThreshold);
284
+ iterations++;
285
+ converged = withinTolerance(m, next, tolerance);
286
+ m = next;
287
+ }
288
+
289
+ const attractors: number[] = [];
290
+ // Column j's attractor: the row of its largest positive entry, the first on a tie.
291
+ const attractorOf = new Int32Array(n).fill(-1);
292
+ for (let j = 0; j < n; j++) {
293
+ let best = 0;
294
+ for (let p = m.ptr[j]; p < m.ptr[j + 1]; p++) {
295
+ if (m.row[p] === j && m.val[p] > 0) {
296
+ attractors.push(j);
297
+ }
298
+ if (m.val[p] > best) {
299
+ best = m.val[p];
300
+ attractorOf[j] = m.row[p];
301
+ }
302
+ }
303
+ }
304
+ const labels = new Uint32Array(n);
305
+ const labelOfAttractor = new Map<number, number>();
306
+ for (let j = 0; j < n; j++) {
307
+ const a = attractorOf[j];
308
+ if (a >= 0) {
309
+ let label = labelOfAttractor.get(a);
310
+ if (label === undefined) {
311
+ label = labelOfAttractor.size;
312
+ labelOfAttractor.set(a, label);
313
+ }
314
+ labels[j] = label;
315
+ }
316
+ }
317
+ let count = labelOfAttractor.size;
318
+ for (let j = 0; j < n; j++) {
319
+ if (attractorOf[j] < 0) {
320
+ labels[j] = count++;
321
+ }
322
+ }
323
+ return { ...withGroups(labels, count), attractors: Uint32Array.from(attractors), iterations, converged };
324
+ }
@@ -0,0 +1,192 @@
1
+ import {
2
+ type AdjacencyView,
3
+ type GraphSnapshot,
4
+ INVALID_INDEX,
5
+ makeMask,
6
+ maskTest,
7
+ type NodeMask,
8
+ type U32,
9
+ } from "@graphty/graph-format";
10
+
11
+ import { isBipartite } from "./bipartite.js";
12
+
13
+ /** Options of {@link maximumBipartiteMatching} and {@link greedyBipartiteMatching}. @public */
14
+ export interface BipartiteMatchingOptions {
15
+ /**
16
+ * The left side. Given together with `right`, the two sides are used as they are and the graph
17
+ * is not tested for bipartiteness; a node on neither side is never matched. When either is
18
+ * missing both come from `isBipartite`: its first side is the left one.
19
+ */
20
+ readonly left?: NodeMask | undefined;
21
+ /** The right side; see `left`. */
22
+ readonly right?: NodeMask | undefined;
23
+ /**
24
+ * Which arcs of a directed snapshot join a left node to a right one. "both" (default) ignores
25
+ * direction, as a matching does. "out" follows out-arcs of left nodes only, as the legacy
26
+ * functions do, and is also passed to `isBipartite` when the sides are inferred. Ignored on an
27
+ * undirected snapshot.
28
+ */
29
+ readonly arcs?: "both" | "out" | undefined;
30
+ }
31
+
32
+ /** Result of the index-based bipartite matchings. @public */
33
+ export interface BipartiteMatchingResult {
34
+ /**
35
+ * The right partner of every matched left node; INVALID_INDEX for an unmatched left node and
36
+ * for every node that is not on the left side.
37
+ */
38
+ readonly matching: U32;
39
+ /** The number of matched pairs. */
40
+ readonly size: number;
41
+ }
42
+
43
+ interface Sides {
44
+ readonly left: NodeMask;
45
+ readonly right: NodeMask;
46
+ readonly views: readonly AdjacencyView[];
47
+ }
48
+
49
+ function resolveSides(s: GraphSnapshot, options: BipartiteMatchingOptions): Sides {
50
+ const views: AdjacencyView[] = s.directed && options.arcs !== "out" ? [s, s.reverse()] : [s];
51
+ if (options.left !== undefined && options.right !== undefined) {
52
+ return { left: options.left, right: options.right, views };
53
+ }
54
+ const { sides } = isBipartite(s, { arcs: options.arcs });
55
+ if (sides === null) {
56
+ throw new Error("Graph is not bipartite");
57
+ }
58
+ const left = makeMask(s.nodeCount);
59
+ for (let w = 0; w < left.length; w++) {
60
+ left[w] = ~sides[w];
61
+ }
62
+ // Clear the padding bits past nodeCount so the mask stays well formed.
63
+ const tail = s.nodeCount % 32;
64
+ if (tail !== 0) {
65
+ left[left.length - 1] &= (1 << tail) - 1;
66
+ }
67
+ return { left, right: sides, views };
68
+ }
69
+
70
+ /**
71
+ * Maximum matching of a bipartite graph by augmenting paths (Kuhn's algorithm, O(V E)): every left
72
+ * node in index order searches depth-first, iteratively, for an augmenting path. Parallel arcs and
73
+ * arc direction (by default) make no difference to the matching size.
74
+ * @param s - The snapshot
75
+ * @param options - The two sides, and which arcs to follow on a directed snapshot
76
+ * @returns The partner of every matched left node and the number of pairs
77
+ * @throws Error when the sides are inferred and the graph is not bipartite
78
+ * @public
79
+ */
80
+ export function maximumBipartiteMatching(
81
+ s: GraphSnapshot,
82
+ options: BipartiteMatchingOptions = {},
83
+ ): BipartiteMatchingResult {
84
+ // ponytail: Kuhn is O(V E) like the legacy function; Hopcroft-Karp (O(sqrt(V) E)) if a caller
85
+ // needs large graphs with long augmenting paths.
86
+ const { left, right, views } = resolveSides(s, options);
87
+ const { nodeCount } = s;
88
+ const matching = new Uint32Array(nodeCount).fill(INVALID_INDEX);
89
+ const mateOfRight = new Uint32Array(nodeCount).fill(INVALID_INDEX);
90
+ const seen = new Uint32Array(nodeCount);
91
+ // The DFS stack of left nodes; view and arc cursors per stacked node; the right node taken to
92
+ // descend from each stacked node.
93
+ const stack = new Uint32Array(nodeCount);
94
+ const viewAt = new Uint8Array(nodeCount);
95
+ const arcAt = new Uint32Array(nodeCount);
96
+ const via = new Uint32Array(nodeCount);
97
+ let size = 0;
98
+ for (let root = 0; root < nodeCount; root++) {
99
+ if (!maskTest(left, root)) {
100
+ continue;
101
+ }
102
+ const stamp = root + 1;
103
+ let top = 0;
104
+ stack[top++] = root;
105
+ viewAt[root] = 0;
106
+ arcAt[root] = 0;
107
+ let free = INVALID_INDEX;
108
+ search: while (top > 0) {
109
+ const x = stack[top - 1];
110
+ for (; viewAt[x] < views.length; viewAt[x]++) {
111
+ const { rowPtr, colIdx } = views[viewAt[x]];
112
+ const end = rowPtr[x + 1];
113
+ // A cursor of 0 means "not started in this view".
114
+ if (arcAt[x] < rowPtr[x]) {
115
+ arcAt[x] = rowPtr[x];
116
+ }
117
+ while (arcAt[x] < end) {
118
+ const v = colIdx[arcAt[x]++];
119
+ if (seen[v] === stamp || !maskTest(right, v)) {
120
+ continue;
121
+ }
122
+ seen[v] = stamp;
123
+ const next = mateOfRight[v];
124
+ if (next === INVALID_INDEX) {
125
+ free = v;
126
+ break search;
127
+ }
128
+ via[x] = v;
129
+ stack[top++] = next;
130
+ viewAt[next] = 0;
131
+ arcAt[next] = 0;
132
+ continue search;
133
+ }
134
+ arcAt[x] = 0;
135
+ }
136
+ top--;
137
+ }
138
+ if (free === INVALID_INDEX) {
139
+ continue;
140
+ }
141
+ // Flip the path: the top node takes the free right node, every node below takes the right
142
+ // node it descended through.
143
+ for (let k = top - 1; k >= 0; k--) {
144
+ const x = stack[k];
145
+ const v = k === top - 1 ? free : via[x];
146
+ matching[x] = v;
147
+ mateOfRight[v] = x;
148
+ }
149
+ size++;
150
+ }
151
+ return { matching, size };
152
+ }
153
+
154
+ /**
155
+ * Greedy maximal bipartite matching: every left node in index order takes its first unmatched
156
+ * right neighbour in row order (the lowest index; out-arcs before in-arcs). The size depends on
157
+ * those orders, so it can differ from the legacy function, which visits left nodes in its
158
+ * colouring BFS order and neighbours in insertion order; it is at least half the maximum.
159
+ * @param s - The snapshot
160
+ * @param options - The two sides, and which arcs to follow on a directed snapshot
161
+ * @returns The partner of every matched left node and the number of pairs
162
+ * @throws Error when the sides are inferred and the graph is not bipartite
163
+ * @public
164
+ */
165
+ export function greedyBipartiteMatching(
166
+ s: GraphSnapshot,
167
+ options: BipartiteMatchingOptions = {},
168
+ ): BipartiteMatchingResult {
169
+ const { left, right, views } = resolveSides(s, options);
170
+ const { nodeCount } = s;
171
+ const matching = new Uint32Array(nodeCount).fill(INVALID_INDEX);
172
+ const taken = new Uint8Array(nodeCount);
173
+ let size = 0;
174
+ for (let u = 0; u < nodeCount; u++) {
175
+ if (!maskTest(left, u)) {
176
+ continue;
177
+ }
178
+ pick: for (const { rowPtr, colIdx } of views) {
179
+ const end = rowPtr[u + 1];
180
+ for (let a = rowPtr[u]; a < end; a++) {
181
+ const v = colIdx[a];
182
+ if (taken[v] === 0 && maskTest(right, v)) {
183
+ taken[v] = 1;
184
+ matching[u] = v;
185
+ size++;
186
+ break pick;
187
+ }
188
+ }
189
+ }
190
+ }
191
+ return { matching, size };
192
+ }