@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,374 @@
1
+ import type { Graph } from "../../core/graph.js";
2
+ import type { NodeId } from "../../types/index.js";
3
+
4
+ /**
5
+ * Simplified Delta-based PageRank that matches the standard algorithm
6
+ * but only processes active nodes for efficiency.
7
+ *
8
+ * Key optimization: Supports incremental updates when graph structure changes,
9
+ * avoiding full recomputation from scratch.
10
+ */
11
+ export class SimpleDeltaPageRank {
12
+ private graph: Graph;
13
+ private nodeCount: number;
14
+ private previousScores: Map<NodeId, number> | null = null;
15
+
16
+ /**
17
+ * Creates a new SimpleDeltaPageRank instance for the given graph.
18
+ * @param graph - The directed graph to compute PageRank on
19
+ */
20
+ constructor(graph: Graph) {
21
+ if (!graph.isDirected) {
22
+ throw new Error("PageRank requires a directed graph");
23
+ }
24
+
25
+ this.graph = graph;
26
+ this.nodeCount = graph.nodeCount;
27
+ }
28
+
29
+ /**
30
+ * Computes PageRank scores for all nodes in the graph using power iteration.
31
+ * @param options - Configuration options for PageRank computation
32
+ * @param options.dampingFactor - Probability of following a link (default: 0.85)
33
+ * @param options.tolerance - Convergence tolerance threshold (default: 1e-6)
34
+ * @param options.maxIterations - Maximum number of iterations (default: 100)
35
+ * @param options.personalization - Personalization vector for Personalized PageRank
36
+ * @param options.weight - Edge attribute name for weighted PageRank
37
+ * @returns Map of node IDs to their PageRank scores
38
+ */
39
+ public compute(
40
+ options: {
41
+ dampingFactor?: number;
42
+ tolerance?: number;
43
+ maxIterations?: number;
44
+ personalization?: Map<NodeId, number>;
45
+ weight?: string;
46
+ } = {},
47
+ ): Map<NodeId, number> {
48
+ const { dampingFactor = 0.85, tolerance = 1e-6, maxIterations = 100, personalization, weight } = options;
49
+
50
+ if (this.nodeCount === 0) {
51
+ return new Map();
52
+ }
53
+
54
+ // Initialize scores
55
+ const scores = new Map<NodeId, number>();
56
+ const newScores = new Map<NodeId, number>();
57
+
58
+ for (const node of this.graph.nodes()) {
59
+ scores.set(node.id, 1.0 / this.nodeCount);
60
+ }
61
+
62
+ // Precompute out-degrees and weights
63
+ const outWeights = new Map<NodeId, number>();
64
+ const danglingNodes = new Set<NodeId>();
65
+
66
+ for (const node of this.graph.nodes()) {
67
+ let totalWeight = 0;
68
+ let outDegree = 0;
69
+
70
+ for (const neighbor of Array.from(this.graph.neighbors(node.id))) {
71
+ outDegree++;
72
+ const edge = this.graph.getEdge(node.id, neighbor);
73
+ totalWeight += edge?.weight ?? 1;
74
+ }
75
+
76
+ outWeights.set(node.id, totalWeight);
77
+ if (outDegree === 0) {
78
+ danglingNodes.add(node.id);
79
+ }
80
+ }
81
+
82
+ // Normalize personalization vector if provided
83
+ let personalVector: Map<NodeId, number> | null = null;
84
+ if (personalization) {
85
+ personalVector = new Map(personalization);
86
+ this.normalizeMap(personalVector);
87
+ }
88
+
89
+ // Power iteration with active node tracking
90
+ let activeNodes = new Set<NodeId>(scores.keys());
91
+ let iteration = 0;
92
+
93
+ for (iteration = 0; iteration < maxIterations; iteration++) {
94
+ // Initialize new scores
95
+ for (const nodeId of scores.keys()) {
96
+ if (personalVector) {
97
+ newScores.set(nodeId, (1 - dampingFactor) * (personalVector.get(nodeId) ?? 0));
98
+ } else {
99
+ newScores.set(nodeId, (1 - dampingFactor) / this.nodeCount);
100
+ }
101
+ }
102
+
103
+ // Handle dangling nodes
104
+ let danglingSum = 0;
105
+ for (const danglingNode of danglingNodes) {
106
+ danglingSum += scores.get(danglingNode) ?? 0;
107
+ }
108
+
109
+ if (danglingSum > 0) {
110
+ const danglingContribution = (dampingFactor * danglingSum) / this.nodeCount;
111
+ for (const nodeId of scores.keys()) {
112
+ const current = newScores.get(nodeId) ?? 0;
113
+ if (personalVector) {
114
+ const personalContrib = danglingContribution * (personalVector.get(nodeId) ?? 0);
115
+ newScores.set(nodeId, current + personalContrib);
116
+ } else {
117
+ newScores.set(nodeId, current + danglingContribution);
118
+ }
119
+ }
120
+ }
121
+
122
+ // Propagate rank from active nodes only
123
+ for (const nodeId of activeNodes) {
124
+ const currentRank = scores.get(nodeId) ?? 0;
125
+ const nodeOutWeight = outWeights.get(nodeId) ?? 0;
126
+
127
+ if (nodeOutWeight > 0) {
128
+ for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
129
+ let edgeWeight = 1;
130
+ if (weight) {
131
+ const edge = this.graph.getEdge(nodeId, neighbor);
132
+ edgeWeight = edge?.weight ?? 1;
133
+ }
134
+
135
+ const contribution = dampingFactor * currentRank * (edgeWeight / nodeOutWeight);
136
+ const neighborRank = newScores.get(neighbor) ?? 0;
137
+ newScores.set(neighbor, neighborRank + contribution);
138
+ }
139
+ }
140
+ }
141
+
142
+ // Check convergence and identify active nodes
143
+ const nextActive = new Set<NodeId>();
144
+ let maxDiff = 0;
145
+
146
+ for (const nodeId of scores.keys()) {
147
+ const oldRank = scores.get(nodeId) ?? 0;
148
+ const newRank = newScores.get(nodeId) ?? 0;
149
+ const diff = Math.abs(newRank - oldRank);
150
+ maxDiff = Math.max(maxDiff, diff);
151
+
152
+ // Mark as active if change is significant
153
+ if (diff > tolerance / 10) {
154
+ nextActive.add(nodeId);
155
+ // Also mark neighbors as potentially active
156
+ for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
157
+ nextActive.add(neighbor);
158
+ }
159
+ for (const neighbor of Array.from(this.graph.inNeighbors(nodeId))) {
160
+ nextActive.add(neighbor);
161
+ }
162
+ }
163
+ }
164
+
165
+ // Swap score maps
166
+ scores.clear();
167
+ for (const [k, v] of newScores) {
168
+ scores.set(k, v);
169
+ }
170
+ newScores.clear();
171
+
172
+ activeNodes = nextActive;
173
+
174
+ if (maxDiff < tolerance) {
175
+ break;
176
+ }
177
+ }
178
+
179
+ // Store scores for potential incremental updates
180
+ this.previousScores = new Map(scores);
181
+
182
+ return scores;
183
+ }
184
+
185
+ /**
186
+ * Perform incremental update after graph modification.
187
+ * This is where delta-based approach provides significant speedup.
188
+ * @param modifiedNodes - Set of nodes that were modified (edges added/removed)
189
+ * @param options - Computation options
190
+ * @param options.dampingFactor - Probability of following a link (default: 0.85)
191
+ * @param options.tolerance - Convergence tolerance threshold (default: 1e-6)
192
+ * @param options.maxIterations - Maximum number of iterations (default: 100)
193
+ * @param options.personalization - Personalization vector for Personalized PageRank
194
+ * @param options.weight - Edge attribute name for weighted PageRank
195
+ * @returns Updated PageRank scores
196
+ */
197
+ public update(
198
+ modifiedNodes: Set<NodeId>,
199
+ options: {
200
+ dampingFactor?: number;
201
+ tolerance?: number;
202
+ maxIterations?: number;
203
+ personalization?: Map<NodeId, number>;
204
+ weight?: string;
205
+ } = {},
206
+ ): Map<NodeId, number> {
207
+ if (!this.previousScores) {
208
+ // No previous computation, do full compute
209
+ return this.compute(options);
210
+ }
211
+
212
+ const { dampingFactor = 0.85, tolerance = 1e-6, maxIterations = 100, personalization, weight } = options;
213
+
214
+ // Initialize scores from previous computation
215
+ const scores = new Map(this.previousScores);
216
+ const newScores = new Map<NodeId, number>();
217
+
218
+ // Ensure all current nodes are included
219
+ for (const node of this.graph.nodes()) {
220
+ if (!scores.has(node.id)) {
221
+ scores.set(node.id, 1.0 / this.nodeCount);
222
+ }
223
+ }
224
+
225
+ // Start with only modified nodes and their neighbors as active
226
+ let activeNodes = new Set<NodeId>();
227
+ for (const nodeId of modifiedNodes) {
228
+ activeNodes.add(nodeId);
229
+ // Add incoming neighbors
230
+ for (const neighbor of Array.from(this.graph.inNeighbors(nodeId))) {
231
+ activeNodes.add(neighbor);
232
+ }
233
+ // Add outgoing neighbors
234
+ for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
235
+ activeNodes.add(neighbor);
236
+ }
237
+ }
238
+
239
+ // Recompute out-weights for modified nodes
240
+ const outWeights = new Map<NodeId, number>();
241
+ const danglingNodes = new Set<NodeId>();
242
+
243
+ for (const node of this.graph.nodes()) {
244
+ let totalWeight = 0;
245
+ let outDegree = 0;
246
+
247
+ for (const neighbor of Array.from(this.graph.neighbors(node.id))) {
248
+ outDegree++;
249
+ const edge = this.graph.getEdge(node.id, neighbor);
250
+ totalWeight += edge?.weight ?? 1;
251
+ }
252
+
253
+ outWeights.set(node.id, totalWeight);
254
+ if (outDegree === 0) {
255
+ danglingNodes.add(node.id);
256
+ }
257
+ }
258
+
259
+ // Normalize personalization vector if provided
260
+ let personalVector: Map<NodeId, number> | null = null;
261
+ if (personalization) {
262
+ personalVector = new Map(personalization);
263
+ this.normalizeMap(personalVector);
264
+ }
265
+
266
+ // Run limited iterations focusing on active nodes
267
+ let iteration = 0;
268
+
269
+ for (iteration = 0; iteration < maxIterations && activeNodes.size > 0; iteration++) {
270
+ // Initialize new scores
271
+ for (const nodeId of scores.keys()) {
272
+ if (personalVector) {
273
+ newScores.set(nodeId, (1 - dampingFactor) * (personalVector.get(nodeId) ?? 0));
274
+ } else {
275
+ newScores.set(nodeId, (1 - dampingFactor) / this.nodeCount);
276
+ }
277
+ }
278
+
279
+ // Handle dangling nodes
280
+ let danglingSum = 0;
281
+ for (const danglingNode of danglingNodes) {
282
+ danglingSum += scores.get(danglingNode) ?? 0;
283
+ }
284
+
285
+ if (danglingSum > 0) {
286
+ const danglingContribution = (dampingFactor * danglingSum) / this.nodeCount;
287
+ for (const nodeId of scores.keys()) {
288
+ const current = newScores.get(nodeId) ?? 0;
289
+ if (personalVector) {
290
+ const personalContrib = danglingContribution * (personalVector.get(nodeId) ?? 0);
291
+ newScores.set(nodeId, current + personalContrib);
292
+ } else {
293
+ newScores.set(nodeId, current + danglingContribution);
294
+ }
295
+ }
296
+ }
297
+
298
+ // Propagate rank from ALL nodes (not just active)
299
+ // This ensures correctness
300
+ for (const nodeId of scores.keys()) {
301
+ const currentRank = scores.get(nodeId) ?? 0;
302
+ const nodeOutWeight = outWeights.get(nodeId) ?? 0;
303
+
304
+ if (nodeOutWeight > 0) {
305
+ for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
306
+ let edgeWeight = 1;
307
+ if (weight) {
308
+ const edge = this.graph.getEdge(nodeId, neighbor);
309
+ edgeWeight = edge?.weight ?? 1;
310
+ }
311
+
312
+ const contribution = dampingFactor * currentRank * (edgeWeight / nodeOutWeight);
313
+ const neighborRank = newScores.get(neighbor) ?? 0;
314
+ newScores.set(neighbor, neighborRank + contribution);
315
+ }
316
+ }
317
+ }
318
+
319
+ // Check convergence only for active nodes
320
+ const nextActive = new Set<NodeId>();
321
+ let maxDiff = 0;
322
+
323
+ for (const nodeId of activeNodes) {
324
+ const oldRank = scores.get(nodeId) ?? 0;
325
+ const newRank = newScores.get(nodeId) ?? 0;
326
+ const diff = Math.abs(newRank - oldRank);
327
+ maxDiff = Math.max(maxDiff, diff);
328
+
329
+ // Mark as active if change is significant
330
+ if (diff > tolerance / 10) {
331
+ nextActive.add(nodeId);
332
+ // Also mark neighbors as potentially active
333
+ for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
334
+ nextActive.add(neighbor);
335
+ }
336
+ for (const neighbor of Array.from(this.graph.inNeighbors(nodeId))) {
337
+ nextActive.add(neighbor);
338
+ }
339
+ }
340
+ }
341
+
342
+ // Swap score maps
343
+ scores.clear();
344
+ for (const [k, v] of newScores) {
345
+ scores.set(k, v);
346
+ }
347
+ newScores.clear();
348
+
349
+ activeNodes = nextActive;
350
+
351
+ if (maxDiff < tolerance) {
352
+ break;
353
+ }
354
+ }
355
+
356
+ // Store scores for future incremental updates
357
+ this.previousScores = new Map(scores);
358
+
359
+ return scores;
360
+ }
361
+
362
+ private normalizeMap(map: Map<NodeId, number>): void {
363
+ let sum = 0;
364
+ for (const value of map.values()) {
365
+ sum += value;
366
+ }
367
+
368
+ if (sum > 0) {
369
+ for (const [key, value] of Array.from(map)) {
370
+ map.set(key, value / sum);
371
+ }
372
+ }
373
+ }
374
+ }