ADR-003 — Probit SUE: Monte Carlo fixed-point certificate with a pinned evaluation stream

Status: accepted (implemented in v1) File: docs/design/adr-003-probit-sue-mc-certificate.md

Context

ADR-001 certifies logit SUE through Dial-STOCH’s closed-form loading map: the harness recomputes L(t(v), θ) deterministically and scores the fixed-point residual ‖v L(t(v))‖₁ / D. Probit SUE (Daganzo & Sheffi 1977) has no closed-form loading map — probit route-choice probabilities are high-dimensional normal orthant integrals, and the practical loading is Monte Carlo simulation (Sheffi & Powell 1982). Every analytic alternative fails at scale (see Rejected alternatives). So the T1 certificate question returns in a harder form: how does a harness certify a fixed point of a map it can only sample?

The solver this ADR certifies is sue-probit-msa, the repo’s first non-deterministic model — it activates the stochastic track (P5/P8) that has been designed but idle: Capabilities(paradigm="sue", deterministic=False, seedable=True, provides_gap=False), macroreplication in run_experiment (already routed by deterministic=False; zero runner changes), and bootstrap aggregation.

Decision 1 — The pinned MC certificate

For a scenario with sue_family="probit", the Evaluator computes from emitted flows v (after the feasibility audit, unchanged):

t     = network.link_cost(v)
v̂     = L_cert(t) = (1/R_cert) · Σ_{i=1..R_cert} AON( max(t + E_i, 1e-9) )
E     = sqrt(β · t0)[None, :] * Z          # (R_cert, n_links), Z iid N(0,1)

E is drawn once per Evaluator from RngBundle(root_seed, macrorep=0) .generator(SOURCE_EVALUATION) — the reserved stream’s documented purpose. Pinning E is legal precisely because the pinned variance is flow-independent (free-flow form, Decision 3), so E never depends on t(v).

Scored columns:

  • sue_fixed_point_residual = ‖v v̂‖₁ / D — the ranking column (same name and semantics as ADR-001; the map is MC where Dial’s was closed-form).

  • sue_residual_se — jackknife standard error over the R_cert samples.

  • sue_residual_floor = Σ_a sqrt(2·s_a²/(π·R_cert)) / D with s_a² the across-sample link-flow variance — the CLT estimate of the expected residual when v is the fixed point (the certificate’s own noise floor; positive bias O(1/√R_cert)).

Significance rule (printed with every probit-SUE table): residual differences below max(sue_residual_floor, 2·sue_residual_se) are ties. On tworoute at R_cert=6400 the jackknife SE dominates the floor (threshold ≈ 4.7e-2), so the rule must take the max, not quote the floor alone. This is also the anti-gaming clause: root_seed is public in the manifest, so a solver could in principle target the fixed point of the finite-R_cert pinned map and certify below floor — sub-threshold residuals therefore carry no ranking information by protocol, and targeting the pinned map buys nothing the rule doesn’t already discount.

Why pinning (rather than fresh draws per certification):

  1. P1 restored exactly — the certificate is a pure deterministic function of (link_flows, scenario, root_seed), byte-reproducible across machines (verified: repeated certification returns identical tuples).

  2. Common random numbers across every model, macrorep, and checkpoint certified in one experiment — the SimOpt “CRN across solutions” design — so comparisons carry far less noise than the marginal floor suggests.

  3. The honest residue of MC error is quantified and printed (se + floor), never hidden.

R_cert is certificate protocol, not instance data (ADR-001/002 precedent): it lives in the scenario card’s sue: block, the manifest, and a CSV column; it is not hashed into the scenario. Measured pins: tworoute R_cert=6400, siouxfalls R_cert=2000 (~1.5e-2 floor, 3–4 s/checkpoint). Certification costs R_cert sp-equivalents per checkpoint — estimators/solvers are documented to emit O(10–20) checkpoints (ADR-002 precedent, never silent thinning).

