@graphty/algorithms 1.2.0 → 1.3.1
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 +490 -336
- package/dist/algorithms.d.ts +42 -2
- package/dist/algorithms.js +11099 -2
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +4 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +124 -170
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +26 -1
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +56 -149
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +3 -3
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +39 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +286 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +80 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js +294 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
- package/dist/src/algorithms/centrality/index.d.ts +2 -0
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +1 -0
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +17 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +45 -0
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +47 -54
- 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 +13 -3
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +50 -35
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +3 -2
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +34 -39
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +112 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain-optimized.js +303 -0
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain.js +17 -76
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +59 -0
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
- package/dist/src/algorithms/community/modularity-utils.js +112 -0
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
- package/dist/src/algorithms/matching/bipartite.js +8 -40
- package/dist/src/algorithms/matching/bipartite.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +1 -12
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +37 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +193 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +5 -2
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +15 -14
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +29 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-unified.js +333 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +54 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.js +368 -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/benchmark-all-algorithms.d.ts +23 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
- package/dist/src/benchmark-all-algorithms.js +462 -0
- package/dist/src/benchmark-all-algorithms.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +16 -15
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +209 -223
- 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 +16 -15
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +57 -21
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/spectral.d.ts +8 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +40 -21
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/core/graph.js +4 -4
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +17 -11
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +141 -217
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +6 -5
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +17 -8
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
- package/dist/src/link-prediction/adamic-adar.js +17 -26
- package/dist/src/link-prediction/adamic-adar.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +7 -11
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/optimized/bit-packed.d.ts +143 -0
- package/dist/src/optimized/bit-packed.d.ts.map +1 -0
- package/dist/src/optimized/bit-packed.js +292 -0
- package/dist/src/optimized/bit-packed.js.map +1 -0
- package/dist/src/optimized/csr-graph.d.ts +86 -0
- package/dist/src/optimized/csr-graph.d.ts.map +1 -0
- package/dist/src/optimized/csr-graph.js +341 -0
- package/dist/src/optimized/csr-graph.js.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts +74 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.js +223 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
- package/dist/src/optimized/graph-adapter.d.ts +62 -0
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
- package/dist/src/optimized/graph-adapter.js +146 -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 +28 -0
- package/dist/src/optimized/index.js.map +1 -0
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +6 -14
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +3 -29
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +2 -0
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +7 -3
- 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 +79 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -0
- package/dist/src/utils/graph-converters.js +196 -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 +52 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -0
- package/dist/src/utils/math-utilities.js +123 -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 +33 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
- package/dist/src/utils/optimization-helpers.js +47 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -0
- package/package.json +18 -3
- package/src/algorithms/centrality/betweenness.ts +167 -199
- package/src/algorithms/centrality/closeness.ts +73 -191
- package/src/algorithms/centrality/degree.ts +3 -3
- package/src/algorithms/centrality/delta-pagerank-simple.ts +366 -0
- package/src/algorithms/centrality/delta-pagerank.ts +435 -0
- package/src/algorithms/centrality/index.ts +2 -0
- package/src/algorithms/centrality/pagerank.ts +66 -0
- package/src/algorithms/community/girvan-newman.ts +52 -59
- package/src/algorithms/community/index.ts +2 -0
- package/src/algorithms/community/label-propagation.ts +64 -35
- package/src/algorithms/community/leiden.ts +40 -41
- package/src/algorithms/community/louvain-optimized.ts +445 -0
- package/src/algorithms/community/louvain.ts +24 -104
- package/src/algorithms/community/modularity-utils.ts +146 -0
- package/src/algorithms/matching/bipartite.ts +8 -44
- package/src/algorithms/shortest-path/bellman-ford.ts +1 -14
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
- package/src/algorithms/shortest-path/dijkstra.ts +17 -15
- package/src/algorithms/traversal/bfs-unified.ts +433 -0
- package/src/algorithms/traversal/bfs-variants.ts +496 -0
- package/src/algorithms/traversal/bfs.ts +10 -228
- package/src/benchmark-all-algorithms.ts +548 -0
- package/src/clustering/hierarchical.ts +241 -251
- package/src/clustering/index.ts +4 -2
- package/src/clustering/k-core.ts +87 -39
- package/src/clustering/spectral.ts +44 -20
- package/src/core/graph.ts +4 -4
- package/src/flow/ford-fulkerson.ts +186 -254
- package/src/flow/min-cut.ts +21 -11
- package/src/index.ts +6 -0
- package/src/link-prediction/adamic-adar.ts +17 -30
- package/src/link-prediction/common-neighbors.ts +7 -16
- package/src/optimized/bit-packed.ts +340 -0
- package/src/optimized/csr-graph.ts +455 -0
- package/src/optimized/direction-optimized-bfs.ts +291 -0
- package/src/optimized/graph-adapter.ts +200 -0
- package/src/optimized/index.ts +36 -0
- package/src/research/grsbm.ts +7 -16
- package/src/research/sync.ts +3 -32
- package/src/research/terahac.ts +10 -2
- package/src/types/index.ts +8 -0
- package/src/utils/algorithm-utilities.ts +2 -0
- package/src/utils/graph-converters.ts +241 -0
- package/src/utils/graph-utilities.ts +199 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/math-utilities.ts +140 -0
- package/src/utils/matrix-utilities.ts +2 -0
- package/src/utils/optimization-helpers.ts +81 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import type {Graph} from "../../core/graph.js";
|
|
2
|
+
import {CSRGraph} from "../../optimized/csr-graph.js";
|
|
3
|
+
import {DirectionOptimizedBFS} from "../../optimized/direction-optimized-bfs.js";
|
|
4
|
+
import {toCSRGraph} from "../../optimized/graph-adapter.js";
|
|
5
|
+
import type {NodeId, ShortestPathResult, TraversalOptions, TraversalResult} from "../../types/index.js";
|
|
6
|
+
import {reconstructPath} from "../../utils/graph-utilities.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Unified BFS implementation that automatically optimizes for large graphs
|
|
10
|
+
*
|
|
11
|
+
* This module provides a single, user-friendly BFS implementation that:
|
|
12
|
+
* - Automatically uses Direction-Optimized BFS for large graphs (>10k nodes)
|
|
13
|
+
* - Maintains backward compatibility with existing APIs
|
|
14
|
+
* - Requires no configuration or understanding of optimization thresholds
|
|
15
|
+
* - Provides the best performance by default
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Cache for CSR conversions to avoid repeated conversions
|
|
19
|
+
const csrCache = new WeakMap<Graph, CSRGraph>();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get or create CSR representation of a graph
|
|
23
|
+
*/
|
|
24
|
+
function getCSRGraph(graph: Graph): CSRGraph {
|
|
25
|
+
let csrGraph = csrCache.get(graph);
|
|
26
|
+
if (!csrGraph) {
|
|
27
|
+
csrGraph = toCSRGraph(graph);
|
|
28
|
+
csrCache.set(graph, csrGraph);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return csrGraph;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Perform breadth-first search starting from a given node
|
|
36
|
+
*
|
|
37
|
+
* Automatically uses the most optimized implementation based on graph size.
|
|
38
|
+
* No configuration needed - just call this function for the best performance.
|
|
39
|
+
*/
|
|
40
|
+
export function breadthFirstSearch(
|
|
41
|
+
graph: Graph,
|
|
42
|
+
startNode: NodeId,
|
|
43
|
+
options: TraversalOptions = {},
|
|
44
|
+
): TraversalResult {
|
|
45
|
+
if (!graph.hasNode(startNode)) {
|
|
46
|
+
throw new Error(`Start node ${String(startNode)} not found in graph`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Automatically use optimized implementation for large graphs
|
|
50
|
+
if (graph.nodeCount > 10000) {
|
|
51
|
+
return breadthFirstSearchOptimized(graph, startNode, options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Standard implementation for smaller graphs
|
|
55
|
+
return breadthFirstSearchStandard(graph, startNode, options);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Standard BFS implementation for smaller graphs
|
|
60
|
+
*/
|
|
61
|
+
function breadthFirstSearchStandard(
|
|
62
|
+
graph: Graph,
|
|
63
|
+
startNode: NodeId,
|
|
64
|
+
options: TraversalOptions = {},
|
|
65
|
+
): TraversalResult {
|
|
66
|
+
const visited = new Set<NodeId>();
|
|
67
|
+
const queue: {node: NodeId, level: number}[] = [];
|
|
68
|
+
const order: NodeId[] = [];
|
|
69
|
+
const tree = new Map<NodeId, NodeId | null>();
|
|
70
|
+
|
|
71
|
+
// Initialize with start node
|
|
72
|
+
queue.push({node: startNode, level: 0});
|
|
73
|
+
visited.add(startNode);
|
|
74
|
+
tree.set(startNode, null);
|
|
75
|
+
|
|
76
|
+
while (queue.length > 0) {
|
|
77
|
+
const current = queue.shift();
|
|
78
|
+
if (!current) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
order.push(current.node);
|
|
83
|
+
|
|
84
|
+
// Call visitor callback if provided
|
|
85
|
+
if (options.visitCallback) {
|
|
86
|
+
options.visitCallback(current.node, current.level);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Early termination if target found
|
|
90
|
+
if (options.targetNode && current.node === options.targetNode) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Explore neighbors
|
|
95
|
+
for (const neighbor of graph.neighbors(current.node)) {
|
|
96
|
+
if (!visited.has(neighbor)) {
|
|
97
|
+
visited.add(neighbor);
|
|
98
|
+
tree.set(neighbor, current.node);
|
|
99
|
+
queue.push({node: neighbor, level: current.level + 1});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {visited, order, tree};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Optimized BFS implementation using Direction-Optimized BFS
|
|
109
|
+
*/
|
|
110
|
+
function breadthFirstSearchOptimized(
|
|
111
|
+
graph: Graph,
|
|
112
|
+
startNode: NodeId,
|
|
113
|
+
options: TraversalOptions = {},
|
|
114
|
+
): TraversalResult {
|
|
115
|
+
const csrGraph = getCSRGraph(graph);
|
|
116
|
+
|
|
117
|
+
const dobfs = new DirectionOptimizedBFS(csrGraph, {
|
|
118
|
+
alpha: 15.0,
|
|
119
|
+
beta: 20.0,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const result = dobfs.search(startNode);
|
|
123
|
+
|
|
124
|
+
// Convert result to TraversalResult format
|
|
125
|
+
const visited = new Set<NodeId>();
|
|
126
|
+
const order: NodeId[] = [];
|
|
127
|
+
const tree = new Map<NodeId, NodeId | null>();
|
|
128
|
+
|
|
129
|
+
// Build traversal order using BFS from distances
|
|
130
|
+
const nodesByDistance = new Map<number, NodeId[]>();
|
|
131
|
+
let maxDistance = 0;
|
|
132
|
+
|
|
133
|
+
for (const [nodeId, distance] of result.distances) {
|
|
134
|
+
visited.add(nodeId);
|
|
135
|
+
tree.set(nodeId, result.parents.get(nodeId) ?? null);
|
|
136
|
+
|
|
137
|
+
if (!nodesByDistance.has(distance)) {
|
|
138
|
+
nodesByDistance.set(distance, []);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
nodesByDistance.get(distance)?.push(nodeId);
|
|
142
|
+
maxDistance = Math.max(maxDistance, distance);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Reconstruct BFS order
|
|
146
|
+
for (let d = 0; d <= maxDistance; d++) {
|
|
147
|
+
const nodes = nodesByDistance.get(d);
|
|
148
|
+
if (nodes) {
|
|
149
|
+
order.push(... nodes);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Handle visit callback if provided
|
|
154
|
+
if (options.visitCallback) {
|
|
155
|
+
for (const [nodeId, distance] of result.distances) {
|
|
156
|
+
options.visitCallback(nodeId, distance);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {visited, order, tree};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Find shortest path between two nodes using BFS
|
|
165
|
+
*
|
|
166
|
+
* Automatically optimized for large graphs. Returns null if no path exists.
|
|
167
|
+
*/
|
|
168
|
+
export function shortestPathBFS(
|
|
169
|
+
graph: Graph,
|
|
170
|
+
source: NodeId,
|
|
171
|
+
target: NodeId,
|
|
172
|
+
): ShortestPathResult | null {
|
|
173
|
+
if (!graph.hasNode(source)) {
|
|
174
|
+
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!graph.hasNode(target)) {
|
|
178
|
+
throw new Error(`Target node ${String(target)} not found in graph`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Special case: source equals target
|
|
182
|
+
if (source === target) {
|
|
183
|
+
return {
|
|
184
|
+
distance: 0,
|
|
185
|
+
path: [source],
|
|
186
|
+
predecessor: new Map([[source, null]]),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Use optimized implementation for large graphs
|
|
191
|
+
if (graph.nodeCount > 10000) {
|
|
192
|
+
return shortestPathBFSOptimized(graph, source, target);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Standard implementation
|
|
196
|
+
return shortestPathBFSStandard(graph, source, target);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Standard shortest path BFS
|
|
201
|
+
*/
|
|
202
|
+
function shortestPathBFSStandard(
|
|
203
|
+
graph: Graph,
|
|
204
|
+
source: NodeId,
|
|
205
|
+
target: NodeId,
|
|
206
|
+
): ShortestPathResult | null {
|
|
207
|
+
const visited = new Set<NodeId>();
|
|
208
|
+
const queue: {node: NodeId, distance: number}[] = [];
|
|
209
|
+
const predecessor = new Map<NodeId, NodeId | null>();
|
|
210
|
+
|
|
211
|
+
// Initialize BFS
|
|
212
|
+
queue.push({node: source, distance: 0});
|
|
213
|
+
visited.add(source);
|
|
214
|
+
predecessor.set(source, null);
|
|
215
|
+
|
|
216
|
+
while (queue.length > 0) {
|
|
217
|
+
const current = queue.shift();
|
|
218
|
+
if (!current) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Target found
|
|
223
|
+
if (current.node === target) {
|
|
224
|
+
const path = reconstructPath(target, predecessor);
|
|
225
|
+
return {
|
|
226
|
+
distance: current.distance,
|
|
227
|
+
path,
|
|
228
|
+
predecessor,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Explore neighbors
|
|
233
|
+
for (const neighbor of graph.neighbors(current.node)) {
|
|
234
|
+
if (!visited.has(neighbor)) {
|
|
235
|
+
visited.add(neighbor);
|
|
236
|
+
predecessor.set(neighbor, current.node);
|
|
237
|
+
queue.push({node: neighbor, distance: current.distance + 1});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// No path found
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Optimized shortest path using Direction-Optimized BFS
|
|
248
|
+
*/
|
|
249
|
+
function shortestPathBFSOptimized(
|
|
250
|
+
graph: Graph,
|
|
251
|
+
source: NodeId,
|
|
252
|
+
target: NodeId,
|
|
253
|
+
): ShortestPathResult | null {
|
|
254
|
+
const csrGraph = getCSRGraph(graph);
|
|
255
|
+
|
|
256
|
+
const dobfs = new DirectionOptimizedBFS(csrGraph, {
|
|
257
|
+
alpha: 15.0,
|
|
258
|
+
beta: 20.0,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Perform BFS
|
|
262
|
+
const result = dobfs.search(source);
|
|
263
|
+
|
|
264
|
+
// Check if target was reached
|
|
265
|
+
const distance = result.distances.get(target);
|
|
266
|
+
if (distance === undefined) {
|
|
267
|
+
return null; // No path found
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Reconstruct path
|
|
271
|
+
const path = reconstructPath(target, result.parents);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
distance,
|
|
275
|
+
path,
|
|
276
|
+
predecessor: result.parents,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Find shortest paths from source to all reachable nodes
|
|
282
|
+
*
|
|
283
|
+
* Automatically optimized for large graphs.
|
|
284
|
+
*/
|
|
285
|
+
export function singleSourceShortestPathBFS(
|
|
286
|
+
graph: Graph,
|
|
287
|
+
source: NodeId,
|
|
288
|
+
): Map<NodeId, ShortestPathResult> {
|
|
289
|
+
if (!graph.hasNode(source)) {
|
|
290
|
+
throw new Error(`Source node ${String(source)} not found in graph`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Use optimized implementation for large graphs
|
|
294
|
+
if (graph.nodeCount > 10000) {
|
|
295
|
+
return singleSourceShortestPathBFSOptimized(graph, source);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Standard implementation
|
|
299
|
+
return singleSourceShortestPathBFSStandard(graph, source);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Standard single-source shortest paths
|
|
304
|
+
*/
|
|
305
|
+
function singleSourceShortestPathBFSStandard(
|
|
306
|
+
graph: Graph,
|
|
307
|
+
source: NodeId,
|
|
308
|
+
): Map<NodeId, ShortestPathResult> {
|
|
309
|
+
const results = new Map<NodeId, ShortestPathResult>();
|
|
310
|
+
const visited = new Set<NodeId>();
|
|
311
|
+
const queue: {node: NodeId, distance: number}[] = [];
|
|
312
|
+
const predecessor = new Map<NodeId, NodeId | null>();
|
|
313
|
+
const distances = new Map<NodeId, number>();
|
|
314
|
+
|
|
315
|
+
// Initialize BFS
|
|
316
|
+
queue.push({node: source, distance: 0});
|
|
317
|
+
visited.add(source);
|
|
318
|
+
predecessor.set(source, null);
|
|
319
|
+
distances.set(source, 0);
|
|
320
|
+
|
|
321
|
+
while (queue.length > 0) {
|
|
322
|
+
const current = queue.shift();
|
|
323
|
+
if (!current) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Explore neighbors
|
|
328
|
+
for (const neighbor of graph.neighbors(current.node)) {
|
|
329
|
+
if (!visited.has(neighbor)) {
|
|
330
|
+
visited.add(neighbor);
|
|
331
|
+
predecessor.set(neighbor, current.node);
|
|
332
|
+
distances.set(neighbor, current.distance + 1);
|
|
333
|
+
queue.push({node: neighbor, distance: current.distance + 1});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Build results after BFS completes
|
|
339
|
+
// This avoids copying the predecessor map for each node
|
|
340
|
+
for (const [node, distance] of distances) {
|
|
341
|
+
const path = reconstructPath(node, predecessor);
|
|
342
|
+
results.set(node, {
|
|
343
|
+
distance,
|
|
344
|
+
path,
|
|
345
|
+
predecessor, // Share the same predecessor map
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return results;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Optimized single-source shortest paths
|
|
354
|
+
*/
|
|
355
|
+
function singleSourceShortestPathBFSOptimized(
|
|
356
|
+
graph: Graph,
|
|
357
|
+
source: NodeId,
|
|
358
|
+
): Map<NodeId, ShortestPathResult> {
|
|
359
|
+
const csrGraph = getCSRGraph(graph);
|
|
360
|
+
|
|
361
|
+
const dobfs = new DirectionOptimizedBFS(csrGraph, {
|
|
362
|
+
alpha: 15.0,
|
|
363
|
+
beta: 20.0,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const bfsResult = dobfs.search(source);
|
|
367
|
+
|
|
368
|
+
// Convert to expected format
|
|
369
|
+
const results = new Map<NodeId, ShortestPathResult>();
|
|
370
|
+
|
|
371
|
+
for (const [nodeId, distance] of bfsResult.distances) {
|
|
372
|
+
// Reconstruct path for each node
|
|
373
|
+
const path = reconstructPath(nodeId, bfsResult.parents);
|
|
374
|
+
|
|
375
|
+
results.set(nodeId, {
|
|
376
|
+
distance,
|
|
377
|
+
path,
|
|
378
|
+
predecessor: bfsResult.parents, // Share the same predecessor map
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return results;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Check if the graph is bipartite using BFS coloring
|
|
387
|
+
*
|
|
388
|
+
* Note: This function does not use Direction-Optimized BFS as the
|
|
389
|
+
* coloring logic is specific and doesn't benefit from the optimization.
|
|
390
|
+
*/
|
|
391
|
+
export function isBipartite(graph: Graph): boolean {
|
|
392
|
+
if (graph.isDirected) {
|
|
393
|
+
throw new Error("Bipartite test requires an undirected graph");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const color = new Map<NodeId, 0 | 1>();
|
|
397
|
+
const visited = new Set<NodeId>();
|
|
398
|
+
|
|
399
|
+
// Check each connected component
|
|
400
|
+
for (const node of Array.from(graph.nodes())) {
|
|
401
|
+
if (!visited.has(node.id)) {
|
|
402
|
+
const queue: NodeId[] = [node.id];
|
|
403
|
+
color.set(node.id, 0);
|
|
404
|
+
visited.add(node.id);
|
|
405
|
+
|
|
406
|
+
while (queue.length > 0) {
|
|
407
|
+
const current = queue.shift();
|
|
408
|
+
if (!current) {
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const currentColor = color.get(current);
|
|
413
|
+
if (currentColor === undefined) {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (const neighbor of Array.from(graph.neighbors(current))) {
|
|
418
|
+
if (!visited.has(neighbor)) {
|
|
419
|
+
// Color with opposite color
|
|
420
|
+
color.set(neighbor, currentColor === 0 ? 1 : 0);
|
|
421
|
+
visited.add(neighbor);
|
|
422
|
+
queue.push(neighbor);
|
|
423
|
+
} else if (color.get(neighbor) === currentColor) {
|
|
424
|
+
// Same color as current node - not bipartite
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return true;
|
|
433
|
+
}
|