ADR-013: multiclass — Dafermos (1972) multiclass-user equilibrium¶
Status: accepted (implemented)
Date: 2026-07-09
Deciders: static-extensions track — the multiclass half of the “multiclass / VI” roadmap item
File: docs/design/adr-013-multiclass.md
Context¶
vi-asym (adr-011) shipped the single-class asymmetric variational inequality
t(v) = t_BPR(v) + C v. adr-011 explicitly deferred the multiclass case
(Dafermos 1972) for one reason: certifying a per-class Wardrop condition needs
per-class link flows, but FlowState.link_flows / Trace / Evaluator.evaluate
were all hardwired to a single aggregate (n_links,) vector — a harness-contract
change, not a self-contained model.
Dafermos (1972, Transportation Science 6(1):73-87, dafermos1972traffic)
generalizes UE to K user classes that share the network but perceive
class-specific, mutually-coupled costs. Each class i routes its own demand
g^i to a Wardrop equilibrium in its cost t^i, but every class’s cost
depends on the joint flow, so the equilibria are simultaneous. Stacking the
class-indexed flows V = (v^1, ..., v^K) this is a block-structured single-class
asymmetric VI
find V* in K = K_1 x ... x K_K s.t. <T(V*), V - V*> >= 0 for all V in K,
i.e. exactly vi-asym promoted one level up (K = 1 recovers it) — with two
genuinely new ingredients: the feasible set is a product of per-class demand
polytopes (routing is per class), and the emitted flow gains a class axis.
Dafermos (1972)’s specific contribution is the model and the symmetry
condition under which it reduces to a single convex program (the integrable
case); the genuinely-asymmetric VI with no equivalent optimization is Smith
(1979) / Dafermos (1980).
Decision¶
Cost model (linear class interaction).
t_a^i(V) = t_a^BPR(v_a) + sum_j M_ij v_a^j, wherev_a = sum_j v_a^jis the total link flow andM = scenario.multiclass.interactionis a(K, K)matrix applied per link.Mis what makes the per-class split well-defined: withM = 0every class sees the identical costt_a^BPR(v_a)and the split is arbitrary (the model degenerates to single-class UE on the summed demand). A symmetricMis the integrable case (the equilibrium minimizes a convex multiclass-Beckmann potential; Dafermos 1972); an asymmetricMis a genuine VI with no equivalent optimization (Smith 1979; Dafermos 1980). This is the block-diagonal generalization ofvi-asym’sC.Schema —
MulticlassDemand+Scenario.multiclass. A new frozen dataclass (core/scenario.py) holdsmatrices((K, n_zones, n_zones),K >= 2per-class OD) andinteraction((K, K)). It is referenced by a new optionalScenario.multiclassfield, content-hashed only when present (appended last, afterlink_interaction), so every scenario without it — the golden Braess instance included — hashes byte-identically (cf00f411…re-asserted intests/test_multiclass.py). It is mutually exclusive with the other six optional task fields, and the aggregatedemandis validated to equal the class sum so every single-class consumer ofdemand(feasibility scale, fairness gate, aggregate loading) stays consistent.Output-contract change (the crux adr-011 deferred), done additively. An OPTIONAL
class_link_flows: (K, n_links) | Noneis added toFlowStateandTrace.record, defaulting toNone. Every single-class model leaves itNone, so their emissions, the aggregatelink_flowscontract, and the CSV schema are byte-identical to before this field existed (the full suite is the guard). It is a first-class emitted object, not aself_reportentry — so the harness recomputes the per-class VI residual from it (P1), never trusts it. When present,link_flowsis the class sum. This is the additive form of exactly the reshape adr-011 flagged as the “real” fix: no existing signature changes, no existing model touched.Solver
multiclass(models/multiclass.py,MulticlassModel, paradigmstatic_ue_multiclass). Multiclass diagonalization (nonlinear Gauss-Seidel): sweep the classes; for classifreeze the other classes’ flows, which makes classi’s cost separable in its own flow — an ordinary single-class UE that Frank-Wolfe (exact Brent line search on the diagonalized cost) solves — then relaxv^i <- v^i + step (v^i_inner - v^i)and use each class’s updated flow immediately. It reusesvi-asym’s inner FW verbatim, once per class.K = 1would collapse tovi-asym. Convergence follows the Dafermos (1982) / Florian & Spiess (1982) diagonal-dominance condition, not monotonicity alone; a coupling that drives a cost non-positive stops the sweep and emits a flow the certificate censors (never a false accept).Certificate (P1). The harness recomputes, from the emitted
V, the class-summed VI residual — the product feasible set makes the VI gap decompose into per-class all-or-nothing minima:gap = (sum_i <t^i(V), v^i> - sum_i min_{y^i in K_i} <t^i(V), y^i>) / sum_i <t^i(V), v^i>,the ordinary relative gap summed over classes at the coupled cost (0 iff
Vsolves the multiclass VI). Feasibility is a per-class conservation audit (each class routes its own demand).beckmann_objectiveis NaN (no potential for an asymmetricM). No new scored CSV key: the residual reusesrelative_gap/average_excess_cost. A multiclass scenario emitted without per-class flows is censored (an aggregate flow cannot certify a per-class equilibrium).Analytic anchors (
multiclass_two_route_scenario, both recomputed in the tests). Diamond routes 1->3->2 / 1->4->2, two classes (carsg=4, trucksg=2), zero BPR slopes so congestion is purely the per-linkM. Because a 2-link route applies the per-link coupling on both legs, the route-level coupling is2 M, and the equilibrium route-A flows solve4 M [p - g_cars/2, q - g_trucks/2]^T = [a2, a2]^T, i.e.[p, q] = [g_cars/2, g_trucks/2] + (a2/4) M^{-1} [1,1]^T:symmetric
M = [[0.5, 0.25], [0.25, 0.5]]→ cars(2.5, 1.5), trucks(1.5, 0.5), aggregate(4, 4, 2, 2), class costs equalized 3.25 / 2.75 — the integrable / multiclass-Beckmann check;asymmetric
M = [[0.5, 0.5], [0, 0.5]](trucks slow cars, not vice versa) → cars(2, 2), trucks(1.75, 0.25), aggregate(3.75, 3.75, 2.25, 2.25)— a genuine-VI flow no Beckmann/FW solver reaches; classes route differently (the multiclass signature). All values are exact multiples of 0.125.
Alternatives considered¶
Breaking reshape of
link_flowsto(K, n_links)everywhere: rejected — it would touch all 25 single-class models and the CSV schema. The optionalclass_link_flowsfield is the additive equivalent (this ADR is the realization of adr-011’s deferred item, done without a breaking change).Per-class flows in
self_report: rejected — the certificate would then be recomputing a scored metric from a trusted self-report, violating P1. Per-class flows must be a first-class emitted object to keep the residual harness-pure.Nonlinear / flow-dependent PCE coupling (Riente de Andrade et al. 2017): deferred — the linear
Mis the minimal faithful coupling that gives a closed-form anchor and reusesvi-asym’s affine machinery; a flow-dependent interaction is a separately-scoped follow-up.A dedicated sequential multiclass solver instead of reusing vi-asym’s FW: the per-class diagonalized subproblem is an ordinary UE, so the established FW+brentq inner loop is reused rather than reimplemented.
Consequences¶
The benchmark gains its first multiclass-user equilibrium model and its first
per-class-flow output, with a sound, harness-recomputed VI certificate and
hand-derived integrable + genuine-VI anchors. The class_link_flows extension is
additive: every prior model and the golden Braess hash are provably unchanged.
Follow-ups: flow-dependent-PCE coupling, per-link (rather than uniform) M, and
reporting the per-class residual breakdown alongside the class sum.
Sourcing¶
Dafermos (1972, Transportation Science 6(1):73-87, dafermos1972traffic, DOI
10.1287/trsc.6.1.73, Crossref-verified) is the multiclass model and the
symmetry/integrability condition; Smith (1979, smith1979existence) and Dafermos
(1980, dafermos1980traffic) are the asymmetric-VI characterization; the
diagonalization / relaxation convergence is Dafermos (1982) / Florian & Spiess
(1982). All three primaries were already in the verified reference canon. The
block-VI reading and both analytic anchors are hand-derived here (cross-verified
against an independent multiclass diagonalization); no number from any paper is
reproduced.