@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
@@ -6,7 +6,7 @@
6
6
  * @module
7
7
  */
8
8
 
9
- import { GraphBuilder, type GraphSnapshot } from "@graphty/graph-format";
9
+ import { GraphBuilder, GraphFormatError, type GraphSnapshot, type U32 } from "@graphty/graph-format";
10
10
 
11
11
  import type { Graph } from "../core/graph.js";
12
12
 
@@ -24,6 +24,27 @@ interface CacheEntry {
24
24
  readonly mutationCount: number;
25
25
  readonly checksum: boolean;
26
26
  readonly snapshot: GraphSnapshot;
27
+ /** Every edge's weight in `graph.edges()` order, as frozen. */
28
+ readonly weights: readonly (number | undefined)[];
29
+ }
30
+
31
+ /**
32
+ * Whether every edge still carries the weight it was frozen with. `mutationCount` counts topology
33
+ * changes only, and `graph.getEdge(u, v)` hands out the live edge, so a weight can change in place
34
+ * without moving the counter.
35
+ * @param graph - The legacy graph
36
+ * @param weights - The weights recorded at freeze
37
+ * @returns True when no weight changed
38
+ */
39
+ function sameWeights(graph: Graph, weights: readonly (number | undefined)[]): boolean {
40
+ let i = 0;
41
+ for (const edge of graph.edges()) {
42
+ // Object.is, so a NaN weight (kept in the topology cache) matches itself.
43
+ if (!Object.is(edge.weight, weights[i++])) {
44
+ return false;
45
+ }
46
+ }
47
+ return i === weights.length;
27
48
  }
28
49
 
29
50
  /**
@@ -34,7 +55,8 @@ interface CacheEntry {
34
55
  const SNAPSHOT_CACHE = new WeakMap<Graph, CacheEntry>();
35
56
 
36
57
  /**
37
- * Freeze a legacy `Graph` into a `GraphSnapshot`, memoised on the graph's `mutationCount`.
58
+ * Freeze a legacy `Graph` into a `GraphSnapshot`, memoised on the graph's `mutationCount` and its
59
+ * edge weights (a weight set in place does not move the counter, so a hit re-reads the weights).
38
60
  *
39
61
  * The builder is created with `weightDtype: "f64"` so a legacy graph's double weights survive
40
62
  * exactly: at freeze, graph-format keeps the original values in an f64 edge column with role
@@ -53,11 +75,33 @@ const SNAPSHOT_CACHE = new WeakMap<Graph, CacheEntry>();
53
75
  */
