Skip to content

Fit saving - #30

Merged
InfinityMonkeyAtWork merged 20 commits into
mainfrom
fit-saving
May 28, 2026
Merged

Fit saving#30
InfinityMonkeyAtWork merged 20 commits into
mainfrom
fit-saving

Conversation

@InfinityMonkeyAtWork

Copy link
Copy Markdown
Owner

No description provided.

Dropped Project.path_run (trspecfit.py:171-173) and removed it from the class docstring.
Dropped File.path_da (trspecfit.py:1125) and removed it from the class docstring.
Rewrote create_model_path (trspecfit.py:1652) — now takes a keyword-only fit_type and produces {path_results}/{File.name}/{fit_type}/{model_name}/. Dropped the mod lookup, the yaml_f_name segment, and the model_unknown fallback (no longer reachable).
Updated 7 call sites with the appropriate fit_type=...: "baseline" (×2), "spectrum", "sbs", "2d" (×3).
utils/fit_io.py (new): SavedFitSlot dataclass (frozen, single source of truth for observed/fit/metrics/observed_sha256/selection_json/history_key); identity helpers (compute_file_fingerprint, compute_history_key, compute_observed_sha256, build_selection_json); per-fit-type extractors (_slot_from_baseline, _slot_from_spectrum, _slot_from_sbs, _slot_from_2d) — all take copied snapshot args, never live Model references.

fit_results.py (new): FitResults class with __iter__, __len__, files(), models(), find(), get(). Immutable after construction; tuple-backed slot list.

trspecfit.py:
Project._fit_history: list[SavedFitSlot] initialized empty in __init__.
Project.results property returns a fresh FitResults snapshot per access.
File.fingerprint() (cached multi-sha) at trspecfit.py:1683.
Four _append_<fit_type>_slot helpers (each guarded against mocked/placeholder result[1]) wired into fit_baseline, fit_spectrum, fit_slice_by_slice (before the seed-template restoration), and fit_2d.
fit_2d exports the full 2D fit result (similar to SbS)
both get added time.csv and energy.csv to be self-contained

base and spectrum fits export fit results (sum plus individual components as one column each in a 2D spreadsheet) [includes energy as first column to be self-contained]

all files saved use project num_fmt and delim arguments in saved csv files
renaming: "num_fmt" was previously named just "fmt"
- docs/design/fit_archive_schema.md: v1 wire format. Positional zero-padded
  group keys, identity in attrs, unified DataFrame encoding (homogeneous
  2D float64 + columns attr; heterogeneous structured c000000-fields +
  columns/dtypes attrs), separate write-side strict identity
  (fingerprint+name+original_path) vs read-side fingerprint matching,
  history_key persisted as non-authoritative, source-dtype preservation
  for raw arrays + observed/fit, project-scoped joint slot deferred to v2.
- utils/fit_io.py: writer (write_archive) + dataclasses (SavedFile,
  SavedProject) + identity helpers (compute_archive_slot_key,
  _find_file_by_fingerprint, _find_slot_by_archive_key) + DataFrame
  encoder (_encode_dataframe). Append-mode default with slot-scoped
  overwrite, pre-checked across all files before any mutation. Foreign
  HDF5 / schema-mismatch rejected; empty stub initialized. Pyright clean.
- PLAN.md: tick completed in-memory + writer items, clarify project-level
  2D wording (per-file 2d slots are in v1; only the joint construct is
  deferred).
Project.save_fits / Project.load_fits / File.save_fit. Default path
./fit_results/<project_name>.fit.h5.

Slot identity now includes file_name. compute_history_key folds
file_name into the hash, save_fits groups by (fingerprint, name)
instead of fingerprint alone, and the live-file lookup
(_find_file_for_slot, renamed) requires both name and fingerprint to
match. Fixes a class of silent bugs where two byte-identical
Project.files with different names would (a) collapse to one slot in
the snapshot, (b) be saved under whichever name appeared first, and
(c) leak across each other's file= filters. Project enforces unique
File.name in-session, so name is enough to break the fingerprint tie;
the archive's full identity tuple stays (fingerprint, name,
original_path).
Filters _fit_history with the same pipeline as save_fits (extracted
into Project._build_saved_project_from_history), then writes a
slot-driven CSV/PNG tree via fit_io.write_csv_export. Output layout
is <root>/<file_name>/<model_name>__<fit_type>[__<hash>]/, with
per-slot params/metrics/conf_ci/mcmc plus fit_1d.csv (1D) or
fit_2d.csv + observed_2d.csv + axis sidecars + 2D residual map (sbs/2d)
plus fit_pars.csv + per-parameter PNGs (sbs).

