TABenchmark tutorials¶
One notebook per benchmark unit — every registered model and estimator, plus each
parallel-track solver — showing how to run it through the public tabench API and,
above all, how the harness certifies the result.
Follow them in order. Notebooks are numbered simple → complex (PI directive,
2026-07-16): tutorials/<NN>-<track>/<MM>-<unit>.ipynb. The numeric prefixes make the
GitHub directory listing itself read as the learning path — start at 01-static/01-aon
and walk up the vintage ladder. Track folders mirror the src/tabench parallel modules,
so a track’s notebooks and its code land in the same review.
The honesty rule¶
A notebook never claims a number it does not compute in that cell. Every scored
quantity is recomputed live by the track’s P1 evaluator from the outputs the model
emitted, in the cell where it is claimed; model self-reports are shown only as
provenance and diffed against the certificate as an honesty check. Analytic anchors are
recomputed, never quoted. Plots draw only quantities certified in the notebook — through
the house visualizer tabench.viz where the artifact is road link flows / an OD matrix;
non-road tracks (cumulative diagrams, occupancy series, (x,t) fields, transit
multigraphs) plot the certified artifact directly with plain matplotlib, stating the
reason in the Visualize cell’s markdown (adr-035 Decision C).
Running them¶
pip install -e '.[dev,viz,tutorials]' # nbclient/nbformat/ipykernel + matplotlib
jupyter lab tutorials/01-static/05-bfw.ipynb # interactive
jupyter nbconvert --to notebook --execute tutorials/01-static/05-bfw.ipynb --stdout # headless
Notebooks are committed executed — every non-empty code cell carries the outputs and
the strictly sequential execution count of one clean top-to-bottom run, so the committed
notebook is itself the rendered tutorial. Drift safety does NOT come from output
identity: CI re-executes every notebook from a cleared state, and the gate is execution
success plus the in-notebook certified asserts. The one exemption ships stripped: the
matsim notebook (JAVA-only toolchain, adr-039), whose executability the matsim CI job
proves instead. Every CI leg enforces this.
tests/test_tutorials.py runs unconditionally on existence + executed-state + metadata +
numbering, no env flag needed. The notebook-EXECUTION test (test_notebook_executes) is
wired in too: the core job’s Python 3.12 leg installs .[dev,viz,tutorials] and sets
TABENCH_RUN_TUTORIALS=1, re-executing every unguarded notebook from a cleared kernel
(the 3.10 leg skips it — redundant kernel-start cost, no extra signal); each of the
torch / sumo / dtalite legs installs its own extra plus tutorials and runs
tests/test_tutorials.py with a -k filter scoped to its own extra-gated notebooks. A
notebook that raises, or whose in-cell certified assert fails, fails CI — run
TABENCH_RUN_TUTORIALS=1 pytest tests/test_tutorials.py -k executes locally for the same
guarantee.
Enforcement¶
tests/test_tutorials.py makes “a new model ships a tutorial” mechanical: every key of
MODEL_REGISTRY ∪ ESTIMATOR_REGISTRY ∪ DYNAMIC_ESTIMATOR_REGISTRY and every unit in the
import-anchored 11-unit parallel-track manifest must map to a notebook (or a same-ADR
covers sibling), notebooks must be committed executed (matsim excepted), and metadata.tabench must match the
folder. A registered unit with no notebook turns the suite red. Guarded units register
only where their extra is installed, so enforcement is automatically environment-correct.
The coverage gate matches a unit u at tutorials/*/[0-9][0-9]-{u}.ipynb, and
metadata.tabench carries the bare names — unit = the registry key, track = the
folder name sans its numeric prefix — so a renumber never touches notebook content.
test_notebook_numbering_is_ordered binds the ordering: each track folder carries its
canonical number (unique across folders), and within a folder the file numbers are unique
and contiguous from 01, so the learning path can never silently drift or collide.
Each notebook carries metadata.tabench = {track, unit, requires_extra, covers}; the CI
executor and the coverage gate key off that, not off filenames.
Same-ADR covers folds¶
Mechanical folds — metadata.tabench.covers mirrors tests/test_tutorials.py::_COVERS
exactly (test-pinned, see Enforcement above):
notebook |
also certifies |
why |
|---|---|---|
|
|
ADR-002 static T2: the stale prior is the baseline the GLS estimator improves on |
|
|
ADR-023 within-day dynamic estimation — one notebook, three estimator variants |
08-tdta/01-pm-td is NOT a registry fold (no pm-td-ue/pm-td-so enforced units exist,
metadata.tabench.covers == []) — it is one notebook exercising both of solve_td_ue’s
and solve_td_so’s objectives over the single ADR-031 path-marginal task.
Index (the learning path, in order)¶
extra blank = core install. Runtimes are wall-clock on the author’s box (kernel start
~2–3 s dominates the small ones); treat them as order-of-magnitude, re-measured in CI.
Every enforced unit and every track tour below has shipped — the ToC is complete, and
_ALLOWLIST in tests/test_tutorials.py is empty (see Enforcement above).
01-static/ — road static assignment (T1), the model-evolution ladder¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
3.9 s |
shipped |
|
02 |
|
4.0 s |
shipped |
|
03 |
|
4.0 s |
shipped |
|
04 |
|
3.5 s |
shipped |
|
05 |
|
3.9 s |
shipped |
|
06 |
|
3.9 s |
shipped |
|
07 |
|
3.8 s |
shipped |
|
08 |
|
3.8 s |
shipped |
|
09 |
|
3.6 s |
shipped |
|
10 |
|
4.2 s |
shipped |
|
11 |
|
4.3 s |
shipped |
|
12 |
|
3.7 s |
shipped |
|
13 |
|
3.9 s |
shipped |
|
14 |
|
3.9 s |
shipped |
|
15 |
|
3.6 s |
shipped |
|
16 |
|
3.5 s |
shipped |
|
17 |
|
3.9 s |
shipped |
|
18 |
|
3.6 s |
shipped |
|
19 |
|
3.6 s |
shipped |
02-day-to-day/ — day-to-day dynamics¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
6.6 s |
shipped |
|
02 |
|
5.5 s |
shipped |
|
03 |
|
5.1 s |
shipped |
|
04 |
|
5.2 s |
shipped |
|
05 |
|
5.7 s |
shipped |
|
06 |
|
7.1 s |
shipped |
|
07 |
|
5.9 s |
shipped |
|
08 |
|
6.0 s |
shipped |
03-estimation/ — T2 OD estimation¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
7.7 s |
shipped |
|
02 |
|
12.5 s |
shipped |
|
03 |
|
7.3 s |
shipped |
|
04 |
|
10.3 s |
shipped |
|
05 |
|
12.3 s |
shipped |
|
06 |
|
7.1 s |
shipped |
|
07 |
|
5.3 s |
shipped |
04-transit/¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
5.2 s |
shipped |
05-dnl/ — dynamic network loading¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
5.5 s |
shipped |
|
02 |
|
4.8 s |
shipped |
|
03 |
|
5.3 s |
shipped |
|
04 |
|
5.0 s |
shipped |
06-bottleneck/ · 07-dta/ · 08-tdta/ — analytic dynamics¶
folder / # |
units |
extra |
runtime |
status |
|---|---|---|---|---|
|
|
5.0 s |
shipped |
|
|
|
5.2 s |
shipped |
|
|
|
5.5 s |
shipped |
|
|
|
5.3 s |
shipped |
|
|
|
37.5 s |
shipped |
09-newell/¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
5.4 s |
shipped |
10-learned/ — optional-extra learned models¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
torch |
19 s |
shipped |
02 |
|
torch |
71 s |
shipped |
11-external/ — external-engine adapters¶
# |
unit |
extra |
runtime |
status |
|---|---|---|---|---|
01 |
|
sumo |
8 s |
shipped |
02 |
|
sumo |
12 s |
shipped |
03 |
|
dtalite |
7 s |
shipped |
04 |
|
sumo |
23 s |
shipped |
12-data/ · 13-experiments/ — tours (outside the enforced unit set)¶
folder / # |
notebook |
extra |
runtime |
status |
|---|---|---|---|---|
|
|
network download |
14 s |
shipped |
|
|
network download |
7 s |
shipped |
|
|
5 s |
shipped |
Data-track notebooks never default-prefetch: xu2024/bo4mob fetch (checksummed) or
raise a clear diagnostic if the network is unreachable, per each notebook’s own guard.