Help us shape the future of Go! Take the 2026 Go Developer Survey

duan-sssp

command module
v0.0.0-...-ec9dee8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 13, 2026 License: MIT Imports: 5 Imported by: 0

README ΒΆ

Breaking the Sorting Barrier for Directed Single-Source Shortest Paths

CI Go Report Card GoDoc Paper

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).

πŸš€ Key Achievement

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.

Time Complexity
  • 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)

πŸ“Š Algorithm Overview

The algorithm combines two classical approaches through recursive partitioning:

  1. Dijkstra's Algorithm: Uses a priority queue to extract minimum distance vertices
  2. Bellman-Ford Algorithm: Relaxes edges through dynamic programming
Key Innovation: Frontier Reduction

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.

Main Components
  1. BMSSP (Bounded Multi-Source Shortest Path): Main recursive algorithm
  2. FindPivots: Identifies pivot vertices with large shortest path trees
  3. BaseCase: Handles small instances with modified Dijkstra
  4. Block-Based Priority Queue: Custom data structure supporting batch operations

πŸ—οΈ Architecture

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

πŸ”§ Installation

go get github.com/phr3nzy/duan-sssp

πŸš€ Quick Start

See QUICKSTART.md for a 5-minute guide to getting started.

πŸ’» Usage

Basic Example
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)
    }
}
Advanced Example: Large Random Graph
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)
}

πŸ§ͺ Benchmarks

Use all your CPU cores with interactive visualization:

# Build the tool
make visualbench

# Run with visualization (uses all 28 cores!)
make visual-web

Or manually:

./visualbench \
  -vertices=10000 \
  -edge-factor=3 \
  -iterations=10 \
  -parallel=true \
  -web=true

This 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.

Standard Benchmark Suite
# 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.

Benchmark Categories
  1. BenchmarkSSSP: Various graph sizes (1K to 100K vertices)
  2. BenchmarkSSSPDensity: Different edge densities (2x to 20x vertices)
  3. BenchmarkTransformation: Graph transformation overhead
  4. BenchmarkFindPivots: Pivot finding performance
  5. BenchmarkBaseCase: Base case algorithm performance
  6. BenchmarkComparison: Duan algorithm vs naive Dijkstra
  7. BenchmarkScalability: Scaling behavior (1K to 50K vertices)
  8. BenchmarkMemoryUsage: Memory allocation patterns
Expected Performance

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.*

πŸ”¬ Algorithm Details

Parameters
  • k: log^(1/3)(n) - Controls pivot threshold
  • t: log^(2/3)(n) - Controls recursion depth
  • l: ⌈log(n)/tβŒ‰ - Maximum recursion levels
Key Lemmas (from paper)
  • 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
Graph Transformation

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

πŸ“ˆ Complexity Analysis

Time Complexity
  • Main algorithm: O(m log^(2/3) n)
  • Transformation: O(m)
  • Total: O(m log^(2/3) n)
Space Complexity
  • Transformed graph: O(m)
  • Distance array: O(n)
  • Priority queue: O(m)
  • Total: O(m)

🎯 Use Cases

This algorithm is particularly beneficial for:

  1. Sparse Graphs: Where m = O(n), achieving O(n log^(2/3) n) time
  2. Large-Scale Networks: Social networks, road networks, internet graphs
  3. Real-Time Systems: Where the log^(2/3) factor provides measurable speedup
  4. Repeated Queries: Combined with preprocessing for multiple SSSP queries

πŸ” Comparison with Other Algorithms

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)

🧩 Implementation Notes

Deterministic

This implementation is fully deterministic, unlike the randomized undirected algorithm by Duan et al. (2023).

Comparison-Addition Model

Only comparison and addition operations on edge weights are used, making it suitable for arbitrary real weights.

Practical Optimizations
  • Slice-based visited tracking instead of maps
  • Batch operations in priority queue
  • Efficient block splitting with median finding
  • Early termination conditions

πŸ“š References

@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}
}

🀝 Contributing

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

πŸ“„ License

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}
}

πŸ‘₯ Contributors

Thank you to all contributors who help improve this implementation!

🌟 Acknowledgments

  • 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

πŸ› Known Limitations

  1. Reachability: Current implementation may not find all reachable vertices in some graph structures - under investigation
  2. Constant factors: The log^(2/3) advantage shows up mainly for large graphs (n > 10,000)
  3. Transformation overhead: Constant-degree transformation adds practical overhead (~10-20%)
  4. Memory: Transformed graph uses ~2Γ— space of original graph
  5. Dense graphs: For very dense graphs (m = Θ(n²)), Dijkstra may still be competitive
  6. Recursion depth: Very large graphs (n > 10K) with deep structures may approach recursion limits

Recent Fixes (v1.0.1):

  • βœ… Fixed stack overflow in FindPivots caused 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.

πŸ“ž Contact

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.

Documentation ΒΆ

The Go Gopher

There is no documentation for this package.

Directories ΒΆ

Path Synopsis
cmd
visualbench command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL