@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
package/src/core/graph.ts CHANGED
@@ -51,7 +51,7 @@ export class Graph {
51
51
  // Remove all edges connected to this node
52
52
  const outgoingEdges = this.adjacencyList.get(id);
53
53
  if (outgoingEdges) {
54
- for (const targetId of outgoingEdges.keys()) {
54
+ for (const targetId of Array.from(outgoingEdges.keys())) {
55
55
  this.removeEdge(id, targetId);
56
56
  }
57
57
  }
@@ -59,13 +59,13 @@ export class Graph {
59
59
  if (this.config.directed) {
60
60
  const incomingEdges = this.incomingEdges.get(id);
61
61
  if (incomingEdges) {
62
- for (const sourceId of incomingEdges.keys()) {
62
+ for (const sourceId of Array.from(incomingEdges.keys())) {
63
63
  this.removeEdge(sourceId, id);
64
64
  }
65
65
  }
66
66
  } else {
67
67
  // For undirected graphs, also remove edges where this node is the target
68
- for (const [nodeId, edges] of this.adjacencyList) {
68
+ for (const [nodeId, edges] of Array.from(this.adjacencyList)) {
69
69
  if (edges.has(id)) {
70
70
  this.removeEdge(nodeId, id);
71
71
  }
@@ -222,7 +222,7 @@ export class Graph {
222
222
  * Get all edges in the graph
223
223
  */
224
224
  *edges(): IterableIterator<Edge> {
225
- for (const [source, edges] of this.adjacencyList) {
225
+ for (const [source, edges] of Array.from(this.adjacencyList)) {
226
226
  for (const edge of edges.values()) {
227
227
  // For undirected graphs, only yield each edge once
228
228
  if (!this.config.directed && source > edge.target) {
@@ -300,12 +300,12 @@ export class Graph {
300
300
  const cloned = new Graph(this.config);
301
301
 
302
302
  // Copy nodes
303
- for (const node of this.nodeMap.values()) {
303
+ for (const node of Array.from(this.nodeMap.values())) {
304
304
  cloned.addNode(node.id, node.data ? {... node.data} : undefined);
305
305
  }
306
306
 
307
307
  // Copy edges
308
- for (const edge of this.edges()) {
308
+ for (const edge of Array.from(this.edges())) {
309
309
  cloned.addEdge(
310
310
  edge.source,
311
311
  edge.target,
@@ -323,4 +323,32 @@ export class Graph {
323
323
  getConfig(): GraphConfig {
324
324
  return {... this.config};
325
325
  }
326
+
327
+ /**
328
+ * Clear all nodes and edges from the graph
329
+ */
330
+ clear(): void {
331
+ this.nodeMap.clear();
332
+ this.adjacencyList.clear();
333
+ this.incomingEdges.clear();
334
+ this.edgeCount = 0;
335
+ }
336
+
337
+ /**
338
+ * Get the number of unique edges in the graph
339
+ * For undirected graphs, each edge is counted once
340
+ */
341
+ get uniqueEdgeCount(): number {
342
+ if (this.config.directed) {
343
+ return this.edgeCount;
344
+ }
345
+
346
+ // For undirected graphs, we need to count each edge only once
347
+ let count = 0;
348
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
349
+ for (const _edge of Array.from(this.edges())) {
350
+ count++;
351
+ }
352
+ return count;
353
+ }
326
354
  }
@@ -103,7 +103,7 @@ export class UnionFind {
103
103
  const root = this.find(element);
104
104
  const component: NodeId[] = [];
105
105
 
106
- for (const [node] of this.parent) {
106
+ for (const [node] of Array.from(this.parent)) {
107
107
  if (this.find(node) === root) {
108
108
  component.push(node);
109
109
  }
@@ -118,7 +118,7 @@ export class UnionFind {
118
118
  getAllComponents(): NodeId[][] {
119
119
  const componentMap = new Map<NodeId, NodeId[]>();
120
120
 
121
- for (const [node] of this.parent) {
121
+ for (const [node] of Array.from(this.parent)) {
122
122
  const root = this.find(node);
123
123
 
124
124
  if (!componentMap.has(root)) {
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Ford-Fulkerson Algorithm for Maximum Flow
3
+ *
4
+ * Finds the maximum flow from source to sink in a flow network
5
+ * using the method of augmenting paths.
6
+ */
7
+
8
+ export interface FlowEdge {
9
+ from: string;
10
+ to: string;
11
+ capacity: number;
12
+ flow: number;
13
+ }
14
+
15
+ export interface FlowNetwork {
16
+ nodes: Set<string>;
17
+ edges: Map<string, Map<string, FlowEdge>>;
18
+ }
19
+
20
+ export interface MaxFlowResult {
21
+ maxFlow: number;
22
+ flowGraph: Map<string, Map<string, number>>;
23
+ minCut?: {source: Set<string>, sink: Set<string>, edges: [string, string][]};
24
+ }
25
+
26
+ /**
27
+ * Ford-Fulkerson algorithm using DFS for finding augmenting paths
28
+ *
29
+ * @param graph - Adjacency list representation with capacities
30
+ * @param source - Source node
31
+ * @param sink - Sink node
32
+ * @returns Maximum flow value and flow graph
33
+ *
34
+ * Time Complexity: O(E * f) where f is the maximum flow
35
+ * Space Complexity: O(V + E)
36
+ */
37
+ export function fordFulkerson(
38
+ graph: Map<string, Map<string, number>>,
39
+ source: string,
40
+ sink: string,
41
+ ): MaxFlowResult {
42
+ if (!graph.has(source) || !graph.has(sink)) {
43
+ return {maxFlow: 0, flowGraph: new Map()};
44
+ }
45
+
46
+ // Create residual graph
47
+ const residualGraph = createResidualGraph(graph);
48
+ const flowGraph = new Map<string, Map<string, number>>();
49
+
50
+ // Initialize flow graph
51
+ for (const [u, neighbors] of graph) {
52
+ flowGraph.set(u, new Map());
53
+ for (const v of neighbors.keys()) {
54
+ const uFlowNeighbors = flowGraph.get(u);
55
+ if (uFlowNeighbors) {
56
+ uFlowNeighbors.set(v, 0);
57
+ }
58
+ }
59
+ }
60
+
61
+ let maxFlow = 0;
62
+ let path = findAugmentingPath(residualGraph, source, sink);
63
+
64
+ while (path !== null) {
65
+ // Find minimum capacity along the path
66
+ let pathFlow = Infinity;
67
+ for (let i = 0; i < path.length - 1; i++) {
68
+ const u = path[i];
69
+ const v = path[i + 1];
70
+ if (!u || !v) {
71
+ continue;
72
+ }
73
+
74
+ const uEdges = residualGraph.get(u);
75
+ if (!uEdges) {
76
+ continue;
77
+ }
78
+
79
+ const capacity = uEdges.get(v);
80
+ if (capacity !== undefined) {
81
+ pathFlow = Math.min(pathFlow, capacity);
82
+ }
83
+ }
84
+
85
+ // Update residual capacities
86
+ for (let i = 0; i < path.length - 1; i++) {
87
+ const u = path[i];
88
+ const v = path[i + 1];
89
+ if (!u || !v) {
90
+ continue;
91
+ }
92
+
93
+ // Update residual graph
94
+ const uEdges = residualGraph.get(u);
95
+ if (uEdges) {
96
+ const currentCapacity = uEdges.get(v);
97
+ if (currentCapacity !== undefined) {
98
+ uEdges.set(v, currentCapacity - pathFlow);
99
+ }
100
+ }
101
+
102
+ // Add reverse edge
103
+ if (!residualGraph.has(v)) {
104
+ residualGraph.set(v, new Map());
105
+ }
106
+
107
+ const vEdges = residualGraph.get(v);
108
+ if (vEdges) {
109
+ vEdges.set(u, (vEdges.get(u) ?? 0) + pathFlow);
110
+ }
111
+
112
+ // Update flow graph
113
+ if (graph.get(u)?.has(v)) {
114
+ const uFlowEdges = flowGraph.get(u);
115
+ if (uFlowEdges) {
116
+ const currentFlow = uFlowEdges.get(v) ?? 0;
117
+ uFlowEdges.set(v, currentFlow + pathFlow);
118
+ }
119
+ } else if (graph.get(v)?.has(u)) {
120
+ // This is a reverse flow
121
+ const vFlowEdges = flowGraph.get(v);
122
+ if (vFlowEdges) {
123
+ const currentFlow = vFlowEdges.get(u) ?? 0;
124
+ vFlowEdges.set(u, currentFlow - pathFlow);
125
+ }
126
+ }
127
+ }
128
+
129
+ maxFlow += pathFlow;
130
+ path = findAugmentingPath(residualGraph, source, sink);
131
+ }
132
+
133
+ // Find minimum cut
134
+ const minCut = findMinCut(residualGraph, source);
135
+
136
+ return {maxFlow, flowGraph, minCut};
137
+ }
138
+
139
+ /**
140
+ * Edmonds-Karp algorithm (Ford-Fulkerson with BFS)
141
+ * More efficient implementation with better time complexity
142
+ *
143
+ * Time Complexity: O(V * E²)
144
+ */
145
+ export function edmondsKarp(
146
+ graph: Map<string, Map<string, number>>,
147
+ source: string,
148
+ sink: string,
149
+ ): MaxFlowResult {
150
+ if (!graph.has(source) || !graph.has(sink)) {
151
+ return {maxFlow: 0, flowGraph: new Map()};
152
+ }
153
+
154
+ const residualGraph = createResidualGraph(graph);
155
+ const flowGraph = new Map<string, Map<string, number>>();
156
+
157
+ // Initialize flow graph
158
+ for (const [u, neighbors] of graph) {
159
+ flowGraph.set(u, new Map());
160
+ for (const v of neighbors.keys()) {
161
+ const uFlowNeighbors = flowGraph.get(u);
162
+ if (uFlowNeighbors) {
163
+ uFlowNeighbors.set(v, 0);
164
+ }
165
+ }
166
+ }
167
+
168
+ let maxFlow = 0;
169
+ let path = findAugmentingPathBFS(residualGraph, source, sink);
170
+
171
+ while (path !== null) {
172
+ let pathFlow = Infinity;
173
+ for (let i = 0; i < path.length - 1; i++) {
174
+ const u = path[i];
175
+ const v = path[i + 1];
176
+ if (!u || !v) {
177
+ continue;
178
+ }
179
+
180
+ const uEdges = residualGraph.get(u);
181
+ if (!uEdges) {
182
+ continue;
183
+ }
184
+
185
+ const capacity = uEdges.get(v);
186
+ if (capacity !== undefined) {
187
+ pathFlow = Math.min(pathFlow, capacity);
188
+ }
189
+ }
190
+
191
+ for (let i = 0; i < path.length - 1; i++) {
192
+ const u = path[i];
193
+ const v = path[i + 1];
194
+ if (!u || !v) {
195
+ continue;
196
+ }
197
+
198
+ const uEdges = residualGraph.get(u);
199
+ if (uEdges) {
200
+ const currentCapacity = uEdges.get(v);
201
+ if (currentCapacity !== undefined) {
202
+ uEdges.set(v, currentCapacity - pathFlow);
203
+ }
204
+ }
205
+
206
+ if (!residualGraph.has(v)) {
207
+ residualGraph.set(v, new Map());
208
+ }
209
+
210
+ const vEdges = residualGraph.get(v);
211
+ if (vEdges) {
212
+ vEdges.set(u, (vEdges.get(u) ?? 0) + pathFlow);
213
+ }
214
+
215
+ if (graph.get(u)?.has(v)) {
216
+ const uFlowEdges = flowGraph.get(u);
217
+ if (uFlowEdges) {
218
+ const currentFlow = uFlowEdges.get(v) ?? 0;
219
+ uFlowEdges.set(v, currentFlow + pathFlow);
220
+ }
221
+ } else if (graph.get(v)?.has(u)) {
222
+ const vFlowEdges = flowGraph.get(v);
223
+ if (vFlowEdges) {
224
+ const currentFlow = vFlowEdges.get(u) ?? 0;
225
+ vFlowEdges.set(u, currentFlow - pathFlow);
226
+ }
227
+ }
228
+ }
229
+
230
+ maxFlow += pathFlow;
231
+ path = findAugmentingPathBFS(residualGraph, source, sink);
232
+ }
233
+
234
+ const minCut = findMinCut(residualGraph, source);
235
+
236
+ return {maxFlow, flowGraph, minCut};
237
+ }
238
+
239
+ /**
240
+ * Create residual graph from original graph
241
+ */
242
+ function createResidualGraph(
243
+ graph: Map<string, Map<string, number>>,
244
+ ): Map<string, Map<string, number>> {
245
+ const residual = new Map<string, Map<string, number>>();
246
+
247
+ for (const [u, neighbors] of graph) {
248
+ residual.set(u, new Map());
249
+ for (const [v, capacity] of neighbors) {
250
+ const uResidualNeighbors = residual.get(u);
251
+ if (uResidualNeighbors) {
252
+ uResidualNeighbors.set(v, capacity);
253
+ }
254
+ }
255
+ }
256
+
257
+ return residual;
258
+ }
259
+
260
+ /**
261
+ * Find augmenting path using DFS
262
+ */
263
+ function findAugmentingPath(
264
+ residualGraph: Map<string, Map<string, number>>,
265
+ source: string,
266
+ sink: string,
267
+ ): string[] | null {
268
+ const visited = new Set<string>();
269
+ const path: string[] = [];
270
+
271
+ function dfs(node: string): boolean {
272
+ if (node === sink) {
273
+ path.push(node);
274
+ return true;
275
+ }
276
+
277
+ visited.add(node);
278
+ path.push(node);
279
+
280
+ const neighbors = residualGraph.get(node);
281
+ if (neighbors) {
282
+ for (const [neighbor, capacity] of neighbors) {
283
+ if (!visited.has(neighbor) && capacity > 0) {
284
+ if (dfs(neighbor)) {
285
+ return true;
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ path.pop();
292
+ return false;
293
+ }
294
+
295
+ if (dfs(source)) {
296
+ return path;
297
+ }
298
+
299
+ return null;
300
+ }
301
+
302
+ /**
303
+ * Find augmenting path using BFS (for Edmonds-Karp)
304
+ */
305
+ function findAugmentingPathBFS(
306
+ residualGraph: Map<string, Map<string, number>>,
307
+ source: string,
308
+ sink: string,
309
+ ): string[] | null {
310
+ const parent = new Map<string, string>();
311
+ const visited = new Set<string>();
312
+ const queue: string[] = [source];
313
+ visited.add(source);
314
+
315
+ while (queue.length > 0) {
316
+ const current = queue.shift();
317
+ if (!current) {
318
+ continue;
319
+ }
320
+
321
+ const neighbors = residualGraph.get(current);
322
+ if (neighbors) {
323
+ for (const [neighbor, capacity] of neighbors) {
324
+ if (!visited.has(neighbor) && capacity > 0) {
325
+ visited.add(neighbor);
326
+ parent.set(neighbor, current);
327
+
328
+ if (neighbor === sink) {
329
+ // Reconstruct path
330
+ const path: string[] = [];
331
+ let node = sink;
332
+ while (node !== source) {
333
+ path.unshift(node);
334
+ const parentNode = parent.get(node);
335
+ if (!parentNode) {
336
+ break;
337
+ }
338
+
339
+ node = parentNode;
340
+ }
341
+ path.unshift(source);
342
+ return path;
343
+ }
344
+
345
+ queue.push(neighbor);
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ return null;
352
+ }
353
+
354
+ /**
355
+ * Find minimum cut from source side
356
+ */
357
+ function findMinCut(
358
+ residualGraph: Map<string, Map<string, number>>,
359
+ source: string,
360
+ ): {source: Set<string>, sink: Set<string>, edges: [string, string][]} {
361
+ // Find all reachable nodes from source in residual graph
362
+ const sourceSet = new Set<string>();
363
+ const queue = [source];
364
+ sourceSet.add(source);
365
+
366
+ while (queue.length > 0) {
367
+ const current = queue.shift();
368
+ if (!current) {
369
+ continue;
370
+ }
371
+
372
+ const neighbors = residualGraph.get(current);
373
+
374
+ if (neighbors) {
375
+ for (const [neighbor, capacity] of neighbors) {
376
+ if (!sourceSet.has(neighbor) && capacity > 0) {
377
+ sourceSet.add(neighbor);
378
+ queue.push(neighbor);
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ // All other nodes are in sink set
385
+ const sinkSet = new Set<string>();
386
+ for (const node of residualGraph.keys()) {
387
+ if (!sourceSet.has(node)) {
388
+ sinkSet.add(node);
389
+ }
390
+ }
391
+
392
+ // Find cut edges
393
+ const cutEdges: [string, string][] = [];
394
+ for (const u of sourceSet) {
395
+ const neighbors = residualGraph.get(u);
396
+ if (neighbors) {
397
+ for (const v of neighbors.keys()) {
398
+ if (sinkSet.has(v)) {
399
+ cutEdges.push([u, v]);
400
+ }
401
+ }
402
+ }
403
+ }
404
+
405
+ return {source: sourceSet, sink: sinkSet, edges: cutEdges};
406
+ }
407
+
408
+ /**
409
+ * Utility function to create a flow network for bipartite matching
410
+ */
411
+ export function createBipartiteFlowNetwork(
412
+ leftNodes: string[],
413
+ rightNodes: string[],
414
+ edges: [string, string][],
415
+ ): {graph: Map<string, Map<string, number>>, source: string, sink: string} {
416
+ const graph = new Map<string, Map<string, number>>();
417
+ const source = "__source__";
418
+ const sink = "__sink__";
419
+
420
+ // Add source connections to left nodes
421
+ graph.set(source, new Map());
422
+ for (const left of leftNodes) {
423
+ const sourceNeighbors = graph.get(source);
424
+ if (sourceNeighbors) {
425
+ sourceNeighbors.set(left, 1);
426
+ }
427
+
428
+ graph.set(left, new Map());
429
+ }
430
+
431
+ // Add edges between left and right
432
+ for (const [left, right] of edges) {
433
+ if (!graph.has(left)) {
434
+ graph.set(left, new Map());
435
+ }
436
+
437
+ const leftNeighbors = graph.get(left);
438
+ if (leftNeighbors) {
439
+ leftNeighbors.set(right, 1);
440
+ }
441
+ }
442
+
443
+ // Add right node connections to sink
444
+ for (const right of rightNodes) {
445
+ if (!graph.has(right)) {
446
+ graph.set(right, new Map());
447
+ }
448
+
449
+ const rightNeighbors = graph.get(right);
450
+ if (rightNeighbors) {
451
+ rightNeighbors.set(sink, 1);
452
+ }
453
+ }
454
+
455
+ graph.set(sink, new Map());
456
+
457
+ return {graph, source, sink};
458
+ }
459
+
@@ -0,0 +1,2 @@
1
+ export * from "./ford-fulkerson.js";
2
+ export * from "./min-cut.js";