The shelf

One page per paper implemented in routelab. Each says what the paper observed, sketches its algorithm, then runs it — set up an environment, bind the technique, ask a question — with a section per variant or refinement the paper adds.

Start with Dijkstra for the shape; every other page assumes it.

Road networks

Paper Technique Page
Dijkstra, A note on two problems in connexion with graphs (1959) Dijkstra() Dijkstra's algorithm
Moore, The shortest path through a maze (1959) BFS() Breadth-first search
Hart, Nilsson & Raphael, A formal basis for the heuristic determination of minimum cost paths (1968) AStar(Euclidean()), AStar(Zero()) A*
Goldberg & Harrelson, Computing the shortest path: A* search meets graph theory (2005) AStar(Landmarks(16)) ALT landmarks
Geisberger, Sanders, Schultes & Delling, Contraction hierarchies (2008) ContractionHierarchy(EdgeDifference()) Contraction hierarchies
Dreyfus, An appraisal of some shortest-path algorithms (1969) TimeDependentDijkstra() Time-dependent Dijkstra

Timetables

Paper Technique Page
Pyrga, Schulz, Wagner & Zaroliagis, Efficient models for timetable information in public transportation systems (2007) TimeExpanded(), TimeDependent() Two models of a timetable
Delling, Pajor & Werneck, Round-based public transit routing (2012) RAPTOR() RAPTOR
Dibbelt, Pajor, Strasser & Wagner, Intriguingly simple and fast transit routing (2013) CSA() Connection scan
Witt, Trip-based public transit routing (2015) TripBased() Trip-based routing
Delling, Dibbelt, Pajor & Werneck, Public transit labeling (2015) PTL() Public transit labeling

Multimodal

Paper Technique Page
Baum, Buchhold, Sauer, Wagner & Zündorf, UnLimited TRAnsfers for multi-modal route planning (2019) ULTRA(RAPTOR()), ULTRA(CSA()) ULTRA
Barrett, Jacob & Marathe, Formal-language-constrained path problems (2000) LabelConstrained(), Modes(...) Label-constrained routing
Dibbelt, Pajor & Wagner, User-constrained multi-modal route planning (2012) §3 — UCCH UCCH() UCCH

The shelf's to do list — Delling, Pajor & Wagner (2009), Geisberger (2010), transfer patterns (2010) — appears in the README; those papers have no page because they have no implementation.

Side by side

The shape every page shares

Three steps, the same for a road network and a timetable.

>>> import routelab as rl

>>> env = rl.Environment()                              # describe a world
>>> env.register(rl.ScalarEdges(("a", "b", 1), ("b", "c", 15)))
Environment(1 layer)

>>> technique = rl.Dijkstra()                           # a configuration, costing nothing
>>> planner = technique.bind(env)                       # preprocessing, if any
>>> planner.route("a", "c").routes[0]                             # the question
Journey('a' → 'b' → 'c', cost=16)

An environment is layers. A GTFS feed, an OSM extract, a table of walks, a hand-written list of edges — each is a layer, and nodes take whatever names you already use: stop ids, OSM node ids, ("bike", 42). Every leg of a journey remembers its layer, which makes a multimodal answer readable rather than just a number.

Configuring and binding are separate because the middle step gets expensive. Sixteen landmarks over a city cost a second and 33 MB; that belongs to a verb, not a constructor. The split also makes a technique a value — something you can name, put in a dictionary, and point at more than one dataset.

A query answers with everything it worked out. route returns an Answer: routes (every journey worth having, best first — usually one), searchspace() (what the search looked at) and raw (the kernel's own table). All three come from the one search, so a route and the picture behind it never cost two.

A technique takes the options its problem needs, on the signature of its own bound planner. DijkstraPlanner.route takes max_cost; RAPTORPlanner.route takes departing and max_transfers; asking either for the other's is a TypeError from Python, and an error a type checker reports before anything runs. There is no registry of option names, so nothing can go stale as the shelf grows.

Reading the code blocks

Two kinds appear on these pages; the prompt tells them apart.

A block with >>> prompts is a test. tests/test_docs.py runs every one as part of the ordinary suite, so a page describing an API that has since moved fails the suite rather than misleading you. They run against fixtures the repository ships: a handful of hand-written edges, or the three-stop GTFS feed under crates/routelab-gtfs/tests/data/tiny, which the timetable pages name TINY_GTFS.

A block without prompts is written to be read. Those run on a real city — Seattle's 258,029 nodes, King County Metro's 421,604 connections — where the numbers are the point and no test suite should download a 65 MB extract to check them. Their outputs are pasted as comments from the benchmarks under benchmarks/.

To run them — or any snippet on these pages — install routelab first. It builds a Rust kernel, so you need a Rust toolchain alongside Python 3.9+:

git clone https://github.com/bmander/routelab && cd routelab
python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'        # builds the Rust kernel via maturin

pytest tests/test_docs.py      # run every prompted block on this shelf

Where else to look