Skip to content

feat(solver): surface bi-exponential fit outcome (FitMode)#162

Merged
daharoni merged 2 commits into
mainfrom
feat/biexp-fit-mode-status
Jul 8, 2026
Merged

feat(solver): surface bi-exponential fit outcome (FitMode)#162
daharoni merged 2 commits into
mainfrom
feat/biexp-fit-mode-status

Conversation

@daharoni

@daharoni daharoni commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

The bi-exponential fit could silently:

  • fall back to slow-only (no fast component),
  • produce a degenerate result — no positive slow amplitude, i.e. the free kernel had no real transient (noise/flat), or
  • return the empty sentinel (0.02/0.4, residual = ∞) on empty input.

The caller had to infer which by inspecting beta_fast == 0 or an infinite residual. That's the second silent-fallback defensibility gap flagged in the config/numerical inventory (after the FFI NaN guard).

Change

Make the fit outcome explicit and carry it through the whole stack.

Rust (biexp_fit.rs) — add enum FitMode { TwoComponent, SlowOnly, Degenerate, Empty } and a fit_mode field on BiexpResult, classified once on the returned result:

  • residual not finite → Empty
  • beta <= 0Degenerate
  • else TwoComponent / SlowOnly by fast-component presence.

FFI — WASM indeca_fit_biexponential serializes fit_mode (variant name) in its result object; PyO3 py_indeca_fit_biexponential appends it as a string (return is now an 8-tuple); Python BiexpFitResult gains a fit_mode field.

App (CaDecon) — the worker parses fitMode into KernelResult; iteration-manager counts Degenerate/Empty subset fits per iteration (degenerateSubsets / totalSubsetFits on the snapshot); KernelDisplay shows a vermillion ⚠ N/K fits degenerate warning when the displayed kernel rests on untrustworthy subset fits.

Tests

  • Rust: Empty on empty input, Degenerate on a flat kernel, usable mode on a clean biexp.
  • Python: test_fit_mode.py covers all three via the public fit_biexponential wrapper.
  • Verified in-browser: a clean synthetic run shows the normal stats, no warning badge, 0 console errors.
  • All green: 131 Rust, 32 Python, 289 TS; cargo fmt/clippy clean on both feature sets; typecheck clean; WASM builds.

Series

Config/numerical-hygiene series. Done: FFI NaN guard (#161), fit-mode status (this). Remaining (lower priority): expose result-changing tuning constants through config; de-dup + name the fast-grid bounds (latent drift hazard) + resolve the two biexp_fit.rs TODOs.

🤖 Generated with Claude Code

daharoni and others added 2 commits July 8, 2026 14:32
The bi-exponential fit could silently fall back to slow-only, produce a
degenerate result (no positive slow amplitude — a noise/flat free kernel), or
return the empty sentinel, and the caller had to infer this from beta_fast==0
or an infinite residual. Make the outcome explicit and carry it through the
whole stack so a suspect kernel is reported, not guessed.

Rust (biexp_fit.rs): add enum FitMode { TwoComponent, SlowOnly, Degenerate,
Empty }; add a fit_mode field to BiexpResult, classified once on the returned
result (residual !finite -> Empty; beta<=0 -> Degenerate; else two/slow by
fast-component presence). Intermediate candidates carry a placeholder.

FFI: WASM indeca_fit_biexponential serializes fit_mode (variant name) in its
result object; PyO3 py_indeca_fit_biexponential appends it as a string to the
return tuple (now 8-tuple); Python BiexpFitResult gains a fit_mode field.

App (CaDecon): worker parses fitMode into KernelResult; iteration-manager
counts Degenerate/Empty subset fits per iteration (degenerateSubsets /
totalSubsetFits on the snapshot); KernelDisplay shows a vermillion
"N/K fits degenerate" warning when the displayed kernel rests on untrustworthy
subset fits.

Tests: Rust (Empty on empty input, Degenerate on flat kernel, usable on a clean
biexp); Python test_fit_mode covers all three via the public wrapper. Verified
in-browser that a clean run shows no warning and no console errors. All suites
green (131 Rust, 32 Python, 289 TS); fmt/clippy clean on both feature sets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fit_biexponential now returns an 8-tuple (added fit_mode); update the
pre-existing tuple-unpacking test that hard-coded 7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@daharoni daharoni merged commit 155922c into main Jul 8, 2026
6 checks passed
@daharoni daharoni deleted the feat/biexp-fit-mode-status branch July 8, 2026 21:41
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