@graphty/algorithms 1.2.0 → 1.4.2

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 (337) hide show
  1. package/README.md +375 -276
  2. package/dist/algorithms.d.ts +42 -2
  3. package/dist/algorithms.js +11402 -2
  4. package/dist/algorithms.js.map +1 -1
  5. package/dist/src/algorithms/centrality/betweenness.d.ts +14 -0
  6. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  7. package/dist/src/algorithms/centrality/betweenness.js +144 -169
  8. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  9. package/dist/src/algorithms/centrality/closeness.d.ts +35 -1
  10. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  11. package/dist/src/algorithms/centrality/closeness.js +70 -149
  12. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  13. package/dist/src/algorithms/centrality/degree.d.ts +7 -0
  14. package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
  15. package/dist/src/algorithms/centrality/degree.js +12 -9
  16. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  17. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +57 -0
  18. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
  19. package/dist/src/algorithms/centrality/delta-pagerank-simple.js +304 -0
  20. package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
  21. package/dist/src/algorithms/centrality/delta-pagerank.d.ts +106 -0
  22. package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
  23. package/dist/src/algorithms/centrality/delta-pagerank.js +320 -0
  24. package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
  25. package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
  26. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
  27. package/dist/src/algorithms/centrality/eigenvector.js +11 -4
  28. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
  29. package/dist/src/algorithms/centrality/hits.d.ts +10 -3
  30. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
  31. package/dist/src/algorithms/centrality/hits.js +11 -4
  32. package/dist/src/algorithms/centrality/hits.js.map +1 -1
  33. package/dist/src/algorithms/centrality/index.d.ts +3 -1
  34. package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
  35. package/dist/src/algorithms/centrality/index.js +2 -1
  36. package/dist/src/algorithms/centrality/index.js.map +1 -1
  37. package/dist/src/algorithms/centrality/katz.d.ts +10 -3
  38. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
  39. package/dist/src/algorithms/centrality/katz.js +13 -8
  40. package/dist/src/algorithms/centrality/katz.js.map +1 -1
  41. package/dist/src/algorithms/centrality/pagerank.d.ts +31 -0
  42. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  43. package/dist/src/algorithms/centrality/pagerank.js +61 -1
  44. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  45. package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
  46. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  47. package/dist/src/algorithms/community/girvan-newman.js +67 -57
  48. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  49. package/dist/src/algorithms/community/index.d.ts +2 -0
  50. package/dist/src/algorithms/community/index.d.ts.map +1 -1
  51. package/dist/src/algorithms/community/index.js.map +1 -1
  52. package/dist/src/algorithms/community/label-propagation.d.ts +11 -4
  53. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  54. package/dist/src/algorithms/community/label-propagation.js +60 -38
  55. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  56. package/dist/src/algorithms/community/leiden.d.ts +3 -3
  57. package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
  58. package/dist/src/algorithms/community/leiden.js +70 -44
  59. package/dist/src/algorithms/community/leiden.js.map +1 -1
  60. package/dist/src/algorithms/community/louvain-optimized.d.ts +149 -0
  61. package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
  62. package/dist/src/algorithms/community/louvain-optimized.js +338 -0
  63. package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
  64. package/dist/src/algorithms/community/louvain.d.ts +2 -3
  65. package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
  66. package/dist/src/algorithms/community/louvain.js +35 -81
  67. package/dist/src/algorithms/community/louvain.js.map +1 -1
  68. package/dist/src/algorithms/community/modularity-utils.d.ts +55 -0
  69. package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
  70. package/dist/src/algorithms/community/modularity-utils.js +108 -0
  71. package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
  72. package/dist/src/algorithms/components/connected.d.ts +23 -0
  73. package/dist/src/algorithms/components/connected.d.ts.map +1 -1
  74. package/dist/src/algorithms/components/connected.js +29 -3
  75. package/dist/src/algorithms/components/connected.js.map +1 -1
  76. package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
  77. package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
  78. package/dist/src/algorithms/matching/bipartite.js +18 -40
  79. package/dist/src/algorithms/matching/bipartite.js.map +1 -1
  80. package/dist/src/algorithms/matching/index.d.ts.map +1 -1
  81. package/dist/src/algorithms/matching/index.js.map +1 -1
  82. package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
  83. package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
  84. package/dist/src/algorithms/matching/isomorphism.js +36 -2
  85. package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
  86. package/dist/src/algorithms/mst/index.d.ts.map +1 -1
  87. package/dist/src/algorithms/mst/index.js.map +1 -1
  88. package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
  89. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
  90. package/dist/src/algorithms/mst/kruskal.js +17 -3
  91. package/dist/src/algorithms/mst/kruskal.js.map +1 -1
  92. package/dist/src/algorithms/mst/prim.d.ts +9 -0
  93. package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
  94. package/dist/src/algorithms/mst/prim.js +9 -0
  95. package/dist/src/algorithms/mst/prim.js.map +1 -1
  96. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
  97. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  98. package/dist/src/algorithms/shortest-path/bellman-ford.js +11 -12
  99. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  100. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +44 -0
  101. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
  102. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +195 -0
  103. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
  104. package/dist/src/algorithms/shortest-path/dijkstra.d.ts +20 -2
  105. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  106. package/dist/src/algorithms/shortest-path/dijkstra.js +30 -14
  107. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  108. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
  109. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
  110. package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
  111. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
  112. package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
  113. package/dist/src/algorithms/shortest-path/index.js.map +1 -1
  114. package/dist/src/algorithms/traversal/bfs-unified.d.ts +42 -0
  115. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
  116. package/dist/src/algorithms/traversal/bfs-unified.js +370 -0
  117. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
  118. package/dist/src/algorithms/traversal/bfs-variants.d.ts +77 -0
  119. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
  120. package/dist/src/algorithms/traversal/bfs-variants.js +402 -0
  121. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
  122. package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
  123. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  124. package/dist/src/algorithms/traversal/bfs.js +5 -182
  125. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  126. package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
  127. package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
  128. package/dist/src/algorithms/traversal/dfs.js +48 -0
  129. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  130. package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
  131. package/dist/src/algorithms/traversal/index.js.map +1 -1
  132. package/dist/src/benchmark-all-algorithms.d.ts +25 -0
  133. package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
  134. package/dist/src/benchmark-all-algorithms.js +491 -0
  135. package/dist/src/benchmark-all-algorithms.js.map +1 -0
  136. package/dist/src/clustering/hierarchical.d.ts +23 -15
  137. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  138. package/dist/src/clustering/hierarchical.js +226 -224
  139. package/dist/src/clustering/hierarchical.js.map +1 -1
  140. package/dist/src/clustering/index.d.ts +4 -2
  141. package/dist/src/clustering/index.d.ts.map +1 -1
  142. package/dist/src/clustering/index.js +2 -2
  143. package/dist/src/clustering/index.js.map +1 -1
  144. package/dist/src/clustering/k-core.d.ts +17 -19
  145. package/dist/src/clustering/k-core.d.ts.map +1 -1
  146. package/dist/src/clustering/k-core.js +68 -30
  147. package/dist/src/clustering/k-core.js.map +1 -1
  148. package/dist/src/clustering/mcl.d.ts +6 -0
  149. package/dist/src/clustering/mcl.d.ts.map +1 -1
  150. package/dist/src/clustering/mcl.js +34 -3
  151. package/dist/src/clustering/mcl.js.map +1 -1
  152. package/dist/src/clustering/spectral.d.ts +11 -0
  153. package/dist/src/clustering/spectral.d.ts.map +1 -1
  154. package/dist/src/clustering/spectral.js +99 -47
  155. package/dist/src/clustering/spectral.js.map +1 -1
  156. package/dist/src/core/graph.d.ts +45 -0
  157. package/dist/src/core/graph.d.ts.map +1 -1
  158. package/dist/src/core/graph.js +49 -5
  159. package/dist/src/core/graph.js.map +1 -1
  160. package/dist/src/data-structures/index.d.ts.map +1 -1
  161. package/dist/src/data-structures/index.js.map +1 -1
  162. package/dist/src/data-structures/priority-queue.d.ts +18 -1
  163. package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
  164. package/dist/src/data-structures/priority-queue.js +26 -5
  165. package/dist/src/data-structures/priority-queue.js.map +1 -1
  166. package/dist/src/data-structures/union-find.d.ts +21 -0
  167. package/dist/src/data-structures/union-find.d.ts.map +1 -1
  168. package/dist/src/data-structures/union-find.js +21 -0
  169. package/dist/src/data-structures/union-find.js.map +1 -1
  170. package/dist/src/flow/ford-fulkerson.d.ts +20 -12
  171. package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
  172. package/dist/src/flow/ford-fulkerson.js +170 -217
  173. package/dist/src/flow/ford-fulkerson.js.map +1 -1
  174. package/dist/src/flow/min-cut.d.ts +6 -8
  175. package/dist/src/flow/min-cut.d.ts.map +1 -1
  176. package/dist/src/flow/min-cut.js +34 -12
  177. package/dist/src/flow/min-cut.js.map +1 -1
  178. package/dist/src/index.d.ts +3 -1
  179. package/dist/src/index.d.ts.map +1 -1
  180. package/dist/src/index.js +6 -1
  181. package/dist/src/index.js.map +1 -1
  182. package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
  183. package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
  184. package/dist/src/link-prediction/adamic-adar.js +47 -30
  185. package/dist/src/link-prediction/adamic-adar.js.map +1 -1
  186. package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
  187. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  188. package/dist/src/link-prediction/common-neighbors.js +31 -14
  189. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  190. package/dist/src/link-prediction/index.d.ts.map +1 -1
  191. package/dist/src/optimized/bit-packed.d.ts +193 -0
  192. package/dist/src/optimized/bit-packed.d.ts.map +1 -0
  193. package/dist/src/optimized/bit-packed.js +342 -0
  194. package/dist/src/optimized/bit-packed.js.map +1 -0
  195. package/dist/src/optimized/csr-graph.d.ts +149 -0
  196. package/dist/src/optimized/csr-graph.d.ts.map +1 -0
  197. package/dist/src/optimized/csr-graph.js +404 -0
  198. package/dist/src/optimized/csr-graph.js.map +1 -0
  199. package/dist/src/optimized/direction-optimized-bfs.d.ts +95 -0
  200. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
  201. package/dist/src/optimized/direction-optimized-bfs.js +242 -0
  202. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
  203. package/dist/src/optimized/graph-adapter.d.ts +111 -0
  204. package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
  205. package/dist/src/optimized/graph-adapter.js +194 -0
  206. package/dist/src/optimized/graph-adapter.js.map +1 -0
  207. package/dist/src/optimized/index.d.ts +14 -0
  208. package/dist/src/optimized/index.d.ts.map +1 -0
  209. package/dist/src/optimized/index.js +30 -0
  210. package/dist/src/optimized/index.js.map +1 -0
  211. package/dist/src/pathfinding/astar.d.ts +25 -9
  212. package/dist/src/pathfinding/astar.d.ts.map +1 -1
  213. package/dist/src/pathfinding/astar.js +25 -10
  214. package/dist/src/pathfinding/astar.js.map +1 -1
  215. package/dist/src/pathfinding/utils.d.ts +15 -6
  216. package/dist/src/pathfinding/utils.d.ts.map +1 -1
  217. package/dist/src/pathfinding/utils.js +19 -7
  218. package/dist/src/pathfinding/utils.js.map +1 -1
  219. package/dist/src/research/grsbm.d.ts +0 -1
  220. package/dist/src/research/grsbm.d.ts.map +1 -1
  221. package/dist/src/research/grsbm.js +28 -23
  222. package/dist/src/research/grsbm.js.map +1 -1
  223. package/dist/src/research/index.d.ts.map +1 -1
  224. package/dist/src/research/index.js.map +1 -1
  225. package/dist/src/research/sync.d.ts +0 -1
  226. package/dist/src/research/sync.d.ts.map +1 -1
  227. package/dist/src/research/sync.js +31 -36
  228. package/dist/src/research/sync.js.map +1 -1
  229. package/dist/src/research/terahac.d.ts +2 -1
  230. package/dist/src/research/terahac.d.ts.map +1 -1
  231. package/dist/src/research/terahac.js +39 -7
  232. package/dist/src/research/terahac.js.map +1 -1
  233. package/dist/src/types/index.d.ts +8 -0
  234. package/dist/src/types/index.d.ts.map +1 -1
  235. package/dist/src/utils/algorithm-utilities.d.ts +2 -0
  236. package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
  237. package/dist/src/utils/algorithm-utilities.js +2 -0
  238. package/dist/src/utils/algorithm-utilities.js.map +1 -0
  239. package/dist/src/utils/graph-converters.d.ts +110 -0
  240. package/dist/src/utils/graph-converters.d.ts.map +1 -0
  241. package/dist/src/utils/graph-converters.js +227 -0
  242. package/dist/src/utils/graph-converters.js.map +1 -0
  243. package/dist/src/utils/graph-utilities.d.ts +62 -0
  244. package/dist/src/utils/graph-utilities.d.ts.map +1 -0
  245. package/dist/src/utils/graph-utilities.js +148 -0
  246. package/dist/src/utils/graph-utilities.js.map +1 -0
  247. package/dist/src/utils/index.d.ts +5 -0
  248. package/dist/src/utils/index.d.ts.map +1 -0
  249. package/dist/src/utils/index.js +5 -0
  250. package/dist/src/utils/index.js.map +1 -0
  251. package/dist/src/utils/math-utilities.d.ts +63 -0
  252. package/dist/src/utils/math-utilities.d.ts.map +1 -0
  253. package/dist/src/utils/math-utilities.js +132 -0
  254. package/dist/src/utils/math-utilities.js.map +1 -0
  255. package/dist/src/utils/matrix-utilities.d.ts +2 -0
  256. package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
  257. package/dist/src/utils/matrix-utilities.js +2 -0
  258. package/dist/src/utils/matrix-utilities.js.map +1 -0
  259. package/dist/src/utils/optimization-helpers.d.ts +42 -0
  260. package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
  261. package/dist/src/utils/optimization-helpers.js +59 -0
  262. package/dist/src/utils/optimization-helpers.js.map +1 -0
  263. package/dist/src/utils/priorityQueue.d.ts +24 -0
  264. package/dist/src/utils/priorityQueue.d.ts.map +1 -1
  265. package/dist/src/utils/priorityQueue.js +34 -4
  266. package/dist/src/utils/priorityQueue.js.map +1 -1
  267. package/package.json +58 -39
  268. package/src/algorithms/centrality/betweenness.ts +185 -200
  269. package/src/algorithms/centrality/closeness.ts +93 -194
  270. package/src/algorithms/centrality/degree.ts +18 -22
  271. package/src/algorithms/centrality/delta-pagerank-simple.ts +374 -0
  272. package/src/algorithms/centrality/delta-pagerank.ts +458 -0
  273. package/src/algorithms/centrality/eigenvector.ts +14 -15
  274. package/src/algorithms/centrality/hits.ts +17 -17
  275. package/src/algorithms/centrality/index.ts +20 -13
  276. package/src/algorithms/centrality/katz.ts +17 -25
  277. package/src/algorithms/centrality/pagerank.ts +91 -16
  278. package/src/algorithms/community/girvan-newman.ts +90 -86
  279. package/src/algorithms/community/index.ts +6 -5
  280. package/src/algorithms/community/label-propagation.ts +69 -48
  281. package/src/algorithms/community/leiden.ts +88 -65
  282. package/src/algorithms/community/louvain-optimized.ts +474 -0
  283. package/src/algorithms/community/louvain.ts +52 -122
  284. package/src/algorithms/community/modularity-utils.ts +134 -0
  285. package/src/algorithms/components/connected.ts +40 -17
  286. package/src/algorithms/matching/bipartite.ts +24 -53
  287. package/src/algorithms/matching/index.ts +4 -4
  288. package/src/algorithms/matching/isomorphism.ts +53 -23
  289. package/src/algorithms/mst/index.ts +3 -4
  290. package/src/algorithms/mst/kruskal.ts +21 -6
  291. package/src/algorithms/mst/prim.ts +13 -4
  292. package/src/algorithms/shortest-path/bellman-ford.ts +16 -28
  293. package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
  294. package/src/algorithms/shortest-path/dijkstra.ts +38 -30
  295. package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
  296. package/src/algorithms/shortest-path/index.ts +5 -5
  297. package/src/algorithms/traversal/bfs-unified.ts +437 -0
  298. package/src/algorithms/traversal/bfs-variants.ts +520 -0
  299. package/src/algorithms/traversal/bfs.ts +5 -229
  300. package/src/algorithms/traversal/dfs.ts +61 -42
  301. package/src/algorithms/traversal/index.ts +3 -3
  302. package/src/benchmark-all-algorithms.ts +580 -0
  303. package/src/clustering/hierarchical.ts +268 -272
  304. package/src/clustering/index.ts +8 -6
  305. package/src/clustering/k-core.ts +93 -61
  306. package/src/clustering/mcl.ts +53 -23
  307. package/src/clustering/spectral.ts +130 -72
  308. package/src/core/graph.ts +58 -18
  309. package/src/data-structures/index.ts +2 -2
  310. package/src/data-structures/priority-queue.ts +36 -11
  311. package/src/data-structures/union-find.ts +22 -1
  312. package/src/flow/ford-fulkerson.ts +221 -262
  313. package/src/flow/min-cut.ts +59 -51
  314. package/src/index.ts +9 -2
  315. package/src/link-prediction/adamic-adar.ts +75 -74
  316. package/src/link-prediction/common-neighbors.ts +49 -49
  317. package/src/link-prediction/index.ts +1 -1
  318. package/src/optimized/bit-packed.ts +390 -0
  319. package/src/optimized/csr-graph.ts +518 -0
  320. package/src/optimized/direction-optimized-bfs.ts +310 -0
  321. package/src/optimized/graph-adapter.ts +248 -0
  322. package/src/optimized/index.ts +34 -0
  323. package/src/pathfinding/astar.ts +40 -25
  324. package/src/pathfinding/utils.ts +20 -13
  325. package/src/research/grsbm.ts +36 -39
  326. package/src/research/index.ts +4 -4
  327. package/src/research/sync.ts +41 -54
  328. package/src/research/terahac.ts +63 -21
  329. package/src/types/index.ts +8 -0
  330. package/src/utils/algorithm-utilities.ts +2 -0
  331. package/src/utils/graph-converters.ts +269 -0
  332. package/src/utils/graph-utilities.ts +173 -0
  333. package/src/utils/index.ts +4 -0
  334. package/src/utils/math-utilities.ts +149 -0
  335. package/src/utils/matrix-utilities.ts +2 -0
  336. package/src/utils/optimization-helpers.ts +87 -0
  337. package/src/utils/priorityQueue.ts +40 -6
