Benchmark engine¶
Note
This page documents python/oracles/ — the benchmark and cross-verification
harness used to develop and audit spectrafit-core. It is not part of the
stable public API described in Python: core API;
it may change without notice between releases.
python/oracles/ compares spectrafit (the Rust kernel, the subject under
test) against a six-backend oracle roster
(python/oracles/backends/__init__.py::get_backends) — lmfit,
jax/optimistix, and three scipy.optimize.least_squares configurations
(scipy-ls-lm, scipy-ls-trf, scipy-ls-dogbox) — across a deterministic
case catalog. On the multimodal optfn category, the three scipy-ls-*
configurations run faster than spectrafit-core itself (see
Limitations for the measured ratios) — the one place
in the suite another backend wins on speed, so naming only two backends
here would hide it. It is test infrastructure —
scenarios, synthetic data, oracle math, and a verification & validation (V&V)
harness — imported by the benchmark CLI at run time and by pytest at
collection time.
Key pieces a contributor will touch¶
oracles.models.MODEL_REGISTRY— onePeakModelentry per model (numpy formula +spectrafit_type+ param names + jax support flag). This is the parity oracle: itsevaluatefunction must be numerically identical to the Rust kernel, since any formula difference shows up as a \(\lvert \Delta r^2 \rvert\) gate failure rather than a crash.oracles.cases—CaseSpec(a fully concrete, serializable benchmark case) andCaseFamily(a declarative generator that expands into many concreteCaseSpecs), organized byCATEGORY_REGISTRY(easy / complex / scaling / lineshapes / reality / edge / optfn / fixed / tied).oracles.engine— the run itself:run_suite(one fit per case per backend, feeding the suite-wide regression map) andrun_featured(a deep dive on one case — per-solver fits, real convergence traces, timing distributions, a Monte-Carlo noise sweep, a scaling sweep, and the covariance correlation matrix).engine.pyis a thin facade over four extracted leaf modules, kept sofrom oracles.engine import Ximport paths still work:_engine_base(shared primitives —_safe_fit,_finite,ProfileContext),_engine_multidim(the N-D and multi-spectrum global-fit showcases),_engine_nested(the nested-model adequacy V&V), and_engine_profile(per-backend profile/timing assembly).oracles.bench_contract— the frozenBenchReportPydantic contract: the single source of truth served at runtime byoracles.api's FastAPI app, from which the web dashboard's TypeScript types are generated (openapi-typescript) — never hand-kept in sync.-
oracles.cli— the Typer CLI (python -m oracles.cli run/gate) that runs the benchmark and enforces the regression gate (spectrafit must not be slower than the baseline solver, and accuracy parity must hold on the LM-family cases). The latest results are published at report.html.Note
The GitHub mirror rebuilds weekly, so it may lag the GitLab pipeline by up to seven days.
-
oracles.migrate— theMIGRATIONSregistry forBenchReportschema version upgrades (additive minor vs. breaking major).
Adding a case for an existing model¶
A single CaseSpec/CaseFamily entry in oracles/cases.py referencing an
existing model registry key. Adding a new model is a larger, multi-crate
change — see Adding a model for the
library-contributor subset (Rust kernel + ModelTypeStr wiring), and
Adding a new benchmark model below for the
full sequence including this benchmark registry step.
Adding a new benchmark model¶
This is the step that comes after Adding a model
has landed the Rust kernel and the ModelTypeStr/ModelType wiring. It makes
the new shape fittable and benchmarked here in python/oracles/.
- Register a
PeakModelinoracles/models.py. Write a numpyevaluate(x, **params) -> yformula function, then add aPeakModel(...)entry to the_BUILTIN_MODELStuple:key(registry/case key),spectrafit_type(must be a realModelTypemember name — validated at registration time byPeakModel._spectrafit_type_is_known_member),param_names,evaluate, and optionallyformula_latex/jax_supported/extra_defaults. Thisevaluateformula is the parity oracle: it must be numerically identical to the Rust kernel, ortests/unit/oracles/test_wheel_eval.py'swheel_parity_pairs()will catch the mismatch as a \(\lvert \Delta r^2 \rvert\) gate failure rather than a crash. - Add a typed component spec in
oracles/cases.py. If the new shape is a peak that case families should generate, add a_Componentsubclass (e.g. theGaussianSpecclass) with aLiteral["<key>"]modeldiscriminator and its own validated fields, then add acase "<key>":arm to thematch model:dispatch inside_peak()soCaseFamilygenerators can place it. - Exercise it in a category. Add the key to the model list a
CaseFamilyiterates — e.g. the_EASY_MODELSlist for theeasycategory — or register a newCategoryDefentry in theCATEGORY_REGISTRYdict if it needs its own category. Categorycountis diversity-driven and asserted against the generator grid in tests, so update both together. - jax support is opt-in per-shape, not automatic. Setting
jax_supported=Truein step 1 also requires acase "<key>":kernel branch inoracles/backends/_jax.py::_kernel(around line 186) — the per-component layout is derived from the registry, but the jax math for each shape is not. - Regenerate the contract if needed. Adding a model usually doesn't
change
bench_contract.py/contract.py; if it does, runuv run poe contract_regen. - Verify. Run
uv run pytest tests/unit/oracles/test_wheel_eval.py(Rust/numpy parity for the new model) anduv run poe benchmark_gate(the regression gate must still pass with the new model in the mix).
Worked examples: any existing _BUILTIN_MODELS entry in oracles/models.py
plus its _peak() arm in cases.py (e.g. gaussian, fano, pearson7)
shows the full pattern end-to-end.
Regenerating the contract¶
After any change to oracles/bench_contract.py (or oracles/contract.py,
the small shared-leaf module holding SolverMeta), regenerate all three
checked-in schema mirrors with uv run poe contract_regen — it drives one
live API instance and writes the Python golden, web/src/openapi.gen.ts, and
web/openapi.snapshot.json in one pass.
Next steps¶
- See these results rendered — Web dashboard covers
the Vite + React app that fetches
/api/reportand renders theBenchReportcontract this registry and gate produce. - Look up the full API surface — Python: benchmark harness
documents every public class and function in
oracles.casesandoracles.modelsreferenced above.