@graphty/algorithms 1.0.1 → 1.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 (294) hide show
  1. package/README.md +1259 -61
  2. package/dist/algorithms.d.ts +2 -0
  3. package/dist/algorithms.d.ts.map +1 -0
  4. package/dist/algorithms.js +2 -0
  5. package/dist/algorithms.js.map +1 -0
  6. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  7. package/dist/src/algorithms/centrality/betweenness.js +22 -7
  8. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  9. package/dist/src/algorithms/centrality/closeness.js +7 -7
  10. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  11. package/dist/src/algorithms/centrality/degree.js +1 -1
  12. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  13. package/dist/src/algorithms/centrality/eigenvector.d.ts +27 -0
  14. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -0
  15. package/dist/src/algorithms/centrality/eigenvector.js +113 -0
  16. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -0
  17. package/dist/src/algorithms/centrality/hits.d.ts +35 -0
  18. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -0
  19. package/dist/src/algorithms/centrality/hits.js +146 -0
  20. package/dist/src/algorithms/centrality/hits.js.map +1 -0
  21. package/dist/src/algorithms/centrality/index.d.ts +6 -0
  22. package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
  23. package/dist/src/algorithms/centrality/index.js +3 -0
  24. package/dist/src/algorithms/centrality/index.js.map +1 -1
  25. package/dist/src/algorithms/centrality/katz.d.ts +30 -0
  26. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -0
  27. package/dist/src/algorithms/centrality/katz.js +85 -0
  28. package/dist/src/algorithms/centrality/katz.js.map +1 -0
  29. package/dist/src/algorithms/centrality/pagerank.js +3 -3
  30. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  31. package/dist/src/algorithms/community/girvan-newman.d.ts +23 -0
  32. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -0
  33. package/dist/src/algorithms/community/girvan-newman.js +297 -0
  34. package/dist/src/algorithms/community/girvan-newman.js.map +1 -0
  35. package/dist/src/algorithms/community/index.d.ts +12 -0
  36. package/dist/src/algorithms/community/index.d.ts.map +1 -0
  37. package/dist/src/algorithms/community/index.js +12 -0
  38. package/dist/src/algorithms/community/index.js.map +1 -0
  39. package/dist/src/algorithms/community/label-propagation.d.ts +42 -0
  40. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -0
  41. package/dist/src/algorithms/community/label-propagation.js +307 -0
  42. package/dist/src/algorithms/community/label-propagation.js.map +1 -0
  43. package/dist/src/algorithms/community/leiden.d.ts +33 -0
  44. package/dist/src/algorithms/community/leiden.d.ts.map +1 -0
  45. package/dist/src/algorithms/community/leiden.js +434 -0
  46. package/dist/src/algorithms/community/leiden.js.map +1 -0
  47. package/dist/src/algorithms/community/louvain.d.ts +19 -0
  48. package/dist/src/algorithms/community/louvain.d.ts.map +1 -0
  49. package/dist/src/algorithms/community/louvain.js +219 -0
  50. package/dist/src/algorithms/community/louvain.js.map +1 -0
  51. package/dist/src/algorithms/components/connected.js +7 -7
  52. package/dist/src/algorithms/components/connected.js.map +1 -1
  53. package/dist/src/algorithms/index.d.ts +7 -0
  54. package/dist/src/algorithms/index.d.ts.map +1 -1
  55. package/dist/src/algorithms/index.js +14 -0
  56. package/dist/src/algorithms/index.js.map +1 -1
  57. package/dist/src/algorithms/matching/bipartite.d.ts +37 -0
  58. package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -0
  59. package/dist/src/algorithms/matching/bipartite.js +127 -0
  60. package/dist/src/algorithms/matching/bipartite.js.map +1 -0
  61. package/dist/src/algorithms/matching/index.d.ts +8 -0
  62. package/dist/src/algorithms/matching/index.d.ts.map +1 -0
  63. package/dist/src/algorithms/matching/index.js +6 -0
  64. package/dist/src/algorithms/matching/index.js.map +1 -0
  65. package/dist/src/algorithms/matching/isomorphism.d.ts +32 -0
  66. package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -0
  67. package/dist/src/algorithms/matching/isomorphism.js +291 -0
  68. package/dist/src/algorithms/matching/isomorphism.js.map +1 -0
  69. package/dist/src/algorithms/mst/index.d.ts +4 -0
  70. package/dist/src/algorithms/mst/index.d.ts.map +1 -0
  71. package/dist/src/algorithms/mst/index.js +3 -0
  72. package/dist/src/algorithms/mst/index.js.map +1 -0
  73. package/dist/src/algorithms/mst/kruskal.d.ts +9 -0
  74. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -0
  75. package/dist/src/algorithms/mst/kruskal.js +43 -0
  76. package/dist/src/algorithms/mst/kruskal.js.map +1 -0
  77. package/dist/src/algorithms/mst/prim.d.ts +5 -0
  78. package/dist/src/algorithms/mst/prim.d.ts.map +1 -0
  79. package/dist/src/algorithms/mst/prim.js +63 -0
  80. package/dist/src/algorithms/mst/prim.js.map +1 -0
  81. package/dist/src/algorithms/shortest-path/bellman-ford.js +2 -2
  82. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  83. package/dist/src/algorithms/shortest-path/dijkstra.js +5 -5
  84. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  85. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +14 -0
  86. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -0
  87. package/dist/src/algorithms/shortest-path/floyd-warshall.js +129 -0
  88. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -0
  89. package/dist/src/algorithms/shortest-path/index.d.ts +2 -0
  90. package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
  91. package/dist/src/algorithms/shortest-path/index.js +1 -0
  92. package/dist/src/algorithms/shortest-path/index.js.map +1 -1
  93. package/dist/src/algorithms/traversal/bfs.js +2 -2
  94. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  95. package/dist/src/algorithms/traversal/dfs.js +11 -11
  96. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  97. package/dist/src/clustering/hierarchical.d.ts +48 -0
  98. package/dist/src/clustering/hierarchical.d.ts.map +1 -0
  99. package/dist/src/clustering/hierarchical.js +439 -0
  100. package/dist/src/clustering/hierarchical.js.map +1 -0
  101. package/dist/src/clustering/index.d.ts +7 -0
  102. package/dist/src/clustering/index.d.ts.map +1 -0
  103. package/dist/src/clustering/index.js +5 -0
  104. package/dist/src/clustering/index.js.map +1 -0
  105. package/dist/src/clustering/k-core.d.ts +63 -0
  106. package/dist/src/clustering/k-core.d.ts.map +1 -0
  107. package/dist/src/clustering/k-core.js +377 -0
  108. package/dist/src/clustering/k-core.js.map +1 -0
  109. package/dist/src/clustering/mcl.d.ts +36 -0
  110. package/dist/src/clustering/mcl.d.ts.map +1 -0
  111. package/dist/src/clustering/mcl.js +343 -0
  112. package/dist/src/clustering/mcl.js.map +1 -0
  113. package/dist/src/clustering/spectral.d.ts +28 -0
  114. package/dist/src/clustering/spectral.d.ts.map +1 -0
  115. package/dist/src/clustering/spectral.js +551 -0
  116. package/dist/src/clustering/spectral.js.map +1 -0
  117. package/dist/src/core/graph.d.ts +9 -0
  118. package/dist/src/core/graph.d.ts.map +1 -1
  119. package/dist/src/core/graph.js +31 -11
  120. package/dist/src/core/graph.js.map +1 -1
  121. package/dist/src/data-structures/priority-queue.js +0 -2
  122. package/dist/src/data-structures/priority-queue.js.map +1 -1
  123. package/dist/src/data-structures/union-find.js +2 -5
  124. package/dist/src/data-structures/union-find.js.map +1 -1
  125. package/dist/src/flow/ford-fulkerson.d.ts +53 -0
  126. package/dist/src/flow/ford-fulkerson.d.ts.map +1 -0
  127. package/dist/src/flow/ford-fulkerson.js +355 -0
  128. package/dist/src/flow/ford-fulkerson.js.map +1 -0
  129. package/dist/src/flow/index.d.ts +3 -0
  130. package/dist/src/flow/index.d.ts.map +1 -0
  131. package/dist/src/flow/index.js +3 -0
  132. package/dist/src/flow/index.js.map +1 -0
  133. package/dist/src/flow/min-cut.d.ts +49 -0
  134. package/dist/src/flow/min-cut.d.ts.map +1 -0
  135. package/dist/src/flow/min-cut.js +383 -0
  136. package/dist/src/flow/min-cut.js.map +1 -0
  137. package/dist/src/index.d.ts +2 -1
  138. package/dist/src/index.d.ts.map +1 -1
  139. package/dist/src/index.js +2 -0
  140. package/dist/src/index.js.map +1 -1
  141. package/dist/src/link-prediction/adamic-adar.d.ts +56 -0
  142. package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -0
  143. package/dist/src/link-prediction/adamic-adar.js +256 -0
  144. package/dist/src/link-prediction/adamic-adar.js.map +1 -0
  145. package/dist/src/link-prediction/common-neighbors.d.ts +50 -0
  146. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -0
  147. package/dist/src/link-prediction/common-neighbors.js +155 -0
  148. package/dist/src/link-prediction/common-neighbors.js.map +1 -0
  149. package/dist/src/link-prediction/index.d.ts +7 -0
  150. package/dist/src/link-prediction/index.d.ts.map +1 -0
  151. package/dist/src/link-prediction/index.js +6 -0
  152. package/dist/src/link-prediction/index.js.map +1 -0
  153. package/dist/src/pathfinding/astar.d.ts +50 -0
  154. package/dist/src/pathfinding/astar.d.ts.map +1 -0
  155. package/dist/src/pathfinding/astar.js +180 -0
  156. package/dist/src/pathfinding/astar.js.map +1 -0
  157. package/dist/src/pathfinding/index.d.ts +3 -0
  158. package/dist/src/pathfinding/index.d.ts.map +1 -0
  159. package/dist/src/pathfinding/index.js +3 -0
  160. package/dist/src/pathfinding/index.js.map +1 -0
  161. package/dist/src/pathfinding/utils.d.ts +18 -0
  162. package/dist/src/pathfinding/utils.d.ts.map +1 -0
  163. package/dist/src/pathfinding/utils.js +68 -0
  164. package/dist/src/pathfinding/utils.js.map +1 -0
  165. package/dist/src/research/grsbm.d.ts +83 -0
  166. package/dist/src/research/grsbm.d.ts.map +1 -0
  167. package/dist/src/research/grsbm.js +411 -0
  168. package/dist/src/research/grsbm.js.map +1 -0
  169. package/dist/src/research/index.d.ts +12 -0
  170. package/dist/src/research/index.d.ts.map +1 -0
  171. package/dist/src/research/index.js +14 -0
  172. package/dist/src/research/index.js.map +1 -0
  173. package/dist/src/research/sync.d.ts +49 -0
  174. package/dist/src/research/sync.d.ts.map +1 -0
  175. package/dist/src/research/sync.js +342 -0
  176. package/dist/src/research/sync.js.map +1 -0
  177. package/dist/src/research/terahac.d.ts +63 -0
  178. package/dist/src/research/terahac.d.ts.map +1 -0
  179. package/dist/src/research/terahac.js +375 -0
  180. package/dist/src/research/terahac.js.map +1 -0
  181. package/dist/src/types/index.d.ts +9 -0
  182. package/dist/src/types/index.d.ts.map +1 -1
  183. package/dist/src/utils/graphNode.d.ts +8 -0
  184. package/dist/src/utils/graphNode.d.ts.map +1 -0
  185. package/dist/src/utils/graphNode.js +2 -0
  186. package/dist/src/utils/graphNode.js.map +1 -0
  187. package/dist/src/utils/priorityQueue.d.ts +21 -0
  188. package/dist/src/utils/priorityQueue.d.ts.map +1 -0
  189. package/dist/src/utils/priorityQueue.js +101 -0
  190. package/dist/src/utils/priorityQueue.js.map +1 -0
  191. package/package.json +13 -2
  192. package/src/algorithms/centrality/betweenness.ts +25 -7
  193. package/src/algorithms/centrality/closeness.ts +7 -7
  194. package/src/algorithms/centrality/degree.ts +1 -1
  195. package/src/algorithms/centrality/eigenvector.ts +162 -0
  196. package/src/algorithms/centrality/hits.ts +210 -0
  197. package/src/algorithms/centrality/index.ts +6 -0
  198. package/src/algorithms/centrality/katz.ts +139 -0
  199. package/src/algorithms/centrality/pagerank.ts +3 -3
  200. package/src/algorithms/community/girvan-newman.ts +372 -0
  201. package/src/algorithms/community/index.ts +13 -0
  202. package/src/algorithms/community/label-propagation.ts +392 -0
  203. package/src/algorithms/community/leiden.ts +572 -0
  204. package/src/algorithms/community/louvain.ts +298 -0
  205. package/src/algorithms/components/connected.ts +7 -7
  206. package/src/algorithms/index.ts +21 -0
  207. package/src/algorithms/matching/bipartite.ts +179 -0
  208. package/src/algorithms/matching/index.ts +8 -0
  209. package/src/algorithms/matching/isomorphism.ts +392 -0
  210. package/src/algorithms/mst/index.ts +4 -0
  211. package/src/algorithms/mst/kruskal.ts +61 -0
  212. package/src/algorithms/mst/prim.ts +80 -0
  213. package/src/algorithms/shortest-path/bellman-ford.ts +2 -2
  214. package/src/algorithms/shortest-path/dijkstra.ts +5 -5
  215. package/src/algorithms/shortest-path/floyd-warshall.ts +170 -0
  216. package/src/algorithms/shortest-path/index.ts +2 -0
  217. package/src/algorithms/traversal/bfs.ts +2 -2
  218. package/src/algorithms/traversal/dfs.ts +11 -11
  219. package/src/clustering/hierarchical.ts +555 -0
  220. package/src/clustering/index.ts +6 -0
  221. package/src/clustering/k-core.ts +460 -0
  222. package/src/clustering/mcl.ts +454 -0
  223. package/src/clustering/spectral.ts +672 -0
  224. package/src/core/graph.ts +34 -6
  225. package/src/data-structures/union-find.ts +2 -2
  226. package/src/flow/ford-fulkerson.ts +459 -0
  227. package/src/flow/index.ts +2 -0
  228. package/src/flow/min-cut.ts +482 -0
  229. package/src/index.ts +5 -0
  230. package/src/link-prediction/adamic-adar.ts +353 -0
  231. package/src/link-prediction/common-neighbors.ts +252 -0
  232. package/src/link-prediction/index.ts +20 -0
  233. package/src/pathfinding/astar.ts +229 -0
  234. package/src/pathfinding/index.ts +2 -0
  235. package/src/pathfinding/utils.ts +87 -0
  236. package/src/research/grsbm.ts +589 -0
  237. package/src/research/index.ts +17 -0
  238. package/src/research/sync.ts +469 -0
  239. package/src/research/terahac.ts +506 -0
  240. package/src/types/index.ts +11 -0
  241. package/src/utils/graphNode.ts +7 -0
  242. package/src/utils/priorityQueue.ts +120 -0
  243. package/dist/test/browser/basic.test.d.ts +0 -2
  244. package/dist/test/browser/basic.test.d.ts.map +0 -1
  245. package/dist/test/browser/basic.test.js +0 -70
  246. package/dist/test/browser/basic.test.js.map +0 -1
  247. package/dist/test/unit/bellman-ford.test.d.ts +0 -2
  248. package/dist/test/unit/bellman-ford.test.d.ts.map +0 -1
  249. package/dist/test/unit/bellman-ford.test.js +0 -231
  250. package/dist/test/unit/bellman-ford.test.js.map +0 -1
  251. package/dist/test/unit/betweenness-centrality.test.d.ts +0 -2
  252. package/dist/test/unit/betweenness-centrality.test.d.ts.map +0 -1
  253. package/dist/test/unit/betweenness-centrality.test.js +0 -311
  254. package/dist/test/unit/betweenness-centrality.test.js.map +0 -1
  255. package/dist/test/unit/bfs.test.d.ts +0 -2
  256. package/dist/test/unit/bfs.test.d.ts.map +0 -1
  257. package/dist/test/unit/bfs.test.js +0 -277
  258. package/dist/test/unit/bfs.test.js.map +0 -1
  259. package/dist/test/unit/closeness.test.d.ts +0 -2
  260. package/dist/test/unit/closeness.test.d.ts.map +0 -1
  261. package/dist/test/unit/closeness.test.js +0 -266
  262. package/dist/test/unit/closeness.test.js.map +0 -1
  263. package/dist/test/unit/connected-components.test.d.ts +0 -2
  264. package/dist/test/unit/connected-components.test.d.ts.map +0 -1
  265. package/dist/test/unit/connected-components.test.js +0 -355
  266. package/dist/test/unit/connected-components.test.js.map +0 -1
  267. package/dist/test/unit/degree-centrality.test.d.ts +0 -2
  268. package/dist/test/unit/degree-centrality.test.d.ts.map +0 -1
  269. package/dist/test/unit/degree-centrality.test.js +0 -260
  270. package/dist/test/unit/degree-centrality.test.js.map +0 -1
  271. package/dist/test/unit/dfs.test.d.ts +0 -2
  272. package/dist/test/unit/dfs.test.d.ts.map +0 -1
  273. package/dist/test/unit/dfs.test.js +0 -350
  274. package/dist/test/unit/dfs.test.js.map +0 -1
  275. package/dist/test/unit/dijkstra.test.d.ts +0 -2
  276. package/dist/test/unit/dijkstra.test.d.ts.map +0 -1
  277. package/dist/test/unit/dijkstra.test.js +0 -311
  278. package/dist/test/unit/dijkstra.test.js.map +0 -1
  279. package/dist/test/unit/graph.test.d.ts +0 -2
  280. package/dist/test/unit/graph.test.d.ts.map +0 -1
  281. package/dist/test/unit/graph.test.js +0 -175
  282. package/dist/test/unit/graph.test.js.map +0 -1
  283. package/dist/test/unit/pagerank.test.d.ts +0 -2
  284. package/dist/test/unit/pagerank.test.d.ts.map +0 -1
  285. package/dist/test/unit/pagerank.test.js +0 -281
  286. package/dist/test/unit/pagerank.test.js.map +0 -1
  287. package/dist/test/unit/priority-queue.test.d.ts +0 -2
  288. package/dist/test/unit/priority-queue.test.d.ts.map +0 -1
  289. package/dist/test/unit/priority-queue.test.js +0 -189
  290. package/dist/test/unit/priority-queue.test.js.map +0 -1
  291. package/dist/test/unit/union-find.test.d.ts +0 -2
  292. package/dist/test/unit/union-find.test.d.ts.map +0 -1
  293. package/dist/test/unit/union-find.test.js +0 -245
  294. package/dist/test/unit/union-find.test.js.map +0 -1
