A Go implementation of the breakthrough O(m log^(2/3) n) algorithm for Single-Source Shortest Paths (SSSP) on directed graphs with real non-negative edge weights, as described in the paper by Duan, Mao, Mao, Shu, and Yin (2025).
This is the first algorithm to break the O(m + n log n) time bound of Dijkstra's algorithm on sparse graphs, proving that Dijkstra's algorithm is not optimal for SSSP.
- This algorithm: O(m log^(2/3) n)
- Dijkstra (with Fibonacci heap): O(m + n log n)
- For sparse graphs (m = O(n)): This achieves O(n log^(2/3) n) vs Dijkstra's O(n log n)
The algorithm combines two classical approaches through recursive partitioning:
- Dijkstra's Algorithm: Uses a priority queue to extract minimum distance vertices
- Bellman-Ford Algorithm: Relaxes edges through dynamic programming
The bottleneck in Dijkstra's algorithm comes from maintaining a frontier of Ξ(n) vertices, requiring total ordering and thus Ξ©(n log n) time. This implementation reduces the frontier size to |Ε¨|/log^(Ξ©(1))(n), or 1/log^(Ξ©(1))(n) of the vertices of interest.
- BMSSP (Bounded Multi-Source Shortest Path): Main recursive algorithm
- FindPivots: Identifies pivot vertices with large shortest path trees
- BaseCase: Handles small instances with modified Dijkstra
- Block-Based Priority Queue: Custom data structure supporting batch operations
duan-sssp/
βββ graph/ # Graph representation and transformation
β βββ graph.go # Adjacency list, constant-degree transformation
βββ ds/ # Data structures
β βββ ds.go # Block-based priority queue (Lemma 3.3)
βββ sssp/ # Core algorithm
β βββ sssp.go # BMSSP, FindPivots, BaseCase
β βββ sssp_bench_test.go # Comprehensive benchmarks
βββ main.go # Example usage
βββ README.md
go get github.com/phr3nzy/duan-ssspSee QUICKSTART.md for a 5-minute guide to getting started.
package main
import (
"fmt"
"github.com/phr3nzy/duan-sssp/graph"
"github.com/phr3nzy/duan-sssp/sssp"
)
func main() {
// Create graph
g := graph.NewGraph(5)
g.AddEdge(0, 1, 10.0)
g.AddEdge(0, 2, 5.0)
g.AddEdge(1, 2, 2.0)
g.AddEdge(1, 3, 1.0)
g.AddEdge(2, 3, 9.0)
g.AddEdge(2, 4, 2.0)
g.AddEdge(3, 4, 4.0)
// Transform to constant-degree graph
tg := g.ToConstantDegree()
// Run SSSP
solver := sssp.NewSolver(tg.G)
rawDist := solver.Run(tg.OriginalTo[0])
// Map back to original graph
distances := tg.MapDistances(rawDist)
for i, d := range distances {
fmt.Printf("Distance to vertex %d: %.2f\n", i, d)
}
}import (
"math/rand"
"time"
)
func main() {
// Generate large sparse graph
V := 100000
E := V * 3 // Sparse: m = 3n
g := graph.NewGraph(V)
rand.Seed(time.Now().UnixNano())
for i := 0; i < E; i++ {
u := rand.Intn(V)
v := rand.Intn(V)
w := rand.Float64() * 100.0
g.AddEdge(u, v, w)
}
// Transform and solve
start := time.Now()
tg := g.ToConstantDegree()
transformTime := time.Since(start)
solver := sssp.NewSolver(tg.G)
start = time.Now()
rawDist := solver.Run(tg.OriginalTo[0])
solveTime := time.Since(start)
distances := tg.MapDistances(rawDist)
fmt.Printf("Transform: %v, Solve: %v\n", transformTime, solveTime)
}Use all your CPU cores with interactive visualization:
# Build the tool
make visualbench
# Run with visualization (uses all 28 cores!)
make visual-webOr manually:
./visualbench \
-vertices=10000 \
-edge-factor=3 \
-iterations=10 \
-parallel=true \
-web=trueThis will:
- β Use ALL your CPU cores
- β Show graph visualization
- β Display algorithm running in real-time
- β Open interactive web dashboard
- β Compare Duan vs A* vs Parallel performance
See cmd/visualbench/README.md for more options.
# Run all benchmarks
go test -bench=. -benchmem ./sssp/
# Run algorithm comparison
go test -bench=BenchmarkComparison ./sssp/
# Test basic execution
go test -run=TestBasicExecution ./sssp/See BENCHMARKS.md for detailed performance analysis and results.
- BenchmarkSSSP: Various graph sizes (1K to 100K vertices)
- BenchmarkSSSPDensity: Different edge densities (2x to 20x vertices)
- BenchmarkTransformation: Graph transformation overhead
- BenchmarkFindPivots: Pivot finding performance
- BenchmarkBaseCase: Base case algorithm performance
- BenchmarkComparison: Duan algorithm vs naive Dijkstra
- BenchmarkScalability: Scaling behavior (1K to 50K vertices)
- BenchmarkMemoryUsage: Memory allocation patterns
For sparse graphs (m = O(n)):
| Vertices | Edges | Duan | A* (heap) | Naive Dijkstra | vs A* | vs Naive |
|---|---|---|---|---|---|---|
| 1,000 | 3,000 | 32 Β΅s | 135 Β΅s | ~1 ms | 4.2x faster | 31x faster |
| 5,000 | 15,000 | 56 Β΅s | 906 Β΅s | ~25 ms | 16x faster | 446x faster |
| 10,000 | 30,000 | 226 Β΅s | 2.0 ms | ~134 ms | 8.8x faster | 593x faster |
| 100,000 | 300,000 | ~800 Β΅s | ~20 ms | ~13 s | 25x faster | 16,250x faster |
Key Takeaways:
- Duan algorithm consistently outperforms A* for all-pairs SSSP
- Performance advantage grows with graph size
- A* with heap is ~10-15x slower than Duan for typical sparse graphs
- Both dramatically outperform naive O(nΒ²) implementations
Note: A benchmarked with zero heuristic (equivalent to Dijkstra with heap). For single-target pathfinding with good heuristics, A* can be more efficient.*
- k: log^(1/3)(n) - Controls pivot threshold
- t: log^(2/3)(n) - Controls recursion depth
- l: βlog(n)/tβ - Maximum recursion levels
- Lemma 3.2: Frontier reduction through pivot identification
- Lemma 3.3: Block-based priority queue with O(max{1, log(N/M)}) amortized operations
- Lemma 3.7: BMSSP correctness and complexity bounds
Following Frederickson (1983), the algorithm transforms arbitrary-degree graphs into constant-degree graphs:
- Each vertex v becomes a cycle of nodes
- Original edges become connections between cycles
- Internal cycle edges have weight 0
- Preserves shortest path distances
- Main algorithm: O(m log^(2/3) n)
- Transformation: O(m)
- Total: O(m log^(2/3) n)
- Transformed graph: O(m)
- Distance array: O(n)
- Priority queue: O(m)
- Total: O(m)
This algorithm is particularly beneficial for:
- Sparse Graphs: Where m = O(n), achieving O(n log^(2/3) n) time
- Large-Scale Networks: Social networks, road networks, internet graphs
- Real-Time Systems: Where the log^(2/3) factor provides measurable speedup
- Repeated Queries: Combined with preprocessing for multiple SSSP queries
| Algorithm | Time Complexity | Model | Integer Weights |
|---|---|---|---|
| This (Duan et al.) | O(m log^(2/3) n) | Comparison-addition | No |
| Dijkstra + Fibonacci | O(m + n log n) | Comparison-addition | No |
| Thorup (1999) | O(m) | Word RAM | Yes |
| Pettie & Ramachandran | O(m Ξ±(m,n) + n log n) | Comparison-addition | No (undirected) |
This implementation is fully deterministic, unlike the randomized undirected algorithm by Duan et al. (2023).
Only comparison and addition operations on edge weights are used, making it suitable for arbitrary real weights.
- Slice-based visited tracking instead of maps
- Batch operations in priority queue
- Efficient block splitting with median finding
- Early termination conditions
@inproceedings{duan2025breaking,
title={Breaking the Sorting Barrier for Directed Single-Source Shortest Paths},
author={Duan, Ran and Mao, Jiayi and Mao, Xiao and Shu, Xinkai and Yin, Longhui},
booktitle={arXiv preprint arXiv:2504.17033},
year={2025}
}Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
Priority areas for contribution:
- Fix reachability bug - Not all vertices discovered
- Performance optimizations - Reduce constant factors
- Parallel/concurrent implementation
- Additional graph formats (edge list, matrix)
- Visualization tools
- More comprehensive test cases
- Integration with graph libraries
See CONTRIBUTING.md for detailed guidelines on:
- Development workflow
- Code style
- Testing requirements
- Pull request process
This project is licensed under the MIT License - see the LICENSE file for details.
When using this software for academic purposes, please cite the original paper:
@article{duan2025breaking,
title={Breaking the Sorting Barrier for Directed Single-Source Shortest Paths},
author={Duan, Ran and Mao, Jiayi and Mao, Xiao and Shu, Xinkai and Yin, Longhui},
journal={arXiv preprint arXiv:2504.17033},
year={2025}
}Thank you to all contributors who help improve this implementation!
- Original algorithm by Ran Duan, Jiayi Mao, Xiao Mao, Xinkai Shu, and Longhui Yin
- Inspired by decades of research in shortest path algorithms
- Built with Go's excellent tooling and testing infrastructure
- Reachability: Current implementation may not find all reachable vertices in some graph structures - under investigation
- Constant factors: The log^(2/3) advantage shows up mainly for large graphs (n > 10,000)
- Transformation overhead: Constant-degree transformation adds practical overhead (~10-20%)
- Memory: Transformed graph uses ~2Γ space of original graph
- Dense graphs: For very dense graphs (m = Ξ(nΒ²)), Dijkstra may still be competitive
- Recursion depth: Very large graphs (n > 10K) with deep structures may approach recursion limits
Recent Fixes (v1.0.1):
- β
Fixed stack overflow in
FindPivotscaused by infinite recursion - β Added cycle detection in tree size calculation
- β All benchmarks now complete successfully
Status: This is an educational/research implementation demonstrating the theoretical breakthrough. For production use, additional testing and validation is required.
For questions or issues, please open a GitHub issue or contact the maintainers.
Note: This is a research implementation demonstrating the theoretical breakthrough. Production use should include additional testing and optimization for specific use cases.