@@ -0,0 +1,518 @@
1
+ import type { NodeId } from "../types/index.js";
2
+
3
+ /**
4
+ * Interface for read-only graph operations
5
+ */
6
+ export interface ReadonlyGraph<TNodeId = NodeId> {
7
+ nodeCount(): number;
8
+ edgeCount(): number;
9
+ hasNode(nodeId: TNodeId): boolean;
10
+ hasEdge(source: TNodeId, target: TNodeId): boolean;
11
+ neighbors(nodeId: TNodeId): IterableIterator<TNodeId>;
12
+ outDegree(nodeId: TNodeId): number;
13
+ nodes(): IterableIterator<TNodeId>;
14
+ }
15
+
16
+ /**
17
+ * Internal data structure for CSR representation
18
+ */
19
+ interface CSRGraphData<TNodeId> {
20
+ // Row pointers: indices where each node's edges start
21
+ rowPointers: Uint32Array;
22
+
23
+ // Column indices: destination nodes for each edge
24
+ columnIndices: Uint32Array;
25
+
26
+ // Edge weights (optional)
27
+ edgeWeights?: Float32Array;
28
+
29
+ // Reverse edges for bottom-up BFS (optional)
30
+ reverseRowPointers?: Uint32Array;
31
+ reverseColumnIndices?: Uint32Array;
32
+
33
+ // Node ID mapping
34
+ nodeIdToIndex: Map<TNodeId, number>;
35
+ indexToNodeId: TNodeId[];
36
+ }
37
+
38
+ /**
39
+ * Compressed Sparse Row (CSR) graph representation
40
+ *
41
+ * Provides cache-efficient graph storage with sequential memory access patterns.
42
+ * Optimized for traversal operations and sparse graphs.
43
+ */
44
+ export class CSRGraph<TNodeId = NodeId> implements ReadonlyGraph<TNodeId> {
45
+ private data: CSRGraphData<TNodeId>;
46
+
47
+ /**
48
+ * Creates a new CSRGraph from an adjacency list representation.
49
+ * @param adjacencyList - Map of node IDs to arrays of neighbor node IDs
50
+ * @param weights - Optional map of edge weights keyed by "source-target" strings
51
+ * @param buildReverse - Whether to build reverse edges for bottom-up BFS (default true)
52
+ */
53
+ constructor(adjacencyList: Map<TNodeId, TNodeId[]>, weights?: Map<string, number>, buildReverse = true) {
54
+ this.data = this.buildCSR(adjacencyList, weights, buildReverse);
55
+ }
56
+
57
+ /**
58
+ * Build CSR structure from adjacency list.
59
+ * @param adjacencyList - Map of node IDs to arrays of neighbor node IDs
60
+ * @param weights - Optional map of edge weights keyed by "source-target" strings
61
+ * @param buildReverse - Whether to build reverse edges for bottom-up BFS
62
+ * @returns The constructed CSR graph data structure
63
+ */
64
+ private buildCSR(
65
+ adjacencyList: Map<TNodeId, TNodeId[]>,
66
+ weights?: Map<string, number>,
67
+ buildReverse = true,
68
+ ): CSRGraphData<TNodeId> {
69
+ // Collect all unique nodes (both sources and targets)
70
+ const allNodes = new Set<TNodeId>();
71
+ for (const [source, neighbors] of adjacencyList) {
72
+ allNodes.add(source);
73
+ for (const neighbor of neighbors) {
74
+ allNodes.add(neighbor);
75
+ }
76
+ }
77
+
78
+ // Sort nodes for consistent ordering. Numeric IDs sort numerically,
79
+ // string IDs sort lexicographically for predictable iteration order.
80
+ const nodes = Array.from(allNodes).sort((a, b) => {
81
+ if (typeof a === "number" && typeof b === "number") {
82
+ return a - b;
83
+ }
84
+
85
+ return String(a).localeCompare(String(b));
86
+ });
87
+ const nodeCount = nodes.length;
88
+
89
+ // Build node mappings
90
+ const nodeIdToIndex = new Map<TNodeId, number>();
91
+ const indexToNodeId: TNodeId[] = [];
92
+
93
+ nodes.forEach((nodeId, index) => {
94
+ nodeIdToIndex.set(nodeId, index);
95
+ indexToNodeId[index] = nodeId;
96
+ });
97
+
98
+ // Count total edges
99
+ let edgeCount = 0;
100
+ for (const neighbors of adjacencyList.values()) {
101
+ edgeCount += neighbors.length;
102
+ }
103
+
104
+ // Allocate arrays
105
+ const rowPointers = new Uint32Array(nodeCount + 1);
106
+ const columnIndices = new Uint32Array(edgeCount);
107
+ const edgeWeights = weights ? new Float32Array(edgeCount) : undefined;
108
+
109
+ // Build CSR structure
110
+ let currentEdge = 0;
111
+ for (let i = 0; i < nodeCount; i++) {
112
+ rowPointers[i] = currentEdge;
113
+ const nodeId = indexToNodeId[i];
114
+ if (nodeId === undefined) {
115
+ throw new Error(`Invalid node index ${String(i)}`);
116
+ }
117
+
118
+ const neighbors = adjacencyList.get(nodeId) ?? [];
119
+
120
+ // Sort neighbors for better cache locality and binary search
121
+ const sortedNeighbors = neighbors
122
+ .map((n) => {
123
+ const index = nodeIdToIndex.get(n);
124
+ if (index === undefined) {
125
+ throw new Error(`Node ${String(n)} not found in nodeIdToIndex map`);
126
+ }
127
+
128
+ return { id: n, index };
129
+ })
130
+ .sort((a, b) => a.index - b.index);
131
+
132
+ for (const neighbor of sortedNeighbors) {
133
+ columnIndices[currentEdge] = neighbor.index;
134
+
135
+ if (edgeWeights && weights) {
136
+ const edgeKey = `${String(nodeId)}-${String(neighbor.id)}`;
137
+ const weight = weights.get(edgeKey);
138
+ if (weight !== undefined) {
139
+ edgeWeights[currentEdge] = weight;
140
+ } else {
141
+ edgeWeights[currentEdge] = 1;
142
+ }
143
+ }
144
+
145
+ currentEdge++;
146
+ }
147
+ }
148
+ rowPointers[nodeCount] = currentEdge;
149
+
150
+ const result: CSRGraphData<TNodeId> = {
151
+ rowPointers,
152
+ columnIndices,
153
+ nodeIdToIndex,
154
+ indexToNodeId,
155
+ };
156
+
157
+ if (edgeWeights) {
158
+ result.edgeWeights = edgeWeights;
159
+ }
160
+
161
+ // Build reverse edges for bottom-up BFS
162
+ if (buildReverse) {
163
+ const reverseAdjacency = new Map<number, number[]>();
164
+ for (let i = 0; i < nodeCount; i++) {
165
+ reverseAdjacency.set(i, []);
166
+ }
167
+
168
+ // Build reverse adjacency from forward edges
169
+ for (let i = 0; i < nodeCount; i++) {
170
+ const start = rowPointers[i];
171
+ const end = rowPointers[i + 1];
172
+ if (start !== undefined && end !== undefined) {
173
+ for (let j = start; j < end; j++) {
174
+ const target = columnIndices[j];
175
+ if (target !== undefined) {
176
+ reverseAdjacency.get(target)?.push(i);
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ // Build reverse CSR
183
+ const reverseRowPointers = new Uint32Array(nodeCount + 1);
184
+ const reverseColumnIndices = new Uint32Array(edgeCount);
185
+
186
+ let reverseEdge = 0;
187
+ for (let i = 0; i < nodeCount; i++) {
188
+ reverseRowPointers[i] = reverseEdge;
189
+ const incoming = reverseAdjacency.get(i) ?? [];
190
+ incoming.sort((a, b) => a - b);
191
+
192
+ for (const source of incoming) {
193
+ reverseColumnIndices[reverseEdge++] = source;
194
+ }
195
+ }
196
+ reverseRowPointers[nodeCount] = reverseEdge;
197
+
198
+ result.reverseRowPointers = reverseRowPointers;
199
+ result.reverseColumnIndices = reverseColumnIndices;
200
+ }
201
+
202
+ return result;
203
+ }
204
+
205
+ // Core API methods
206
+ /**
207
+ * Get the total number of nodes in the graph.
208
+ * @returns The number of nodes
209
+ */
210
+ nodeCount(): number {
211
+ return this.data.indexToNodeId.length;
212
+ }
213
+
214
+ /**
215
+ * Get the total number of edges in the graph.
216
+ * @returns The number of edges
217
+ */
218
+ edgeCount(): number {
219
+ return this.data.columnIndices.length;
220
+ }
221
+
222
+ /**
223
+ * Check if a node exists in the graph.
224
+ * @param nodeId - The node ID to check
225
+ * @returns True if the node exists, false otherwise
226
+ */
227
+ hasNode(nodeId: TNodeId): boolean {
228
+ return this.data.nodeIdToIndex.has(nodeId);
229
+ }
230
+
231
+ /**
232
+ * Check if an edge exists between two nodes.
233
+ * @param source - The source node ID
234
+ * @param target - The target node ID
235
+ * @returns True if the edge exists, false otherwise
236
+ */
237
+ hasEdge(source: TNodeId, target: TNodeId): boolean {
238
+ const sourceIndex = this.data.nodeIdToIndex.get(source);
239
+ const targetIndex = this.data.nodeIdToIndex.get(target);
240
+
241
+ if (sourceIndex === undefined || targetIndex === undefined) {
242
+ return false;
243
+ }
244
+
245
+ const start = this.data.rowPointers[sourceIndex];
246
+ const end = this.data.rowPointers[sourceIndex + 1];
247
+
248
+ // Binary search for target in sorted neighbors
249
+ if (start === undefined || end === undefined) {
250
+ return false;
251
+ }
252
+
253
+ return this.binarySearch(this.data.columnIndices, targetIndex, start, end) !== -1;
254
+ }
255
+
256
+ /**
257
+ * Get neighbors of a node as node IDs.
258
+ * @param nodeId - The node ID to get neighbors for
259
+ * @returns An iterator over neighbor node IDs
260
+ */
261
+ neighbors(nodeId: TNodeId): IterableIterator<TNodeId> {
262
+ const nodeIndex = this.data.nodeIdToIndex.get(nodeId);
263
+ if (nodeIndex === undefined) {
264
+ return new Set<TNodeId>().values();
265
+ }
266
+
267
+ const start = this.data.rowPointers[nodeIndex];
268
+ const end = this.data.rowPointers[nodeIndex + 1];
269
+
270
+ const { columnIndices, indexToNodeId } = this.data;
271
+ function* generateNeighbors(): Generator<TNodeId> {
272
+ if (start !== undefined && end !== undefined) {
273
+ for (let i = start; i < end; i++) {
274
+ const idx = columnIndices[i];
275
+ if (idx !== undefined) {
276
+ const nodeId = indexToNodeId[idx];
277
+ if (nodeId !== undefined) {
278
+ yield nodeId;
279
+ }
280
+ }
281
+ }
282
+ }
283
+ }
284
+
285
+ return generateNeighbors();
286
+ }
287
+
288
+ /**
289
+ * Get all nodes in the graph.
290
+ * @returns An iterator over all node IDs
291
+ */
292
+ nodes(): IterableIterator<TNodeId> {
293
+ return this.data.indexToNodeId.values();
294
+ }
295
+
296
+ /**
297
+ * Get neighbors as indices (internal use).
298
+ * @param nodeIndex - The internal index of the node
299
+ * @returns Array of neighbor indices
300
+ */
301
+ getNeighborIndices(nodeIndex: number): number[] {
302
+ if (nodeIndex < 0 || nodeIndex >= this.data.indexToNodeId.length) {
303
+ return [];
304
+ }
305
+
306
+ const start = this.data.rowPointers[nodeIndex];
307
+ const end = this.data.rowPointers[nodeIndex + 1];
308
+ if (start === undefined || end === undefined) {
309
+ return [];
310
+ }
311
+
312
+ return Array.from(this.data.columnIndices.subarray(start, end));
313
+ }
314
+
315
+ /**
316
+ * Get the out-degree (number of outgoing edges) of a node.
317
+ * @param nodeId - The node ID to get the out-degree for
318
+ * @returns The number of outgoing edges from the node
319
+ */
320
+ outDegree(nodeId: TNodeId): number {
321
+ const nodeIndex = this.data.nodeIdToIndex.get(nodeId);
322
+ if (nodeIndex === undefined) {
323
+ return 0;
324
+ }
325
+
326
+ return this.outDegreeByIndex(nodeIndex);
327
+ }
328
+
329
+ /**
330
+ * Get out-degree by index (internal use).
331
+ * @param nodeIndex - The internal index of the node
332
+ * @returns The number of outgoing edges from the node
333
+ */
334
+ outDegreeByIndex(nodeIndex: number): number {
335
+ const start = this.data.rowPointers[nodeIndex];
336
+ const end = this.data.rowPointers[nodeIndex + 1];
337
+ if (start !== undefined && end !== undefined) {
338
+ return end - start;
339
+ }
340
+
341
+ return 0;
342
+ }
343
+
344
+ /**
345
+ * Iterator support for neighbor indices.
346
+ * @param nodeIndex - The internal index of the node
347
+ * @yields The indices of neighboring nodes
348
+ */
349
+ *iterateNeighborIndices(nodeIndex: number): Generator<number> {
350
+ const start = this.data.rowPointers[nodeIndex];
351
+ const end = this.data.rowPointers[nodeIndex + 1];
352
+
353
+ if (start !== undefined && end !== undefined) {
354
+ for (let i = start; i < end; i++) {
355
+ const value = this.data.columnIndices[i];
356
+ if (value !== undefined) {
357
+ yield value;
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Iterator support for incoming neighbor indices (for bottom-up BFS).
365
+ * @param nodeIndex - The internal index of the node
366
+ * @yields The indices of nodes with edges pointing to this node
367
+ */
368
+ *iterateIncomingNeighborIndices(nodeIndex: number): Generator<number> {
369
+ if (!this.data.reverseRowPointers || !this.data.reverseColumnIndices) {
370
+ return;
371
+ }
372
+
373
+ const start = this.data.reverseRowPointers[nodeIndex];
374
+ const end = this.data.reverseRowPointers[nodeIndex + 1];
375
+
376
+ if (start !== undefined && end !== undefined) {
377
+ for (let i = start; i < end; i++) {
378
+ const value = this.data.reverseColumnIndices[i];
379
+ if (value !== undefined) {
380
+ yield value;
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Convert node ID to internal index.
388
+ * @param nodeId - The node ID to convert
389
+ * @returns The internal index for the node
390
+ */
391
+ nodeToIndex(nodeId: TNodeId): number {
392
+ const index = this.data.nodeIdToIndex.get(nodeId);
393
+ if (index === undefined) {
394
+ throw new Error(`Node ${String(nodeId)} not found in graph`);
395
+ }
396
+
397
+ return index;
398
+ }
399
+
400
+ /**
401
+ * Convert internal index to node ID.
402
+ * @param index - The internal index to convert
403
+ * @returns The node ID for the given index
404
+ */
405
+ indexToNodeId(index: number): TNodeId {
406
+ const nodeId = this.data.indexToNodeId[index];
407
+ if (nodeId === undefined) {
408
+ throw new Error(`Index ${String(index)} out of bounds`);
409
+ }
410
+
411
+ return nodeId;
412
+ }
413
+
414
+ /**
415
+ * Get edge weight between two nodes.
416
+ * @param source - The source node ID
417
+ * @param target - The target node ID
418
+ * @returns The edge weight, or undefined if the edge doesn't exist or has no weight
419
+ */
420
+ getEdgeWeight(source: TNodeId, target: TNodeId): number | undefined {
421
+ if (!this.data.edgeWeights) {
422
+ return undefined;
423
+ }
424
+
425
+ const sourceIndex = this.data.nodeIdToIndex.get(source);
426
+ const targetIndex = this.data.nodeIdToIndex.get(target);
427
+
428
+ if (sourceIndex === undefined || targetIndex === undefined) {
429
+ return undefined;
430
+ }
431
+
432
+ const start = this.data.rowPointers[sourceIndex];
433
+ const end = this.data.rowPointers[sourceIndex + 1];
434
+
435
+ if (start === undefined || end === undefined) {
436
+ return undefined;
437
+ }
438
+
439
+ const edgeIndex = this.binarySearch(this.data.columnIndices, targetIndex, start, end);
440
+ if (edgeIndex === -1) {
441
+ return undefined;
442
+ }
443
+
444
+ return this.data.edgeWeights[edgeIndex];
445
+ }
446
+
447
+ /**
448
+ * Binary search for target in sorted array.
449
+ * @param arr - The typed array to search in
450
+ * @param target - The value to search for
451
+ * @param start - The starting index (inclusive)
452
+ * @param end - The ending index (exclusive)
453
+ * @returns The index of the target, or -1 if not found
454
+ */
455
+ private binarySearch(arr: Uint32Array, target: number, start: number, end: number): number {
456
+ let left = start;
457
+ let right = end - 1;
458
+
459
+ while (left <= right) {
460
+ const mid = (left + right) >>> 1;
461
+ const value = arr[mid];
462
+
463
+ if (value === undefined) {
464
+ return -1;
465
+ }
466
+
467
+ if (value === target) {
468
+ return mid;
469
+ }
470
+
471
+ if (value < target) {
472
+ left = mid + 1;
473
+ } else {
474
+ right = mid - 1;
475
+ }
476
+ }
477
+
478
+ return -1;
479
+ }
480
+
481
+ /**
482
+ * Create CSR graph from standard Graph interface.
483
+ * @param graph - The graph object to convert, must implement nodes, neighbors, hasNode, and optionally getEdge
484
+ * @param graph.nodes - Function that returns an iterator over node objects with id property
485
+ * @param graph.neighbors - Function that returns an iterator over neighbor node IDs
486
+ * @param graph.hasNode - Function that checks if a node exists in the graph
487
+ * @param graph.getEdge - Optional function that returns edge data including weight
488
+ * @returns A new CSRGraph instance
489
+ */
490
+ static fromGraph<TNodeId = NodeId>(graph: {
491
+ nodes(): IterableIterator<{ id: TNodeId }>;
492
+ neighbors(nodeId: TNodeId): IterableIterator<TNodeId>;
493
+ hasNode(nodeId: TNodeId): boolean;
494
+ getEdge?(source: TNodeId, target: TNodeId): { weight?: number } | undefined;
495
+ }): CSRGraph<TNodeId> {
496
+ const adjacencyList = new Map<TNodeId, TNodeId[]>();
497
+ const weights = new Map<string, number>();
498
+
499
+ // Build adjacency list
500
+ for (const node of graph.nodes()) {
501
+ const neighbors: TNodeId[] = [];
502
+ for (const neighbor of graph.neighbors(node.id)) {
503
+ neighbors.push(neighbor);
504
+
505
+ // Get edge weight if available
506
+ if (graph.getEdge) {
507
+ const edge = graph.getEdge(node.id, neighbor);
508
+ if (edge?.weight !== undefined) {
509
+ weights.set(`${String(node.id)}-${String(neighbor)}`, edge.weight);
510
+ }
511
+ }
512
+ }
513
+ adjacencyList.set(node.id, neighbors);
514
+ }
515
+
516
+ return new CSRGraph(adjacencyList, weights.size > 0 ? weights : undefined);
517
+ }
518
+ }