@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,436 @@
1
+ import { type F64, type GraphSnapshot, renumberPartition, type U32 } from "@graphty/graph-format";
2
+
3
+ import { type LabelResult, withGroups } from "./components.js";
4
+ import { exactEdgeWeights, mulberry32 } from "./label-propagation.js";
5
+ import { modularity } from "./modularity.js";
6
+
7
+ /**
8
+ * Options of the index-based Leiden. The names and defaults are the legacy `leiden`'s, but
9
+ * `maxIterations` counts something else: see below.
10
+ * @public
11
+ */
12
+ export interface LeidenOptions {
13
+ /** Resolution gamma: above 1 favours smaller communities; default 1. */
14
+ readonly resolution?: number | undefined;
15
+ /** Seed of the visit orders; default 42. */
16
+ readonly randomSeed?: number | undefined;
17
+ /**
18
+ * Cap on whole passes over the original graph, each running as many levels as it needs; default
19
+ * 100. The legacy `leiden` caps levels instead, so its `maxIterations: 1` is one level where
20
+ * this is one full pass. The result's `iterations` counts levels.
21
+ */
22
+ readonly maxIterations?: number | undefined;
23
+ /** Stop once a pass improves modularity by no more than this; default 1e-7. */
24
+ readonly threshold?: number | undefined;
25
+ }
26
+
27
+ /** Result of the index-based Leiden: a partition plus the modularity it reaches. @public */
28
+ export interface LeidenResult extends LabelResult {
29
+ /** Modularity of the returned partition, at the requested resolution. */
30
+ readonly modularity: number;
31
+ /** Levels processed, summed over every pass. */
32
+ readonly iterations: number;
33
+ }
34
+
35
+ /** The randomness of refinement: 0.01 as in the paper and leidenalg. */
36
+ const THETA = 0.01;
37
+
38
+ /**
39
+ * Passes in a row that may fail to improve modularity by more than the threshold before the run
40
+ * stops. A failed pass is discarded, but the next one draws fresh visit orders and can still find a
41
+ * move the last one missed: on the shared random fixtures, stopping at the first failure leaves the
42
+ * mean modularity over 40 seeds below the legacy function's, and three restores it.
43
+ */
44
+ const PATIENCE = 3;
45
+
46
+ /** One level of the hierarchy: a snapshot and the exact weight of each of its logical edges. */
47
+ interface Level {
48
+ readonly s: GraphSnapshot;
49
+ /** Exact weight per logical edge. */
50
+ readonly edgeW: F64;
51
+ /** Exact weight per arc. */
52
+ readonly arcW: F64;
53
+ /** Weighted degree per node, a self-loop counted twice (graph-format's `weightedDegree()` rule). */
54
+ readonly deg: F64;
55
+ }
56
+
57
+ /**
58
+ * Per-arc weights and weighted degrees of a level.
59
+ * @param s - An undirected snapshot
60
+ * @param edgeW - Exact weight per logical edge
61
+ * @returns The level
62
+ */
63
+ function levelOf(s: GraphSnapshot, edgeW: F64): Level {
64
+ const { nodeCount: n, rowPtr, colIdx, arcToEdge } = s;
65
+ const arcW = new Float64Array(s.arcCount);
66
+ const deg = new Float64Array(n);
67
+ for (let u = 0; u < n; u++) {
68
+ for (let a = rowPtr[u], end = rowPtr[u + 1]; a < end; a++) {
69
+ const w = edgeW[arcToEdge[a]];
70
+ arcW[a] = w;
71
+ // An undirected self-loop is one arc; it adds to the degree twice.
72
+ deg[u] += colIdx[a] === u ? 2 * w : w;
73
+ }
74
+ }
75
+ return { s, edgeW, arcW, deg };
76
+ }
77
+
78
+ /**
79
+ * Shuffle `0..n-1` with the generator.
80
+ * @param n - Length
81
+ * @param rand - Generator
82
+ * @returns The permutation
83
+ */
84
+ function shuffled(n: number, rand: () => number): U32 {
85
+ const order = new Uint32Array(n);
86
+ for (let i = 0; i < n; i++) {
87
+ order[i] = i;
88
+ }
89
+ for (let i = n - 1; i >= 1; i--) {
90
+ const j = Math.floor(rand() * (i + 1));
91
+ const t = order[i];
92
+ order[i] = order[j];
93
+ order[j] = t;
94
+ }
95
+ return order;
96
+ }
97
+
98
+ /**
99
+ * Leiden's fast local moving: nodes wait in a queue started as a seeded shuffle; a visited node
100
+ * moves to the neighbouring community (or an empty one) with the largest modularity gain, and when
101
+ * it moves, its neighbours outside its new community are queued again. Ends when the queue empties.
102
+ * @param level - The level
103
+ * @param comm - Community of every node, below the node count; moved in place
104
+ * @param rand - Generator
105
+ * @param resolution - Resolution gamma
106
+ * @param m2 - Twice the total edge weight
107
+ */
108
+ function moveNodesFast(level: Level, comm: U32, rand: () => number, resolution: number, m2: number): void {
109
+ const { s, arcW, deg } = level;
110
+ const { nodeCount: n, rowPtr, colIdx } = s;
111
+ const tot = new Float64Array(n);
112
+ const size = new Uint32Array(n);
113
+ for (let u = 0; u < n; u++) {
114
+ tot[comm[u]] += deg[u];
115
+ size[comm[u]]++;
116
+ }
117
+ const empty: number[] = [];
118
+ for (let c = n - 1; c >= 0; c--) {
119
+ if (size[c] === 0) {
120
+ empty.push(c);
121
+ }
122
+ }
123
+ const acc = new Float64Array(n);
124
+ const stamp = new Int32Array(n).fill(-1);
125
+ const touched = new Uint32Array(n);
126
+ const capacity = n + 1;
127
+ const queue = new Uint32Array(capacity);
128
+ queue.set(shuffled(n, rand));
129
+ const queued = new Uint8Array(n).fill(1);
130
+ let head = 0;
131
+ let tail = n;
132
+ let visit = 0;
133
+ while (head !== tail) {
134
+ const u = queue[head];
135
+ head = head + 1 === capacity ? 0 : head + 1;
136
+ queued[u] = 0;
137
+ visit++;
138
+ const cu = comm[u];
139
+ const ku = deg[u];
140
+ tot[cu] -= ku;
141
+ size[cu]--;
142
+ let cnt = 0;
143
+ for (let a = rowPtr[u], end = rowPtr[u + 1]; a < end; a++) {
144
+ const v = colIdx[a];
145
+ if (v === u) {
146
+ continue;
147
+ }
148
+ const c = comm[v];
149
+ if (stamp[c] !== visit) {
150
+ stamp[c] = visit;
151
+ acc[c] = 0;
152
+ touched[cnt++] = c;
153
+ }
154
+ acc[c] += arcW[a];
155
+ }
156
+ let best = cu;
157
+ let bestGain = (stamp[cu] === visit ? acc[cu] : 0) - (resolution * ku * tot[cu]) / m2;
158
+ for (let i = 0; i < cnt; i++) {
159
+ const c = touched[i];
160
+ const gain = acc[c] - (resolution * ku * tot[c]) / m2;
161
+ if (c !== cu && gain > bestGain) {
162
+ bestGain = gain;
163
+ best = c;
164
+ }
165
+ }
166
+ // An empty community gains 0; when u's own is empty now, staying already is that choice.
167
+ if (size[cu] > 0 && bestGain < 0) {
168
+ best = empty[empty.length - 1];
169
+ }
170
+ if (best !== cu && size[best] === 0) {
171
+ empty.pop();
172
+ }
173
+ tot[best] += ku;
174
+ size[best]++;
175
+ if (best === cu) {
176
+ continue;
177
+ }
178
+ comm[u] = best;
179
+ if (size[cu] === 0) {
180
+ empty.push(cu);
181
+ }
182
+ for (let a = rowPtr[u], end = rowPtr[u + 1]; a < end; a++) {
183
+ const v = colIdx[a];
184
+ if (queued[v] === 0 && comm[v] !== best) {
185
+ queued[v] = 1;
186
+ queue[tail] = v;
187
+ tail = tail + 1 === capacity ? 0 : tail + 1;
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Leiden's refinement: inside every community, nodes start alone and, visited in a seeded order,
195
+ * a node still alone and well connected to its community merges into the well-connected
196
+ * sub-community of that community, drawn at random weighted by its modularity gain. Every
197
+ * sub-community is connected, which is the guarantee Leiden adds over Louvain.
198
+ *
199
+ * The target is drawn at random among the candidates with a non-negative gain, with probability
200
+ * proportional to exp(gain / theta) where the gain is in modularity units, as the paper does
201
+ * (theta 0.01, leidenalg's default).
202
+ * @param level - The level
203
+ * @param comm - Community of every node
204
+ * @param rand - Generator
205
+ * @param resolution - Resolution gamma
206
+ * @param m2 - Twice the total edge weight
207
+ * @returns The sub-community of every node, labelled by a member's index (not dense)
208
+ */
209
+ function refine(level: Level, comm: U32, rand: () => number, resolution: number, m2: number): U32 {
210
+ const { s, arcW, deg } = level;
211
+ const { nodeCount: n, rowPtr, colIdx } = s;
212
+ const commTot = new Float64Array(n);
213
+ for (let u = 0; u < n; u++) {
214
+ commTot[comm[u]] += deg[u];
215
+ }
216
+ const ref = new Uint32Array(n);
217
+ const refTot = new Float64Array(n);
218
+ const refSize = new Uint32Array(n);
219
+ // Weight from each sub-community to the rest of its community.
220
+ const ext = new Float64Array(n);
221
+ for (let u = 0; u < n; u++) {
222
+ ref[u] = u;
223
+ refTot[u] = deg[u];
224
+ refSize[u] = 1;
225
+ for (let a = rowPtr[u], end = rowPtr[u + 1]; a < end; a++) {
226
+ const v = colIdx[a];
227
+ if (v !== u && comm[v] === comm[u]) {
228
+ ext[u] += arcW[a];
229
+ }
230
+ }
231
+ }
232
+ const acc = new Float64Array(n);
233
+ const stamp = new Int32Array(n).fill(-1);
234
+ const touched = new Uint32Array(n);
235
+ const order = shuffled(n, rand);
236
+ for (let i = 0; i < n; i++) {
237
+ const v = order[i];
238
+ const c = comm[v];
239
+ const kv = deg[v];
240
+ if (refSize[v] !== 1 || ext[v] < (resolution * kv * (commTot[c] - kv)) / m2) {
241
+ continue;
242
+ }
243
+ let cnt = 0;
244
+ for (let a = rowPtr[v], end = rowPtr[v + 1]; a < end; a++) {
245
+ const x = colIdx[a];
246
+ if (x === v || comm[x] !== c) {
247
+ continue;
248
+ }
249
+ const r = ref[x];
250
+ if (stamp[r] !== v) {
251
+ stamp[r] = v;
252
+ acc[r] = 0;
253
+ touched[cnt++] = r;
254
+ }
255
+ acc[r] += arcW[a];
256
+ }
257
+ // Candidates: well-connected sub-communities with a non-negative gain, drawn with probability
258
+ // exp(gain in modularity units / theta), shifted by the best gain so exp never overflows.
259
+ const gainOf = (r: number): number => acc[r] - (resolution * kv * refTot[r]) / m2;
260
+ let maxGain = -Infinity;
261
+ let kept = 0;
262
+ for (let j = 0; j < cnt; j++) {
263
+ const r = touched[j];
264
+ const gain = gainOf(r);
265
+ if (gain >= 0 && ext[r] >= (resolution * refTot[r] * (commTot[c] - refTot[r])) / m2) {
266
+ touched[kept++] = r;
267
+ maxGain = Math.max(maxGain, gain);
268
+ }
269
+ }
270
+ if (kept === 0) {
271
+ continue;
272
+ }
273
+ const weight = (r: number): number => Math.exp(((gainOf(r) - maxGain) * 2) / m2 / THETA);
274
+ let total = 0;
275
+ for (let j = 0; j < kept; j++) {
276
+ total += weight(touched[j]);
277
+ }
278
+ let draw = rand() * total;
279
+ let best = touched[kept - 1];
280
+ for (let j = 0; j < kept - 1; j++) {
281
+ draw -= weight(touched[j]);
282
+ if (draw < 0) {
283
+ best = touched[j];
284
+ break;
285
+ }
286
+ }
287
+ ref[v] = best;
288
+ refSize[v] = 0;
289
+ refSize[best]++;
290
+ refTot[best] += kv;
291
+ // The edges between v and its new sub-community are now inside it.
292
+ ext[best] += ext[v] - 2 * acc[best];
293
+ }
294
+ return ref;
295
+ }
296
+
297
+ /**
298
+ * One pass of Leiden from a starting partition of the original graph: local moving, refinement,
299
+ * aggregation by the refined partition with each aggregate node starting in its community, repeated
300
+ * until local moving leaves every community a single node of its level.
301
+ * @param base - Level 0, the original graph
302
+ * @param start - Dense community of every original node
303
+ * @param rand - Generator
304
+ * @param resolution - Resolution gamma
305
+ * @param m2 - Twice the total edge weight
306
+ * @returns The partition of the original nodes (not dense) and the levels processed
307
+ */
308
+ function pass(
309
+ base: Level,
310
+ start: U32,
311
+ rand: () => number,
312
+ resolution: number,
313
+ m2: number,
314
+ ): { comm: U32; levels: number } {
315
+ const n0 = base.s.nodeCount;
316
+ // Which node of the current level holds each original node.
317
+ const top = new Uint32Array(n0);
318
+ for (let u = 0; u < n0; u++) {
319
+ top[u] = u;
320
+ }
321
+ let level = base;
322
+ let comm = start.slice();
323
+ let levels = 0;
324
+ for (;;) {
325
+ moveNodesFast(level, comm, rand, resolution, m2);
326
+ levels++;
327
+ const dense = renumberPartition(comm);
328
+ if (dense.count === level.s.nodeCount) {
329
+ break;
330
+ }
331
+ let blocks = refine(level, dense.labels, rand, resolution, m2);
332
+ if (renumberPartition(blocks).count === level.s.nodeCount) {
333
+ // No sub-community merged: aggregate by the communities themselves, as Louvain does, so
334
+ // the next level is smaller.
335
+ blocks = dense.labels;
336
+ }
337
+ const d = level.s.contract(blocks);
338
+ const { nodeRemap, edgeRemap } = d;
339
+ const next = d.snapshot;
340
+ const edgeW = new Float64Array(next.edgeCount);
341
+ for (let e = 0; e < level.edgeW.length; e++) {
342
+ edgeW[edgeRemap === null ? e : edgeRemap[e]] += level.edgeW[e];
343
+ }
344
+ const nextComm = new Uint32Array(next.nodeCount);
345
+ for (let u = 0; u < level.s.nodeCount; u++) {
346
+ nextComm[nodeRemap === null ? u : nodeRemap[u]] = dense.labels[u];
347
+ }
348
+ if (nodeRemap !== null) {
349
+ for (let u = 0; u < n0; u++) {
350
+ top[u] = nodeRemap[top[u]];
351
+ }
352
+ }
353
+ comm = renumberPartition(nextComm).labels;
354
+ level = levelOf(next, edgeW);
355
+ }
356
+ const out = new Uint32Array(n0);
357
+ for (let u = 0; u < n0; u++) {
358
+ out[u] = comm[top[u]];
359
+ }
360
+ return { comm: out, levels };
361
+ }
362
+
363
+ /**
364
+ * Leiden community detection (Traag, Waltman and van Eck, Sci. Rep. 9:5233, 2019) over an
365
+ * undirected snapshot: fast local moving, refinement into well-connected sub-communities, and
366
+ * aggregation by `contract()` once per level. Whole passes repeat from the previous answer, keeping
367
+ * a pass only when it raises modularity, until three passes in a row improve it by no more than
368
+ * `threshold` or `maxIterations` passes have run; every pass starts by moving single original
369
+ * nodes, so the answer is also a local optimum of those moves.
370
+ *
371
+ * Modularity follows graph-format's `weightedDegree()` convention (a self-loop counts twice), which
372
+ * on a graph without self-loops equals the legacy `leiden`'s. Weights are the exact f64 ones when
373
+ * the snapshot keeps them. The partition is not the legacy function's move for move: the legacy
374
+ * `leiden` splits disconnected communities rather than refining them, and visits nodes in another
375
+ * order; what this guarantees is connected communities. Both are randomised heuristics, so for one
376
+ * seed either can come out ahead; on the shared fixtures this one's modularity is at least the
377
+ * legacy's at the default seed and on average over seeds.
378
+ * @param s - An undirected snapshot
379
+ * @param options - Resolution, seed, pass cap and stopping threshold
380
+ * @returns The partition, its modularity, and the levels processed
381
+ * @throws Error on a directed snapshot; RangeError for a negative, NaN or infinite weight or a
382
+ * fractional seed
383
+ * @public
384
+ */
385
+ export function leiden(s: GraphSnapshot, options: LeidenOptions = {}): LeidenResult {
386
+ if (s.directed) {
387
+ throw new Error("Leiden requires an undirected graph. Pass s.toUndirected().snapshot.");
388
+ }
389
+ const resolution = options.resolution ?? 1;
390
+ const randomSeed = options.randomSeed ?? 42;
391
+ const maxIterations = options.maxIterations ?? 100;
392
+ const threshold = options.threshold ?? 1e-7;
393
+ if (!Number.isInteger(randomSeed)) {
394
+ throw new RangeError(`randomSeed must be a finite integer, got ${randomSeed}`);
395
+ }
396
+ const n = s.nodeCount;
397
+ const given = exactEdgeWeights(s) ?? s.edgeList().weights;
398
+ const edgeW = given === null ? new Float64Array(s.edgeCount).fill(1) : Float64Array.from(given);
399
+ for (let e = 0; e < edgeW.length; e++) {
400
+ const w = edgeW[e];
401
+ if (!(w >= 0) || w === Infinity) {
402
+ throw new RangeError(`edge ${e} has weight ${w}; Leiden needs finite, non-negative weights`);
403
+ }
404
+ }
405
+ const base = levelOf(s, edgeW);
406
+ let m2 = 0;
407
+ for (let u = 0; u < n; u++) {
408
+ m2 += base.deg[u];
409
+ }
410
+ let labels = new Uint32Array(n);
411
+ for (let u = 0; u < n; u++) {
412
+ labels[u] = u;
413
+ }
414
+ if (m2 === 0) {
415
+ return { ...withGroups(labels, n), modularity: 0, iterations: 0 };
416
+ }
417
+ const quality = (partition: U32): number => modularity(s, partition, { resolution, weights: base.arcW });
418
+ const rand = mulberry32(randomSeed);
419
+ let q = quality(labels);
420
+ let iterations = 0;
421
+ let idle = 0;
422
+ for (let i = 0; i < maxIterations && idle < PATIENCE; i++) {
423
+ const { comm, levels } = pass(base, labels, rand, resolution, m2);
424
+ iterations += levels;
425
+ const candidate = renumberPartition(comm).labels;
426
+ const qNext = quality(candidate);
427
+ if (qNext > q) {
428
+ idle = qNext - q <= threshold ? idle + 1 : 0;
429
+ labels = candidate;
430
+ q = qNext;
431
+ } else {
432
+ idle++;
433
+ }
434
+ }
435
+ return { ...withGroups(labels, renumberPartition(labels).count), modularity: q, iterations };
436
+ }