Skip to content

Required CPU backend qualification

The required CI jobs Solver qualification (numpy|jax|torch, 3.12) install the selected namespace explicitly and run scripts/qualify_backend.py. The runner fails for an absent backend, no executed cases, any selected skip, or numerical failure. Ordinary optional skips in baseline tests are separate evidence. Provider integration tests run separately; provider success cannot substitute for these core contracts.

The same required backend runner includes native stored-time observations. Independent scalar/field values and error contracts run with NumPy; JAX JIT/vmap and initial-state/observation-parameter gradients run in float64; CPU Torch gradients run in float64 and float32. Each callback must preserve state namespace, dtype and its declared output shape.

The runner also requires sampled-window reduce_observations contracts. NumPy checks independent nonuniform linear integrals/counter changes, released generator states, reused producer buffers and separate RK4/quadrature refinement with history disabled. JAX JIT/vmap and state/parameter gradients and CPU Torch float32/float64 gradients use independent weighted-linear references. These qualify supplied static sample windows, not method-specific dense output, off-grid boundaries or constant-memory reverse-mode differentiation.

Separate required Compiler observations (numpy|jax|torch, 3.12) jobs install op_system's merged observation contract at immutable commit 0becde52474b48fef6b6f3c42a344498a17552a8, then run scripts/qualify_backend.py BACKEND --compiler-contract. These reject absent backends, selected skips and empty selection as well. A cell-average linear density on two meshes gives the same physical integral and sample at a shared declared coordinate after fixed RK4 integration. JAX JIT/AD and CPU Torch AD check state, dynamic-rate and observation-only gain derivatives against analytic exponentials. The fixture uses float64 and fixed output/substep grids; live adaptive gradients, mesh derivatives and arbitrary callbacks are not qualified by it. just qualify includes these compiler checks after the core CPU matrix. just qualify-observations assumes those namespaces are already installed. The pinned compiler is a development/CI contract, not a core runtime dependency. Those jobs also exercise an actual previous rate table through deterministic forcing boundaries on all three namespaces in float32 and float64. JAX x64 is enabled even for the float32 state, so the time table can retain float64 coordinates; both incoming and outgoing one-sided evaluation must stay correct.

Required qualification also includes per-run random streams. NumPy checks all four stochastic run interfaces against replay, stream-ownership, indexed launch-order and analytic constant-birth contracts. JAX/CPU Torch check that the NumPy convenience path rejects native states before consuming randomness. Existing functional-key sampler tests remain in the baseline stochastic suite; these additions do not qualify new native PRNGs or stochastic derivatives.

Required explicit forcing contracts cover float32/float64 NumPy/JAX/CPU Torch fixed, adaptive and frozen eager replay against independent piecewise additive solutions. NumPy RK4 and Dormand–Prince refinement checks recover their smooth-segment orders across a held-table boundary. JAX scan/checkpoint/JIT/vmap parameter gradients and CPU Torch eager gradients use piecewise exponential references on a fixed mesh. Real op_system tables, provider preparation and structured layouts are checked separately in provider integration tests. These do not qualify implicit/hybrid forcing or derivatives of jump locations.

Method and execution matrix

The table describes the small required structured workload, not all possible models, versions, devices, or differentiation transforms. Each qualified fixed method runs in float32 and float64 on all three CPU namespaces. JAX x64 is scoped explicitly for double precision; a float32 test also runs with x64 enabled.

Method NumPy fixed values JAX fixed values CPU Torch fixed values Required live adaptive / frozen replay evidence
Euler Qualified Qualified Qualified Other baseline tests; unqualified here
Heun Qualified Qualified Qualified Other baseline tests; unqualified here
RK4 Qualified Qualified Qualified Other baseline tests; unqualified here
Dormand–Prince 5(4) Qualified Qualified Qualified Qualified on all three namespaces
IMEX Euler Qualified Qualified Qualified Other baseline tests; unqualified here
IMEX Heun–trapezoidal Qualified Qualified Qualified Qualified on all three namespaces
IMEX TR-BDF2 Qualified Qualified Qualified Other baseline tests; unqualified here
IMEX ARK3 Qualified Qualified Qualified Other baseline tests; unqualified here
Linearly implicit Euler Qualified Qualified Qualified Other baseline tests; unqualified here
Linearly implicit trapezoidal Qualified Qualified Qualified Other baseline tests; unqualified here
BDF2 Qualified Qualified Qualified Adaptive configuration rejected
ROS2 Qualified Qualified Qualified Qualified on all three namespaces
Nonlinear SDIRK2 Qualified Qualified Qualified Qualified eager replay; forced scan rejected