Honesty check change: ADR-001’s byte-equality regression does not transfer — the solver samples its own stream at its own R, and (Decision 3) its R=1 self-reported direction norm does not decay to zero even at the fixed point (measured ≈1.5 on tworoute near v*, vs a floor ≈0.04). So there is no probit analogue of the logit honesty regression: self_sue_residual is recorded as provenance only (which stream, which R, what the solver saw), never diffed against the certified residual, because the two quantities measure different things (a running descent-direction norm vs a fixed-point residual under the pinned map). The certificate’s own sue_residual_se and sue_residual_floor — printed on every row — are the integrity mechanism: they bound how much of the certified residual is MC noise, and the significance rule discounts anything below max(floor, 2·se). The self-report cannot inflate a ranking (only the certified column ranks) and cannot be gamed into looking converged, so no cross-check is needed.

Decision 2 — sue_family scenario dial and hashing

Scenario gains sue_family: str = "logit" (validated in {"logit", "probit"}; unrelated to Scenario.family, which is data lineage for the trained_on gate). sue_theta is reused as the family’s dispersion dial — logit: θ in 1/(native cost unit); probit: β = perception variance per unit free-flow time, native cost units (cards must state units, P9). Validation: sue_family == "probit" requires sue_theta is not None.

content_hash() appends f"sue_family={self.sue_family};" only when sue_family != "logit", after the existing conditional sue_theta block. Verified consequences: (a) every existing scenario hashes byte-identically — golden Braess hash cf00f411… recomputed unchanged; (b) probit and logit tasks on the same network/demand/θ can never collide; (c) solvers dispatch truthfully — sue-msa raises on probit scenarios, sue-probit-msa raises on logit ones.

Decision 3 — Solver pins (sue-probit-msa), sources per formula

  • Variance spec (pinned): var(T_a) = β·t0_a (free-flow). Sheffi & Powell (1982)’s equivalent unconstrained program requires a flow-independent perception-error distribution; Sheffi (1985) eq. [12.57] uses exactly this form for the probit SUE example. Catalogued disagreements: Sheffi ch. 11 eq. [11.12] (pure loading, no equilibrium) uses β·t_a “for simplicity”; CiudadSim/ScicosLab implements standard deviation ∝ current time; oyama’s ngev-mte uses sqrt(θ·c) with current cost. The free-flow form is the only one consistent with the Sheffi–Powell equilibrium theory — and it is what makes the certificate’s pinned E legal.

  • Draws per iteration (default factor R=1). Sheffi (1985, pp. 333–334) reports fig. 12.10 (one draw per iteration) “displays the best convergence pattern” and recommends the minimum computational effort per iteration (paraphrase; page-cited). Replicated here: equal 300-sp-call budget on Sioux Falls gives certified residual 8.0e-2 at R=1, k=300 vs 1.38e-1 at R=10, k=30. R stays a declared factor so the trade-off is exhibitable.

  • Step size (pinned): α_k = 1/k plain MSA (Sheffi ch. 12; Powell & Sheffi 1982 Blum conditions — already SHIPPED for the logit solver). No Polyak averaging: with 1/k steps the iterate is the running average of sampled loads.

  • Negative sampled times: truncate at max(·, 1e-9) (PathEngine requires strictly positive costs; Sheffi p. 300 sanctions truncation). Truncation bias is measurable and β-dependent: at the tworoute card dial β=0.1 the flow bias is below MC resolution (measured −8e-5 ± 3e-4, consistent with zero); at β=0.5 it is +1.25e-2 flow — documented hazard, not an anchor.

  • Initialization: load at free-flow costs t(0) (Sheffi ch. 12 Step 0), mirroring sue_logit.py.

  • provides_gap=False: the R=1 self-reported direction norm ‖y_k v_k‖₁/D does not decay (measured O(1) at k=5000) — the model must not early-stop on budget.target_met; budget axes only. Self-report carries the raw direction norm plus a Sheffi eq. [12.52]-style moving average (m=3), provenance-only.

  • Checkpoint indexing: trace.record fires on v_k before the k-th update; a k-iteration run’s final certified state is v_k, not v_{k+1}.

