Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Resource contract for collective and gated models

A collective regime and a value-dependent transition each buy something a singleton model does not have, and each costs something a singleton model does not pay. This page names the workloads those costs are measured on, says which axis each one is allowed to grow along, and states what a regression is.

It is a contract, not a report: the numbers live in the ASV history, and what is written down here is the shape each cost is allowed to have. A change that moves a level is reviewed against the history; a change that moves an order is a defect whatever the level.

The workloads

All of them are in benchmarks/asv/bench_collective_household.py. The first six run over the marriage market of lcm_examples.collective_household: two singles who marry under mutual consent, a household with a participation constraint on each partner, and a dissolution edge keyed by the continuing household. ReferenceChainSolve is not that model — it builds a synthetic chain of collective links with no gated edge, no consent and no dissolution, so that reference depth is the only thing varying.

WorkloadClassWhat it isolates
Model constructionCollectiveHouseholdConstructThe phase scan, the lowering of the collective declarations, and per-edge parameter discovery, with nothing traced or compiled.
First-solve assemblyCollectiveHouseholdSolve.track_compilation_timeA whole first solve(): tracing, lowering, compiling-or-loading every kernel and fold, AND running the backward induction. See the note below.
Warm solveCollectiveHouseholdSolve.time_executionWhat an estimation loop pays per parameter vector.
Host memory, solveCollectiveHouseholdSolve.peakmem_executionResident peak while backward induction runs.
Device memory, solveCollectiveHouseholdSolveGpuPeakMemDevice peak on the same workload.
Simulation over cohort sizeCollectiveHouseholdSimulate, n_subjects ∈ {1e3, 1e4, 1e5}Routing: one gate evaluation per edge per period over the whole population.
Transitive reference depthReferenceChainSolve, depth ∈ {1, 2, 4, 8}The closure a value constraint opens: link k reads link k-1 in the same period.
Device memory, simulationCollectiveHouseholdSimulateGpuPeakMemDevice peak while routing the cohort.
Device memory, chain solveReferenceChainSolveGpuPeakMemDevice peak as reference depth grows.

The budgets

Each budget is an order, because that is what a benchmark suite can defend across machines and backends. A level is defended by the ASV history on one machine.

What the first-solve number is, and is not

track_compilation_time is not a compilation time. The timed region is a whole model.solve() call, so the number is everything that first solve does: tracing, lowering, either compiling each program or loading it from the persistent cache, and then running the backward induction itself. Which of compile-or-load happened is invisible in the number, and so is the execution term.

Measured on this workload — benchmarks/asv/bench_collective_household.py at commit 1feb6b0a, on an NVIDIA GeForce RTX 3070 (8 GiB) under the CUDA backend at float32 (jax_enable_x64 left at its default), with JAX_COMPILATION_CACHE_DIR pointed at a fresh directory asserted empty before the run and holding 60 entries after:

seconds
first solve, empty cache1.99
first solve of a freshly built model, cache populated1.11
repeat solve of the same model object0.04

So the cache covers a little under half of a cold first solve, and execution is about 2% of it — small here, but it is a term that grows with the grid while the assembly terms grow with the number of programs, so the split is not fixed. The saving from a cache hit also grows with the model, because compilation grows with program size while tracing and lowering grow with program count.

Three things follow, all about how to read a change in this line:

Where a pointwise reoptimization mode would sit

off_grid="pointwise" reads the operands at the landing point and gates them there, in both phases, using the kernels the model already has. A future reoptimization mode — recomputing the target’s own optimum at the realized point — is a different kernel and is kept statically separate from the default one: it must be selectable per edge, must not appear in a model that did not ask for it, and carries its own entries in this table before it is offered. Its cost is O(subjects × target action grid) per edge per period, which is a different order from the default’s O(subjects), so the two may not share a budget line.

Running them

These workloads run under the ordinary ASV tasks; Benchmarking has the machine registration, the run and compare invocations, and the publish workflows.

One caveat is specific to this suite. The GPU peak-memory companions need a CUDA environment and a device that publishes memory statistics, and there is no skip path: nothing here raises ASV’s NotImplementedError or detects a device, so on a machine without one they fail rather than abstain. Run them only where a GPU is present; the host peakmem_* rows are the portable ones.