@graphty/algorithms 1.2.0 → 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 +375 -276
- package/dist/algorithms.d.ts +42 -2
- package/dist/algorithms.js +11402 -2
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +14 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +144 -169
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +35 -1
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +70 -149
- 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 +12 -9
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +57 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +304 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +106 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js +320 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
- 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 +3 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +2 -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 +31 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +61 -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 +67 -57
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/index.d.ts +2 -0
- 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 +11 -4
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +60 -38
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +3 -3
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +70 -44
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +149 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain-optimized.js +338 -0
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
- 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 +35 -81
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +55 -0
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
- package/dist/src/algorithms/community/modularity-utils.js +108 -0
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
- 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 +18 -40
- 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 +11 -12
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +44 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +195 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +20 -2
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +30 -14
- 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 +42 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-unified.js +370 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +77 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.js +402 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +5 -182
- 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 +25 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
- package/dist/src/benchmark-all-algorithms.js +491 -0
- package/dist/src/benchmark-all-algorithms.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +23 -15
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +226 -224
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts +4 -2
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js +2 -2
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +17 -19
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +68 -30
- 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 +11 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +99 -47
- 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 +49 -5
- 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 +20 -12
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +170 -217
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +6 -8
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +34 -12
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -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 +47 -30
- 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 +31 -14
- 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 +193 -0
- package/dist/src/optimized/bit-packed.d.ts.map +1 -0
- package/dist/src/optimized/bit-packed.js +342 -0
- package/dist/src/optimized/bit-packed.js.map +1 -0
- package/dist/src/optimized/csr-graph.d.ts +149 -0
- package/dist/src/optimized/csr-graph.d.ts.map +1 -0
- package/dist/src/optimized/csr-graph.js +404 -0
- package/dist/src/optimized/csr-graph.js.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts +95 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.js +242 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
- package/dist/src/optimized/graph-adapter.d.ts +111 -0
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
- package/dist/src/optimized/graph-adapter.js +194 -0
- package/dist/src/optimized/graph-adapter.js.map +1 -0
- package/dist/src/optimized/index.d.ts +14 -0
- package/dist/src/optimized/index.d.ts.map +1 -0
- package/dist/src/optimized/index.js +30 -0
- package/dist/src/optimized/index.js.map +1 -0
- 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 +28 -23
- 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 +31 -36
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +2 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +39 -7
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/types/index.d.ts +8 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/algorithm-utilities.d.ts +2 -0
- package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
- package/dist/src/utils/algorithm-utilities.js +2 -0
- package/dist/src/utils/algorithm-utilities.js.map +1 -0
- package/dist/src/utils/graph-converters.d.ts +110 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -0
- package/dist/src/utils/graph-converters.js +227 -0
- package/dist/src/utils/graph-converters.js.map +1 -0
- package/dist/src/utils/graph-utilities.d.ts +62 -0
- package/dist/src/utils/graph-utilities.d.ts.map +1 -0
- package/dist/src/utils/graph-utilities.js +148 -0
- package/dist/src/utils/graph-utilities.js.map +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +5 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/math-utilities.d.ts +63 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -0
- package/dist/src/utils/math-utilities.js +132 -0
- package/dist/src/utils/math-utilities.js.map +1 -0
- package/dist/src/utils/matrix-utilities.d.ts +2 -0
- package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
- package/dist/src/utils/matrix-utilities.js +2 -0
- package/dist/src/utils/matrix-utilities.js.map +1 -0
- package/dist/src/utils/optimization-helpers.d.ts +42 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
- package/dist/src/utils/optimization-helpers.js +59 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -0
- 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 +58 -39
- package/src/algorithms/centrality/betweenness.ts +185 -200
- package/src/algorithms/centrality/closeness.ts +93 -194
- package/src/algorithms/centrality/degree.ts +18 -22
- package/src/algorithms/centrality/delta-pagerank-simple.ts +374 -0
- package/src/algorithms/centrality/delta-pagerank.ts +458 -0
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -13
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +91 -16
- package/src/algorithms/community/girvan-newman.ts +90 -86
- package/src/algorithms/community/index.ts +6 -5
- package/src/algorithms/community/label-propagation.ts +69 -48
- package/src/algorithms/community/leiden.ts +88 -65
- package/src/algorithms/community/louvain-optimized.ts +474 -0
- package/src/algorithms/community/louvain.ts +52 -122
- package/src/algorithms/community/modularity-utils.ts +134 -0
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +24 -53
- 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 -28
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
- package/src/algorithms/shortest-path/dijkstra.ts +38 -30
- 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 +437 -0
- package/src/algorithms/traversal/bfs-variants.ts +520 -0
- package/src/algorithms/traversal/bfs.ts +5 -229
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +580 -0
- package/src/clustering/hierarchical.ts +268 -272
- package/src/clustering/index.ts +8 -6
- package/src/clustering/k-core.ts +93 -61
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +130 -72
- package/src/core/graph.ts +58 -18
- 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 +221 -262
- package/src/flow/min-cut.ts +59 -51
- package/src/index.ts +9 -2
- package/src/link-prediction/adamic-adar.ts +75 -74
- package/src/link-prediction/common-neighbors.ts +49 -49
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +390 -0
- package/src/optimized/csr-graph.ts +518 -0
- package/src/optimized/direction-optimized-bfs.ts +310 -0
- package/src/optimized/graph-adapter.ts +248 -0
- package/src/optimized/index.ts +34 -0
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +36 -39
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +41 -54
- package/src/research/terahac.ts +63 -21
- package/src/types/index.ts +8 -0
- package/src/utils/algorithm-utilities.ts +2 -0
- package/src/utils/graph-converters.ts +269 -0
- package/src/utils/graph-utilities.ts +173 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/math-utilities.ts +149 -0
- package/src/utils/matrix-utilities.ts +2 -0
- package/src/utils/optimization-helpers.ts +87 -0
- package/src/utils/priorityQueue.ts +40 -6
|
@@ -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,5 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import type {NodeId, ShortestPathResult} from "../../types/index.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";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Bellman-Ford algorithm implementation for single-source shortest paths
|
|
@@ -43,12 +44,12 @@ export interface BellmanFordResult {
|
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
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
|
|
46
51
|
*/
|
|
47
|
-
export function bellmanFord(
|
|
48
|
-
graph: Graph,
|
|
49
|
-
source: NodeId,
|
|
50
|
-
options: BellmanFordOptions = {},
|
|
51
|
-
): BellmanFordResult {
|
|
52
|
+
export function bellmanFord(graph: Graph, source: NodeId, options: BellmanFordOptions = {}): BellmanFordResult {
|
|
52
53
|
if (!graph.hasNode(source)) {
|
|
53
54
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
54
55
|
}
|
|
@@ -129,12 +130,12 @@ export function bellmanFord(
|
|
|
129
130
|
|
|
130
131
|
/**
|
|
131
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
|
|
132
137
|
*/
|
|
133
|
-
export function bellmanFordPath(
|
|
134
|
-
graph: Graph,
|
|
135
|
-
source: NodeId,
|
|
136
|
-
target: NodeId,
|
|
137
|
-
): ShortestPathResult | null {
|
|
138
|
+
export function bellmanFordPath(graph: Graph, source: NodeId, target: NodeId): ShortestPathResult | null {
|
|
138
139
|
if (!graph.hasNode(source)) {
|
|
139
140
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
140
141
|
}
|
|
@@ -143,7 +144,7 @@ export function bellmanFordPath(
|
|
|
143
144
|
throw new Error(`Target node ${String(target)} not found in graph`);
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
const result = bellmanFord(graph, source, {target});
|
|
147
|
+
const result = bellmanFord(graph, source, { target });
|
|
147
148
|
|
|
148
149
|
if (result.hasNegativeCycle) {
|
|
149
150
|
throw new Error("Graph contains a negative cycle");
|
|
@@ -167,6 +168,8 @@ export function bellmanFordPath(
|
|
|
167
168
|
|
|
168
169
|
/**
|
|
169
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
|
|
170
173
|
*/
|
|
171
174
|
export function hasNegativeCycle(graph: Graph): boolean {
|
|
172
175
|
const nodes = Array.from(graph.nodes());
|
|
@@ -197,18 +200,3 @@ export function hasNegativeCycle(graph: Graph): boolean {
|
|
|
197
200
|
|
|
198
201
|
return false;
|
|
199
202
|
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Reconstruct path from predecessor map
|
|
203
|
-
*/
|
|
204
|
-
function reconstructPath(target: NodeId, predecessors: Map<NodeId, NodeId | null>): NodeId[] {
|
|
205
|
-
const path: NodeId[] = [];
|
|
206
|
-
let current: NodeId | null = target;
|
|
207
|
-
|
|
208
|
-
while (current !== null) {
|
|
209
|
-
path.unshift(current);
|
|
210
|
-
current = predecessors.get(current) ?? null;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return path;
|
|
214
|
-
}
|
|
@@ -0,0 +1,248 @@
|
|
|
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
|
+
|
|
5
|
+
interface SearchState {
|
|
6
|
+
distances: Map<NodeId, number>;
|
|
7
|
+
previous: Map<NodeId, NodeId | null>;
|
|
8
|
+
visited: Set<NodeId>;
|
|
9
|
+
frontier: PriorityQueue<NodeId>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Bidirectional Dijkstra's algorithm implementation
|
|
14
|
+
*
|
|
15
|
+
* Performs simultaneous forward and backward searches to find shortest paths
|
|
16
|
+
* more efficiently than standard Dijkstra for point-to-point queries.
|
|
17
|
+
*
|
|
18
|
+
* Time Complexity: O(2 * sqrt(V + E) log V) expected for random graphs
|
|
19
|
+
* Space Complexity: O(V)
|
|
20
|
+
*
|
|
21
|
+
* Key optimizations:
|
|
22
|
+
* - Alternating expansion based on frontier size
|
|
23
|
+
* - Early termination when searches meet
|
|
24
|
+
* - Efficient meeting point detection
|
|
25
|
+
*/
|
|
26
|
+
export class BidirectionalDijkstra {
|
|
27
|
+
private graph: Graph;
|
|
28
|
+
private forwardSearch: SearchState;
|
|
29
|
+
private backwardSearch: SearchState;
|
|
30
|
+
private meetingNode: NodeId | null = null;
|
|
31
|
+
private shortestDistance = Infinity;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new BidirectionalDijkstra instance
|
|
35
|
+
* @param graph - The graph to search for shortest paths
|
|
36
|
+
*/
|
|
37
|
+
constructor(graph: Graph) {
|
|
38
|
+
this.graph = graph;
|
|
39
|
+
this.forwardSearch = this.initSearchState();
|
|
40
|
+
this.backwardSearch = this.initSearchState();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private initSearchState(): SearchState {
|
|
44
|
+
return {
|
|
45
|
+
distances: new Map(),
|
|
46
|
+
previous: new Map(),
|
|
47
|
+
visited: new Set(),
|
|
48
|
+
frontier: new PriorityQueue<NodeId>(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
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
|
+
*/
|
|
58
|
+
public findShortestPath(source: NodeId, target: NodeId): ShortestPathResult | null {
|
|
59
|
+
// Reset state for new search
|
|
60
|
+
this.reset();
|
|
61
|
+
if (!this.graph.hasNode(source)) {
|
|
62
|
+
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!this.graph.hasNode(target)) {
|
|
66
|
+
throw new Error(`Target node ${String(target)} not found in graph`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Special case: source equals target
|
|
70
|
+
if (source === target) {
|
|
71
|
+
return {
|
|
72
|
+
distance: 0,
|
|
73
|
+
path: [source],
|
|
74
|
+
predecessor: new Map([[source, null]]),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Initialize forward search from source
|
|
79
|
+
this.forwardSearch.distances.set(source, 0);
|
|
80
|
+
this.forwardSearch.previous.set(source, null);
|
|
81
|
+
this.forwardSearch.frontier.enqueue(source, 0);
|
|
82
|
+
|
|
83
|
+
// Initialize backward search from target
|
|
84
|
+
this.backwardSearch.distances.set(target, 0);
|
|
85
|
+
this.backwardSearch.previous.set(target, null);
|
|
86
|
+
this.backwardSearch.frontier.enqueue(target, 0);
|
|
87
|
+
|
|
88
|
+
// Main search loop
|
|
89
|
+
while (!this.forwardSearch.frontier.isEmpty() || !this.backwardSearch.frontier.isEmpty()) {
|
|
90
|
+
// Alternate between forward and backward search
|
|
91
|
+
// Choose the search with smaller frontier for better performance
|
|
92
|
+
let searchExpanded = false;
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
!this.forwardSearch.frontier.isEmpty() &&
|
|
96
|
+
(this.backwardSearch.frontier.isEmpty() ||
|
|
97
|
+
this.forwardSearch.frontier.size() <= this.backwardSearch.frontier.size())
|
|
98
|
+
) {
|
|
99
|
+
if (this.expandSearch(this.forwardSearch, this.backwardSearch, true)) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
searchExpanded = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!this.backwardSearch.frontier.isEmpty() && !searchExpanded) {
|
|
107
|
+
if (this.expandSearch(this.backwardSearch, this.forwardSearch, false)) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Continue until both frontiers are empty or we've found an optimal path
|
|
113
|
+
if (this.forwardSearch.frontier.isEmpty() && this.backwardSearch.frontier.isEmpty()) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (this.meetingNode === null) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
distance: this.shortestDistance,
|
|
124
|
+
path: this.reconstructPath(),
|
|
125
|
+
predecessor: new Map(), // Combined from both searches
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private expandSearch(search: SearchState, oppositeSearch: SearchState, isForward: boolean): boolean {
|
|
130
|
+
const current = search.frontier.dequeue();
|
|
131
|
+
if (current === undefined) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const distance = search.distances.get(current);
|
|
136
|
+
if (distance === undefined) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (search.visited.has(current)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
search.visited.add(current);
|
|
145
|
+
|
|
146
|
+
// Check if we've met the opposite search
|
|
147
|
+
if (oppositeSearch.distances.has(current)) {
|
|
148
|
+
const totalDistance = distance + (oppositeSearch.distances.get(current) ?? 0);
|
|
149
|
+
if (totalDistance < this.shortestDistance) {
|
|
150
|
+
this.shortestDistance = totalDistance;
|
|
151
|
+
this.meetingNode = current;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Explore neighbors
|
|
156
|
+
const neighbors = isForward
|
|
157
|
+
? Array.from(this.graph.neighbors(current))
|
|
158
|
+
: Array.from(this.graph.inNeighbors(current));
|
|
159
|
+
|
|
160
|
+
for (const neighbor of neighbors) {
|
|
161
|
+
if (search.visited.has(neighbor)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const edge = isForward ? this.graph.getEdge(current, neighbor) : this.graph.getEdge(neighbor, current);
|
|
166
|
+
|
|
167
|
+
if (!edge) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const edgeWeight = edge.weight ?? 1;
|
|
172
|
+
if (edgeWeight < 0) {
|
|
173
|
+
throw new Error("Bidirectional Dijkstra does not support negative edge weights");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const newDistance = distance + edgeWeight;
|
|
177
|
+
const currentNeighborDistance = search.distances.get(neighbor);
|
|
178
|
+
|
|
179
|
+
if (!search.distances.has(neighbor) || newDistance < (currentNeighborDistance ?? Infinity)) {
|
|
180
|
+
search.distances.set(neighbor, newDistance);
|
|
181
|
+
search.previous.set(neighbor, current);
|
|
182
|
+
search.frontier.enqueue(neighbor, newDistance);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private getMinDistance(frontier: PriorityQueue<NodeId>): number {
|
|
190
|
+
if (frontier.isEmpty()) {
|
|
191
|
+
return Infinity;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Since we can't peek at priority directly, we estimate based on the smallest known distance
|
|
195
|
+
const minDist = Infinity;
|
|
196
|
+
|
|
197
|
+
// For a more accurate implementation, we'd need to track the minimum distance
|
|
198
|
+
// For now, we'll use a simple heuristic
|
|
199
|
+
return minDist;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private reconstructPath(): NodeId[] {
|
|
203
|
+
if (!this.meetingNode) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const path: NodeId[] = [];
|
|
208
|
+
|
|
209
|
+
// Reconstruct forward path
|
|
210
|
+
let current: NodeId | null = this.meetingNode;
|
|
211
|
+
const forwardPath: NodeId[] = [];
|
|
212
|
+
|
|
213
|
+
while (current !== null) {
|
|
214
|
+
forwardPath.push(current);
|
|
215
|
+
current = this.forwardSearch.previous.get(current) ?? null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Add forward path in reverse order (except meeting node)
|
|
219
|
+
for (let i = forwardPath.length - 1; i > 0; i--) {
|
|
220
|
+
const node = forwardPath[i];
|
|
221
|
+
if (node !== undefined) {
|
|
222
|
+
path.push(node);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Add meeting node
|
|
227
|
+
path.push(this.meetingNode);
|
|
228
|
+
|
|
229
|
+
// Reconstruct and add backward path
|
|
230
|
+
current = this.backwardSearch.previous.get(this.meetingNode) ?? null;
|
|
231
|
+
while (current !== null) {
|
|
232
|
+
path.push(current);
|
|
233
|
+
current = this.backwardSearch.previous.get(current) ?? null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return path;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Reset the search state for reuse
|
|
241
|
+
*/
|
|
242
|
+
public reset(): void {
|
|
243
|
+
this.forwardSearch = this.initSearchState();
|
|
244
|
+
this.backwardSearch = this.initSearchState();
|
|
245
|
+
this.meetingNode = null;
|
|
246
|
+
this.shortestDistance = Infinity;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type {Graph} from "../../core/graph.js";
|
|
2
|
-
import {PriorityQueue} from "../../data-structures/priority-queue.js";
|
|
3
|
-
import type {DijkstraOptions, 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 { DijkstraOptions, NodeId, ShortestPathResult } from "../../types/index.js";
|
|
4
|
+
import { reconstructPath } from "../../utils/graph-utilities.js";
|
|
5
|
+
import { BidirectionalDijkstra } from "./bidirectional-dijkstra.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Dijkstra's algorithm implementation for single-source shortest paths
|
|
@@ -11,12 +13,12 @@ import type {DijkstraOptions, NodeId, ShortestPathResult} from "../../types/inde
|
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Find shortest paths from source to all reachable nodes using Dijkstra's algorithm
|
|
16
|
+
* @param graph - The graph to search
|
|
17
|
+
* @param source - The starting node for the search
|
|
18
|
+
* @param options - Algorithm options including optional target for early termination
|
|
19
|
+
* @returns A map of node IDs to their shortest path results
|
|
14
20
|
*/
|
|
15
|
-
export function dijkstra(
|
|
16
|
-
graph: Graph,
|
|
17
|
-
source: NodeId,
|
|
18
|
-
options: DijkstraOptions = {},
|
|
19
|
-
): Map<NodeId, ShortestPathResult> {
|
|
21
|
+
export function dijkstra(graph: Graph, source: NodeId, options: DijkstraOptions = {}): Map<NodeId, ShortestPathResult> {
|
|
20
22
|
if (!graph.hasNode(source)) {
|
|
21
23
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
22
24
|
}
|
|
@@ -113,12 +115,21 @@ export function dijkstra(
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
/**
|
|
116
|
-
* Find shortest path between two specific nodes using Dijkstra's algorithm
|
|
118
|
+
* Find shortest path between two specific nodes using optimized Dijkstra's algorithm
|
|
119
|
+
*
|
|
120
|
+
* Uses bidirectional search by default for improved performance on point-to-point queries.
|
|
121
|
+
* Automatically falls back to standard Dijkstra for very small graphs or when explicitly disabled.
|
|
122
|
+
* @param graph - The graph to search
|
|
123
|
+
* @param source - The starting node for the path
|
|
124
|
+
* @param target - The destination node for the path
|
|
125
|
+
* @param options - Algorithm options including bidirectional search toggle
|
|
126
|
+
* @returns The shortest path result or null if no path exists
|
|
117
127
|
*/
|
|
118
128
|
export function dijkstraPath(
|
|
119
129
|
graph: Graph,
|
|
120
130
|
source: NodeId,
|
|
121
131
|
target: NodeId,
|
|
132
|
+
options: DijkstraOptions = {},
|
|
122
133
|
): ShortestPathResult | null {
|
|
123
134
|
if (!graph.hasNode(source)) {
|
|
124
135
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
@@ -137,18 +148,28 @@ export function dijkstraPath(
|
|
|
137
148
|
};
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
|
|
151
|
+
// Use bidirectional search by default for point-to-point queries
|
|
152
|
+
// Only disable if explicitly requested or graph is very small (overhead dominates)
|
|
153
|
+
const useBidirectional = options.bidirectional !== false && graph.nodeCount > 10;
|
|
154
|
+
|
|
155
|
+
if (useBidirectional) {
|
|
156
|
+
const biDijkstra = new BidirectionalDijkstra(graph);
|
|
157
|
+
return biDijkstra.findShortestPath(source, target);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Fallback to standard dijkstra
|
|
161
|
+
const results = dijkstra(graph, source, { target });
|
|
141
162
|
return results.get(target) ?? null;
|
|
142
163
|
}
|
|
143
164
|
|
|
144
165
|
/**
|
|
145
166
|
* Single-source shortest paths with early termination optimization
|
|
167
|
+
* @param graph - The graph to search
|
|
168
|
+
* @param source - The starting node for the search
|
|
169
|
+
* @param cutoff - Optional maximum distance to search
|
|
170
|
+
* @returns A map of node IDs to their distances from the source
|
|
146
171
|
*/
|
|
147
|
-
export function singleSourceShortestPath(
|
|
148
|
-
graph: Graph,
|
|
149
|
-
source: NodeId,
|
|
150
|
-
cutoff?: number,
|
|
151
|
-
): Map<NodeId, number> {
|
|
172
|
+
export function singleSourceShortestPath(graph: Graph, source: NodeId, cutoff?: number): Map<NodeId, number> {
|
|
152
173
|
if (!graph.hasNode(source)) {
|
|
153
174
|
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
154
175
|
}
|
|
@@ -228,6 +249,8 @@ export function singleSourceShortestPath(
|
|
|
228
249
|
/**
|
|
229
250
|
* All-pairs shortest paths using repeated Dijkstra
|
|
230
251
|
* Note: For dense graphs, consider Floyd-Warshall algorithm instead
|
|
252
|
+
* @param graph - The graph to compute all-pairs shortest paths for
|
|
253
|
+
* @returns A nested map of source to target to distance
|
|
231
254
|
*/
|
|
232
255
|
export function allPairsShortestPath(graph: Graph): Map<NodeId, Map<NodeId, number>> {
|
|
233
256
|
const results = new Map<NodeId, Map<NodeId, number>>();
|
|
@@ -239,18 +262,3 @@ export function allPairsShortestPath(graph: Graph): Map<NodeId, Map<NodeId, numb
|
|
|
239
262
|
|
|
240
263
|
return results;
|
|
241
264
|
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Reconstruct path from predecessor map
|
|
245
|
-
*/
|
|
246
|
-
function reconstructPath(target: NodeId, previous: Map<NodeId, NodeId | null>): NodeId[] {
|
|
247
|
-
const path: NodeId[] = [];
|
|
248
|
-
let current: NodeId | null = target;
|
|
249
|
-
|
|
250
|
-
while (current !== null) {
|
|
251
|
-
path.unshift(current);
|
|
252
|
-
current = previous.get(current) ?? null;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return path;
|
|
256
|
-
}
|