Disambiguation: slot dirs gain a history_key[:8] suffix when one
(file, model, fit_type) triple has multiple selections; per-file dirs
get a positional ordinal (__000, __001, ...) when two SavedFile
records share a name, so even byte-identical records resolve to
distinct directories. Per-file PlotConfig is preserved by passing a
dict[file_name, PlotConfig] keyed by the live File.plot_config.
Overwrite is per-slot directory, pre-checked across all slots.
- FitResults.compare_models: filter on (file, models, fit_type), defend against
  silent grid drift via observed_sha256 grouped by (fingerprint, file_name,
  fit_type), aggregate SbS per-slice metrics (median/mean/sum/long).
- FitResults.plot_residuals: 1D side-by-side panels, 2D residual heatmaps with
  one global diverging scale shared across subplots.
- File.export_fit + File.compare_models: 1-line delegates to Project /
  FitResults; existing save_fit kept.
- File.save_sbs_fit / save_2d_fit: deprecated wrappers (DeprecationWarning,
  stacklevel=2) over renamed _save_*_fit_legacy impls. Internal auto-export
  call sites updated. Behavior preserved byte-for-byte.
- TODO.md: name the v1.0.0 removal targets under "Remove legacy/backwards-compat".
- tests/test_fit_archive_roundtrip.py: 10 save→load field-equality
  tests across F1/F3/F6/F8 × applicable fit types.
- tests/test_fit_history.py: history accumulation + snapshot-collapse
  and selection-identity (base_t_ind / e_lim / t_lim) tests.
- tests/test_export_fits_parity.py: parity vs legacy
  _save_sbs_fit_legacy / _save_2d_fit_legacy outputs.
- docs/design/repo_architecture.md: fit_results.py + utils/fit_io.py
  module entries, new save/export/load architecture section.
- Remove dead File.load_fit TODO stub.
Add `Project.auto_export` (default True, settable via project.yaml or
post-init mutation) to suppress the automatic CSV+PNG writes triggered
on every fit_baseline / fit_spectrum / fit_slice_by_slice / fit_2d /
Project.fit_2d. Driven by parameter-sweep, ML training-data, and the
long-term real-time-fitting workflows where the ~6 CSVs and several
PNGs per fit dominate per-fit cost on small data.
anything above 2s marked as slow
…ma()

Slots now carry their own σ snapshot (noise_type, sigma_source, sigma_type,
sigma_data, sigma_eff) and a 7-key metrics dict (chi2_raw, chi2_red_raw,
chi2, chi2_red, r2, aic, bic). Calibrated chi2/chi2_red are populated only
when σ was set on the File at fit time; raw fields are always populated.
This bumps the HDF5 schema_version to "2" — archives written by the older
schema can no longer be read (clean break before the branch ships).

Sigma now enters via File.set_sigma() (forward-looking; existing slots
keep their snapshot, immutable-slot invariant from PLAN.md preserved).
Project YAML grew flat noise_type / sigma_source / sigma_type / sigma_data
defaults with validation, and File inherits them at construction.
compare_models() lost its sigma= kwarg: the default column set is dynamic
(4 cols without σ, 6 with), explicit calibrated-metric requests raise a
helpful KeyError when no matched slot has σ, and SbS sum-mode aggregates
both chi2_red flavors as Σ numerator / Σ DoF so the "≈ 1" reading stays
canonical.

Other: notebook 10_model_comparison/ uses file.set_sigma() once and
exercises every column meaning; targeted tests at tests/test_file.py::
TestSetSigma, tests/test_fit_history.py::TestFitResultsCompareModelsSigma
Columns, and slot-side noise round-trip in test_fit_archive_roundtrip.py;
PLAN.md updated to lock the schema decisions, refresh the slot-extractor
signatures, and check off the notebook task; .gitignore catches *.fit.h5
artifacts.
@InfinityMonkeyAtWork
InfinityMonkeyAtWork merged commit a72ef8e into main May 28, 2026
3 checks passed
@InfinityMonkeyAtWork
InfinityMonkeyAtWork deleted the fit-saving branch May 28, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant