mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-10-28 12:41:28 +01:00
initial implementation
This commit is contained in:
1 parent
4677e1003e
commit
b74c0afb97
11 files changed
+634
-154
No files matched your search
@@ -40,6 +40,7 @@ from .Cluster import Cluster
|
||||
|
||||
from ._aare import Gaussian, RisingScurve, FallingScurve, Pol1, Pol2, GaussianErfcPlateau, GaussianChargeSharing, GaussianChargeSharingKb
|
||||
from ._aare import fit
|
||||
from ._aare import Minimizer
|
||||
from ._aare import Interpolator
|
||||
from ._aare import calculate_eta2, calculate_eta3, calculate_cross_eta3, calculate_full_eta2
|
||||
from ._aare import reduce_to_2x2, reduce_to_3x3
|
||||
|
||||
+26
-2
@@ -22,9 +22,11 @@ fit_dispatch(const aare::FitModel<Model> &model,
|
||||
template <typename Model> void bind_fit_model(py::module &m, const char *name) {
|
||||
using FM = aare::FitModel<Model>;
|
||||
py::class_<FM>(m, name)
|
||||
.def(py::init<unsigned int, unsigned int, double, bool>(),
|
||||
.def(py::init<unsigned int, unsigned int, double, bool,
|
||||
aare::Minimizer>(),
|
||||
py::arg("strategy") = 0, py::arg("max_calls") = 100,
|
||||
py::arg("tolerance") = 0.5, py::arg("compute_errors") = false)
|
||||
py::arg("tolerance") = 0.5, py::arg("compute_errors") = false,
|
||||
py::arg("minimizer") = aare::Minimizer::Migrad)
|
||||
.def("SetParLimits",
|
||||
py::overload_cast<unsigned int, double, double>(&FM::SetParLimits),
|
||||
py::arg("idx"), py::arg("lo"), py::arg("hi"))
|
||||
@@ -59,6 +61,7 @@ template <typename Model> void bind_fit_model(py::module &m, const char *name) {
|
||||
.def_property("tolerance", &FM::tolerance, &FM::SetTolerance)
|
||||
.def_property("compute_errors", &FM::compute_errors,
|
||||
&FM::SetComputeErrors)
|
||||
.def_property("minimizer", &FM::minimizer, &FM::SetMinimizer)
|
||||
.def(
|
||||
"__call__",
|
||||
[](const FM & /*self*/,
|
||||
@@ -88,6 +91,9 @@ template <typename Model> void bind_fit_model(py::module &m, const char *name) {
|
||||
R"doc(
|
||||
Fit this model to 1D or 3D data using Minuit2.
|
||||
|
||||
The minimizer is selected by the ``minimizer`` property
|
||||
(``Minimizer.Migrad`` by default, or ``Minimizer.Fumili``).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : array_like, shape (n_scan,)
|
||||
@@ -227,6 +233,22 @@ fit_dispatch(const aare::FitModel<Model> &model,
|
||||
}
|
||||
|
||||
void define_fit_bindings(py::module &m) {
|
||||
// The enum must exist before it is used as a constructor default below.
|
||||
py::enum_<aare::Minimizer>(m, "Minimizer", R"doc(
|
||||
Minuit2 minimizer used by the fit models.
|
||||
|
||||
``Migrad`` is the default variable-metric minimizer. With
|
||||
``compute_errors`` its parameter errors come from Hesse.
|
||||
|
||||
``Fumili`` is a Gauss-Newton style minimizer that takes the gradient
|
||||
and a linearised Hessian from the analytic derivatives of the model.
|
||||
It usually needs far fewer function evaluations than Migrad. With
|
||||
``compute_errors`` its parameter errors come from the covariance of
|
||||
the linearised Hessian, without an extra Hesse step.
|
||||
)doc")
|
||||
.value("Migrad", aare::Minimizer::Migrad)
|
||||
.value("Fumili", aare::Minimizer::Fumili);
|
||||
|
||||
// ── Bind model classes ──────────────────────────────────────────
|
||||
bind_fit_model<aare::model::Gaussian>(m, "Gaussian");
|
||||
bind_fit_model<aare::model::GaussianErfcPlateau>(m, "GaussianErfcPlateau");
|
||||
@@ -325,6 +347,8 @@ void define_fit_bindings(py::module &m) {
|
||||
model : Pol1, Pol2, Gaussian, RisingScurve, or FallingScurve
|
||||
Configured model object. User-set limits, fixed parameters,
|
||||
and start values take precedence over automatic estimates.
|
||||
The model's ``minimizer`` property selects Migrad (default)
|
||||
or Fumili.
|
||||
x : array_like, shape (n_scan,)
|
||||
Scan points (e.g. energy or threshold values).
|
||||
y : array_like, shape (n_scan,) or (rows, cols, n_scan)
|
||||
|
||||
@@ -13,3 +13,41 @@ def test_gaussian_model_evaluates_and_fits_data():
|
||||
result = model.fit(x, y)
|
||||
|
||||
np.testing.assert_allclose(result["par"], expected, atol=2e-3)
|
||||
|
||||
|
||||
def test_minimizer_can_be_selected_and_fumili_fits_data():
|
||||
x = np.linspace(-5.0, 5.0, 51)
|
||||
expected = np.array([20.0, 0.5, 1.2])
|
||||
|
||||
assert aare.Gaussian().minimizer == aare.Minimizer.Migrad
|
||||
|
||||
model = aare.Gaussian(minimizer=aare.Minimizer.Fumili)
|
||||
assert model.minimizer == aare.Minimizer.Fumili
|
||||
|
||||
y = model(x, expected)
|
||||
result = model.fit(x, y)
|
||||
np.testing.assert_allclose(result["par"], expected, atol=2e-3)
|
||||
|
||||
model.minimizer = aare.Minimizer.Migrad
|
||||
assert model.minimizer == aare.Minimizer.Migrad
|
||||
|
||||
|
||||
def test_fumili_and_migrad_agree_on_3d_data_with_errors():
|
||||
x = np.linspace(-5.0, 5.0, 51)
|
||||
expected = np.array([20.0, 0.5, 1.2])
|
||||
single = aare.Gaussian()(x, expected)
|
||||
y = np.tile(single, (2, 3, 1))
|
||||
y_err = np.ones_like(y)
|
||||
|
||||
migrad = aare.Gaussian(compute_errors=True)
|
||||
fumili = aare.Gaussian(compute_errors=True, minimizer=aare.Minimizer.Fumili)
|
||||
|
||||
with_migrad = migrad.fit(x, y, y_err, n_threads=2)
|
||||
with_fumili = fumili.fit(x, y, y_err, n_threads=2)
|
||||
|
||||
assert with_fumili["par"].shape == (2, 3, 3)
|
||||
assert with_fumili["par_err"].shape == (2, 3, 3)
|
||||
for result in (with_migrad, with_fumili):
|
||||
np.testing.assert_allclose(result["par"], np.broadcast_to(expected, (2, 3, 3)), atol=2e-3)
|
||||
np.testing.assert_allclose(result["chi2"], 0.0, atol=1e-3)
|
||||
np.testing.assert_allclose(with_fumili["par_err"], with_migrad["par_err"], rtol=2e-2)
|
||||
Reference in new issue
Block a user