Commit Graph
6 Commits
Author SHA1 Message Date
Khalil FerjaouiandClaude Opus 5.5 28c8defc5a Fix convergence and precision bugs in the LM and VarPro minimizers (#388)
Build on RHEL9 / build (push) Failing after 5s
Run tests using data on local RHEL8 / build (push) Failing after 2m23s
Build on RHEL8 / build (push) Successful in 3m36s
## Fix convergence and precision bugs in the LM and VarPro minimizers

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:37:06 +02:00
Erik FröjdhandClaude Fable 5.1 a211a3e8e2 Add Minimizer::VarPro, a variable projection minimizer
Every bundled model now declares the parameters it is linear in
(linear_par, basis_and_grad) and, except the polynomials, a vectorisable
bulk basis pass (basis_columns) built on the new model::fast_exp. The
Levenberg-Marquardt iteration is factored into a shared DampedGaussNewton
driver with unchanged results; VarPro solves the linear parameters exactly
at every trial point and iterates over the nonlinear ones, rejecting
degenerate trial points and polishing the converged point with one
undamped step. Limits on linear parameters, pixels that do not converge
and models without the separable structure fall back to
LevenbergMarquardt. Parameter errors are the same Gauss-Newton estimates.
The [fit] tests run VarPro through every configuration and check the
separable structure of each model against its derivatives.

Measured on an M1 Max, single thread: FallingScurve population 5.5 us per
fit against 11.7 us with LevenbergMarquardt, and 2 wrong minima in 4096
pixels against 30; Gaussian 3.3 vs 4.3 us; GaussianChargeSharingKb 33 vs
49 us; Pol2 0.34 vs 1.52 us. Migrad stays the default.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 21:36:53 +02:00
Erik Frojdh 5beabf1888 optimizations
Build on RHEL9 / build (push) Successful in 2m52s
Build on RHEL8 / build (push) Successful in 3m31s
Run tests using data on local RHEL8 / build (push) Successful in 4m28s
2026-09-25 18:05:43 +02:00
Erik FröjdhandClaude Fable 5.1 86a773f6db Add a built-in Levenberg-Marquardt minimizer selectable like Migrad and Fumili
Bring the dependency-free Levenberg-Marquardt solver from dev/custom-fit in
as Minimizer::LevenbergMarquardt next to Minuit2's Migrad (default) and
Fumili, selected through the FitModel constructor, SetMinimizer() or the
Python `minimizer` argument and property.

- FitModel becomes plain data (limits, fixed flags, start values, minimizer
  settings) with index and limit validation. Minuit2 user parameters are
  built per pixel in src/FitMinuit2.cpp, the only translation unit that
  includes Minuit2 headers.
- fit_3d fits through a per-thread PixelFitter so the LM path allocates
  nothing per pixel, and no minimizer allocates a per-pixel result array.
- Fixed parameters and parameters ending on a limit report an error of 0
  with every minimizer; user start values are applied before the validity
  check and clamped into the limits.
- Minuit2's Fumili cannot converge once a two-sided limit is active, so
  such pixels are refitted with Migrad. The Minuit2 minimizers keep a wide
  two-sided range for one-sided limits because Minuit2's one-sided
  transform converges markedly slower.
- The [fit] tests run every scenario through all three minimizers and
  check that they agree; the benchmark gains LM and data-cube cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 10:58:16 +02:00
Erik Fröjdh b74c0afb97 initial implementation 2026-09-24 17:20:44 +02:00
Erik Fröjdh 4f6b69fba6 Removing deprecated use of lmfit (#344)
Build on RHEL9 / build (push) Successful in 2m44s
Build on RHEL8 / build (push) Successful in 3m9s
Run tests using data on local RHEL8 / build (push) Successful in 3m58s
Build on local RHEL8 / build (push) Successful in 2m44s
Removed the lmfit based fitting and the old API for calling functions

closes #296
2026-08-14 10:31:07 +02:00