@graphty/algorithms 1.3.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -413
- package/dist/algorithms.js +564 -261
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +10 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +27 -6
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +11 -2
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +17 -3
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.d.ts +7 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +9 -6
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +21 -3
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +25 -7
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +28 -2
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.js +30 -4
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.js +11 -4
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
- package/dist/src/algorithms/centrality/hits.d.ts +10 -3
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/hits.js +11 -4
- package/dist/src/algorithms/centrality/hits.js.map +1 -1
- package/dist/src/algorithms/centrality/index.d.ts +1 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +1 -1
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +10 -3
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/katz.js +13 -8
- package/dist/src/algorithms/centrality/katz.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +14 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +16 -1
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +21 -4
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/index.d.ts.map +1 -1
- package/dist/src/algorithms/community/index.js.map +1 -1
- package/dist/src/algorithms/community/label-propagation.d.ts +0 -3
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +13 -6
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +0 -1
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +37 -6
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +37 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.js +42 -7
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -1
- package/dist/src/algorithms/community/louvain.d.ts +2 -3
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain.js +19 -6
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +0 -4
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.js +1 -5
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -1
- package/dist/src/algorithms/components/connected.d.ts +23 -0
- package/dist/src/algorithms/components/connected.d.ts.map +1 -1
- package/dist/src/algorithms/components/connected.js +29 -3
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
- package/dist/src/algorithms/matching/bipartite.js +10 -0
- package/dist/src/algorithms/matching/bipartite.js.map +1 -1
- package/dist/src/algorithms/matching/index.d.ts.map +1 -1
- package/dist/src/algorithms/matching/index.js.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
- package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.js +36 -2
- package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
- package/dist/src/algorithms/mst/index.d.ts.map +1 -1
- package/dist/src/algorithms/mst/index.js.map +1 -1
- package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
- package/dist/src/algorithms/mst/kruskal.js +17 -3
- package/dist/src/algorithms/mst/kruskal.js.map +1 -1
- package/dist/src/algorithms/mst/prim.d.ts +9 -0
- package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
- package/dist/src/algorithms/mst/prim.js +9 -0
- package/dist/src/algorithms/mst/prim.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +11 -4
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +18 -16
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +15 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +15 -0
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
- package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/index.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +13 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +37 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +23 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.js +34 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +1 -1
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +48 -0
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/index.js.map +1 -1
- package/dist/src/benchmark-all-algorithms.d.ts +2 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -1
- package/dist/src/benchmark-all-algorithms.js +32 -3
- package/dist/src/benchmark-all-algorithms.js.map +1 -1
- package/dist/src/clustering/hierarchical.d.ts +8 -1
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +22 -6
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +2 -5
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +14 -12
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/mcl.d.ts +6 -0
- package/dist/src/clustering/mcl.d.ts.map +1 -1
- package/dist/src/clustering/mcl.js +34 -3
- package/dist/src/clustering/mcl.js.map +1 -1
- package/dist/src/clustering/spectral.d.ts +4 -1
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +61 -28
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/core/graph.d.ts +45 -0
- package/dist/src/core/graph.d.ts.map +1 -1
- package/dist/src/core/graph.js +45 -1
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/data-structures/index.d.ts.map +1 -1
- package/dist/src/data-structures/index.js.map +1 -1
- package/dist/src/data-structures/priority-queue.d.ts +18 -1
- package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
- package/dist/src/data-structures/priority-queue.js +26 -5
- package/dist/src/data-structures/priority-queue.js.map +1 -1
- package/dist/src/data-structures/union-find.d.ts +21 -0
- package/dist/src/data-structures/union-find.d.ts.map +1 -1
- package/dist/src/data-structures/union-find.js +21 -0
- package/dist/src/data-structures/union-find.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +4 -2
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +31 -2
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +0 -3
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +17 -4
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
- package/dist/src/link-prediction/adamic-adar.js +36 -10
- package/dist/src/link-prediction/adamic-adar.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +27 -6
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/link-prediction/index.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.d.ts +103 -53
- package/dist/src/optimized/bit-packed.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.js +104 -54
- package/dist/src/optimized/bit-packed.js.map +1 -1
- package/dist/src/optimized/csr-graph.d.ts +87 -24
- package/dist/src/optimized/csr-graph.d.ts.map +1 -1
- package/dist/src/optimized/csr-graph.js +87 -24
- package/dist/src/optimized/csr-graph.js.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.d.ts +37 -16
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.js +39 -20
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
- package/dist/src/optimized/graph-adapter.d.ts +58 -9
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -1
- package/dist/src/optimized/graph-adapter.js +58 -10
- package/dist/src/optimized/graph-adapter.js.map +1 -1
- package/dist/src/optimized/index.d.ts +1 -1
- package/dist/src/optimized/index.d.ts.map +1 -1
- package/dist/src/optimized/index.js +3 -1
- package/dist/src/optimized/index.js.map +1 -1
- package/dist/src/pathfinding/astar.d.ts +25 -9
- package/dist/src/pathfinding/astar.d.ts.map +1 -1
- package/dist/src/pathfinding/astar.js +25 -10
- package/dist/src/pathfinding/astar.js.map +1 -1
- package/dist/src/pathfinding/utils.d.ts +15 -6
- package/dist/src/pathfinding/utils.d.ts.map +1 -1
- package/dist/src/pathfinding/utils.js +19 -7
- package/dist/src/pathfinding/utils.js.map +1 -1
- package/dist/src/research/grsbm.d.ts +0 -1
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +22 -9
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/index.d.ts.map +1 -1
- package/dist/src/research/index.js.map +1 -1
- package/dist/src/research/sync.d.ts +0 -1
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +28 -7
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +0 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +32 -4
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/utils/graph-converters.d.ts +31 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -1
- package/dist/src/utils/graph-converters.js +31 -0
- package/dist/src/utils/graph-converters.js.map +1 -1
- package/dist/src/utils/graph-utilities.d.ts.map +1 -1
- package/dist/src/utils/graph-utilities.js.map +1 -1
- package/dist/src/utils/math-utilities.d.ts +11 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -1
- package/dist/src/utils/math-utilities.js +14 -5
- package/dist/src/utils/math-utilities.js.map +1 -1
- package/dist/src/utils/optimization-helpers.d.ts +9 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -1
- package/dist/src/utils/optimization-helpers.js +12 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -1
- package/dist/src/utils/priorityQueue.d.ts +24 -0
- package/dist/src/utils/priorityQueue.d.ts.map +1 -1
- package/dist/src/utils/priorityQueue.js +34 -4
- package/dist/src/utils/priorityQueue.js.map +1 -1
- package/package.json +49 -45
- package/src/algorithms/centrality/betweenness.ts +34 -17
- package/src/algorithms/centrality/closeness.ts +34 -17
- package/src/algorithms/centrality/degree.ts +15 -19
- package/src/algorithms/centrality/delta-pagerank-simple.ts +36 -28
- package/src/algorithms/centrality/delta-pagerank.ts +34 -11
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -15
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +26 -17
- package/src/algorithms/community/girvan-newman.ts +39 -28
- package/src/algorithms/community/index.ts +6 -7
- package/src/algorithms/community/label-propagation.ts +20 -28
- package/src/algorithms/community/leiden.ts +61 -37
- package/src/algorithms/community/louvain-optimized.ts +49 -20
- package/src/algorithms/community/louvain.ts +35 -25
- package/src/algorithms/community/modularity-utils.ts +5 -17
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +17 -10
- package/src/algorithms/matching/index.ts +4 -4
- package/src/algorithms/matching/isomorphism.ts +53 -23
- package/src/algorithms/mst/index.ts +3 -4
- package/src/algorithms/mst/kruskal.ts +21 -6
- package/src/algorithms/mst/prim.ts +13 -4
- package/src/algorithms/shortest-path/bellman-ford.ts +16 -15
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +28 -28
- package/src/algorithms/shortest-path/dijkstra.ts +23 -17
- package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
- package/src/algorithms/shortest-path/index.ts +5 -5
- package/src/algorithms/traversal/bfs-unified.ts +64 -60
- package/src/algorithms/traversal/bfs-variants.ts +61 -37
- package/src/algorithms/traversal/bfs.ts +1 -7
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +57 -25
- package/src/clustering/hierarchical.ts +40 -34
- package/src/clustering/index.ts +8 -8
- package/src/clustering/k-core.ts +26 -42
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +91 -57
- package/src/core/graph.ts +54 -14
- package/src/data-structures/index.ts +2 -2
- package/src/data-structures/priority-queue.ts +36 -11
- package/src/data-structures/union-find.ts +22 -1
- package/src/flow/ford-fulkerson.ts +62 -35
- package/src/flow/min-cut.ts +43 -45
- package/src/index.ts +3 -2
- package/src/link-prediction/adamic-adar.ts +65 -51
- package/src/link-prediction/common-neighbors.ts +45 -36
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +106 -56
- package/src/optimized/csr-graph.ts +93 -30
- package/src/optimized/direction-optimized-bfs.ts +44 -25
- package/src/optimized/graph-adapter.ts +64 -16
- package/src/optimized/index.ts +7 -9
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +30 -24
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +39 -23
- package/src/research/terahac.ts +54 -20
- package/src/utils/graph-converters.ts +42 -14
- package/src/utils/graph-utilities.ts +11 -37
- package/src/utils/math-utilities.ts +17 -8
- package/src/utils/optimization-helpers.ts +27 -21
- package/src/utils/priorityQueue.ts +40 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import {UnionFind} from "../../data-structures/union-find.js";
|
|
3
|
-
import type {NodeId} from "../../types/index.js";
|
|
1
|
+
import type { Graph } from "../../core/graph.js";
|
|
2
|
+
import { UnionFind } from "../../data-structures/union-find.js";
|
|
3
|
+
import type { NodeId } from "../../types/index.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Connected components algorithms
|
|
@@ -11,10 +11,14 @@ import type {NodeId} from "../../types/index.js";
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Find connected components in an undirected graph using Union-Find
|
|
14
|
+
* @param graph - The input graph (must be undirected)
|
|
15
|
+
* @returns Array of connected components, each containing an array of node IDs
|
|
14
16
|
*/
|
|
15
17
|
export function connectedComponents(graph: Graph): NodeId[][] {
|
|
16
18
|
if (graph.isDirected) {
|
|
17
|
-
throw new Error(
|
|
19
|
+
throw new Error(
|
|
20
|
+
"Connected components algorithm requires an undirected graph. Use stronglyConnectedComponents for directed graphs.",
|
|
21
|
+
);
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
const nodes = Array.from(graph.nodes()).map((node) => node.id);
|
|
@@ -35,6 +39,8 @@ export function connectedComponents(graph: Graph): NodeId[][] {
|
|
|
35
39
|
|
|
36
40
|
/**
|
|
37
41
|
* Find connected components using DFS (alternative implementation)
|
|
42
|
+
* @param graph - The input graph (must be undirected)
|
|
43
|
+
* @returns Array of connected components, each containing an array of node IDs
|
|
38
44
|
*/
|
|
39
45
|
export function connectedComponentsDFS(graph: Graph): NodeId[][] {
|
|
40
46
|
if (graph.isDirected) {
|
|
@@ -57,13 +63,12 @@ export function connectedComponentsDFS(graph: Graph): NodeId[][] {
|
|
|
57
63
|
|
|
58
64
|
/**
|
|
59
65
|
* DFS helper for connected components
|
|
66
|
+
* @param graph - The input graph
|
|
67
|
+
* @param nodeId - The starting node ID
|
|
68
|
+
* @param visited - Set of already visited node IDs
|
|
69
|
+
* @param component - Array to accumulate component members
|
|
60
70
|
*/
|
|
61
|
-
function dfsComponent(
|
|
62
|
-
graph: Graph,
|
|
63
|
-
nodeId: NodeId,
|
|
64
|
-
visited: Set<NodeId>,
|
|
65
|
-
component: NodeId[],
|
|
66
|
-
): void {
|
|
71
|
+
function dfsComponent(graph: Graph, nodeId: NodeId, visited: Set<NodeId>, component: NodeId[]): void {
|
|
67
72
|
visited.add(nodeId);
|
|
68
73
|
component.push(nodeId);
|
|
69
74
|
|
|
@@ -76,6 +81,8 @@ function dfsComponent(
|
|
|
76
81
|
|
|
77
82
|
/**
|
|
78
83
|
* Find the number of connected components
|
|
84
|
+
* @param graph - The input graph (must be undirected)
|
|
85
|
+
* @returns The number of connected components in the graph
|
|
79
86
|
*/
|
|
80
87
|
export function numberOfConnectedComponents(graph: Graph): number {
|
|
81
88
|
return connectedComponents(graph).length;
|
|
@@ -83,6 +90,8 @@ export function numberOfConnectedComponents(graph: Graph): number {
|
|
|
83
90
|
|
|
84
91
|
/**
|
|
85
92
|
* Check if the graph is connected (has exactly one connected component)
|
|
93
|
+
* @param graph - The input graph (must be undirected)
|
|
94
|
+
* @returns True if the graph is connected, false otherwise
|
|
86
95
|
*/
|
|
87
96
|
export function isConnected(graph: Graph): boolean {
|
|
88
97
|
return numberOfConnectedComponents(graph) <= 1;
|
|
@@ -90,6 +99,8 @@ export function isConnected(graph: Graph): boolean {
|
|
|
90
99
|
|
|
91
100
|
/**
|
|
92
101
|
* Find the largest connected component
|
|
102
|
+
* @param graph - The input graph (must be undirected)
|
|
103
|
+
* @returns Array of node IDs in the largest connected component
|
|
93
104
|
*/
|
|
94
105
|
export function largestConnectedComponent(graph: Graph): NodeId[] {
|
|
95
106
|
const components = connectedComponents(graph);
|
|
@@ -98,13 +109,14 @@ export function largestConnectedComponent(graph: Graph): NodeId[] {
|
|
|
98
109
|
return [];
|
|
99
110
|
}
|
|
100
111
|
|
|
101
|
-
return components.reduce((largest, current) =>
|
|
102
|
-
current.length > largest.length ? current : largest,
|
|
103
|
-
);
|
|
112
|
+
return components.reduce((largest, current) => (current.length > largest.length ? current : largest));
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
116
|
* Get the connected component containing a specific node
|
|
117
|
+
* @param graph - The input graph (must be undirected)
|
|
118
|
+
* @param nodeId - The node ID to find the component for
|
|
119
|
+
* @returns Array of node IDs in the same component as the specified node
|
|
108
120
|
*/
|
|
109
121
|
export function getConnectedComponent(graph: Graph, nodeId: NodeId): NodeId[] {
|
|
110
122
|
if (!graph.hasNode(nodeId)) {
|
|
@@ -125,6 +137,8 @@ export function getConnectedComponent(graph: Graph, nodeId: NodeId): NodeId[] {
|
|
|
125
137
|
|
|
126
138
|
/**
|
|
127
139
|
* Find strongly connected components using Tarjan's algorithm
|
|
140
|
+
* @param graph - The input graph (must be directed)
|
|
141
|
+
* @returns Array of strongly connected components, each containing an array of node IDs
|
|
128
142
|
*/
|
|
129
143
|
export function stronglyConnectedComponents(graph: Graph): NodeId[][] {
|
|
130
144
|
if (!graph.isDirected) {
|
|
@@ -198,6 +212,8 @@ export function stronglyConnectedComponents(graph: Graph): NodeId[][] {
|
|
|
198
212
|
|
|
199
213
|
/**
|
|
200
214
|
* Check if a directed graph is strongly connected
|
|
215
|
+
* @param graph - The input graph (must be directed)
|
|
216
|
+
* @returns True if the graph is strongly connected, false otherwise
|
|
201
217
|
*/
|
|
202
218
|
export function isStronglyConnected(graph: Graph): boolean {
|
|
203
219
|
if (!graph.isDirected) {
|
|
@@ -211,10 +227,14 @@ export function isStronglyConnected(graph: Graph): boolean {
|
|
|
211
227
|
/**
|
|
212
228
|
* Find weakly connected components in a directed graph
|
|
213
229
|
* (treat the graph as undirected for connectivity)
|
|
230
|
+
* @param graph - The input graph (must be directed)
|
|
231
|
+
* @returns Array of weakly connected components, each containing an array of node IDs
|
|
214
232
|
*/
|
|
215
233
|
export function weaklyConnectedComponents(graph: Graph): NodeId[][] {
|
|
216
234
|
if (!graph.isDirected) {
|
|
217
|
-
throw new Error(
|
|
235
|
+
throw new Error(
|
|
236
|
+
"Weakly connected components are for directed graphs. Use connectedComponents for undirected graphs.",
|
|
237
|
+
);
|
|
218
238
|
}
|
|
219
239
|
|
|
220
240
|
const nodes = Array.from(graph.nodes()).map((node) => node.id);
|
|
@@ -235,6 +255,8 @@ export function weaklyConnectedComponents(graph: Graph): NodeId[][] {
|
|
|
235
255
|
|
|
236
256
|
/**
|
|
237
257
|
* Check if a directed graph is weakly connected
|
|
258
|
+
* @param graph - The input graph (must be directed)
|
|
259
|
+
* @returns True if the graph is weakly connected, false otherwise
|
|
238
260
|
*/
|
|
239
261
|
export function isWeaklyConnected(graph: Graph): boolean {
|
|
240
262
|
if (!graph.isDirected) {
|
|
@@ -246,6 +268,8 @@ export function isWeaklyConnected(graph: Graph): boolean {
|
|
|
246
268
|
|
|
247
269
|
/**
|
|
248
270
|
* Find condensation graph (quotient graph of strongly connected components)
|
|
271
|
+
* @param graph - The input graph (must be directed)
|
|
272
|
+
* @returns Object containing the condensed graph, component map, and component arrays
|
|
249
273
|
*/
|
|
250
274
|
export function condensationGraph(graph: Graph): {
|
|
251
275
|
condensedGraph: Graph;
|
|
@@ -258,7 +282,7 @@ export function condensationGraph(graph: Graph): {
|
|
|
258
282
|
|
|
259
283
|
const components = stronglyConnectedComponents(graph);
|
|
260
284
|
const componentMap = new Map<NodeId, number>();
|
|
261
|
-
const condensedGraph = new (graph.constructor as new (config: {directed: boolean}) => Graph)({directed: true});
|
|
285
|
+
const condensedGraph = new (graph.constructor as new (config: { directed: boolean }) => Graph)({ directed: true });
|
|
262
286
|
|
|
263
287
|
// Map each node to its component index
|
|
264
288
|
for (let i = 0; i < components.length; i++) {
|
|
@@ -279,8 +303,7 @@ export function condensationGraph(graph: Graph): {
|
|
|
279
303
|
const sourceComponent = componentMap.get(edge.source);
|
|
280
304
|
const targetComponent = componentMap.get(edge.target);
|
|
281
305
|
|
|
282
|
-
if (sourceComponent !== undefined && targetComponent !== undefined &&
|
|
283
|
-
sourceComponent !== targetComponent) {
|
|
306
|
+
if (sourceComponent !== undefined && targetComponent !== undefined && sourceComponent !== targetComponent) {
|
|
284
307
|
const edgeKey = `${String(sourceComponent)}-${String(targetComponent)}`;
|
|
285
308
|
|
|
286
309
|
if (!addedEdges.has(edgeKey)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../../types/index.js";
|
|
3
|
-
import {bfsColoringWithPartitions} from "../traversal/bfs-variants.js";
|
|
1
|
+
import type { Graph } from "../../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../../types/index.js";
|
|
3
|
+
import { bfsColoringWithPartitions } from "../traversal/bfs-variants.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Maximum Bipartite Matching implementation using Hopcroft-Karp algorithm
|
|
@@ -25,13 +25,17 @@ export interface BipartiteMatchingOptions {
|
|
|
25
25
|
/**
|
|
26
26
|
* Find maximum matching in a bipartite graph using augmenting path algorithm
|
|
27
27
|
* (simplified implementation that's more reliable than Hopcroft-Karp for this context)
|
|
28
|
+
* @param graph - The bipartite graph to find maximum matching for
|
|
29
|
+
* @param options - Optional configuration including left and right node partitions
|
|
30
|
+
* @returns The maximum matching as a map from left nodes to right nodes and the matching size
|
|
31
|
+
* @throws Error if the graph is not bipartite
|
|
28
32
|
*/
|
|
29
33
|
export function maximumBipartiteMatching(
|
|
30
34
|
graph: Graph,
|
|
31
35
|
options: BipartiteMatchingOptions = {},
|
|
32
36
|
): BipartiteMatchingResult {
|
|
33
37
|
// If partitions not provided, try to infer them
|
|
34
|
-
let {leftNodes, rightNodes} = options;
|
|
38
|
+
let { leftNodes, rightNodes } = options;
|
|
35
39
|
|
|
36
40
|
if (!leftNodes || !rightNodes) {
|
|
37
41
|
const partition = bipartitePartition(graph);
|
|
@@ -87,8 +91,10 @@ export function maximumBipartiteMatching(
|
|
|
87
91
|
|
|
88
92
|
/**
|
|
89
93
|
* Check if graph is bipartite and return the partition
|
|
94
|
+
* @param graph - The graph to check for bipartiteness
|
|
95
|
+
* @returns An object with left and right node sets if bipartite, or null if not bipartite
|
|
90
96
|
*/
|
|
91
|
-
export function bipartitePartition(graph: Graph): {left: Set<NodeId
|
|
97
|
+
export function bipartitePartition(graph: Graph): { left: Set<NodeId>; right: Set<NodeId> } | null {
|
|
92
98
|
const result = bfsColoringWithPartitions(graph);
|
|
93
99
|
|
|
94
100
|
if (!result.isBipartite || !result.partitions) {
|
|
@@ -104,12 +110,13 @@ export function bipartitePartition(graph: Graph): {left: Set<NodeId>, right: Set
|
|
|
104
110
|
/**
|
|
105
111
|
* Simple greedy bipartite matching algorithm
|
|
106
112
|
* Useful for comparison or when Hopcroft-Karp is overkill
|
|
113
|
+
* @param graph - The bipartite graph to find matching for
|
|
114
|
+
* @param options - Optional configuration including left and right node partitions
|
|
115
|
+
* @returns The matching as a map from left nodes to right nodes and the matching size
|
|
116
|
+
* @throws Error if the graph is not bipartite
|
|
107
117
|
*/
|
|
108
|
-
export function greedyBipartiteMatching(
|
|
109
|
-
|
|
110
|
-
options: BipartiteMatchingOptions = {},
|
|
111
|
-
): BipartiteMatchingResult {
|
|
112
|
-
let {leftNodes, rightNodes} = options;
|
|
118
|
+
export function greedyBipartiteMatching(graph: Graph, options: BipartiteMatchingOptions = {}): BipartiteMatchingResult {
|
|
119
|
+
let { leftNodes, rightNodes } = options;
|
|
113
120
|
|
|
114
121
|
if (!leftNodes || !rightNodes) {
|
|
115
122
|
const partition = bipartitePartition(graph);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Graph matching algorithms
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export type {BipartiteMatchingOptions, BipartiteMatchingResult} from "./bipartite.js";
|
|
6
|
-
export {bipartitePartition, greedyBipartiteMatching, maximumBipartiteMatching} from "./bipartite.js";
|
|
7
|
-
export type {IsomorphismOptions, IsomorphismResult} from "./isomorphism.js";
|
|
8
|
-
export {findAllIsomorphisms, isGraphIsomorphic} from "./isomorphism.js";
|
|
5
|
+
export type { BipartiteMatchingOptions, BipartiteMatchingResult } from "./bipartite.js";
|
|
6
|
+
export { bipartitePartition, greedyBipartiteMatching, maximumBipartiteMatching } from "./bipartite.js";
|
|
7
|
+
export type { IsomorphismOptions, IsomorphismResult } from "./isomorphism.js";
|
|
8
|
+
export { findAllIsomorphisms, isGraphIsomorphic } from "./isomorphism.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../../types/index.js";
|
|
1
|
+
import type { Graph } from "../../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../../types/index.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Graph Isomorphism (VF2) algorithm implementation
|
|
@@ -37,19 +37,19 @@ interface VF2State {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Check if two graphs are isomorphic using VF2 algorithm
|
|
40
|
+
* @param graph1 - The first graph to compare
|
|
41
|
+
* @param graph2 - The second graph to compare
|
|
42
|
+
* @param options - Optional configuration for node and edge matching predicates
|
|
43
|
+
* @returns An object indicating if the graphs are isomorphic and an optional mapping between nodes
|
|
40
44
|
*/
|
|
41
|
-
export function isGraphIsomorphic(
|
|
42
|
-
graph1: Graph,
|
|
43
|
-
graph2: Graph,
|
|
44
|
-
options: IsomorphismOptions = {},
|
|
45
|
-
): IsomorphismResult {
|
|
45
|
+
export function isGraphIsomorphic(graph1: Graph, graph2: Graph, options: IsomorphismOptions = {}): IsomorphismResult {
|
|
46
46
|
// Quick checks
|
|
47
47
|
if (graph1.nodeCount !== graph2.nodeCount || graph1.totalEdgeCount !== graph2.totalEdgeCount) {
|
|
48
|
-
return {isIsomorphic: false};
|
|
48
|
+
return { isIsomorphic: false };
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (graph1.isDirected !== graph2.isDirected) {
|
|
52
|
-
return {isIsomorphic: false};
|
|
52
|
+
return { isIsomorphic: false };
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const nodes1 = Array.from(graph1.nodes()).map((n) => n.id);
|
|
@@ -61,7 +61,7 @@ export function isGraphIsomorphic(
|
|
|
61
61
|
|
|
62
62
|
for (let i = 0; i < degrees1.length; i++) {
|
|
63
63
|
if (degrees1[i] !== degrees2[i]) {
|
|
64
|
-
return {isIsomorphic: false};
|
|
64
|
+
return { isIsomorphic: false };
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -87,11 +87,19 @@ export function isGraphIsomorphic(
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
return {isIsomorphic: false};
|
|
90
|
+
return { isIsomorphic: false };
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* VF2 recursive search
|
|
95
|
+
* @param g1 - The first graph being compared
|
|
96
|
+
* @param g2 - The second graph being compared
|
|
97
|
+
* @param state - The current VF2 algorithm state containing mappings and terminal sets
|
|
98
|
+
* @param nodes1 - Array of node IDs from the first graph
|
|
99
|
+
* @param nodes2 - Array of node IDs from the second graph
|
|
100
|
+
* @param options - Configuration options for matching predicates
|
|
101
|
+
* @param mappings - Array to collect found isomorphism mappings
|
|
102
|
+
* @returns True if an isomorphism was found and search should stop, false otherwise
|
|
95
103
|
*/
|
|
96
104
|
function vf2Recurse(
|
|
97
105
|
g1: Graph,
|
|
@@ -127,6 +135,12 @@ function vf2Recurse(
|
|
|
127
135
|
|
|
128
136
|
/**
|
|
129
137
|
* Get candidate pairs for the next mapping
|
|
138
|
+
* @param g1 - The first graph being compared
|
|
139
|
+
* @param g2 - The second graph being compared
|
|
140
|
+
* @param state - The current VF2 algorithm state containing mappings and terminal sets
|
|
141
|
+
* @param nodes1 - Array of node IDs from the first graph
|
|
142
|
+
* @param nodes2 - Array of node IDs from the second graph
|
|
143
|
+
* @returns Array of candidate node pairs to try matching
|
|
130
144
|
*/
|
|
131
145
|
function getCandidatePairs(
|
|
132
146
|
g1: Graph,
|
|
@@ -187,6 +201,13 @@ function getCandidatePairs(
|
|
|
187
201
|
|
|
188
202
|
/**
|
|
189
203
|
* Check if a pair is feasible
|
|
204
|
+
* @param g1 - The first graph being compared
|
|
205
|
+
* @param g2 - The second graph being compared
|
|
206
|
+
* @param state - The current VF2 algorithm state containing mappings and terminal sets
|
|
207
|
+
* @param node1 - The candidate node from the first graph
|
|
208
|
+
* @param node2 - The candidate node from the second graph
|
|
209
|
+
* @param options - Configuration options for matching predicates
|
|
210
|
+
* @returns True if the node pair can be feasibly matched, false otherwise
|
|
190
211
|
*/
|
|
191
212
|
function isFeasible(
|
|
192
213
|
g1: Graph,
|
|
@@ -222,8 +243,14 @@ function isFeasible(
|
|
|
222
243
|
}
|
|
223
244
|
|
|
224
245
|
// Check terminal set sizes
|
|
225
|
-
let new1In = 0;
|
|
226
|
-
let
|
|
246
|
+
let new1In = 0;
|
|
247
|
+
let new1Out = 0;
|
|
248
|
+
let term1In = 0;
|
|
249
|
+
let term1Out = 0;
|
|
250
|
+
let new2In = 0;
|
|
251
|
+
let new2Out = 0;
|
|
252
|
+
let term2In = 0;
|
|
253
|
+
let term2Out = 0;
|
|
227
254
|
|
|
228
255
|
for (const neighbor of neighbors1) {
|
|
229
256
|
if (state.core1.has(neighbor)) {
|
|
@@ -271,20 +298,19 @@ function isFeasible(
|
|
|
271
298
|
}
|
|
272
299
|
}
|
|
273
300
|
|
|
274
|
-
return term1In === term2In && term1Out === term2Out &&
|
|
275
|
-
new1In === new2In && new1Out === new2Out;
|
|
301
|
+
return term1In === term2In && term1Out === term2Out && new1In === new2In && new1Out === new2Out;
|
|
276
302
|
}
|
|
277
303
|
|
|
278
304
|
/**
|
|
279
305
|
* Add a pair to the mapping and update terminal sets
|
|
306
|
+
* @param g1 - The first graph being compared
|
|
307
|
+
* @param g2 - The second graph being compared
|
|
308
|
+
* @param state - The current VF2 algorithm state containing mappings and terminal sets
|
|
309
|
+
* @param node1 - The node from the first graph to add to the mapping
|
|
310
|
+
* @param node2 - The node from the second graph to add to the mapping
|
|
311
|
+
* @returns A new VF2 state with the pair added and terminal sets updated
|
|
280
312
|
*/
|
|
281
|
-
function addPair(
|
|
282
|
-
g1: Graph,
|
|
283
|
-
g2: Graph,
|
|
284
|
-
state: VF2State,
|
|
285
|
-
node1: NodeId,
|
|
286
|
-
node2: NodeId,
|
|
287
|
-
): VF2State {
|
|
313
|
+
function addPair(g1: Graph, g2: Graph, state: VF2State, node1: NodeId, node2: NodeId): VF2State {
|
|
288
314
|
const newState: VF2State = {
|
|
289
315
|
core1: new Map(state.core1),
|
|
290
316
|
core2: new Map(state.core2),
|
|
@@ -340,6 +366,10 @@ function addPair(
|
|
|
340
366
|
|
|
341
367
|
/**
|
|
342
368
|
* Find all isomorphisms between two graphs
|
|
369
|
+
* @param graph1 - The first graph to compare
|
|
370
|
+
* @param graph2 - The second graph to compare
|
|
371
|
+
* @param options - Optional configuration for node and edge matching predicates
|
|
372
|
+
* @returns An array of all possible node mappings between the graphs
|
|
343
373
|
*/
|
|
344
374
|
export function findAllIsomorphisms(
|
|
345
375
|
graph1: Graph,
|
|
@@ -386,7 +416,7 @@ export function findAllIsomorphisms(
|
|
|
386
416
|
|
|
387
417
|
// Find all isomorphisms
|
|
388
418
|
const mappings: Map<NodeId, NodeId>[] = [];
|
|
389
|
-
vf2Recurse(graph1, graph2, state, nodes1, nodes2, {...
|
|
419
|
+
vf2Recurse(graph1, graph2, state, nodes1, nodes2, { ...options, findAllMappings: true }, mappings);
|
|
390
420
|
|
|
391
421
|
return mappings;
|
|
392
422
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export type {MSTResult} from "./kruskal.js";
|
|
2
|
-
export {kruskalMST, minimumSpanningTree} from "./kruskal.js";
|
|
3
|
-
export {primMST} from "./prim.js";
|
|
4
|
-
|
|
1
|
+
export type { MSTResult } from "./kruskal.js";
|
|
2
|
+
export { kruskalMST, minimumSpanningTree } from "./kruskal.js";
|
|
3
|
+
export { primMST } from "./prim.js";
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import {Graph} from "../../core/graph.js";
|
|
2
|
-
import {UnionFind} from "../../data-structures/union-find.js";
|
|
3
|
-
import type {Edge} from "../../types/index.js";
|
|
1
|
+
import { Graph } from "../../core/graph.js";
|
|
2
|
+
import { UnionFind } from "../../data-structures/union-find.js";
|
|
3
|
+
import type { Edge } from "../../types/index.js";
|
|
4
4
|
|
|
5
5
|
export interface MSTResult {
|
|
6
6
|
edges: Edge[];
|
|
7
7
|
totalWeight: number;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Computes the minimum spanning tree of an undirected graph using Kruskal's algorithm.
|
|
12
|
+
* The algorithm sorts edges by weight and greedily adds edges that don't create cycles,
|
|
13
|
+
* using a union-find data structure to efficiently detect cycles.
|
|
14
|
+
* @param graph - The undirected graph to compute the MST for
|
|
15
|
+
* @returns The minimum spanning tree as a set of edges and the total weight
|
|
16
|
+
* @throws Error if the graph is directed or not connected
|
|
17
|
+
*/
|
|
10
18
|
export function kruskalMST(graph: Graph): MSTResult {
|
|
11
19
|
if (graph.isDirected) {
|
|
12
20
|
throw new Error("Kruskal's algorithm requires an undirected graph");
|
|
@@ -16,9 +24,10 @@ export function kruskalMST(graph: Graph): MSTResult {
|
|
|
16
24
|
const visitedEdges = new Set<string>();
|
|
17
25
|
|
|
18
26
|
for (const edge of Array.from(graph.edges())) {
|
|
19
|
-
const edgeKey =
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
const edgeKey =
|
|
28
|
+
edge.source < edge.target
|
|
29
|
+
? `${String(edge.source)}-${String(edge.target)}`
|
|
30
|
+
: `${String(edge.target)}-${String(edge.source)}`;
|
|
22
31
|
|
|
23
32
|
if (!visitedEdges.has(edgeKey)) {
|
|
24
33
|
visitedEdges.add(edgeKey);
|
|
@@ -56,6 +65,12 @@ export function kruskalMST(graph: Graph): MSTResult {
|
|
|
56
65
|
};
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Alias for kruskalMST. Computes the minimum spanning tree of an undirected graph.
|
|
70
|
+
* @param graph - The undirected graph to compute the MST for
|
|
71
|
+
* @returns The minimum spanning tree as a set of edges and the total weight
|
|
72
|
+
* @throws Error if the graph is directed or not connected
|
|
73
|
+
*/
|
|
59
74
|
export function minimumSpanningTree(graph: Graph): MSTResult {
|
|
60
75
|
return kruskalMST(graph);
|
|
61
76
|
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import {Graph} from "../../core/graph.js";
|
|
2
|
-
import {PriorityQueue} from "../../data-structures/priority-queue.js";
|
|
3
|
-
import type {Edge, NodeId} from "../../types/index.js";
|
|
4
|
-
import type {MSTResult} from "./kruskal.js";
|
|
1
|
+
import { Graph } from "../../core/graph.js";
|
|
2
|
+
import { PriorityQueue } from "../../data-structures/priority-queue.js";
|
|
3
|
+
import type { Edge, NodeId } from "../../types/index.js";
|
|
4
|
+
import type { MSTResult } from "./kruskal.js";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Computes the minimum spanning tree of an undirected graph using Prim's algorithm.
|
|
8
|
+
* The algorithm grows the MST from a starting node by repeatedly adding the minimum
|
|
9
|
+
* weight edge that connects a node in the tree to a node outside the tree.
|
|
10
|
+
* @param graph - The undirected graph to compute the MST for
|
|
11
|
+
* @param startNode - Optional starting node; if not provided, uses the first node in the graph
|
|
12
|
+
* @returns The minimum spanning tree as a set of edges and the total weight
|
|
13
|
+
* @throws Error if the graph is directed, the start node is not found, or the graph is not connected
|
|
14
|
+
*/
|
|
6
15
|
export function primMST(graph: Graph, startNode?: NodeId): MSTResult {
|
|
7
16
|
if (graph.isDirected) {
|
|
8
17
|
throw new Error("Prim's algorithm requires an undirected graph");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import type {NodeId, ShortestPathResult} from "../../types/index.js";
|
|
3
|
-
import {reconstructPath} from "../../utils/graph-utilities.js";
|
|
1
|
+
import type { Graph } from "../../core/graph.js";
|
|
2
|
+
import type { NodeId, ShortestPathResult } from "../../types/index.js";
|
|
3
|
+
import { reconstructPath } from "../../utils/graph-utilities.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Bellman-Ford algorithm implementation for single-source shortest paths
|
|
@@ -44,12 +44,12 @@ export interface BellmanFordResult {
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Find shortest paths from source using Bellman-Ford algorithm
|
|
47
|
+
* @param graph - The graph to search
|
|
48
|
+
* @param source - The starting node for the search
|
|
49
|
+
* @param options - Algorithm options including optional target for early termination
|
|
50
|
+
* @returns The distances, predecessors, and negative cycle information
|
|
47
51
|
*/
|
|
48
|
-
export function bellmanFord(
|
|
49
|
-
graph: Graph,
|
|
50
|
-
source: NodeId,
|
|
51
|
-
options: BellmanFordOptions = {},
|
|
52
|
-
): BellmanFordResult {
|
|
52
|
+
export function bellmanFord(graph: Graph, source: NodeId, options: BellmanFordOptions = {}): BellmanFordResult {
|
|
53
53
|
if (!graph.hasNode(source)) {
|
|
54
54
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
55
55
|
}
|
|
@@ -130,12 +130,12 @@ export function bellmanFord(
|
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* Find shortest path between two specific nodes using Bellman-Ford
|
|
133
|
+
* @param graph - The graph to search
|
|
134
|
+
* @param source - The starting node for the path
|
|
135
|
+
* @param target - The destination node for the path
|
|
136
|
+
* @returns The shortest path result or null if no path exists
|
|
133
137
|
*/
|
|
134
|
-
export function bellmanFordPath(
|
|
135
|
-
graph: Graph,
|
|
136
|
-
source: NodeId,
|
|
137
|
-
target: NodeId,
|
|
138
|
-
): ShortestPathResult | null {
|
|
138
|
+
export function bellmanFordPath(graph: Graph, source: NodeId, target: NodeId): ShortestPathResult | null {
|
|
139
139
|
if (!graph.hasNode(source)) {
|
|
140
140
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
141
141
|
}
|
|
@@ -144,7 +144,7 @@ export function bellmanFordPath(
|
|
|
144
144
|
throw new Error(`Target node ${String(target)} not found in graph`);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
const result = bellmanFord(graph, source, {target});
|
|
147
|
+
const result = bellmanFord(graph, source, { target });
|
|
148
148
|
|
|
149
149
|
if (result.hasNegativeCycle) {
|
|
150
150
|
throw new Error("Graph contains a negative cycle");
|
|
@@ -168,6 +168,8 @@ export function bellmanFordPath(
|
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
170
|
* Check if graph has negative cycles using Bellman-Ford
|
|
171
|
+
* @param graph - The graph to check for negative cycles
|
|
172
|
+
* @returns True if the graph contains a negative cycle
|
|
171
173
|
*/
|
|
172
174
|
export function hasNegativeCycle(graph: Graph): boolean {
|
|
173
175
|
const nodes = Array.from(graph.nodes());
|
|
@@ -198,4 +200,3 @@ export function hasNegativeCycle(graph: Graph): boolean {
|
|
|
198
200
|
|
|
199
201
|
return false;
|
|
200
202
|
}
|
|
201
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import {PriorityQueue} from "../../data-structures/priority-queue.js";
|
|
3
|
-
import type {NodeId, ShortestPathResult} from "../../types/index.js";
|
|
1
|
+
import type { Graph } from "../../core/graph.js";
|
|
2
|
+
import { PriorityQueue } from "../../data-structures/priority-queue.js";
|
|
3
|
+
import type { NodeId, ShortestPathResult } from "../../types/index.js";
|
|
4
4
|
|
|
5
5
|
interface SearchState {
|
|
6
6
|
distances: Map<NodeId, number>;
|
|
@@ -30,6 +30,10 @@ export class BidirectionalDijkstra {
|
|
|
30
30
|
private meetingNode: NodeId | null = null;
|
|
31
31
|
private shortestDistance = Infinity;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new BidirectionalDijkstra instance
|
|
35
|
+
* @param graph - The graph to search for shortest paths
|
|
36
|
+
*/
|
|
33
37
|
constructor(graph: Graph) {
|
|
34
38
|
this.graph = graph;
|
|
35
39
|
this.forwardSearch = this.initSearchState();
|
|
@@ -46,10 +50,13 @@ export class BidirectionalDijkstra {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/**
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
* Find shortest path between source and target nodes
|
|
54
|
+
* @param source - The starting node for the path
|
|
55
|
+
* @param target - The destination node for the path
|
|
56
|
+
* @returns The shortest path result or null if no path exists
|
|
57
|
+
*/
|
|
51
58
|
public findShortestPath(source: NodeId, target: NodeId): ShortestPathResult | null {
|
|
52
|
-
|
|
59
|
+
// Reset state for new search
|
|
53
60
|
this.reset();
|
|
54
61
|
if (!this.graph.hasNode(source)) {
|
|
55
62
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
@@ -79,15 +86,16 @@ export class BidirectionalDijkstra {
|
|
|
79
86
|
this.backwardSearch.frontier.enqueue(target, 0);
|
|
80
87
|
|
|
81
88
|
// Main search loop
|
|
82
|
-
while (!this.forwardSearch.frontier.isEmpty() ||
|
|
83
|
-
!this.backwardSearch.frontier.isEmpty()) {
|
|
89
|
+
while (!this.forwardSearch.frontier.isEmpty() || !this.backwardSearch.frontier.isEmpty()) {
|
|
84
90
|
// Alternate between forward and backward search
|
|
85
91
|
// Choose the search with smaller frontier for better performance
|
|
86
92
|
let searchExpanded = false;
|
|
87
93
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
if (
|
|
95
|
+
!this.forwardSearch.frontier.isEmpty() &&
|
|
96
|
+
(this.backwardSearch.frontier.isEmpty() ||
|
|
97
|
+
this.forwardSearch.frontier.size() <= this.backwardSearch.frontier.size())
|
|
98
|
+
) {
|
|
91
99
|
if (this.expandSearch(this.forwardSearch, this.backwardSearch, true)) {
|
|
92
100
|
break;
|
|
93
101
|
}
|
|
@@ -102,8 +110,7 @@ export class BidirectionalDijkstra {
|
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
// Continue until both frontiers are empty or we've found an optimal path
|
|
105
|
-
if (this.forwardSearch.frontier.isEmpty() &&
|
|
106
|
-
this.backwardSearch.frontier.isEmpty()) {
|
|
113
|
+
if (this.forwardSearch.frontier.isEmpty() && this.backwardSearch.frontier.isEmpty()) {
|
|
107
114
|
break;
|
|
108
115
|
}
|
|
109
116
|
}
|
|
@@ -119,11 +126,7 @@ export class BidirectionalDijkstra {
|
|
|
119
126
|
};
|
|
120
127
|
}
|
|
121
128
|
|
|
122
|
-
private expandSearch(
|
|
123
|
-
search: SearchState,
|
|
124
|
-
oppositeSearch: SearchState,
|
|
125
|
-
isForward: boolean,
|
|
126
|
-
): boolean {
|
|
129
|
+
private expandSearch(search: SearchState, oppositeSearch: SearchState, isForward: boolean): boolean {
|
|
127
130
|
const current = search.frontier.dequeue();
|
|
128
131
|
if (current === undefined) {
|
|
129
132
|
return false;
|
|
@@ -150,18 +153,16 @@ export class BidirectionalDijkstra {
|
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
// Explore neighbors
|
|
153
|
-
const neighbors = isForward
|
|
154
|
-
Array.from(this.graph.neighbors(current))
|
|
155
|
-
Array.from(this.graph.inNeighbors(current));
|
|
156
|
+
const neighbors = isForward
|
|
157
|
+
? Array.from(this.graph.neighbors(current))
|
|
158
|
+
: Array.from(this.graph.inNeighbors(current));
|
|
156
159
|
|
|
157
160
|
for (const neighbor of neighbors) {
|
|
158
161
|
if (search.visited.has(neighbor)) {
|
|
159
162
|
continue;
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
const edge = isForward ?
|
|
163
|
-
this.graph.getEdge(current, neighbor) :
|
|
164
|
-
this.graph.getEdge(neighbor, current);
|
|
165
|
+
const edge = isForward ? this.graph.getEdge(current, neighbor) : this.graph.getEdge(neighbor, current);
|
|
165
166
|
|
|
166
167
|
if (!edge) {
|
|
167
168
|
continue;
|
|
@@ -175,8 +176,7 @@ export class BidirectionalDijkstra {
|
|
|
175
176
|
const newDistance = distance + edgeWeight;
|
|
176
177
|
const currentNeighborDistance = search.distances.get(neighbor);
|
|
177
178
|
|
|
178
|
-
if (!search.distances.has(neighbor) ||
|
|
179
|
-
newDistance < (currentNeighborDistance ?? Infinity)) {
|
|
179
|
+
if (!search.distances.has(neighbor) || newDistance < (currentNeighborDistance ?? Infinity)) {
|
|
180
180
|
search.distances.set(neighbor, newDistance);
|
|
181
181
|
search.previous.set(neighbor, current);
|
|
182
182
|
search.frontier.enqueue(neighbor, newDistance);
|
|
@@ -237,8 +237,8 @@ export class BidirectionalDijkstra {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
* Reset the search state for reuse
|
|
241
|
+
*/
|
|
242
242
|
public reset(): void {
|
|
243
243
|
this.forwardSearch = this.initSearchState();
|
|
244
244
|
this.backwardSearch = this.initSearchState();
|