54
76
  export function toSnapshot(graph: Graph, options: ToSnapshotOptions = {}): GraphSnapshot {
55
77
  const checksum = options.checksum === true;
56
- const cached = SNAPSHOT_CACHE.get(graph);
78
+ return cachedFreeze(SNAPSHOT_CACHE, graph, checksum, true);
79
+ }
80
+
81
+ /**
82
+ * Freeze `graph` through `cache`, reusing the entry while the graph is unchanged.
83
+ * @param cache - The cache to read and replace the entry in
84
+ * @param graph - The legacy graph
85
+ * @param checksum - Whether to record checksums
86
+ * @param weighted - Whether to keep the edge weights
87
+ * @returns The snapshot
88
+ */
89
+ function cachedFreeze(
90
+ cache: WeakMap<Graph, CacheEntry>,
91
+ graph: Graph,
92
+ checksum: boolean,
93
+ weighted: boolean,
94
+ ): GraphSnapshot {
95
+ const cached = cache.get(graph);
57
96
  // A checksummed snapshot answers a plain request; a plain one cannot answer a checksummed
58
97
  // request -- validate({ checksum: true }) throws E_INVALID_SNAPSHOT ("no-checksum") when none
59
98
  // were recorded (graph-format/src/types/snapshot.ts:401-405).
60
- if (cached !== undefined && cached.mutationCount === graph.mutationCount && (cached.checksum || !checksum)) {
99
+ if (
100
+ cached !== undefined &&
101
+ cached.mutationCount === graph.mutationCount &&
102
+ (cached.checksum || !checksum) &&
103
+ sameWeights(graph, cached.weights)
104
+ ) {
61
105
  return cached.snapshot;
62
106
  }
63
107
  const builder = new GraphBuilder({
@@ -69,10 +113,116 @@ export function toSnapshot(graph: Graph, options: ToSnapshotOptions = {}): Graph
69
113
  for (const node of graph.nodes()) {
70
114
  builder.addNode(node.id);
71
115
  }
116
+ const weights: (number | undefined)[] = [];
72
117
  for (const edge of graph.edges()) {
73
- builder.addEdge(edge.source, edge.target, edge.weight);
118
+ builder.addEdge(edge.source, edge.target, weighted ? edge.weight : undefined);
119
+ weights.push(edge.weight);
74
120
  }
75
121
  const snapshot = builder.freeze({ label: "algorithms.toSnapshot", checksum });
76
- SNAPSHOT_CACHE.set(graph, { mutationCount: graph.mutationCount, checksum, snapshot });
122
+ cache.set(graph, { mutationCount: graph.mutationCount, checksum, snapshot, weights });
77
123
  return snapshot;
78
124
  }
125
+
126
+ /** Snapshots frozen without weights, for graphs holding a NaN weight; see {@link toTopologySnapshot}. */
127
+ const TOPOLOGY_CACHE = new WeakMap<Graph, CacheEntry>();
128
+
129
+ /**
130
+ * {@link toSnapshot}, or null when an edge weight is NaN: a legacy `Graph` accepts one, a snapshot
131
+ * does not. A facade whose legacy answer depends on how NaN compares keeps its legacy code for such
132
+ * a graph.
133
+ * @param graph - The legacy graph
134
+ * @param options - Conversion options
135
+ * @returns The snapshot, or null
136
+ */
137
+ export function toSnapshotOrNull(graph: Graph, options: ToSnapshotOptions = {}): GraphSnapshot | null {
138
+ try {
139
+ return toSnapshot(graph, options);
140
+ } catch (error) {
141
+ if (error instanceof GraphFormatError && error.code === "E_INVALID_WEIGHT") {
142
+ return null;
143
+ }
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * A snapshot for a port that reads no weights (a traversal, the components, a clustering or link
150
+ * prediction that counts neighbours): {@link toSnapshot}, or,
151
+ * when a NaN weight stops that, the same nodes and edges frozen without weights, so a graph with a
152
+ * NaN weight traverses as it always did.
153
+ * @param graph - The legacy graph
154
+ * @returns The snapshot; its weights are not to be read
155
+ */
156
+ export function toTopologySnapshot(graph: Graph): GraphSnapshot {
157
+ return toSnapshotOrNull(graph) ?? cachedFreeze(TOPOLOGY_CACHE, graph, false, false);
158
+ }
159
+
160
+ /**
161
+ * The order a legacy `Graph` hands out each node's neighbours in, as the `arcOrder` option of the
162
+ * order-sensitive traversals (`depthFirstSearch`, `topologicalSort`, `stronglyConnectedComponents`,
163
+ * `condensation`, `breadthFirstSearch`). A snapshot row is sorted by neighbour index, while the
164
+ * legacy adjacency Map is in insertion order, so a traversal that must visit nodes in the legacy
165
+ * order -- a facade keeping its published result -- passes this.
166
+ * @param graph - The legacy graph
167
+ * @param s - A snapshot with the graph's nodes in the same order and its neighbour pairs, such as
168
+ * `toSnapshot(graph)`; parallel arcs of one pair are kept together in row order
169
+ * @returns For every row, its arcs in legacy neighbour order
170
+ */
171
+ export function legacyArcOrder(graph: Graph, s: GraphSnapshot): U32 {
172
+ const order = new Uint32Array(s.arcCount);
173
+ let next = 0;
174
+ for (const node of graph.nodes()) {
175
+ const u = s.ids.requireIndex(node.id);
176
+ if (next !== s.rowPtr[u]) {
177
+ throw new Error("legacyArcOrder: the snapshot does not hold this graph's nodes and neighbours");
178
+ }
179
+ for (const neighbor of graph.neighbors(node.id)) {
180
+ // Every arc to this neighbour, in row order: the legacy graph holds one edge per pair,
181
+ // but a multigraph snapshot of it may hold several.
182
+ const [lo, hi] = s.arcsBetween(u, s.ids.requireIndex(neighbor));
183
+ for (let a = lo; a < hi; a++) {
184
+ order[next++] = a;
185
+ }
186
+ }
187
+ if (next !== s.rowPtr[u + 1]) {
188
+ throw new Error("legacyArcOrder: the snapshot does not hold this graph's nodes and neighbours");
189
+ }
190
+ }
191
+ return order;
192
+ }
193
+
194
+ const LEGACY_ONLY_CACHE = new WeakMap<Graph, { readonly mutationCount: number; readonly value: boolean }>();
195
+
196
+ /**
197
+ * Whether a facade must run its legacy code on this graph because a snapshot cannot stand in for it:
198
+ * - an edge weight is NaN, which the graph-format builder rejects, so `toSnapshot` would throw where
199
+ * the legacy code returns a result (the unweighted functions never even read the weight);
200
+ * - two node ids share a spelling (the number 1 and the string "1"), so a result keyed by
201
+ * `String(id)` holds one entry for both, and which value it holds depends on how the legacy code
202
+ * wrote it (betweenness adds the two, others keep the last).
203
+ * Memoised on the graph's `mutationCount`, like {@link toSnapshot}.
204
+ * @param graph - The legacy graph
205
+ * @returns True when the facade must call its legacy implementation
206
+ */
207
+ export function needsLegacyCode(graph: Graph): boolean {
208
+ const cached = LEGACY_ONLY_CACHE.get(graph);
209
+ if (cached?.mutationCount === graph.mutationCount) {
210
+ return cached.value;
211
+ }
212
+ let value = false;
213
+ for (const edge of graph.edges()) {
214
+ if (Number.isNaN(edge.weight)) {
215
+ value = true;
216
+ break;
217
+ }
218
+ }
219
+ if (!value) {
220
+ const spellings = new Set<string>();
221
+ for (const node of graph.nodes()) {
222
+ spellings.add(String(node.id));
223
+ }
224
+ value = spellings.size !== graph.nodeCount;
225
+ }
226
+ LEGACY_ONLY_CACHE.set(graph, { mutationCount: graph.mutationCount, value });
227
+ return value;
228
+ }
@@ -0,0 +1,252 @@
1
+ import type { Graph } from "../core/graph.js";
2
+ import type { NodeId } from "../types/index.js";
3
+ import { getCommonNeighbors, getIntermediateNodes } from "../utils/graph-utilities.js";
4
+
5
+ /**
6
+ * Common Neighbors link prediction implementation
7
+ *
8
+ * Predicts the likelihood of a link between two nodes based on the number
9
+ * of common neighbors they share. The intuition is that nodes with many
10
+ * common neighbors are more likely to be connected.
11
+ *
12
+ * Time complexity: O(k²) where k is the average degree
13
+ * Space complexity: O(k)
14
+ */
15
+
16
+ export interface LinkPredictionScore {
17
+ source: NodeId;
18
+ target: NodeId;
19
+ score: number;
20
+ }
21
+
22
+ export interface LinkPredictionOptions {
23
+ directed?: boolean; // Consider direction (default: false)
24
+ includeExisting?: boolean; // Include existing edges (default: false)
25
+ topK?: number; // Return only top K predictions
26
+ }
27
+
28
+ /**
29
+ * Calculate common neighbors score for a pair of nodes
30
+ * @param graph - The input graph
31
+ * @param source - The source node ID
32
+ * @param target - The target node ID
33
+ * @param options - Link prediction options
34
+ * @returns The number of common neighbors
35
+ */
36
+ export function commonNeighborsScore(
37
+ graph: Graph,
38
+ source: NodeId,
39
+ target: NodeId,
40
+ options: LinkPredictionOptions = {},
41
+ ): number {
42
+ if (!graph.hasNode(source) || !graph.hasNode(target)) {
43
+ return 0;
44
+ }
45
+
46
+ const { directed = false } = options;
47
+
48
+ // Use utility function to get common neighbors
49
+ // For directed graphs, we want intermediate nodes that form paths source->X->target
50
+ const commonNeighborsSet = directed
51
+ ? getIntermediateNodes(graph, source, target)
52
+ : getCommonNeighbors(graph, source, target, false);
53
+
54
+ return commonNeighborsSet.size;
55
+ }
56
+
57
+ /**
58
+ * Calculate common neighbors scores for all possible node pairs
59
+ * @param graph - The input graph
60
+ * @param options - Link prediction options
61
+ * @returns Array of link prediction scores sorted by score descending
62
+ */
63
+ export function commonNeighborsPrediction(graph: Graph, options: LinkPredictionOptions = {}): LinkPredictionScore[] {
64
+ const { directed = false, includeExisting = false, topK } = options;
65
+
66
+ const scores: LinkPredictionScore[] = [];
67
+ const nodes = Array.from(graph.nodes()).map((n) => n.id);
68
+
69
+ for (let i = 0; i < nodes.length; i++) {
70
+ for (let j = i + 1; j < nodes.length; j++) {
71
+ const source = nodes[i];
72
+ const target = nodes[j];
73
+
74
+ if (source === undefined || target === undefined) {
75
+ continue;
76
+ }
77
+
78
+ // Skip existing edges unless requested
79
+ if (!includeExisting && graph.hasEdge(source, target)) {
80
+ continue;
81
+ }
82
+
83
+ const score = commonNeighborsScore(graph, source, target, { directed });
84
+
85
+ if (score > 0) {
86
+ scores.push({ source, target, score });
87
+
88
+ // For undirected graphs, also add the reverse pair
89
+ if (!directed && source !== target) {
90
+ scores.push({ source: target, target: source, score });
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ // Sort by score in descending order
97
+ scores.sort((a, b) => b.score - a.score);
98
+
99
+ // Return top K if specified
100
+ if (topK && topK > 0) {
101
+ return scores.slice(0, topK);
102
+ }
103
+
104
+ return scores;
105
+ }
106
+
107
+ /**
108
+ * Calculate common neighbors scores for specific node pairs
109
+ * @param graph - The input graph
110
+ * @param pairs - Array of node ID pairs to calculate scores for
111
+ * @param options - Link prediction options
112
+ * @returns Array of link prediction scores for the specified pairs
113
+ */
114
+ export function commonNeighborsForPairs(
115
+ graph: Graph,
116
+ pairs: [NodeId, NodeId][],
117
+ options: LinkPredictionOptions = {},
118
+ ): LinkPredictionScore[] {
119
+ return pairs.map(([source, target]) => ({
120
+ source,
121
+ target,
122
+ score: commonNeighborsScore(graph, source, target, options),
123
+ }));
124
+ }
125
+
126
+ /**
127
+ * Get top candidates for link prediction for a specific node
128
+ * @param graph - The input graph
129
+ * @param node - The node ID to get candidates for
130
+ * @param options - Link prediction options with optional candidate list
131
+ * @returns Array of top link prediction candidates sorted by score descending
132
+ */
133
+ export function getTopCandidatesForNode(
134
+ graph: Graph,
135
+ node: NodeId,
136
+ options: LinkPredictionOptions & { candidates?: NodeId[] } = {},
137
+ ): LinkPredictionScore[] {
138
+ if (!graph.hasNode(node)) {
139
+ return [];
140
+ }
141
+
142
+ const { directed = false, includeExisting = false, topK = 10, candidates } = options;
143
+
144
+ const scores: LinkPredictionScore[] = [];
145
+ const targetNodes = candidates ?? Array.from(graph.nodes()).map((n) => n.id);
146
+
147
+ for (const target of targetNodes) {
148
+ if (target === node) {
149
+ continue;
150
+ }
151
+
152
+ // Skip existing edges unless requested
153
+ if (!includeExisting && graph.hasEdge(node, target)) {
154
+ continue;
155
+ }
156
+
157
+ const score = commonNeighborsScore(graph, node, target, { directed });
158
+
159
+ if (score > 0) {
160
+ scores.push({ source: node, target, score });
161
+ }
162
+ }
163
+
164
+ // Sort by score in descending order
165
+ scores.sort((a, b) => b.score - a.score);
166
+
167
+ return scores.slice(0, topK);
168
+ }
169
+
170
+ /**
171
+ * Calculate precision and recall for link prediction evaluation
172
+ * @param trainingGraph - The training graph without test edges
173
+ * @param testEdges - Array of node pairs that are actual edges
174
+ * @param nonEdges - Array of node pairs that are not edges
175
+ * @param options - Link prediction options
176
+ * @returns Evaluation metrics including precision, recall, F1 score, and AUC
177
+ */
178
+ export function evaluateCommonNeighbors(
179
+ trainingGraph: Graph,
180
+ testEdges: [NodeId, NodeId][],
181
+ nonEdges: [NodeId, NodeId][],
182
+ options: LinkPredictionOptions = {},
183
+ ): {
184
+ precision: number;
185
+ recall: number;
186
+ f1Score: number;
187
+ auc: number;
188
+ } {
189
+ // Get scores for test edges and non-edges
190
+ const testScores = commonNeighborsForPairs(trainingGraph, testEdges, options);
191
+ const nonEdgeScores = commonNeighborsForPairs(trainingGraph, nonEdges, options);
192
+
193
+ // Combine and sort all scores
194
+ const allScores = [
195
+ ...testScores.map((s) => ({ ...s, isActualEdge: true })),
196
+ ...nonEdgeScores.map((s) => ({ ...s, isActualEdge: false })),
197
+ ].sort((a, b) => b.score - a.score);
198
+
199
+ // Calculate precision and recall at different thresholds
200
+ let truePositives = 0;
201
+ let falsePositives = 0;
202
+ let bestF1 = 0;
203
+ let bestPrecision = 0;
204
+ let bestRecall = 0;
205
+
206
+ const totalPositives = testEdges.length;
207
+
208
+ for (const scoreItem of allScores) {
209
+ if (scoreItem.isActualEdge) {
210
+ truePositives++;
211
+ } else {
212
+ falsePositives++;
213
+ }
214
+
215
+ const precision = truePositives / (truePositives + falsePositives);
216
+ const recall = truePositives / totalPositives;
217
+ const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
218
+
219
+ if (f1 > bestF1) {
220
+ bestF1 = f1;
221
+ bestPrecision = precision;
222
+ bestRecall = recall;
223
+ }
224
+ }
225
+
226
+ // Calculate AUC (Area Under Curve)
227
+ let auc = 0;
228
+ let tpCount = 0;
229
+ let fpCount = 0;
230
+
231
+ for (const item of allScores) {
232
+ if (item.isActualEdge) {
233
+ tpCount++;
234
+ } else {
235
+ auc += tpCount;
236
+ fpCount++;
237
+ }
238
+ }
239
+
240
+ if (tpCount > 0 && fpCount > 0) {
241
+ auc = auc / (tpCount * fpCount);
242
+ } else {
243
+ auc = 0.5; // Random performance
244
+ }
245
+
246
+ return {
247
+ precision: bestPrecision,
248
+ recall: bestRecall,
249
+ f1Score: bestF1,
250
+ auc,
251
+ };
252
+ }