mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-11-01 05: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
@@ -6,6 +6,7 @@
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -79,3 +80,172 @@ TEST_CASE("Minuit2 fits a Gaussian data cube in parallel", "[fit]") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fumili fits weighted and unweighted Gaussian data", "[fit]") {
|
||||
aare::NDArray<double, 1> x({n_points});
|
||||
aare::NDArray<double, 1> y({n_points});
|
||||
aare::NDArray<double, 1> y_err({n_points}, 1.0);
|
||||
fill_gaussian(x, y, 120.0, 0.8, 1.3);
|
||||
|
||||
aare::FitModel<aare::model::Gaussian> unweighted_model;
|
||||
REQUIRE(unweighted_model.minimizer() == aare::Minimizer::Migrad);
|
||||
unweighted_model.SetMinimizer(aare::Minimizer::Fumili);
|
||||
REQUIRE(unweighted_model.minimizer() == aare::Minimizer::Fumili);
|
||||
|
||||
const auto unweighted =
|
||||
aare::fit_pixel(unweighted_model, x.view(), y.view());
|
||||
|
||||
REQUIRE(unweighted.size() == 4);
|
||||
CHECK(unweighted(0) == Catch::Approx(120.0).epsilon(1e-5));
|
||||
CHECK(unweighted(1) == Catch::Approx(0.8).epsilon(1e-5));
|
||||
CHECK(unweighted(2) == Catch::Approx(1.3).epsilon(1e-5));
|
||||
CHECK(unweighted(3) == Catch::Approx(0.0).margin(1e-4));
|
||||
|
||||
const aare::FitModel<aare::model::Gaussian> weighted_model(
|
||||
0, 100, 0.5, true, aare::Minimizer::Fumili);
|
||||
const auto weighted =
|
||||
aare::fit_pixel(weighted_model, x.view(), y.view(), y_err.view());
|
||||
|
||||
REQUIRE(weighted.size() == 7);
|
||||
CHECK(weighted(0) == Catch::Approx(120.0).epsilon(1e-5));
|
||||
CHECK(weighted(1) == Catch::Approx(0.8).epsilon(1e-5));
|
||||
CHECK(weighted(2) == Catch::Approx(1.3).epsilon(1e-5));
|
||||
for (ssize_t k = 3; k < 6; ++k)
|
||||
CHECK(weighted(k) > 0.0);
|
||||
CHECK(weighted(6) == Catch::Approx(0.0).margin(1e-4));
|
||||
}
|
||||
|
||||
TEST_CASE("Fumili parameter errors agree with Migrad and Hesse", "[fit]") {
|
||||
aare::NDArray<double, 1> x({n_points});
|
||||
aare::NDArray<double, 1> y({n_points});
|
||||
aare::NDArray<double, 1> y_err({n_points}, 2.0);
|
||||
fill_gaussian(x, y, 120.0, 0.8, 1.3);
|
||||
|
||||
const aare::FitModel<aare::model::Gaussian> migrad(0, 100, 0.5, true,
|
||||
aare::Minimizer::Migrad);
|
||||
const aare::FitModel<aare::model::Gaussian> fumili(0, 100, 0.5, true,
|
||||
aare::Minimizer::Fumili);
|
||||
|
||||
const auto with_migrad =
|
||||
aare::fit_pixel(migrad, x.view(), y.view(), y_err.view());
|
||||
const auto with_fumili =
|
||||
aare::fit_pixel(fumili, x.view(), y.view(), y_err.view());
|
||||
|
||||
REQUIRE(with_migrad.size() == 7);
|
||||
REQUIRE(with_fumili.size() == 7);
|
||||
for (ssize_t k = 0; k < 3; ++k)
|
||||
CHECK(with_fumili(k) == Catch::Approx(with_migrad(k)).epsilon(1e-5));
|
||||
for (ssize_t k = 3; k < 6; ++k)
|
||||
CHECK(with_fumili(k) == Catch::Approx(with_migrad(k)).epsilon(1e-2));
|
||||
}
|
||||
|
||||
TEST_CASE("Fumili honours fixed parameters and limits", "[fit]") {
|
||||
aare::NDArray<double, 1> x({n_points});
|
||||
aare::NDArray<double, 1> y({n_points});
|
||||
fill_gaussian(x, y, 120.0, 0.8, 1.3);
|
||||
|
||||
aare::FitModel<aare::model::Gaussian> model(0, 100, 0.5, false,
|
||||
aare::Minimizer::Fumili);
|
||||
model.FixParameter("mu", 0.8);
|
||||
model.SetParLimits("sigma", 0.5, 5.0);
|
||||
|
||||
const auto result = aare::fit_pixel(model, x.view(), y.view());
|
||||
|
||||
REQUIRE(result.size() == 4);
|
||||
CHECK(result(0) == Catch::Approx(120.0).epsilon(1e-5));
|
||||
CHECK(result(1) == 0.8);
|
||||
CHECK(result(2) == Catch::Approx(1.3).epsilon(1e-5));
|
||||
CHECK(result(3) == Catch::Approx(0.0).margin(1e-4));
|
||||
}
|
||||
|
||||
TEST_CASE("Fumili fits a rising S-curve", "[fit]") {
|
||||
// f(x) = (p0 + p1*x) + 0.5*(1 + erf((x - mu)/(sqrt(2)*sigma))) * (A + C*(x
|
||||
// - mu))
|
||||
const std::vector<double> truth = {5.0, 0.1, 30.0, 4.0, 200.0, 0.5};
|
||||
aare::NDArray<double, 1> x({n_points});
|
||||
aare::NDArray<double, 1> y({n_points});
|
||||
for (ssize_t i = 0; i < n_points; ++i) {
|
||||
x(i) = static_cast<double>(i);
|
||||
y(i) = aare::model::RisingScurve::eval(x(i), truth);
|
||||
}
|
||||
|
||||
const aare::FitModel<aare::model::RisingScurve> model(
|
||||
0, 200, 0.5, false, aare::Minimizer::Fumili);
|
||||
const auto result = aare::fit_pixel(model, x.view(), y.view());
|
||||
|
||||
REQUIRE(result.size() == 7);
|
||||
for (std::size_t k = 0; k < truth.size(); ++k)
|
||||
CHECK(result(k) == Catch::Approx(truth[k]).epsilon(1e-3));
|
||||
}
|
||||
|
||||
TEST_CASE("Fumili fits a Gaussian data cube in parallel", "[fit]") {
|
||||
aare::NDArray<double, 1> x({n_points});
|
||||
aare::NDArray<double, 1> values({n_points});
|
||||
fill_gaussian(x, values, 80.0, -0.6, 0.9);
|
||||
|
||||
aare::NDArray<double, 3> y({2, 2, n_points});
|
||||
aare::NDArray<double, 3> y_err({2, 2, n_points}, 1.0);
|
||||
for (ssize_t row = 0; row < 2; ++row) {
|
||||
for (ssize_t col = 0; col < 2; ++col) {
|
||||
for (ssize_t i = 0; i < n_points; ++i)
|
||||
y(row, col, i) = values(i);
|
||||
}
|
||||
}
|
||||
|
||||
aare::NDArray<double, 3> par({2, 2, 3});
|
||||
aare::NDArray<double, 3> err({2, 2, 3});
|
||||
aare::NDArray<double, 2> chi2({2, 2});
|
||||
const aare::FitModel<aare::model::Gaussian> model(0, 100, 0.5, true,
|
||||
aare::Minimizer::Fumili);
|
||||
|
||||
aare::fit_3d(model, x.view(), y.view(), y_err.view(), par.view(),
|
||||
err.view(), chi2.view(), 2);
|
||||
|
||||
// Fumili stops once the estimated remaining chi2 decrease is below
|
||||
// tolerance * 1e-4, which leaves deviations of order 1e-5 relative here.
|
||||
for (ssize_t row = 0; row < 2; ++row) {
|
||||
for (ssize_t col = 0; col < 2; ++col) {
|
||||
CHECK(par(row, col, 0) == Catch::Approx(80.0).epsilon(1e-4));
|
||||
CHECK(par(row, col, 1) == Catch::Approx(-0.6).epsilon(1e-4));
|
||||
CHECK(par(row, col, 2) == Catch::Approx(0.9).epsilon(1e-4));
|
||||
for (ssize_t k = 0; k < 3; ++k)
|
||||
CHECK(err(row, col, k) > 0.0);
|
||||
CHECK(chi2(row, col) == Catch::Approx(0.0).margin(1e-3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Copying a FitModel keeps the minimizer", "[fit]") {
|
||||
const aare::FitModel<aare::model::Pol1> original(0, 100, 0.5, false,
|
||||
aare::Minimizer::Fumili);
|
||||
const aare::FitModel<aare::model::Pol1> copied(original);
|
||||
aare::FitModel<aare::model::Pol1> assigned;
|
||||
assigned = original;
|
||||
|
||||
CHECK(copied.minimizer() == aare::Minimizer::Fumili);
|
||||
CHECK(assigned.minimizer() == aare::Minimizer::Fumili);
|
||||
}
|
||||
|
||||
TEST_CASE("Fitting with every parameter fixed returns the fixed values",
|
||||
"[fit]") {
|
||||
aare::NDArray<double, 1> x({n_points});
|
||||
aare::NDArray<double, 1> y({n_points});
|
||||
fill_gaussian(x, y, 120.0, 0.8, 1.3);
|
||||
|
||||
for (const auto minimizer :
|
||||
{aare::Minimizer::Migrad, aare::Minimizer::Fumili}) {
|
||||
aare::FitModel<aare::model::Gaussian> model(0, 100, 0.5, true,
|
||||
minimizer);
|
||||
model.FixParameter("A", 120.0);
|
||||
model.FixParameter("mu", 0.8);
|
||||
model.FixParameter("sigma", 1.3);
|
||||
|
||||
const auto result = aare::fit_pixel(model, x.view(), y.view());
|
||||
|
||||
REQUIRE(result.size() == 7);
|
||||
CHECK(result(0) == 120.0);
|
||||
CHECK(result(1) == 0.8);
|
||||
CHECK(result(2) == 1.3);
|
||||
CHECK(result(6) == Catch::Approx(0.0).margin(1e-12));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user