@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.
- package/README.md +1259 -61
- 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 +551 -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 +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- 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/research/grsbm.d.ts +83 -0
- package/dist/src/research/grsbm.d.ts.map +1 -0
- package/dist/src/research/grsbm.js +411 -0
- package/dist/src/research/grsbm.js.map +1 -0
- package/dist/src/research/index.d.ts +12 -0
- package/dist/src/research/index.d.ts.map +1 -0
- package/dist/src/research/index.js +14 -0
- package/dist/src/research/index.js.map +1 -0
- package/dist/src/research/sync.d.ts +49 -0
- package/dist/src/research/sync.d.ts.map +1 -0
- package/dist/src/research/sync.js +342 -0
- package/dist/src/research/sync.js.map +1 -0
- package/dist/src/research/terahac.d.ts +63 -0
- package/dist/src/research/terahac.d.ts.map +1 -0
- package/dist/src/research/terahac.js +375 -0
- package/dist/src/research/terahac.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 +13 -2
- 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 +672 -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 +5 -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/research/grsbm.ts +589 -0
- package/src/research/index.ts +17 -0
- package/src/research/sync.ts +469 -0
- package/src/research/terahac.ts +506 -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,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum Cut Algorithms
|
|
3
|
+
*
|
|
4
|
+
* Various algorithms for finding minimum cuts in graphs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {fordFulkerson} from "./ford-fulkerson.js";
|
|
8
|
+
|
|
9
|
+
export interface MinCutResult {
|
|
10
|
+
cutValue: number;
|
|
11
|
+
partition1: Set<string>;
|
|
12
|
+
partition2: Set<string>;
|
|
13
|
+
cutEdges: {from: string, to: string, weight: number}[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Find minimum s-t cut using max flow
|
|
18
|
+
* The minimum cut value equals the maximum flow value (max-flow min-cut theorem)
|
|
19
|
+
*
|
|
20
|
+
* @param graph - Weighted graph
|
|
21
|
+
* @param source - Source node
|
|
22
|
+
* @param sink - Sink node
|
|
23
|
+
* @returns Minimum cut information
|
|
24
|
+
*
|
|
25
|
+
* Time Complexity: Same as max flow algorithm used
|
|
26
|
+
*/
|
|
27
|
+
export function minSTCut(
|
|
28
|
+
graph: Map<string, Map<string, number>>,
|
|
29
|
+
source: string,
|
|
30
|
+
sink: string,
|
|
31
|
+
): MinCutResult {
|
|
32
|
+
const flowResult = fordFulkerson(graph, source, sink);
|
|
33
|
+
|
|
34
|
+
if (!flowResult.minCut) {
|
|
35
|
+
return {
|
|
36
|
+
cutValue: 0,
|
|
37
|
+
partition1: new Set(),
|
|
38
|
+
partition2: new Set(),
|
|
39
|
+
cutEdges: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cutEdges: {from: string, to: string, weight: number}[] = [];
|
|
44
|
+
|
|
45
|
+
// Find actual cut edges and their weights
|
|
46
|
+
for (const [u, v] of flowResult.minCut.edges) {
|
|
47
|
+
const weight = graph.get(u)?.get(v) ?? 0;
|
|
48
|
+
if (weight > 0) {
|
|
49
|
+
cutEdges.push({from: u, to: v, weight});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
cutValue: flowResult.maxFlow,
|
|
55
|
+
partition1: flowResult.minCut.source,
|
|
56
|
+
partition2: flowResult.minCut.sink,
|
|
57
|
+
cutEdges,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Stoer-Wagner algorithm for finding global minimum cut
|
|
63
|
+
* Finds the minimum cut that separates the graph into two parts
|
|
64
|
+
*
|
|
65
|
+
* @param graph - Undirected weighted graph
|
|
66
|
+
* @returns Global minimum cut
|
|
67
|
+
*
|
|
68
|
+
* Time Complexity: O(V³) or O(VE + V² log V) with heap
|
|
69
|
+
*/
|
|
70
|
+
export function stoerWagner(
|
|
71
|
+
graph: Map<string, Map<string, number>>,
|
|
72
|
+
): MinCutResult {
|
|
73
|
+
// Convert to undirected if necessary
|
|
74
|
+
const undirectedGraph = makeUndirected(graph);
|
|
75
|
+
|
|
76
|
+
if (undirectedGraph.size < 2) {
|
|
77
|
+
return {
|
|
78
|
+
cutValue: 0,
|
|
79
|
+
partition1: new Set(undirectedGraph.keys()),
|
|
80
|
+
partition2: new Set(),
|
|
81
|
+
cutEdges: [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Initialize
|
|
86
|
+
const nodes = Array.from(undirectedGraph.keys());
|
|
87
|
+
let minCutValue = Infinity;
|
|
88
|
+
let bestPartition = new Set<string>();
|
|
89
|
+
const contractionMap = new Map<string, Set<string>>();
|
|
90
|
+
|
|
91
|
+
// Initialize contraction map
|
|
92
|
+
for (const node of nodes) {
|
|
93
|
+
contractionMap.set(node, new Set([node]));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Contract graph V-1 times
|
|
97
|
+
while (nodes.length > 1) {
|
|
98
|
+
const cut = minimumCutPhase(undirectedGraph, nodes);
|
|
99
|
+
|
|
100
|
+
if (cut.value < minCutValue) {
|
|
101
|
+
minCutValue = cut.value;
|
|
102
|
+
// Store the actual nodes that would be in partition with t
|
|
103
|
+
const cutTNodes = contractionMap.get(cut.t);
|
|
104
|
+
if (cutTNodes) {
|
|
105
|
+
bestPartition = new Set(cutTNodes);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Contract the last two nodes
|
|
110
|
+
const tNodes = contractionMap.get(cut.t);
|
|
111
|
+
const sNodes = contractionMap.get(cut.s);
|
|
112
|
+
if (!tNodes || !sNodes) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const node of tNodes) {
|
|
117
|
+
sNodes.add(node);
|
|
118
|
+
}
|
|
119
|
+
contractionMap.delete(cut.t);
|
|
120
|
+
|
|
121
|
+
contractNodes(undirectedGraph, nodes, cut.s, cut.t);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Build result
|
|
125
|
+
const partition1 = bestPartition;
|
|
126
|
+
const partition2 = new Set<string>();
|
|
127
|
+
|
|
128
|
+
for (const node of graph.keys()) {
|
|
129
|
+
if (!partition1.has(node)) {
|
|
130
|
+
partition2.add(node);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Find cut edges
|
|
135
|
+
const cutEdges: {from: string, to: string, weight: number}[] = [];
|
|
136
|
+
for (const u of partition1) {
|
|
137
|
+
const neighbors = graph.get(u);
|
|
138
|
+
if (neighbors) {
|
|
139
|
+
for (const [v, weight] of neighbors) {
|
|
140
|
+
if (partition2.has(v)) {
|
|
141
|
+
cutEdges.push({from: u, to: v, weight});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
cutValue: minCutValue,
|
|
149
|
+
partition1,
|
|
150
|
+
partition2,
|
|
151
|
+
cutEdges,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Minimum cut phase of Stoer-Wagner algorithm
|
|
157
|
+
*/
|
|
158
|
+
function minimumCutPhase(
|
|
159
|
+
graph: Map<string, Map<string, number>>,
|
|
160
|
+
nodes: string[],
|
|
161
|
+
): {s: string, t: string, value: number, partition: string[]} {
|
|
162
|
+
const n = nodes.length;
|
|
163
|
+
const weight = new Map<string, number>();
|
|
164
|
+
const added = new Set<string>();
|
|
165
|
+
const order: string[] = [];
|
|
166
|
+
|
|
167
|
+
// Initialize weights to 0
|
|
168
|
+
for (const node of nodes) {
|
|
169
|
+
weight.set(node, 0);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Start with arbitrary node
|
|
173
|
+
let lastAdded = nodes[0];
|
|
174
|
+
if (!lastAdded) {
|
|
175
|
+
return {s: "", t: "", value: 0, partition: []};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
added.add(lastAdded);
|
|
179
|
+
order.push(lastAdded);
|
|
180
|
+
|
|
181
|
+
// Add remaining nodes
|
|
182
|
+
for (let i = 1; i < n; i++) {
|
|
183
|
+
// Update weights
|
|
184
|
+
if (!lastAdded) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const neighbors = graph.get(lastAdded);
|
|
189
|
+
if (neighbors) {
|
|
190
|
+
for (const [neighbor, w] of neighbors) {
|
|
191
|
+
if (!added.has(neighbor) && nodes.includes(neighbor)) {
|
|
192
|
+
const currentWeight = weight.get(neighbor);
|
|
193
|
+
if (currentWeight !== undefined) {
|
|
194
|
+
weight.set(neighbor, currentWeight + w);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Find maximum weight node not yet added
|
|
201
|
+
let maxWeight = -1;
|
|
202
|
+
let maxNode = "";
|
|
203
|
+
|
|
204
|
+
for (const node of nodes) {
|
|
205
|
+
const nodeWeight = weight.get(node);
|
|
206
|
+
if (!added.has(node) && nodeWeight !== undefined && nodeWeight > maxWeight) {
|
|
207
|
+
maxWeight = nodeWeight;
|
|
208
|
+
maxNode = node;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
added.add(maxNode);
|
|
213
|
+
order.push(maxNode);
|
|
214
|
+
lastAdded = maxNode;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const s = order[order.length - 2];
|
|
218
|
+
const t = order[order.length - 1];
|
|
219
|
+
if (!s || !t) {
|
|
220
|
+
return {s: "", t: "", value: 0, partition: []};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const cutValue = weight.get(t) ?? 0;
|
|
224
|
+
|
|
225
|
+
// Partition is all nodes except t
|
|
226
|
+
const partition = order.slice(0, -1);
|
|
227
|
+
|
|
228
|
+
return {s, t, value: cutValue, partition};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Contract two nodes in the graph
|
|
233
|
+
*/
|
|
234
|
+
function contractNodes(
|
|
235
|
+
graph: Map<string, Map<string, number>>,
|
|
236
|
+
nodes: string[],
|
|
237
|
+
s: string,
|
|
238
|
+
t: string,
|
|
239
|
+
): void {
|
|
240
|
+
// Merge t into s
|
|
241
|
+
const sNeighbors = graph.get(s);
|
|
242
|
+
const tNeighbors = graph.get(t);
|
|
243
|
+
if (!sNeighbors || !tNeighbors) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Add t's edges to s
|
|
248
|
+
for (const [neighbor, weight] of tNeighbors) {
|
|
249
|
+
if (neighbor !== s) {
|
|
250
|
+
sNeighbors.set(neighbor, (sNeighbors.get(neighbor) ?? 0) + weight);
|
|
251
|
+
|
|
252
|
+
// Update neighbor's edge to point to s instead of t
|
|
253
|
+
const neighborEdges = graph.get(neighbor);
|
|
254
|
+
if (neighborEdges?.has(t)) {
|
|
255
|
+
neighborEdges.delete(t);
|
|
256
|
+
neighborEdges.set(s, (neighborEdges.get(s) ?? 0) + weight);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Remove t from graph
|
|
262
|
+
graph.delete(t);
|
|
263
|
+
sNeighbors.delete(t);
|
|
264
|
+
|
|
265
|
+
// Remove t from nodes array
|
|
266
|
+
const index = nodes.indexOf(t);
|
|
267
|
+
if (index > -1) {
|
|
268
|
+
nodes.splice(index, 1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Convert directed graph to undirected
|
|
274
|
+
*/
|
|
275
|
+
function makeUndirected(
|
|
276
|
+
graph: Map<string, Map<string, number>>,
|
|
277
|
+
): Map<string, Map<string, number>> {
|
|
278
|
+
const undirected = new Map<string, Map<string, number>>();
|
|
279
|
+
|
|
280
|
+
// Initialize all nodes
|
|
281
|
+
for (const node of graph.keys()) {
|
|
282
|
+
undirected.set(node, new Map());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Add edges in both directions
|
|
286
|
+
for (const [u, neighbors] of graph) {
|
|
287
|
+
for (const [v, weight] of neighbors) {
|
|
288
|
+
const uNeighbors = undirected.get(u);
|
|
289
|
+
if (uNeighbors) {
|
|
290
|
+
uNeighbors.set(v, weight);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!undirected.has(v)) {
|
|
294
|
+
undirected.set(v, new Map());
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const vNeighbors = undirected.get(v);
|
|
298
|
+
if (vNeighbors) {
|
|
299
|
+
vNeighbors.set(u, weight);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return undirected;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Karger's randomized min-cut algorithm
|
|
309
|
+
* Probabilistic algorithm that finds min cut with high probability
|
|
310
|
+
*
|
|
311
|
+
* @param graph - Undirected graph
|
|
312
|
+
* @param iterations - Number of iterations (higher = better accuracy)
|
|
313
|
+
* @returns Minimum cut found
|
|
314
|
+
*
|
|
315
|
+
* Time Complexity: O(V² * iterations)
|
|
316
|
+
*/
|
|
317
|
+
export function kargerMinCut(
|
|
318
|
+
graph: Map<string, Map<string, number>>,
|
|
319
|
+
iterations = 100,
|
|
320
|
+
): MinCutResult {
|
|
321
|
+
let minCutValue = Infinity;
|
|
322
|
+
let bestPartition1 = new Set<string>();
|
|
323
|
+
let bestPartition2 = new Set<string>();
|
|
324
|
+
|
|
325
|
+
for (let i = 0; i < iterations; i++) {
|
|
326
|
+
const result = kargerSingleRun(graph);
|
|
327
|
+
|
|
328
|
+
if (result.cutValue < minCutValue) {
|
|
329
|
+
minCutValue = result.cutValue;
|
|
330
|
+
bestPartition1 = result.partition1;
|
|
331
|
+
bestPartition2 = result.partition2;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Find cut edges
|
|
336
|
+
const cutEdges: {from: string, to: string, weight: number}[] = [];
|
|
337
|
+
for (const u of bestPartition1) {
|
|
338
|
+
const neighbors = graph.get(u);
|
|
339
|
+
if (neighbors) {
|
|
340
|
+
for (const [v, weight] of neighbors) {
|
|
341
|
+
if (bestPartition2.has(v)) {
|
|
342
|
+
cutEdges.push({from: u, to: v, weight});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
cutValue: minCutValue,
|
|
350
|
+
partition1: bestPartition1,
|
|
351
|
+
partition2: bestPartition2,
|
|
352
|
+
cutEdges,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Single run of Karger's algorithm
|
|
358
|
+
*/
|
|
359
|
+
function kargerSingleRun(
|
|
360
|
+
graph: Map<string, Map<string, number>>,
|
|
361
|
+
): {cutValue: number, partition1: Set<string>, partition2: Set<string>} {
|
|
362
|
+
// Create a copy of the graph
|
|
363
|
+
const workGraph = new Map<string, Map<string, number>>();
|
|
364
|
+
const superNodes = new Map<string, Set<string>>();
|
|
365
|
+
|
|
366
|
+
// Initialize
|
|
367
|
+
for (const [node, neighbors] of graph) {
|
|
368
|
+
workGraph.set(node, new Map(neighbors));
|
|
369
|
+
superNodes.set(node, new Set([node]));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Contract until 2 nodes remain
|
|
373
|
+
while (workGraph.size > 2) {
|
|
374
|
+
// Pick random edge
|
|
375
|
+
const edges: [string, string, number][] = [];
|
|
376
|
+
for (const [u, neighbors] of workGraph) {
|
|
377
|
+
for (const [v, weight] of neighbors) {
|
|
378
|
+
if (u < v) { // Avoid duplicates
|
|
379
|
+
edges.push([u, v, weight]);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (edges.length === 0) {
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const randomIndex = Math.floor(Math.random() * edges.length);
|
|
389
|
+
const edge = edges[randomIndex];
|
|
390
|
+
if (!edge) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const [u, v] = edge;
|
|
395
|
+
|
|
396
|
+
// Contract edge
|
|
397
|
+
if (u && v) {
|
|
398
|
+
contractKarger(workGraph, superNodes, u, v);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Calculate cut value
|
|
403
|
+
const nodes = Array.from(workGraph.keys());
|
|
404
|
+
if (nodes.length < 2) {
|
|
405
|
+
return {
|
|
406
|
+
cutValue: 0,
|
|
407
|
+
partition1: new Set(),
|
|
408
|
+
partition2: new Set(),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const node1 = nodes[0];
|
|
413
|
+
const node2 = nodes[1];
|
|
414
|
+
if (!node1 || !node2) {
|
|
415
|
+
return {
|
|
416
|
+
cutValue: 0,
|
|
417
|
+
partition1: new Set(),
|
|
418
|
+
partition2: new Set(),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const cutValue = workGraph.get(node1)?.get(node2) ?? 0;
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
cutValue,
|
|
426
|
+
partition1: superNodes.get(node1) ?? new Set(),
|
|
427
|
+
partition2: superNodes.get(node2) ?? new Set(),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Contract edge in Karger's algorithm
|
|
433
|
+
*/
|
|
434
|
+
function contractKarger(
|
|
435
|
+
graph: Map<string, Map<string, number>>,
|
|
436
|
+
superNodes: Map<string, Set<string>>,
|
|
437
|
+
u: string,
|
|
438
|
+
v: string,
|
|
439
|
+
): void {
|
|
440
|
+
// Merge v into u
|
|
441
|
+
const uNeighbors = graph.get(u);
|
|
442
|
+
const vNeighbors = graph.get(v);
|
|
443
|
+
if (!uNeighbors || !vNeighbors) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Merge supernodes
|
|
448
|
+
const uSuper = superNodes.get(u);
|
|
449
|
+
const vSuper = superNodes.get(v);
|
|
450
|
+
if (!uSuper || !vSuper) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
for (const node of vSuper) {
|
|
455
|
+
uSuper.add(node);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Merge edges
|
|
459
|
+
for (const [neighbor, weight] of vNeighbors) {
|
|
460
|
+
if (neighbor !== u) {
|
|
461
|
+
uNeighbors.set(neighbor, (uNeighbors.get(neighbor) ?? 0) + weight);
|
|
462
|
+
|
|
463
|
+
// Update neighbor's edges
|
|
464
|
+
const neighborEdges = graph.get(neighbor);
|
|
465
|
+
if (neighborEdges) {
|
|
466
|
+
neighborEdges.delete(v);
|
|
467
|
+
if (neighbor !== u) {
|
|
468
|
+
neighborEdges.set(u, (neighborEdges.get(u) ?? 0) + weight);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Remove self-loops
|
|
475
|
+
uNeighbors.delete(u);
|
|
476
|
+
uNeighbors.delete(v);
|
|
477
|
+
|
|
478
|
+
// Remove v
|
|
479
|
+
graph.delete(v);
|
|
480
|
+
superNodes.delete(v);
|
|
481
|
+
}
|
|
482
|
+
|
package/src/index.ts
CHANGED
|
@@ -17,9 +17,11 @@ export type {
|
|
|
17
17
|
ComponentResult,
|
|
18
18
|
DijkstraOptions,
|
|
19
19
|
Edge,
|
|
20
|
+
FloydWarshallResult,
|
|
20
21
|
GirvanNewmanOptions,
|
|
21
22
|
GraphConfig,
|
|
22
23
|
LouvainOptions,
|
|
24
|
+
MSTResult,
|
|
23
25
|
Node,
|
|
24
26
|
NodeId,
|
|
25
27
|
PageRankOptions,
|
|
@@ -31,5 +33,8 @@ export type {
|
|
|
31
33
|
// Algorithm exports
|
|
32
34
|
export * from "./algorithms/index.js";
|
|
33
35
|
|
|
36
|
+
// Research algorithms exports (Priority 4)
|
|
37
|
+
export * from "./research/index.js";
|
|
38
|
+
|
|
34
39
|
// Data structure exports
|
|
35
40
|
export * from "./data-structures/index.js";
|