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

03-estimation/01-gls

prior

ADR-002 static T2: the stale prior is the baseline the GLS estimator improves on

03-estimation/07-od-dynamic

od-dynamic-sim, od-dynamic-seq, prior-profile

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

aon

3.9 s

shipped

02

msa

4.0 s

shipped

03

fw

4.0 s

shipped

04

cfw

3.5 s

shipped

05

bfw

3.9 s

shipped

06

gp

3.9 s

shipped

07

oba

3.8 s

shipped

08

algb

3.8 s

shipped

09

tapas

3.6 s

shipped

10

sue-msa

4.2 s

shipped

11

sue-probit-msa

4.3 s

shipped

12

so-bfw

3.7 s

shipped

13

fw-elastic

3.9 s

shipped

14

evans

3.9 s

shipped

15

br-ue

3.6 s

shipped

16

sc-tap

3.5 s

shipped

17

vi-asym

3.9 s

shipped

18

multiclass

3.6 s

shipped

19

learned-surrogate

3.6 s

shipped

02-day-to-day/ — day-to-day dynamics

#

unit

extra

runtime

status

01

dtd-swap

6.6 s

shipped

02

dtd-swap-sue

5.5 s

shipped

03

dtd-link

5.1 s

shipped

04

dtd-friesz

5.2 s

shipped

05

dtd-horowitz

5.7 s

shipped

06

dtd-stochastic

7.1 s

shipped

07

dtd-unifying

5.9 s

shipped

08

dtd-cumlog

6.0 s

shipped

03-estimation/ — T2 OD estimation

#

unit

extra

runtime

status

01

gls (covers prior)

7.7 s

shipped

02

spiess

12.5 s

shipped

03

vzw-entropy

7.3 s

shipped

04

od-congested

10.3 s

shipped

05

spsa

12.3 s

shipped

06

od-kalman

7.1 s

shipped

07

od-dynamic (covers od-dynamic-sim, od-dynamic-seq, prior-profile)

5.3 s

shipped

04-transit/

#

unit

extra

runtime

status

01

transit-strategy

5.2 s

shipped

05-dnl/ — dynamic network loading

#

unit

extra

runtime

status

01

ctm

5.5 s

shipped

02

ltm

4.8 s

shipped

03

godunov

5.3 s

shipped

04

node-model

5.0 s

shipped

06-bottleneck/ · 07-dta/ · 08-tdta/ — analytic dynamics

folder / #

units

extra

runtime

status

06-bottleneck 01

vickrey

5.0 s

shipped

06-bottleneck 02

vi-due

5.2 s

shipped

07-dta 01

merchant-nemhauser

5.5 s

shipped

07-dta 02

lp-so-dta

5.3 s

shipped

08-tdta 01

pm-td

37.5 s

shipped

09-newell/

#

unit

extra

runtime

status

01

newell-3det

5.4 s

shipped

10-learned/ — optional-extra learned models

#

unit

extra

runtime

status

01

implicit-ue-nn

torch

19 s

shipped

02

het-gnn

torch

71 s

shipped

11-external/ — external-engine adapters

#

unit

extra

runtime

status

01

sumo-marouter

sumo

8 s

shipped

02

sumo-duaiterate

sumo

12 s

shipped

03

dtalite-tap

dtalite

7 s

shipped

04

spsa-sumo

sumo

23 s

shipped

12-data/ · 13-experiments/ — tours (outside the enforced unit set)

folder / #

notebook

extra

runtime

status

12-data 01

xu2024

network download

14 s

shipped

12-data 02

bo4mob

network download

7 s

shipped

13-experiments 01

profiles

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.