ssspx is a clean, typed, and tested implementation of a deterministic Single-Source Shortest Paths solver for directed graphs with non-negative weights.
It follows a BMSSP-style divide-and-conquer design (levels, FindPivots, bounded base case) and includes a switchable frontier, an optional constant-outdegree transform, a CLI, exports, and a small benchmark harness.
Research context: this is an implementation of the algorithmic approach from “Breaking the Sorting Barrier for Directed Single-Source Shortest Paths” (arXiv:2504.17033) by Duan, Mao, Mao, Shu, and Yin. Distances are exact. For the advertised asymptotics, use the block frontier with the constant-outdegree transform enabled.
- Two frontiers:
block(paper-style) andheap(baseline) - Optional constant-outdegree transform (cap configurable, default 4)
- Path reconstruction in original vertex IDs (works with or without transform)
- Shortest-path DAG export to JSON and GraphML
- CLI with CSV/TSV/JSONL/MTX/GraphML input, random graphs, and optional cProfile
- Benchmark harness vs. Dijkstra
- Typed (mypy), linted (flake8/black/isort), tested (pytest + Hypothesis)
- Edge weights must be non-negative. Supplying a negative weight raises a
GraphFormatErrorthat points to the exact offending edge.
Full guides and the API reference live at the project documentation site. Algorithmic details are covered in the BMSSP design notes, and key trade-offs are tracked in Architecture Decision Records.
Public modules and classes are listed in our
API stability policy. Deprecated names emit warnings and
are removed after the stated remove_in release.
Contributions are welcome! See CONTRIBUTING.md for setup and testing instructions.
# in your repo
poetry install
pre-commit install # optional but recommendedPython ≥ 3.9. NumPy is optional (for a CSR backend).
Install with NumPy extras:
poetry install -E numpy-backend>>> from ssspx import Graph, SSSPSolver, SolverConfig
>>> G = Graph.from_edges(4, [(0, 1, 1.0), (1, 2, 2.0), (0, 2, 4.0), (2, 3, 1.0)])
>>> solver = SSSPSolver(G, 0, config=SolverConfig())
>>> solver.solve().distances # doctest: +NORMALIZE_WHITESPACE
[0.0, 1.0, 3.0, 4.0]# Edges from file (auto-detected: CSV, JSONL, MTX, GraphML)
poetry run ssspx --edges docs/examples/small.csv --source 0 \
--frontier block --export-graphml out.graphml --export-json out.json
# Override format if needed
poetry run ssspx --edges docs/examples/small --format csv --source 0
# Random graph
poetry run ssspx --random --n 1000 --m 5000 --source 0 \
--frontier heap --no-transform
# Reproducible random graph
poetry run ssspx --random --n 10 --m 20 --seed 123
# Profiling to file + brief text report on stderr
poetry run ssspx --random --n 5000 --m 20000 --source 0 \
--frontier block --profile --profile-out prof.cprof 2> prof.txt
# Structured log line with counters
poetry run ssspx --random --n 10 --m 20 --log-jsonAfter solve(), extract a path in original vertex IDs:
>>> from ssspx import Graph, SSSPSolver
>>> G = Graph.from_edges(3, [(0,1,1.0), (1,2,2.0)])
>>> solver = SSSPSolver(G, 0)
>>> _ = solver.solve()
>>> solver.path(2)
[0, 1, 2]With the transform enabled, clone cycles are compressed away. Empty list means unreachable.
from ssspx.export import export_dag_json, export_dag_graphml
json_str = export_dag_json(G, res.distances)
gml_str = export_dag_graphml(G, res.distances)
with open("sp_dag.json","w") as f: f.write(json_str)
with open("sp_dag.graphml","w") as f: f.write(gml_str)The DAG contains edges (u → v) where d[v] == d[u] + w(u,v).
from ssspx.graph_numpy import NumpyGraph
edges = [(0, 1, 1.0), (1, 2, 2.0), (0, 2, 4.0)]
G_np = NumpyGraph.from_edges(3, edges)
res = SSSPSolver(G_np, 0).solve()NumpyGraph is drop-in because the solver only needs G.n and iteration over G.adj[u] -> (v, w).
poetry run python -m ssspx.benchThis uses a small default graph set; provide --sizes to benchmark custom
graphs. Treat results as sanity checks. For serious evaluations, pin seeds and
sizes and run multiple trials.
# Unit + property tests
poetry run pytest -q --maxfail=1 --disable-warnings --cov=ssspx --cov-report=term-missing
# Static checks
poetry run mypy src/ssspx
poetry run flake8 src/ssspx
poetry run black --check src tests
poetry run isort --check-only src tests
# All hooks locally
pre-commit run --all-filesContinuous integration runs Bandit on src/ and
pip-audit on the installed dependencies. The build
fails if Bandit reports any HIGH severity issues; medium and low findings are logged for
manual review. To reproduce locally:
poetry run bandit -r src/ssspx -c bandit.yaml --severity-level high
poetry run pip-auditResolve flagged problems by updating dependencies or refactoring code before committing.
If pip-audit reports an unfixed issue, temporarily suppress it with --ignore-vuln <ID> and
track the upstream resolution.
Configured for semantic-release with Conventional Commits (Angular style). On pushes to main, it bumps the version, updates CHANGELOG.md, and creates a GitHub Release.
Releases are archived on Zenodo. The latest release DOI is 10.5281/zenodo.17381998 (badge at the top).
Common prefixes: feat:, fix:, perf:, refactor:, docs:, test:, chore:.
If you use ssspx in academic work, please cite:
The algorithmic paper
Duan, R., Mao, J., Mao, X., Shu, X., & Yin, L. (2025). Breaking the Sorting Barrier for Directed Single-Source Shortest Paths. arXiv:2504.17033.
This software
@software{ribeiro_ssspx_2025,
author = {Ribeiro, Diogo},
title = {ssspx — Production-grade Single-Source Shortest Paths (Python)},
year = {2025},
doi = {10.5281/zenodo.17381998},
url = {https://github.com/DiogoRibeiro7/bmssp},
publisher = {Zenodo}
}MIT — see LICENSE.