@graphty/algorithms 1.0.1 → 1.1.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.
- package/README.md +827 -60
- package/dist/algorithms.d.ts +2 -0
- package/dist/algorithms.d.ts.map +1 -0
- package/dist/algorithms.js +2 -0
- package/dist/algorithms.js.map +1 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +22 -7
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +7 -7
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +1 -1
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.d.ts +27 -0
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/eigenvector.js +113 -0
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -0
- package/dist/src/algorithms/centrality/hits.d.ts +35 -0
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/hits.js +146 -0
- package/dist/src/algorithms/centrality/hits.js.map +1 -0
- package/dist/src/algorithms/centrality/index.d.ts +6 -0
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +3 -0
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +30 -0
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/katz.js +85 -0
- package/dist/src/algorithms/centrality/katz.js.map +1 -0
- package/dist/src/algorithms/centrality/pagerank.js +3 -3
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts +23 -0
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -0
- package/dist/src/algorithms/community/girvan-newman.js +297 -0
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -0
- package/dist/src/algorithms/community/index.d.ts +12 -0
- package/dist/src/algorithms/community/index.d.ts.map +1 -0
- package/dist/src/algorithms/community/index.js +12 -0
- package/dist/src/algorithms/community/index.js.map +1 -0
- package/dist/src/algorithms/community/label-propagation.d.ts +42 -0
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -0
- package/dist/src/algorithms/community/label-propagation.js +307 -0
- package/dist/src/algorithms/community/label-propagation.js.map +1 -0
- package/dist/src/algorithms/community/leiden.d.ts +33 -0
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -0
- package/dist/src/algorithms/community/leiden.js +434 -0
- package/dist/src/algorithms/community/leiden.js.map +1 -0
- package/dist/src/algorithms/community/louvain.d.ts +19 -0
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain.js +219 -0
- package/dist/src/algorithms/community/louvain.js.map +1 -0
- package/dist/src/algorithms/components/connected.js +7 -7
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/index.d.ts +7 -0
- package/dist/src/algorithms/index.d.ts.map +1 -1
- package/dist/src/algorithms/index.js +14 -0
- package/dist/src/algorithms/index.js.map +1 -1
- package/dist/src/algorithms/matching/bipartite.d.ts +37 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -0
- package/dist/src/algorithms/matching/bipartite.js +127 -0
- package/dist/src/algorithms/matching/bipartite.js.map +1 -0
- package/dist/src/algorithms/matching/index.d.ts +8 -0
- package/dist/src/algorithms/matching/index.d.ts.map +1 -0
- package/dist/src/algorithms/matching/index.js +6 -0
- package/dist/src/algorithms/matching/index.js.map +1 -0
- package/dist/src/algorithms/matching/isomorphism.d.ts +32 -0
- package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -0
- package/dist/src/algorithms/matching/isomorphism.js +291 -0
- package/dist/src/algorithms/matching/isomorphism.js.map +1 -0
- package/dist/src/algorithms/mst/index.d.ts +4 -0
- package/dist/src/algorithms/mst/index.d.ts.map +1 -0
- package/dist/src/algorithms/mst/index.js +3 -0
- package/dist/src/algorithms/mst/index.js.map +1 -0
- package/dist/src/algorithms/mst/kruskal.d.ts +9 -0
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -0
- package/dist/src/algorithms/mst/kruskal.js +43 -0
- package/dist/src/algorithms/mst/kruskal.js.map +1 -0
- package/dist/src/algorithms/mst/prim.d.ts +5 -0
- package/dist/src/algorithms/mst/prim.d.ts.map +1 -0
- package/dist/src/algorithms/mst/prim.js +63 -0
- package/dist/src/algorithms/mst/prim.js.map +1 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js +2 -2
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +5 -5
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +14 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +129 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -0
- package/dist/src/algorithms/shortest-path/index.d.ts +2 -0
- package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/index.js +1 -0
- package/dist/src/algorithms/shortest-path/index.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +2 -2
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +11 -11
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/clustering/hierarchical.d.ts +48 -0
- package/dist/src/clustering/hierarchical.d.ts.map +1 -0
- package/dist/src/clustering/hierarchical.js +439 -0
- package/dist/src/clustering/hierarchical.js.map +1 -0
- package/dist/src/clustering/index.d.ts +7 -0
- package/dist/src/clustering/index.d.ts.map +1 -0
- package/dist/src/clustering/index.js +5 -0
- package/dist/src/clustering/index.js.map +1 -0
- package/dist/src/clustering/k-core.d.ts +63 -0
- package/dist/src/clustering/k-core.d.ts.map +1 -0
- package/dist/src/clustering/k-core.js +377 -0
- package/dist/src/clustering/k-core.js.map +1 -0
- package/dist/src/clustering/mcl.d.ts +36 -0
- package/dist/src/clustering/mcl.d.ts.map +1 -0
- package/dist/src/clustering/mcl.js +343 -0
- package/dist/src/clustering/mcl.js.map +1 -0
- package/dist/src/clustering/spectral.d.ts +28 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -0
- package/dist/src/clustering/spectral.js +447 -0
- package/dist/src/clustering/spectral.js.map +1 -0
- package/dist/src/core/graph.d.ts +9 -0
- package/dist/src/core/graph.d.ts.map +1 -1
- package/dist/src/core/graph.js +31 -11
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/data-structures/priority-queue.js +0 -2
- package/dist/src/data-structures/priority-queue.js.map +1 -1
- package/dist/src/data-structures/union-find.js +2 -5
- package/dist/src/data-structures/union-find.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +53 -0
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -0
- package/dist/src/flow/ford-fulkerson.js +355 -0
- package/dist/src/flow/ford-fulkerson.js.map +1 -0
- package/dist/src/flow/index.d.ts +3 -0
- package/dist/src/flow/index.d.ts.map +1 -0
- package/dist/src/flow/index.js +3 -0
- package/dist/src/flow/index.js.map +1 -0
- package/dist/src/flow/min-cut.d.ts +49 -0
- package/dist/src/flow/min-cut.d.ts.map +1 -0
- package/dist/src/flow/min-cut.js +383 -0
- package/dist/src/flow/min-cut.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts +56 -0
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -0
- package/dist/src/link-prediction/adamic-adar.js +256 -0
- package/dist/src/link-prediction/adamic-adar.js.map +1 -0
- package/dist/src/link-prediction/common-neighbors.d.ts +50 -0
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -0
- package/dist/src/link-prediction/common-neighbors.js +155 -0
- package/dist/src/link-prediction/common-neighbors.js.map +1 -0
- package/dist/src/link-prediction/index.d.ts +7 -0
- package/dist/src/link-prediction/index.d.ts.map +1 -0
- package/dist/src/link-prediction/index.js +6 -0
- package/dist/src/link-prediction/index.js.map +1 -0
- package/dist/src/pathfinding/astar.d.ts +50 -0
- package/dist/src/pathfinding/astar.d.ts.map +1 -0
- package/dist/src/pathfinding/astar.js +180 -0
- package/dist/src/pathfinding/astar.js.map +1 -0
- package/dist/src/pathfinding/index.d.ts +3 -0
- package/dist/src/pathfinding/index.d.ts.map +1 -0
- package/dist/src/pathfinding/index.js +3 -0
- package/dist/src/pathfinding/index.js.map +1 -0
- package/dist/src/pathfinding/utils.d.ts +18 -0
- package/dist/src/pathfinding/utils.d.ts.map +1 -0
- package/dist/src/pathfinding/utils.js +68 -0
- package/dist/src/pathfinding/utils.js.map +1 -0
- package/dist/src/types/index.d.ts +9 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/graphNode.d.ts +8 -0
- package/dist/src/utils/graphNode.d.ts.map +1 -0
- package/dist/src/utils/graphNode.js +2 -0
- package/dist/src/utils/graphNode.js.map +1 -0
- package/dist/src/utils/priorityQueue.d.ts +21 -0
- package/dist/src/utils/priorityQueue.d.ts.map +1 -0
- package/dist/src/utils/priorityQueue.js +101 -0
- package/dist/src/utils/priorityQueue.js.map +1 -0
- package/package.json +3 -1
- package/src/algorithms/centrality/betweenness.ts +25 -7
- package/src/algorithms/centrality/closeness.ts +7 -7
- package/src/algorithms/centrality/degree.ts +1 -1
- package/src/algorithms/centrality/eigenvector.ts +162 -0
- package/src/algorithms/centrality/hits.ts +210 -0
- package/src/algorithms/centrality/index.ts +6 -0
- package/src/algorithms/centrality/katz.ts +139 -0
- package/src/algorithms/centrality/pagerank.ts +3 -3
- package/src/algorithms/community/girvan-newman.ts +372 -0
- package/src/algorithms/community/index.ts +13 -0
- package/src/algorithms/community/label-propagation.ts +392 -0
- package/src/algorithms/community/leiden.ts +572 -0
- package/src/algorithms/community/louvain.ts +298 -0
- package/src/algorithms/components/connected.ts +7 -7
- package/src/algorithms/index.ts +21 -0
- package/src/algorithms/matching/bipartite.ts +179 -0
- package/src/algorithms/matching/index.ts +8 -0
- package/src/algorithms/matching/isomorphism.ts +392 -0
- package/src/algorithms/mst/index.ts +4 -0
- package/src/algorithms/mst/kruskal.ts +61 -0
- package/src/algorithms/mst/prim.ts +80 -0
- package/src/algorithms/shortest-path/bellman-ford.ts +2 -2
- package/src/algorithms/shortest-path/dijkstra.ts +5 -5
- package/src/algorithms/shortest-path/floyd-warshall.ts +170 -0
- package/src/algorithms/shortest-path/index.ts +2 -0
- package/src/algorithms/traversal/bfs.ts +2 -2
- package/src/algorithms/traversal/dfs.ts +11 -11
- package/src/clustering/hierarchical.ts +555 -0
- package/src/clustering/index.ts +6 -0
- package/src/clustering/k-core.ts +460 -0
- package/src/clustering/mcl.ts +454 -0
- package/src/clustering/spectral.ts +545 -0
- package/src/core/graph.ts +34 -6
- package/src/data-structures/union-find.ts +2 -2
- package/src/flow/ford-fulkerson.ts +459 -0
- package/src/flow/index.ts +2 -0
- package/src/flow/min-cut.ts +482 -0
- package/src/index.ts +2 -0
- package/src/link-prediction/adamic-adar.ts +353 -0
- package/src/link-prediction/common-neighbors.ts +252 -0
- package/src/link-prediction/index.ts +20 -0
- package/src/pathfinding/astar.ts +229 -0
- package/src/pathfinding/index.ts +2 -0
- package/src/pathfinding/utils.ts +87 -0
- package/src/types/index.ts +11 -0
- package/src/utils/graphNode.ts +7 -0
- package/src/utils/priorityQueue.ts +120 -0
- package/dist/test/browser/basic.test.d.ts +0 -2
- package/dist/test/browser/basic.test.d.ts.map +0 -1
- package/dist/test/browser/basic.test.js +0 -70
- package/dist/test/browser/basic.test.js.map +0 -1
- package/dist/test/unit/bellman-ford.test.d.ts +0 -2
- package/dist/test/unit/bellman-ford.test.d.ts.map +0 -1
- package/dist/test/unit/bellman-ford.test.js +0 -231
- package/dist/test/unit/bellman-ford.test.js.map +0 -1
- package/dist/test/unit/betweenness-centrality.test.d.ts +0 -2
- package/dist/test/unit/betweenness-centrality.test.d.ts.map +0 -1
- package/dist/test/unit/betweenness-centrality.test.js +0 -311
- package/dist/test/unit/betweenness-centrality.test.js.map +0 -1
- package/dist/test/unit/bfs.test.d.ts +0 -2
- package/dist/test/unit/bfs.test.d.ts.map +0 -1
- package/dist/test/unit/bfs.test.js +0 -277
- package/dist/test/unit/bfs.test.js.map +0 -1
- package/dist/test/unit/closeness.test.d.ts +0 -2
- package/dist/test/unit/closeness.test.d.ts.map +0 -1
- package/dist/test/unit/closeness.test.js +0 -266
- package/dist/test/unit/closeness.test.js.map +0 -1
- package/dist/test/unit/connected-components.test.d.ts +0 -2
- package/dist/test/unit/connected-components.test.d.ts.map +0 -1
- package/dist/test/unit/connected-components.test.js +0 -355
- package/dist/test/unit/connected-components.test.js.map +0 -1
- package/dist/test/unit/degree-centrality.test.d.ts +0 -2
- package/dist/test/unit/degree-centrality.test.d.ts.map +0 -1
- package/dist/test/unit/degree-centrality.test.js +0 -260
- package/dist/test/unit/degree-centrality.test.js.map +0 -1
- package/dist/test/unit/dfs.test.d.ts +0 -2
- package/dist/test/unit/dfs.test.d.ts.map +0 -1
- package/dist/test/unit/dfs.test.js +0 -350
- package/dist/test/unit/dfs.test.js.map +0 -1
- package/dist/test/unit/dijkstra.test.d.ts +0 -2
- package/dist/test/unit/dijkstra.test.d.ts.map +0 -1
- package/dist/test/unit/dijkstra.test.js +0 -311
- package/dist/test/unit/dijkstra.test.js.map +0 -1
- package/dist/test/unit/graph.test.d.ts +0 -2
- package/dist/test/unit/graph.test.d.ts.map +0 -1
- package/dist/test/unit/graph.test.js +0 -175
- package/dist/test/unit/graph.test.js.map +0 -1
- package/dist/test/unit/pagerank.test.d.ts +0 -2
- package/dist/test/unit/pagerank.test.d.ts.map +0 -1
- package/dist/test/unit/pagerank.test.js +0 -281
- package/dist/test/unit/pagerank.test.js.map +0 -1
- package/dist/test/unit/priority-queue.test.d.ts +0 -2
- package/dist/test/unit/priority-queue.test.d.ts.map +0 -1
- package/dist/test/unit/priority-queue.test.js +0 -189
- package/dist/test/unit/priority-queue.test.js.map +0 -1
- package/dist/test/unit/union-find.test.d.ts +0 -2
- package/dist/test/unit/union-find.test.d.ts.map +0 -1
- package/dist/test/unit/union-find.test.js +0 -245
- package/dist/test/unit/union-find.test.js.map +0 -1
|
@@ -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
|
+
|