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. - The BMSSP solver includes safety limits (caps on iterations, recursion
depth, and frontier size) to prevent runaway behavior on large graphs. If a
cap is hit, the solver may terminate early and leave some reachable vertices
at
infwhiledijkstra_referencecontinues to find them. You can detect this viaSSSPSolver.summary()and checking whetheriterations_protected > 0. These safety limits are not currently exposed as public configuration knobs.
If you see unexpectedly many inf distances on large networks, the solver may
have hit its safety limits. To diagnose:
- Call
SSSPSolver.summary()and check ifiterations_protected > 0.
Workarounds (until limits are configurable):
- For guaranteed reachability parity, use
dijkstra_reference. - Try
frontier="heap"withuse_transform=False, which can reduce the chance of hitting safety caps at the cost of performance.
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
# Multiple sources + a path to a target
poetry run ssspx --edges docs/examples/small.csv --sources 0,2 --target 3
# Emit structured log line and write metrics JSON
poetry run ssspx --edges docs/examples/small.csv --log-json --metrics-out metrics.json
# Print a sample CSV to stdout
poetry run ssspx --example
# 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-jsonCLI notes:
- Results are emitted as JSON to stdout unless
--log-jsonis set. - With
--log-json, the logger writes a structured line to stdout and the distances payload is suppressed. --format csvaccepts comma- or tab-separated files (so.tsvworks).--format jsonlaccepts.jsonand.jsonlline-delimited inputs.
CLI reference (grouped):
Input:
| Flag | Default | Description |
|---|---|---|
--edges PATH |
required (unless --random/--example) |
Input edges file. |
--random |
false | Generate a random graph. |
--example |
false | Print a sample CSV and exit. |
--format {csv,jsonl,mtx,graphml} |
auto | Input format override. |
--n |
10 | Vertex count for random graphs. |
--m |
20 | Edge count for random graphs. |
--seed |
0 | Random graph seed. |
Solver:
| Flag | Default | Description |
|---|---|---|
--source ID |
0 | Single source vertex id. |
--sources LIST |
none | Comma-separated source ids. |
--target ID |
none | Target id for path output. |
--frontier {block,heap} |
block |
Frontier implementation. |
--no-transform |
false | Disable constant-outdegree transform. |
--target-outdeg N |
4 | Outdegree cap when transforming. |
Output:
| Flag | Default | Description |
|---|---|---|
--export-json PATH |
none | Write DAG as JSON. |
--export-graphml PATH |
none | Write DAG as GraphML. |
--metrics-out PATH |
none | Write run metrics JSON. |
--profile |
false | Enable cProfile. |
--profile-out PATH |
none | Write .prof file. |
Logging:
| Flag | Default | Description |
|---|---|---|
--log-json |
false | Emit structured log line. |
--log-level {debug,info,warning} |
warning |
Log verbosity. |
--verbose |
false | Show full tracebacks. |
After 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.