Decision 4 — Macroreps and bootstrap aggregation

Macroreps are independent solver trajectories: RngBundle(root_seed, macrorep=m), solver-internal draws on model source 0 with replication=k per outer iteration. The certificate uses SOURCE_EVALUATION under macrorep=0 so all macroreps share one pinned map (comparable numbers). Measured: macrorep spread of the certified residual at k=100 on Sioux Falls is 0.023 — dominating the certificate SE of 0.004 — so macroreplication, not a tighter certificate, is what CIs need (recommend M=10 tworoute, M=5 siouxfalls).

What was missing: run_experiment writes per-macrorep rows and lists SOURCE_BOOTSTRAP in the manifest but never draws from it. Ship experiments/bootstrap.py::bootstrap_ci(values, root_seed, B=10000) — percentile (never parametric, P5) CIs drawn on SOURCE_BOOTSTRAP, applied to the final certified residual across macroreps.

Analytic anchors (recomputed in-test, never trusted digits)

Two-route (two_route_scenario, D=4, links (1,1,1,0.5) free-flow): routes are disjoint 2-link chains, so perceived route costs are independent normals and probit has a closed form through the binormal difference:

P(A) = Φ( (c_B(D−f_A) − c_A(f_A)) / sqrt(3.5·β) ),  c_A(f)=2+f, c_B(g)=1.5+2g
f_A* : f_A = D·P(A)   (brentq in-test)

β=0.1 → f_A* = 2.4443574168 (UE 2.5; logit θ=0.5 anchor 2.2990959494). Free-flow first iterate f_A(1) = 4·Φ(−0.5/√0.35) = 0.7960494390. Certified residual at the exact analytic v*: consistent with pure MC noise (6.9e-3 at R_cert=1600 vs floor 3.9e-2). Seeded reproducibility: same (root_seed, macrorep) byte-identical; different macrorep differs. Sioux Falls smoke: β=0.5 (native 0.01 h units), R=1, k=30 in ~0.1 s.

Consequences

  • The certificate degrades gracefully from closed-form (logit) to sampled (probit) without changing its meaning: one pinned, model-blind map per task; residual + uncertainty; ranking ties below the noise threshold.

  • The stochastic track (P5/P8) is now exercised end to end: macroreps, spawn-key streams, bootstrap CIs — machinery every future simulator/DTA adapter will reuse.

  • MC certification cost (R_cert AON sweeps per checkpoint) is the T1-stochastic analogue of ADR-002’s pinned bfw run — controlled by checkpoint conventions.

  • Golden Braess hash cf00f411… unchanged; logit tasks hash as before.

Rejected alternatives

  • Sheffi–Powell objective Z(v) as the certificate: needs satisfaction terms S_rs = E[min_k C_k] per OD — path-level MC on top of link MC, no zero-at-equilibrium residual semantics. Rejected.

  • Clark’s approximation as a deterministic analytic L: requires path enumeration; Sheffi p. 300: inaccurate beyond 10–20 alternatives. Rejected.

  • Certify probit emissions against the logit-Dial map: certifies the wrong fixed point (tworoute: logit θ=0.5 → 2.299 vs probit β=0.1 → 2.444). Rejected outright.

  • Fresh draws per certification (no pinning): unbiased but breaks P8 byte-reproducibility and destroys CRN across models. Rejected.

  • Adaptive R_cert (sequential stopping on SE): unequal treatment across models, non-pinned bytes. Rejected — pin R_cert, record se/floor.

References

  • Daganzo, C.F. & Sheffi, Y. (1977). On stochastic models of traffic assignment. Transportation Science 11(3), 253–274.

  • Sheffi, Y. & Powell, W.B. (1982). An algorithm for the equilibrium assignment problem with random link times. Networks 12(2), 191–207.

  • Powell, W.B. & Sheffi, Y. (1982). The convergence of equilibrium algorithms with predetermined step sizes. Transportation Science 16(1), 45–55.

  • Sheffi, Y. (1985). Urban Transportation Networks. Prentice-Hall. Ch. 11–12.