Skip to content

Hierarchical scaling and comparison measurements

The hierarchical ODE fixture supplies independent cell references, deterministic observations, shared/local/IC parameters, four regimes, and the statistical objective. This measurement layer invokes that same problem in fresh serial CPU workers. Small CI cases qualify the protocol and supported comparison configurations; larger timing sweeps are opt-in. There are no speed or memory thresholds in CI.

Setup and bounded commands

uv sync --dev --extra benchmark
just benchmark-hierarchical --groups 1 --local-dimensions 1 --steps 4 \
  --solvers core external diffrax-fixed numpy scipy \
  --derivatives trajectory grad-fwd grad-rev hessian logdet-grad \
  --repeats 3 --timeout 180 --require-success --output /tmp/hierarchical-small.jsonl

The optional benchmark extra installs Diffrax; it is not a numerical-core runtime dependency. Python/JAX/Diffrax/SciPy versions are reported, so retain the resolved environment with results. Concrete NumPy and SciPy references produce trajectory rows, not derivative rows. A larger sweep can vary:

just benchmark-hierarchical --groups 2 4 8 --local-dimensions 1 2 \
  --steps 8 32 128 --regimes nonstiff stiff --coupling independent coupled \
  --solvers core external diffrax-fixed --derivatives grad-rev hessian logdet-grad \
  --repeats 5 --timeout 300 --output /tmp/hierarchical-scaling.jsonl

--steps sets a minimum-step policy by capping discovery/adaptive dt_max at 1/steps; it does not invent accepted steps. Actual accepted counts are reported. Output times remain [0, 0.2, 0.5, 1], so increasing step count does not also increase observation dimension. Increasing local dimension adds rate and IC parameters per group: total fitted dimension is 2 + 2*groups*local_dimension.

Compare equivalent tasks

Solver label Task and configuration
core Built-in Dormand–Prince accepted-mesh replay with JAX scan/checkpointing
external Public fixed_explicit_step in an external checkpointed scan, including FSAL reuse, on the identical mesh
diffrax-fixed Diffrax Dormand–Prince with StepTo on that exact frozen mesh and the same output times
diffrax-adaptive Separate live-adaptive task: Tsit5 for nonstiff problems, Kvaerno5 for stiff problems, PID tolerances 2e-8/2e-10, with the same maximum-step policy
numpy Eager core replay of the identical nominal mesh; trajectory parity/reference timing
scipy Independent cell-loop DOP853 solve at 2e-12/2e-14; accurate trajectory reference timing

Fixed-mesh comparisons hold step times, method, parameters, outputs and objective constant. Adaptive comparison rows identify their own accepted/rejected steps and the producing core mesh count separately; their workload is not identical to frozen replay. Do not combine unlike tasks into a claimed solver speedup. Reference-RHS loops intentionally differ from the native vectorized RHS, so SciPy timings identify the chosen trajectory-reference implementation.

Diffrax configurations follow its solver recommendations, adjoint contracts, and Hessian example. Reverse gradient and trajectory rows use RecursiveCheckpointAdjoint, forward rows use ForwardMode, and Hessian/log-determinant-gradient rows use DirectAdjoint. RK stages select scan_kind="bounded", and saved-output loops use the adjoint appropriate to the transform. DirectAdjoint supports both modes but costs can depend on max_steps; that bound is recorded. Fixed rows set it to the actual mesh length. Adaptive rows use a stated bound based on discovery, and failures remain visible rather than implying unsupported AD.

All current measurements use float64 CPU execution. GPU and other namespaces are outside this measurement protocol. The regularized Hessian is H+25*I; positive definiteness and nonfinite values are checked before a row qualifies for interpretation. See the fixture guide for priors and nonzero residuals.

JSON fields and timing boundaries

Each completed row is correctness-gated against the independent trajectory. Gradient rows compare independent-reference centered differences; Hessian and log-determinant-gradient rows compare centered differences of their relevant native derivatives. The fixture's separate required tests also check forward/ reverse agreement and refinement. Failed agreement produces an error row.

Fields Meaning
groups, local_parameter_dimension, parameter_count, state_shape Problem dimensions and fitted initial-condition contribution
accepted_steps, core_discovery_accepted_steps, solver_step_statistics Actual measured-solver steps and nominal core discovery counts; Diffrax statistics distinguish rejection
core_discovery_seconds, reference_seconds, backend_setup_seconds Preprocessing/reference/imported-backend setup, outside selected compiled numerical timing
trace_lower_seconds, compile_seconds First selected transform tracing/lowering and compilation in a fresh process
first_execution_seconds, warm_execution_seconds Synchronized execution after compilation and full warm samples
total_worker_seconds Time inside the measurement function, including setup and validation; process startup/imports occur before this timer
peak_process_rss_mib Whole worker peak RSS, including imports, setup, validation and any extra validation compilations; not isolated live solver storage
xla_buffer_bytes Selected compiled transform's reported argument/output/temp/alias buffer requirements; may be unavailable
trajectory_max_absolute_error, derivative_max_absolute_error, regularized_hessian_min_eigenvalue Agreement and positive-definiteness evidence
versions, python, hardware, provenance, numerical_configuration Exact packages, CPU/thread settings, commit/dirty flag/content hashes, adjoint/stage/controller/step bounds

A fresh worker prevents previous cases' compilation/memory caches from becoming an advantage. Workers also disable JAX's persistent compilation cache, even if the parent configured a cache directory, and report persistent_compilation_cache_enabled: false. See JAX's cache setting. First compiled-transform timing occurs before the postmeasurement accuracy checks; every result is validated before emission. Warm calls block until completed. RSS includes the full worker lifecycle, while XLA buffer analysis describes only the selected compiled transform. Concrete reference rows have no tracing/compilation or XLA-memory claims. backend_setup_seconds covers array/backend initialization after JAX imports; the process timeout and RSS cover startup/import costs too.

Timeouts are serialized with status: timeout, censored: true and no timing result. They are not measured speedups. Errors include diagnostics and return codes; --require-success makes any failed/censored case fail the command. Retain failed rows when reporting a sweep rather than selecting only favorable results. CI uploads its tiny JSON protocol evidence as an artifact; virtual runner timings are not publication scaling claims.

For publication, save the JSON, environment resolution, exact source tag/commit and content hashes with the archive. Report hardware, dimension and mesh policy, objective/regularization, tolerances, derivative/adjoint mode, repetitions, timeout policy, and agreement before interpreting runtime or memory trends.