@@ -67,7 +67,7 @@ export function betweennessCentrality(
67
67
 
68
68
  stack.push(current);
69
69
 
70
- for (const neighbor of graph.neighbors(current)) {
70
+ for (const neighbor of Array.from(graph.neighbors(current))) {
71
71
  const currentDistance = distance.get(current);
72
72
  let neighborDistance = distance.get(neighbor);
73
73
 
@@ -112,7 +112,17 @@ export function betweennessCentrality(
112
112
  const vDelta = delta.get(v) ?? 0;
113
113
 
114
114
  if (vSigma > 0 && wSigma > 0) {
115
- const contribution = (vSigma / wSigma) * (1 + wDelta);
115
+ let contribution = (vSigma / wSigma) * (1 + wDelta);
116
+
117
+ // Apply endpoints option: when false, exclude endpoint contributions
118
+ if (!options.endpoints) {
119
+ // For standard betweenness, don't count paths that only involve endpoints
120
+ const isTargetEndpoint = predecessors.get(w)?.length === 0 && w !== source;
121
+ if (isTargetEndpoint) {
122
+ contribution = 0; // Exclude endpoint contributions
123
+ }
124
+ }
125
+
116
126
  delta.set(v, vDelta + contribution);
117
127
  }
118
128
  }
@@ -187,7 +197,7 @@ export function edgeBetweennessCentrality(
187
197
  const edgeCentrality = new Map<string, number>();
188
198
 
189
199
  // Initialize edge centrality scores
190
- for (const edge of graph.edges()) {
200
+ for (const edge of Array.from(graph.edges())) {
191
201
  const edgeKey = `${String(edge.source)}-${String(edge.target)}`;
192
202
  edgeCentrality.set(edgeKey, 0);
193
203
  }
@@ -222,7 +232,7 @@ export function edgeBetweennessCentrality(
222
232
 
223
233
  stack.push(current);
224
234
 
225
- for (const neighbor of graph.neighbors(current)) {
235
+ for (const neighbor of Array.from(graph.neighbors(current))) {
226
236
  const currentDistance = distance.get(current);
227
237
  let neighborDistance = distance.get(neighbor);
228
238
 
@@ -264,7 +274,15 @@ export function edgeBetweennessCentrality(
264
274
  const vSigma = sigma.get(v) ?? 0;
265
275
 
266
276
  if (vSigma > 0 && wSigma > 0) {
267
- const edgeContribution = vSigma / wSigma * (1 + wDelta);
277
+ let edgeContribution = vSigma / wSigma * (1 + wDelta);
278
+
279
+ // Apply endpoints option for edge betweenness
280
+ if (!options.endpoints) {
281
+ const isTargetEndpoint = predecessors.get(w)?.length === 0 && w !== source;
282
+ if (isTargetEndpoint) {
283
+ edgeContribution = 0; // Exclude endpoint contributions
284
+ }
285
+ }
268
286
 
269
287
  // Update edge centrality
270
288
  const edgeKey = `${String(v)}-${String(w)}`;
@@ -281,7 +299,7 @@ export function edgeBetweennessCentrality(
281
299
 
282
300
  // For undirected graphs, divide by 2 (each shortest path is counted twice)
283
301
  if (!graph.isDirected) {
284
- for (const [edgeKey, centrality] of edgeCentrality) {
302
+ for (const [edgeKey, centrality] of Array.from(edgeCentrality)) {
285
303
  edgeCentrality.set(edgeKey, centrality / 2);
286
304
  }
287
305
  }
@@ -292,7 +310,7 @@ export function edgeBetweennessCentrality(
292
310
  const normalizationFactor = graph.isDirected ? (n - 1) * (n - 2) : ((n - 1) * (n - 2)) / 2;
293
311
 
294
312
  if (normalizationFactor > 0) {
295
- for (const [edgeKey, centrality] of edgeCentrality) {
313
+ for (const [edgeKey, centrality] of Array.from(edgeCentrality)) {
296
314
  edgeCentrality.set(edgeKey, centrality / normalizationFactor);
297
315
  }
298
316
  }
@@ -66,7 +66,7 @@ export function nodeClosenessCentrality(
66
66
 
67
67
  if (options.harmonic) {
68
68
  // Harmonic centrality: sum of reciprocals of distances
69
- for (const [targetNode, distance] of distances) {
69
+ for (const [targetNode, distance] of Array.from(distances)) {
70
70
  if (targetNode !== node && distance > 0) {
71
71
  centrality += 1 / distance;
72
72
  }
@@ -76,7 +76,7 @@ export function nodeClosenessCentrality(
76
76
  let totalDistance = 0;
77
77
  let reachableNodes = 0;
78
78
 
79
- for (const [targetNode, distance] of distances) {
79
+ for (const [targetNode, distance] of Array.from(distances)) {
80
80
  if (targetNode !== node) {
81
81
  totalDistance += distance;
82
82
  reachableNodes++;
@@ -134,7 +134,7 @@ function singleSourceShortestPathLengths(
134
134
  }
135
135
 
136
136
  // Explore neighbors
137
- for (const neighbor of graph.neighbors(current.node)) {
137
+ for (const neighbor of Array.from(graph.neighbors(current.node))) {
138
138
  if (!visited.has(neighbor)) {
139
139
  const newDistance = current.distance + 1;
140
140
 
@@ -192,7 +192,7 @@ export function nodeWeightedClosenessCentrality(
192
192
 
193
193
  if (options.harmonic) {
194
194
  // Harmonic centrality: sum of reciprocals of distances
195
- for (const [targetNode, distance] of distances) {
195
+ for (const [targetNode, distance] of Array.from(distances)) {
196
196
  if (targetNode !== node && distance > 0 && distance < Infinity) {
197
197
  centrality += 1 / distance;
198
198
  }
@@ -202,7 +202,7 @@ export function nodeWeightedClosenessCentrality(
202
202
  let totalDistance = 0;
203
203
  let reachableNodes = 0;
204
204
 
205
- for (const [targetNode, distance] of distances) {
205
+ for (const [targetNode, distance] of Array.from(distances)) {
206
206
  if (targetNode !== node && distance < Infinity) {
207
207
  totalDistance += distance;
208
208
  reachableNodes++;
@@ -244,7 +244,7 @@ function dijkstraDistances(
244
244
  const pq: {node: NodeId, distance: number}[] = [];
245
245
 
246
246
  // Initialize distances
247
- for (const node of graph.nodes()) {
247
+ for (const node of Array.from(graph.nodes())) {
248
248
  distances.set(node.id, node.id === source ? 0 : Infinity);
249
249
  }
250
250
 
@@ -267,7 +267,7 @@ function dijkstraDistances(
267
267
  }
268
268
 
269
269
  // Explore neighbors
270
- for (const neighbor of graph.neighbors(current.node)) {
270
+ for (const neighbor of Array.from(graph.neighbors(current.node))) {
271
271
  if (visited.has(neighbor)) {
272
272
  continue;
273
273
  }
@@ -21,7 +21,7 @@ export function degreeCentrality(
21
21
  // Calculate normalization factor
22
22
  const maxPossibleDegree = graph.isDirected ? nodeCount - 1 : nodeCount - 1;
23
23
 
24
- for (const node of graph.nodes()) {
24
+ for (const node of Array.from(graph.nodes())) {
25
25
  let degree: number;
26
26
 
27
27
  if (graph.isDirected && options.mode) {
@@ -0,0 +1,162 @@
1
+ import type {Graph} from "../../core/graph.js";
2
+ import type {CentralityOptions, CentralityResult} from "../../types/index.js";
3
+
4
+ /**
5
+ * Eigenvector centrality implementation
6
+ *
7
+ * Measures the influence of a node based on the influence of its neighbors.
8
+ * A node has high eigenvector centrality if it is connected to nodes
9
+ * that themselves have high eigenvector centrality.
10
+ *
11
+ * Time complexity: O(V + E) per iteration
12
+ * Space complexity: O(V)
13
+ */
14
+
15
+ export interface EigenvectorCentralityOptions extends CentralityOptions {
16
+ maxIterations?: number; // Maximum iterations (default: 100)
17
+ tolerance?: number; // Convergence tolerance (default: 1e-6)
18
+ startVector?: Map<string, number>; // Initial vector (optional)
19
+ }
20
+
21
+ /**
22
+ * Calculate eigenvector centrality for all nodes in the graph
23
+ * Uses the power iteration method to find the dominant eigenvector
24
+ */
25
+ export function eigenvectorCentrality(
26
+ graph: Graph,
27
+ options: EigenvectorCentralityOptions = {},
28
+ ): CentralityResult {
29
+ const {
30
+ maxIterations = 100,
31
+ tolerance = 1e-6,
32
+ normalized = true,
33
+ startVector,
34
+ } = options;
35
+
36
+ const centrality: CentralityResult = {};
37
+ const nodes = Array.from(graph.nodes());
38
+ const nodeIds = nodes.map((node) => node.id);
39
+
40
+ if (nodeIds.length === 0) {
41
+ return centrality;
42
+ }
43
+
44
+ // Initialize the eigenvector
45
+ let currentVector = new Map<string, number>();
46
+ let previousVector = new Map<string, number>();
47
+
48
+ if (startVector) {
49
+ // Use provided start vector
50
+ for (const nodeId of nodeIds) {
51
+ const key = nodeId.toString();
52
+ currentVector.set(key, startVector.get(key) ?? 1.0 / Math.sqrt(nodeIds.length));
53
+ }
54
+ } else {
55
+ // Initialize with uniform distribution
56
+ const initialValue = 1.0 / Math.sqrt(nodeIds.length);
57
+ for (const nodeId of nodeIds) {
58
+ currentVector.set(nodeId.toString(), initialValue);
59
+ }
60
+ }
61
+
62
+ // Power iteration
63
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
64
+ previousVector = new Map(currentVector);
65
+ currentVector = new Map();
66
+
67
+ // Update each node's centrality based on neighbors
68
+ for (const nodeId of nodeIds) {
69
+ let sum = 0;
70
+ const neighbors = Array.from(graph.neighbors(nodeId));
71
+
72
+ for (const neighbor of neighbors) {
73
+ const neighborKey = neighbor.toString();
74
+ const prevValue = previousVector.get(neighborKey);
75
+ sum += prevValue ?? 0;
76
+ }
77
+
78
+ currentVector.set(nodeId.toString(), sum);
79
+ }
80
+
81
+ // Normalize the vector
82
+ let norm = 0;
83
+ for (const value of Array.from(currentVector.values())) {
84
+ norm += value * value;
85
+ }
86
+ norm = Math.sqrt(norm);
87
+
88
+ if (norm === 0) {
89
+ // Graph has no edges or is disconnected
90
+ for (const nodeId of nodeIds) {
91
+ centrality[nodeId.toString()] = 0;
92
+ }
93
+ return centrality;
94
+ }
95
+
96
+ // Normalize the vector
97
+ for (const [nodeId, value] of Array.from(currentVector)) {
98
+ currentVector.set(nodeId, value / norm);
99
+ }
100
+
101
+ // Check for convergence
102
+ let maxDiff = 0;
103
+ for (const [nodeId, value] of Array.from(currentVector)) {
104
+ const prevValue = previousVector.get(nodeId) ?? 0;
105
+ const diff = Math.abs(value - prevValue);
106
+ maxDiff = Math.max(maxDiff, diff);
107
+ }
108
+
109
+ if (maxDiff < tolerance) {
110
+ break;
111
+ }
112
+ }
113
+
114
+ // Prepare results
115
+ for (const [nodeId, value] of Array.from(currentVector)) {
116
+ centrality[nodeId] = value;
117
+ }
118
+
119
+ // Additional normalization if requested (normalize to [0,1] range)
120
+ if (normalized) {
121
+ let maxValue = 0;
122
+ let minValue = Number.POSITIVE_INFINITY;
123
+
124
+ for (const value of Object.values(centrality)) {
125
+ maxValue = Math.max(maxValue, value);
126
+ minValue = Math.min(minValue, value);
127
+ }
128
+
129
+ const range = maxValue - minValue;
130
+ if (range > 0) {
131
+ for (const nodeId of Object.keys(centrality)) {
132
+ const centralityValue = centrality[nodeId];
133
+ if (centralityValue !== undefined) {
134
+ centrality[nodeId] = (centralityValue - minValue) / range;
135
+ }
136
+ }
137
+ } else {
138
+ // All values are the same, set to 1
139
+ for (const nodeId of Object.keys(centrality)) {
140
+ centrality[nodeId] = maxValue > 0 ? 1 : 0;
141
+ }
142
+ }
143
+ }
144
+
145
+ return centrality;
146
+ }
147
+
148
+ /**
149
+ * Calculate eigenvector centrality for a specific node
150
+ */
151
+ export function nodeEigenvectorCentrality(
152
+ graph: Graph,
153
+ nodeId: string | number,
154
+ options: EigenvectorCentralityOptions = {},
155
+ ): number {
156
+ if (!graph.hasNode(nodeId)) {
157
+ throw new Error(`Node ${String(nodeId)} not found in graph`);
158
+ }
159
+
160
+ const centrality = eigenvectorCentrality(graph, options);
161
+ return centrality[nodeId.toString()] ?? 0;
162
+ }
@@ -0,0 +1,210 @@
1
+ import type {Graph} from "../../core/graph.js";
2
+ import type {CentralityOptions} from "../../types/index.js";
3
+
4
+ /**
5
+ * HITS (Hyperlink-Induced Topic Search) algorithm implementation
6
+ *
7
+ * Identifies hub and authority scores for nodes in a graph.
8
+ * - Authorities: nodes with valuable information (pointed to by hubs)
9
+ * - Hubs: nodes that point to many authorities
10
+ *
11
+ * Originally designed for web page ranking but applicable to any directed network.
12
+ *
13
+ * Time complexity: O(V + E) per iteration
14
+ * Space complexity: O(V)
15
+ */
16
+
17
+ export interface HITSResult {
18
+ hubs: Record<string, number>;
19
+ authorities: Record<string, number>;
20
+ }
21
+
22
+ export interface HITSOptions extends CentralityOptions {
23
+ maxIterations?: number; // Maximum iterations (default: 100)
24
+ tolerance?: number; // Convergence tolerance (default: 1e-6)
25
+ }
26
+
27
+ /**
28
+ * Calculate HITS hub and authority scores for all nodes in the graph
29
+ * Uses iterative method with normalization
30
+ */
31
+ export function hits(
32
+ graph: Graph,
33
+ options: HITSOptions = {},
34
+ ): HITSResult {
35
+ const {
36
+ maxIterations = 100,
37
+ tolerance = 1e-6,
38
+ normalized = true,
39
+ } = options;
40
+
41
+ const hubs: Record<string, number> = {};
42
+ const authorities: Record<string, number> = {};
43
+
44
+ const nodes = Array.from(graph.nodes());
45
+ const nodeIds = nodes.map((node) => node.id);
46
+
47
+ if (nodeIds.length === 0) {
48
+ return {hubs, authorities};
49
+ }
50
+
51
+ // Initialize scores
52
+ let currentHubs = new Map<string, number>();
53
+ let currentAuthorities = new Map<string, number>();
54
+ let previousHubs = new Map<string, number>();
55
+ let previousAuthorities = new Map<string, number>();
56
+
57
+ // Start with uniform distribution
58
+ const initialValue = 1.0 / Math.sqrt(nodeIds.length);
59
+ for (const nodeId of nodeIds) {
60
+ const key = nodeId.toString();
61
+ currentHubs.set(key, initialValue);
62
+ currentAuthorities.set(key, initialValue);
63
+ }
64
+
65
+ // Iterative computation
66
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
67
+ previousHubs = new Map(currentHubs);
68
+ previousAuthorities = new Map(currentAuthorities);
69
+
70
+ // Update authority scores
71
+ // Authority score = sum of hub scores of nodes pointing to it
72
+ const newAuthorities = new Map<string, number>();
73
+ for (const nodeId of nodeIds) {
74
+ let authorityScore = 0;
75
+ const inNeighbors = Array.from(graph.inNeighbors(nodeId));
76
+
77
+ for (const inNeighbor of inNeighbors) {
78
+ const neighborKey = inNeighbor.toString();
79
+ authorityScore += previousHubs.get(neighborKey) ?? 0;
80
+ }
81
+
82
+ newAuthorities.set(nodeId.toString(), authorityScore);
83
+ }
84
+
85
+ // Update hub scores
86
+ // Hub score = sum of authority scores of nodes it points to
87
+ const newHubs = new Map<string, number>();
88
+ for (const nodeId of nodeIds) {
89
+ let hubScore = 0;
90
+ const outNeighbors = Array.from(graph.outNeighbors(nodeId));
91
+
92
+ for (const outNeighbor of outNeighbors) {
93
+ const neighborKey = outNeighbor.toString();
94
+ hubScore += previousAuthorities.get(neighborKey) ?? 0;
95
+ }
96
+
97
+ newHubs.set(nodeId.toString(), hubScore);
98
+ }
99
+
100
+ // Normalize authority scores
101
+ let authNorm = 0;
102
+ for (const value of Array.from(newAuthorities.values())) {
103
+ authNorm += value * value;
104
+ }
105
+ authNorm = Math.sqrt(authNorm);
106
+
107
+ if (authNorm > 0) {
108
+ for (const [nodeId, value] of Array.from(newAuthorities)) {
109
+ newAuthorities.set(nodeId, value / authNorm);
110
+ }
111
+ }
112
+
113
+ // Normalize hub scores
114
+ let hubNorm = 0;
115
+ for (const value of Array.from(newHubs.values())) {
116
+ hubNorm += value * value;
117
+ }
118
+ hubNorm = Math.sqrt(hubNorm);
119
+
120
+ if (hubNorm > 0) {
121
+ for (const [nodeId, value] of Array.from(newHubs)) {
122
+ newHubs.set(nodeId, value / hubNorm);
123
+ }
124
+ }
125
+
126
+ currentAuthorities = newAuthorities;
127
+ currentHubs = newHubs;
128
+
129
+ // Check for convergence
130
+ let maxDiff = 0;
131
+ for (const [nodeId, value] of currentHubs) {
132
+ const prevValue = previousHubs.get(nodeId) ?? 0;
133
+ const diff = Math.abs(value - prevValue);
134
+ maxDiff = Math.max(maxDiff, diff);
135
+ }
136
+ for (const [nodeId, value] of currentAuthorities) {
137
+ const prevValue = previousAuthorities.get(nodeId) ?? 0;
138
+ const diff = Math.abs(value - prevValue);
139
+ maxDiff = Math.max(maxDiff, diff);
140
+ }
141
+
142
+ if (maxDiff < tolerance) {
143
+ break;
144
+ }
145
+ }
146
+
147
+ // Prepare results
148
+ for (const [nodeId, value] of currentHubs) {
149
+ hubs[nodeId] = value;
150
+ }
151
+ for (const [nodeId, value] of currentAuthorities) {
152
+ authorities[nodeId] = value;
153
+ }
154
+
155
+ // The algorithm already normalizes to unit L2 norm during iterations
156
+ // Additional max normalization is only applied if explicitly requested
157
+ if (!normalized) {
158
+ // If normalization is explicitly disabled, normalize to [0, 1] range
159
+ let maxHub = 0;
160
+ let maxAuth = 0;
161
+
162
+ for (const value of Object.values(hubs)) {
163
+ maxHub = Math.max(maxHub, value);
164
+ }
165
+ for (const value of Object.values(authorities)) {
166
+ maxAuth = Math.max(maxAuth, value);
167
+ }
168
+
169
+ if (maxHub > 0) {
170
+ for (const nodeId of Object.keys(hubs)) {
171
+ const hubValue = hubs[nodeId];
172
+ if (hubValue !== undefined) {
173
+ hubs[nodeId] = hubValue / maxHub;
174
+ }
175
+ }
176
+ }
177
+
178
+ if (maxAuth > 0) {
179
+ for (const nodeId of Object.keys(authorities)) {
180
+ const authValue = authorities[nodeId];
181
+ if (authValue !== undefined) {
182
+ authorities[nodeId] = authValue / maxAuth;
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ return {hubs, authorities};
189
+ }
190
+
191
+ /**
192
+ * Calculate HITS scores for a specific node
193
+ */
194
+ export function nodeHITS(
195
+ graph: Graph,
196
+ nodeId: string | number,
197
+ options: HITSOptions = {},
198
+ ): {hub: number, authority: number} {
199
+ if (!graph.hasNode(nodeId)) {
200
+ throw new Error(`Node ${String(nodeId)} not found in graph`);
201
+ }
202
+
203
+ const result = hits(graph, options);
204
+ const key = nodeId.toString();
205
+
206
+ return {
207
+ hub: result.hubs[key] ?? 0,
208
+ authority: result.authorities[key] ?? 0,
209
+ };
210
+ }
@@ -7,5 +7,11 @@ export {betweennessCentrality, edgeBetweennessCentrality, nodeBetweennessCentral
7
7
  export type {ClosenessCentralityOptions} from "./closeness.js";
8
8
  export {closenessCentrality, nodeClosenessCentrality, nodeWeightedClosenessCentrality, weightedClosenessCentrality} from "./closeness.js";
9
9
  export {degreeCentrality, nodeDegreeCentrality} from "./degree.js";
10
+ export type {EigenvectorCentralityOptions} from "./eigenvector.js";
11
+ export {eigenvectorCentrality, nodeEigenvectorCentrality} from "./eigenvector.js";
12
+ export type {HITSOptions, HITSResult} from "./hits.js";
13
+ export {hits, nodeHITS} from "./hits.js";
14
+ export type {KatzCentralityOptions} from "./katz.js";
15
+ export {katzCentrality, nodeKatzCentrality} from "./katz.js";
10
16
  export type {PageRankOptions, PageRankResult} from "./pagerank.js";
11
17
  export {pageRank, pageRankCentrality, personalizedPageRank, topPageRankNodes} from "./pagerank.js";
@@ -0,0 +1,139 @@
1
+ import type {Graph} from "../../core/graph.js";
2
+ import type {CentralityOptions, CentralityResult} from "../../types/index.js";
3
+
4
+ /**
5
+ * Katz centrality implementation
6
+ *
7
+ * A generalization of eigenvector centrality that gives each node a base amount
8
+ * of influence regardless of its position in the network. Uses an attenuation
9
+ * factor (alpha) to control how much a node's centrality depends on its neighbors.
10
+ *
11
+ * Katz centrality = alpha * sum(neighbors' centrality) + beta
12
+ *
13
+ * Time complexity: O(V + E) per iteration
14
+ * Space complexity: O(V)
15
+ */
16
+
17
+ export interface KatzCentralityOptions extends CentralityOptions {
18
+ alpha?: number; // Attenuation factor (default: 0.1)
19
+ beta?: number; // Base centrality weight (default: 1.0)
20
+ maxIterations?: number; // Maximum iterations (default: 100)
21
+ tolerance?: number; // Convergence tolerance (default: 1e-6)
22
+ }
23
+
24
+ /**
25
+ * Calculate Katz centrality for all nodes in the graph
26
+ * Uses iterative method to compute centrality scores
27
+ */
28
+ export function katzCentrality(
29
+ graph: Graph,
30
+ options: KatzCentralityOptions = {},
31
+ ): CentralityResult {
32
+ const {
33
+ alpha = 0.1,
34
+ beta = 1.0,
35
+ maxIterations = 100,
36
+ tolerance = 1e-6,
37
+ normalized = true,
38
+ } = options;
39
+
40
+ const centrality: CentralityResult = {};
41
+ const nodes = Array.from(graph.nodes());
42
+ const nodeIds = nodes.map((node) => node.id);
43
+
44
+ if (nodeIds.length === 0) {
45
+ return centrality;
46
+ }
47
+
48
+ // Check if alpha is valid (should be less than 1/lambda_max)
49
+ // For simplicity, we'll trust the user's choice or use conservative default
50
+
51
+ // Initialize centrality scores
52
+ let currentScores = new Map<string, number>();
53
+ let previousScores = new Map<string, number>();
54
+
55
+ // Start with beta for all nodes
56
+ for (const nodeId of nodeIds) {
57
+ currentScores.set(nodeId.toString(), beta);
58
+ }
59
+
60
+ // Iterative computation
61
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
62
+ previousScores = new Map(currentScores);
63
+ currentScores = new Map();
64
+
65
+ // Update each node's centrality
66
+ for (const nodeId of nodeIds) {
67
+ let sum = 0;
68
+
69
+ // For directed graphs, use in-neighbors
70
+ // For undirected graphs, use all neighbors
71
+ const neighbors = graph.isDirected ?
72
+ Array.from(graph.inNeighbors(nodeId)) :
73
+ graph.neighbors(nodeId);
74
+
75
+ for (const neighbor of neighbors) {
76
+ const neighborKey = neighbor.toString();
77
+ sum += previousScores.get(neighborKey) ?? 0;
78
+ }
79
+
80
+ currentScores.set(nodeId.toString(), (alpha * sum) + beta);
81
+ }
82
+
83
+ // Check for convergence
84
+ let maxDiff = 0;
85
+ for (const [nodeId, value] of Array.from(currentScores)) {
86
+ const prevValue = previousScores.get(nodeId) ?? 0;
87
+ const diff = Math.abs(value - prevValue);
88
+ maxDiff = Math.max(maxDiff, diff);
89
+ }
90
+
91
+ if (maxDiff < tolerance) {
92
+ break;
93
+ }
94
+ }
95
+
96
+ // Prepare results
97
+ for (const [nodeId, value] of Array.from(currentScores)) {
98
+ centrality[nodeId] = value;
99
+ }
100
+
101
+ // Normalize if requested
102
+ if (normalized) {
103
+ let maxValue = 0;
104
+ let minValue = Number.POSITIVE_INFINITY;
105
+
106
+ for (const value of Object.values(centrality)) {
107
+ maxValue = Math.max(maxValue, value);
108
+ minValue = Math.min(minValue, value);
109
+ }
110
+
111
+ const range = maxValue - minValue;
112
+ if (range > 0) {
113
+ for (const nodeId of Object.keys(centrality)) {
114
+ const centralityValue = centrality[nodeId];
115
+ if (centralityValue !== undefined) {
116
+ centrality[nodeId] = (centralityValue - minValue) / range;
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ return centrality;
123
+ }
124
+
125
+ /**
126
+ * Calculate Katz centrality for a specific node
127
+ */
128
+ export function nodeKatzCentrality(
129
+ graph: Graph,
130
+ nodeId: string | number,
131
+ options: KatzCentralityOptions = {},
132
+ ): number {
133
+ if (!graph.hasNode(nodeId)) {
134
+ throw new Error(`Node ${String(nodeId)} not found in graph`);
135
+ }
136
+
137
+ const centrality = katzCentrality(graph, options);
138
+ return centrality[nodeId.toString()] ?? 0;
139
+ }