tests/op_engine/test_backend_qualification.py defines a state tensor with shape (2 compartments, 3 groups, 3 trait cells). Reflecting transport couples trait cells along a named, non-default operator axis; scalar loss acts across every cell. The independent solution uses a SciPy matrix exponential, with separate per-batch total-decay assertions. Unequal initial cells expose axis ordering and broadcasting errors. Tests inspect result/history shapes, dtype, namespace, numerical values, and convergence diagnostics for nonlinear stages.

The IMEX fixture splits scalar loss from native dense transport. Its factories use the appropriate implicit-Euler/trapezoidal stage schemes, including both TR-BDF2 factories. Linearly implicit Jacobians act along the trait axis; SDIRK2 receives a full flattened-system Jacobian. Geometry and method choices remain static. This is portability evidence on a linear structured model, with nonlinear stage machinery, rather than a stiff accuracy survey.

AD, loops, sparse acceleration, and hardware

Capability Required evidence / boundary
Differentiation JAX JIT/AD and CPU Torch functional AD in float64 through structured Dormand–Prince, IMEX ARK3, ROS2, and SDIRK2; initial-state derivatives agree with an exact reference. Additional transformations remain separately qualified.
Native matrix builders Both precisions, namespaces and independent advection/diffusion stencils; dynamic coefficient derivatives through JAX JIT/AD and CPU Torch AD.
Live adaptive controller Eager Python branches extract error scalars. Preserving JAX arrays does not make the controller JIT compatible.
Frozen replay A NumPy-recorded mesh is portable to each namespace. JAX auto uses its scan adapter except SDIRK2, whose diagnostics retain eager replay; NumPy/Torch auto retain eager loops. Derivatives remain conditional on the frozen mesh.
SciPy sparse factorization Required NumPy sparse solve; JAX/Torch state with SciPy sparse operators is rejected explicitly. SciPy remains a core dependency, and its sparse acceleration is a separate namespace contract. Native dense operators use the state's linalg.solve.
Operator preprocessing Host NumPy/SciPy stage-builder and Kronecker utilities are separate from namespace-aware matrices and native caller-supplied dense factories. Host factories that call float(dt) do not accept traced step sizes; use native factories for compiled replay.
CUDA / CuPy / JAX GPU / Torch GPU No required GPU runner and no GPU release qualification established here. Required evidence is CPU only.

CuPy is an optional runtime extra. On a suitable CUDA host, install op-engine[cupy] and run the GPU contract explicitly:

uv sync --dev --extra cupy
uv run --no-sync pytest tests/op_engine/test_array_namespace.py -k cupy -rs

Inspect the result: a missing device or skip does not qualify GPU execution. Record CUDA/device/package versions, dtype, executed cases and their results before adding GPU claims to a release. This opt-in check currently covers a CuPy sparse-solve contract; it does not establish the complete method matrix.

Local and release commands

With a supported complete Python installation and uv/just:

just qualify-numpy
just qualify-jax
just qualify-torch
# All required CPU contracts:
just qualify

The Torch recipe installs CPU wheels explicitly. These targets refresh extras in the root environment; use uv run --no-sync or separate environments when retaining a namespace selection matters. The runner records Python/backend versions in its log. Baseline Python 3.11–3.14 CI, provider tests, strict typing, Ruff, strict docs and installed wheels remain required alongside qualification: just ci, just docs, and just build-all.

Before a release, record the CPU qualification results and any separately executed GPU evidence. A green baseline with optional skips is insufficient. This matrix addresses numerical parity and execution boundaries; scientific validity and accuracy outside these fixtures need independent references. See backend strategies for functional steps and loop contracts.