ADR-033: xu2024-dataset — the Xu et al. (2024) 20-US-city cross-domain axis¶
Status: accepted (implemented)
Date: 2026-07-15
Deciders: non-solver roadmap — redeeming the adr-006 cross-domain-axis promise
File: docs/design/adr-033-xu2024-dataset.md
Context¶
Every scored instance in TABenchmark so far is either a hand-built analytic anchor or one of the four donated TNTP networks (Sioux Falls, Anaheim, Barcelona, Winnipeg). adr-006 (learned-model certification) named the missing piece explicitly: “The Xu et al. 2024 real-city dataset, disjoint from TNTP and CC-BY, is a natural future cross-domain test set; it is referenced, not vendored.” This ADR redeems that promise — a benchmark that claims to test whether a method (learned or classical) generalizes needs instances off the standard four.
The artifact. Xu, Zheng, Hu, Feng & Ma (2024), A unified dataset for the
city-scale traffic assignment model in 20 U.S. cities, Scientific Data 11:325,
DOI 10.1038/s41597-024-03149-8
(xu2024unified, canon reference 276). The data live on figshare
(10.6084/m9.figshare.24235696, v4) as one 276 MB zip (file id 48908890,
whole-zip md5 3f7632e00599588abecbcfc488f862b2), CC BY 4.0. Each of the 20
cities ships an OSM-derived network, an OD demand matrix, and computed equilibria
from two solvers (TransCAD and AequilibraE). The integration target is the
per-city AequilibraE trio: network.csv (link table + per-city-calibrated
BPR α/β), od_demand.aem (the OD matrix), and assignment_result.csv (the
published UE link flows).
The discovery (the load-bearing finding). The dataset’s published AequilibraE
runs injected the OD demand at node ids 1..Z — which are not the tract
centroids the dataset intends. This is not an inference: it is visible in the
authors’ own published AequilibraE_assignment.py, which calls
g.prepare_graph(np.arange(zones)+1). Measured consequence: with centroids taken
as 1..Z, the shipped AequilibraE flows conserve machine-exactly (max node
residual ~1e-11); with the intended tract nodes (10000000+k) conservation fails
wildly, and those nodes sit a median 6.4 km from the corresponding tract. The
dataset’s TransCAD side used the correct centroids (and a different
connector speed), so the two shipped solver results are different instances.
Any “cross-solver agreement” claim on the shipped artifacts would therefore be
false, and this ADR never makes one.
Decision 1 — Ship Variant A (AS-PUBLISHED), defer Variant B¶
We ship the as-published AequilibraE instances: node ids 1..Z are the zone
centroids, exactly as the published run treated them. These are self-consistent
and machine-verifiable — the published flows conserve and (approximately)
equilibrate on this graph — and need only the numpy/stdlib parsing of the trio.
first_thru_node=1: the published run allowed through-centroid flow and the
low-id “centroids” are real intersections, not pure zone stubs.
The corrected tract-centroid variant (Variant B) — demand at the true tract centroids, cross-checkable against the TransCAD reference — is deferred. It needs DBF/SHP/TransCAD-convention parsing, its reference is only ~1e-2 accurate, and it carries the connector-speed divergence; it is named future work, not part of this sprint.
Decision 2 — The wrong-centroid defect is a documented known-defect (P2)¶
The defect is recorded prominently, in the same honest spirit as the TNTP unit
quirks: in the module docstring (data/xu2024.py), in each scenario card’s
known_defect field, and in every scenario’s ReferenceSolution.note. The
shipped instances are labelled AS-PUBLISHED throughout. The one claim the data
cannot support — cross-solver TransCAD↔AequilibraE agreement — is stated as
off-limits everywhere it might be tempting.
Decision 3 — P9 integration by HTTP-range extraction, on a separate registry¶
Data are fetched, never vendored (P9). The primary fetch path opens the figshare
zip over HTTP byte ranges (_HttpRangeFile + zipfile): the central
directory is read from the zip’s 276 MB tail, and only the three needed members
are inflated — a single-city load transfers ~1–4 MB, never the whole archive
(measured: Honolulu’s trio downloads + builds in ~12 s over ~12 range requests).
Each extracted file is verified against a pinned SHA-256; a documented fallback
(whole-zip download + md5 verify + extract) covers the case where a server
refuses ranges. The version-immutable figshare file id keeps the URL alive across
future deposit versions.
The 17 cities live in a separate XU2024_REGISTRY, deliberately not in
the TNTP REGISTRY that CI prefetches (.github/workflows/ci.yml runs
[fetch(s) for s in REGISTRY.values()]). So a CI run never pulls 276 MB on its
own: the two rungs’ trios are fetched lazily only when their tests run, and the
other 15 cities are never fetched in CI at all. load_scenario("xu2024-<city>")
routes to the builder; the family key xu2024-<city> gives the P7 fairness gate
the cross-domain axis for free.
Decision 4 — Honest validation: the published flows are a LOOSE reference¶
The published AequilibraE flows are not a best-known oracle like the TNTP
*_flow.tntp solutions (those certify at AEC ~1e-15). Their own recomputed
relative gap is ~1e-3 — roughly 11 orders looser — because the paper solved to a
gap target of 0.001. They are used only as a provenance cross-check, never as
a regression oracle. The real validation is four measured facts:
Builder invariants on all 20 cities (the audit below): unique links,
fft > 0,cap > 0, BPR α,β ≥ 0, node-id ranges,link_id = 1..L, the AEM header size identity, and the1..Z-centroid convention.Published-flow conservation, recomputed from the flows: max node-balance residual ≤ 3e-10 (worst: New York ~2e-10; Honolulu 1.1e-11) across the 17 shipped cities. The prose bound is deliberately loose of the measurement — the residual is summation-order sensitive (two review lenses measured New York at 2.00e-10 and 2.04e-10), so a zero-headroom “≤ 2.0e-10” pin would be fragile; the test asserts the far looser
< 1e-9.The repo’s own bfw certifying a tighter gap on the identical instance — genuine cross-implementation agreement (AequilibraE bfw ↔ tabench bfw). On Honolulu,
BiconjugateFrankWolfeModelreaches relative gap 1.07e-4 in 400 iterations (~100 s, measured 104.5 s on the dev box), with correlation 0.99992 to the published flows and Σ|Δv|/Σv = 0.96%. (The CI anchor uses a small budget and checks feasibility + correlation > 0.99; the tight number is this in-sprint measurement, not a CI pin.)Correlation with the published flows as provenance (Honolulu 0.99992).
Decision 5 — The 20-city audit: 17 ship, 3 excluded and named¶
The builder + invariants + conservation battery was run on all 20 cities
in-sprint. 17 pass and ship. 3 are excluded and named: Washington,
Pittsburgh, Phoenix. Their failure is itself further evidence of the
wrong-centroid defect — a handful of centroid ids in 1..Z are absent from
the network as low-id nodes (they exist only as their 10000000+k tract ids) yet
still carry demand (Washington 2 of 179; Pittsburgh 5 of 149; Phoenix 4 of 378),
so no valid Network can be built on the 1..Z convention. They are candidates
for the deferred Variant B. BPR α/β vary by city (per-city grid-search
calibration, paper Table 5) but are uniform within each city; published gaps span
7.6e-4–1.37e-3.
# |
City |
Zones |
Nodes |
Links |
BPR α |
BPR β |
Demand |
Published rgap |
Status |
|---|---|---|---|---|---|---|---|---|---|
01 |
San Francisco |
194 |
4986 |
18002 |
0.5 |
1.8 |
168,828 |
1.27e-03 |
ship |
02 |
Seattle |
139 |
6891 |
27361 |
0.6 |
3.0 |
133,900 |
9.18e-04 |
ship |
03 |
Portland |
157 |
8245 |
31939 |
0.5 |
1.2 |
136,471 |
8.97e-04 |
ship |
04 |
Las Vegas |
175 |
7823 |
28831 |
0.5 |
1.3 |
76,532 |
1.02e-03 |
ship |
05 |
Chicago |
819 |
14434 |
54469 |
0.5 |
1.2 |
468,616 |
1.21e-03 |
ship |
06 |
New Orleans |
185 |
7217 |
24073 |
0.6 |
1.8 |
59,875 |
7.78e-04 |
ship |
07 |
Austin |
199 |
10717 |
40158 |
0.5 |
1.5 |
202,867 |
1.03e-03 |
ship |
08 |
Minneapolis |
130 |
4004 |
15363 |
0.15 |
1.8 |
60,167 |
7.58e-04 |
ship |
09 |
Dallas |
328 |
21389 |
77818 |
0.6 |
1.3 |
200,434 |
1.04e-03 |
ship |
10 |
Milwaukee |
234 |
8521 |
30747 |
0.5 |
1.5 |
98,220 |
9.37e-04 |
ship |
11 |
New York |
2005 |
28626 |
99408 |
0.25 |
1.5 |
1,782,020 |
9.47e-04 |
ship |
12 |
Washington |
179 |
6136 |
23573 |
0.5 |
1.5 |
123,084 |
— |
excluded |
13 |
Boston |
191 |
5542 |
20487 |
0.25 |
2.0 |
119,746 |
1.37e-03 |
ship |
14 |
Philadelphia |
389 |
10410 |
38641 |
0.5 |
1.2 |
233,554 |
9.49e-04 |
ship |
15 |
Pittsburgh |
149 |
3532 |
13662 |
0.5 |
2.0 |
48,436 |
— |
excluded |
16 |
Miami |
108 |
4121 |
15108 |
0.5 |
1.5 |
39,412 |
8.92e-04 |
ship |
17 |
Atlanta |
141 |
5207 |
20243 |
0.2 |
1.5 |
61,069 |
8.10e-04 |
ship |
18 |
Phoenix |
378 |
15324 |
58070 |
0.15 |
1.2 |
284,808 |
— |
excluded |
19 |
Denver |
175 |
9205 |
34724 |
0.5 |
1.5 |
120,434 |
1.00e-03 |
ship |
20 |
Honolulu |
117 |
2982 |
11205 |
0.5 |
1.5 |
107,515 |
1.04e-03 |
ship |
Rungs¶
Honolulu (rung 5, 11,205 links) and San Francisco (rung 6, 18,002 links)
are the CI-sized rungs (scenarios/5honolulu-xu2024.yaml,
scenarios/6sanfrancisco-xu2024.yaml): small enough to fetch (~1 MB trio) and
certify quickly. The other 15 cities build via the identical path but are
local-only by download/solve cost (New York is ~10× Honolulu). New York, at 2,005
zones and 99,408 links, is the largest instance in the entire benchmark.
Consequences¶
New:
src/tabench/data/xu2024.py(registry of 17 cities; HTTP-range fetcher — primary ranged path with a narrowed whole-zip fallback that fires only on a transport error, and a pre-transfer size guard against the pinned v4 artifact on both paths; AEM/network/assignment parsers with drift guards on the 1..Z zone index and the fully-directed convention; scenario builder; citation); two scenario cards;tests/test_xu2024.py(20 tests).load_scenarioroutesxu2024-<city>;xu2024_scenario,XU2024_REGISTRY,XU2024_RUNGS,Xu2024UpstreamErrorare exported from the package root / data layer.Unchanged: no new dependency (numpy/stdlib only — no pandas); the Evaluator, the fairness gate, the CI prefetch, and every content hash, including the golden Braess hash
cf00f411…(re-asserted intest_xu2024.py). Thexu2024data node indocs/model-specs.jsonstaysshipped:false/not-a-solver, matching the TNTP (stabler2016transportation) precedent — a dataset is not a solver.Cache-eviction skip semantics (inherited from the TNTP fetcher): a checksum mismatch evicts the cached file and raises
ChecksumError; a subsequent offline load in the same session then reports “data unavailable” and skips (via the houseload_or_skip) — that skip reflects the eviction, not a missing initial download. CI closes this window withTABENCH_REQUIRE_DATA=1, under which any data failure is a hard error, never a skip.Gaps deliberately left: the corrected tract-centroid Variant B (with a TransCAD cross-check) and its three currently-excluded cities; the 15 non-rung cities are available but not CI-run. A learned-model cross-domain train/test split (TNTP → xu2024) is now possible but is future work, not shipped here.
Adversarial review¶
Three independent lenses (fetcher/P9 integrity, builder correctness + honest-
validation claims, numerics/infra/integration), each executing repros against the
uncommitted tree and the REAL figshare artifact. The first sprint of the review
program with zero confirmed material defects (21 sprints reviewed, 20 with at
least one confirmed material defect): every corruption, tamper, truncation, and
upstream-v5-swap attack ended in a loud ChecksumError with nothing unverified
left in the cache — the measure-first, integrate-second dossier (which had already
solved Honolulu and found the upstream defect before any integration code existed)
is the plausible cause, and the pattern is worth keeping.
Hygiene/precision findings, all fixed: the “≤ 2.0e-10” conservation prose
bound had zero headroom (one lens recomputed New York at 2.037e-10 while another
measured exactly 2.0e-10 — summation-order sensitivity; the BLAS lesson in prose
form; restated with headroom); a failed whole-zip fallback stranded an unbounded
.part file in the cache (now try/finally-unlinked); the whole-zip fallback
triggered on ANY ranged-path exception including permanent structural errors,
contradicting the ci.yml “never the 276 MB zip” comment (now transport-only, with
ZIP_SIZE as a live pre-transfer guard raising Xu2024UpstreamError on an
upstream artifact change BEFORE any bulk transfer); adr-006 still called the set
“future work” (touched up); the .aem zone index and direction column are now
asserted at parse time (verified true on all 17 cities — upstream drift becomes
loud, not silent); wall-time claims harmonized; the checksum-eviction offline-skip
semantics disclosed.
Attacked and survived (highlights): per-file sha256 checked on EVERY load,
post-decompress (byte flips in all three file kinds → loud, evicted, re-fetch
heals); a tampered zip member with a VALID CRC still refused; truncated ranged
responses and mixed 206/200 servers never silently parse; a v5 upstream re-upload
cannot poison either fetch path; the CI byte-budget claim measured TRUE against
real figshare (both rungs = 1,027,033 bytes in 24 range reads + 2 probes — 0.37%
of the 276 MB zip); warm-cache tests fully offline, cold-cache skips clean,
TABENCH_REQUIRE_DATA=1 turns skips loud; CLI byte-parity for every existing
scenario (all 12 existing content hashes identical, run/fetch outputs
byte-identical against HEAD); two cold builds byte-identical (hash
1baa0c04… stable across processes and caches); the excluded-city analysis
reproduced exactly (the missing low-id centroids still carry demand — further
evidence of the wrong-centroid defect); the Honolulu bfw anchor reproduced to
every printed digit (rgap 1.074e-4 @ 400 iters, corr 0.999919); no cross-solver
claim vs TransCAD anywhere (repo-wide sweep — every mention is a disclaimer);
tools/generate_models.py reproduces the shipped MODELS.md byte-identically;
the golden Braess hash untouched.