mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-07-28 06:02:51 +02:00
- Move Chi2.hpp from include/aare/ to src/ (private) - Pimpl on FitModel<Model>: MnUserParameters/MnStrategy behind opaque src/FitModelImpl.hpp, no Minuit2 includes in public headers - Move fit_pixel/fit_3d bodies to Fit.cpp with explicit instantiations for all 8 models; drop FCN template param from public API - CMake: aare::Minuit2 wrapped in $<BUILD_INTERFACE:...> (hidden from exported targets, same pattern as lmfit), MINUIT2_INSTALL OFF, Chi2.hpp removed from PUBLICHEADERS - Update python bindings and benchmark callsites accordingly --------- Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch> Co-authored-by: Alice <alice.mazzoleni@psi.ch>
140 lines
4.1 KiB
C++
140 lines
4.1 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
#pragma once
|
|
#include "aare/Models.hpp"
|
|
#include "aare/NDView.hpp"
|
|
#include <Minuit2/FCNGradientBase.h>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace aare {
|
|
|
|
namespace func {
|
|
|
|
/**
|
|
* @brief Generic chi-squared FCN with analytic gradient for a 1D model.
|
|
*
|
|
* @tparam Model A model struct that satisfies:
|
|
* - static constexpr std::size_t npar;
|
|
* - static double eval(double x, const std::vector<double>& par);
|
|
* - static void eval_and_grad(double x, const std::vector<double>& par,
|
|
* double& f, std::array<double, npar>& g);
|
|
* - static bool is_valid(const std::vector<double>& par);
|
|
*
|
|
* Gradient:
|
|
* d(chi2)/dp_k = -2 * sum_i w_i * (y_i - f_i) * df_i/dp_k
|
|
*
|
|
* where w_i = 1/sigma_i^2 (weighted) or 1 (unweighted).
|
|
*
|
|
* By providing analytic gradients we avoid 2*npar extra function evaluations
|
|
* per Minuit step that would otherwise be spent on finite differences.
|
|
*
|
|
* @throws std::invalid_argument if par.size() != Model::npar.
|
|
*
|
|
* Invalid model parameters do not throw; they return a large penalty
|
|
* (and a zero gradient fallback) so the minimizer can remain in control.
|
|
*/
|
|
template <class Model>
|
|
class Chi2Model1DGrad : public ROOT::Minuit2::FCNGradientBase {
|
|
public:
|
|
Chi2Model1DGrad(NDView<double, 1> x, NDView<double, 1> y)
|
|
: x_(x), y_(y), s_(), weighted_(false) {}
|
|
|
|
Chi2Model1DGrad(NDView<double, 1> x, NDView<double, 1> y,
|
|
NDView<double, 1> y_err)
|
|
: x_(x), y_(y), s_(y_err), weighted_(true) {}
|
|
|
|
~Chi2Model1DGrad() override = default;
|
|
|
|
double operator()(const std::vector<double> &par) const override {
|
|
if (par.size() != Model::npar) {
|
|
throw std::invalid_argument(
|
|
"Chi2Model1DGrad: wrong parameter vector size.");
|
|
}
|
|
|
|
if (!Model::is_valid(par))
|
|
return 1e20;
|
|
|
|
double chi2 = 0.0;
|
|
|
|
if (weighted_) {
|
|
for (ssize_t i = 0; i < x_.size(); ++i) {
|
|
const double si = s_[i];
|
|
if (si == 0.0)
|
|
continue;
|
|
|
|
const double f_i = Model::eval(x_[i], par);
|
|
const double r_i = y_[i] - f_i;
|
|
chi2 += (r_i * r_i) / (si * si);
|
|
}
|
|
} else {
|
|
for (ssize_t i = 0; i < x_.size(); ++i) {
|
|
const double f_i = Model::eval(x_[i], par);
|
|
const double r_i = y_[i] - f_i;
|
|
chi2 += r_i * r_i;
|
|
}
|
|
}
|
|
|
|
return chi2;
|
|
}
|
|
|
|
std::vector<double>
|
|
Gradient(const std::vector<double> &par) const override {
|
|
if (par.size() != Model::npar) {
|
|
throw std::invalid_argument(
|
|
"Chi2Model1DGrad: wrong parameter vector size.");
|
|
}
|
|
|
|
std::vector<double> grad(Model::npar, 0.0);
|
|
if (!Model::is_valid(par))
|
|
return grad;
|
|
|
|
std::array<double, Model::npar> df{};
|
|
double f_i = 0.0;
|
|
|
|
if (weighted_) {
|
|
for (ssize_t i = 0; i < x_.size(); ++i) {
|
|
const double si = s_[i];
|
|
if (si == 0.0)
|
|
continue;
|
|
|
|
Model::eval_and_grad(x_[i], par, f_i, df);
|
|
|
|
const double r_i = y_[i] - f_i;
|
|
const double c = -2.0 * r_i / (si * si);
|
|
|
|
for (std::size_t k = 0; k < Model::npar; ++k) {
|
|
grad[k] += c * df[k];
|
|
}
|
|
}
|
|
} else {
|
|
for (ssize_t i = 0; i < x_.size(); ++i) {
|
|
Model::eval_and_grad(x_[i], par, f_i, df);
|
|
|
|
const double r_i = y_[i] - f_i;
|
|
const double c = -2.0 * r_i;
|
|
|
|
for (std::size_t k = 0; k < Model::npar; ++k) {
|
|
grad[k] += c * df[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
return grad;
|
|
}
|
|
|
|
/** @brief Error definition: 1.0 for chi-squared (delta_chi2 = 1 ->
|
|
* 1-sigma). */
|
|
double Up() const override { return 1.0; }
|
|
|
|
private:
|
|
NDView<double, 1> x_;
|
|
NDView<double, 1> y_;
|
|
NDView<double, 1> s_;
|
|
bool weighted_;
|
|
};
|
|
|
|
} // namespace func
|
|
|
|
} // namespace aare
|