diff --git a/CHANGELOG b/CHANGELOG index 4e3704a6af90c0243fe62eba7fc33de83b6778ce..a39c2dc3cf3ddbb02711ae82d7e893a91faf34d4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ > API changes: 1) Python: major changes in SpecularSimulation interface. Please consult web-documentation for details. + 2) GUI: project compatibility with previous versions is broken. BornAgain-1.15.0, released 2019.02.25 > API changes: diff --git a/Core/Fitting/FitObjective.cpp b/Core/Fitting/FitObjective.cpp index 29fb54ade3fa0c036d51e7d2b2ee3885d32a44d3..0dc1296d31fc10eb61f0d21cee3699c77a63e01c 100644 --- a/Core/Fitting/FitObjective.cpp +++ b/Core/Fitting/FitObjective.cpp @@ -18,6 +18,7 @@ #include "FitStatus.h" #include "MinimizerResult.h" #include "ObjectiveMetric.h" +#include "ObjectiveMetricUtils.h" #include "Parameters.h" #include "PyFittingCallbacks.h" #include "Simulation.h" @@ -200,14 +201,30 @@ void FitObjective::run_simulations(const Fit::Parameters& params) void FitObjective::setChiSquaredModule(const IChiSquaredModule& module) { + std::cout << "Warning in FitObjective::setChiSquaredModule: setChiSquaredModule is deprecated " + "and will be removed in future versions. Please use " + "FitObjective::setObjectiveMetric instead." + << std::endl; + std::unique_ptr<IChiSquaredModule> chi_module(module.clone()); m_metric_module = std::make_unique<ChiModuleWrapper>(std::move(chi_module)); } +void FitObjective::setObjectiveMetric(std::unique_ptr<ObjectiveMetric> metric) +{ + m_metric_module = std::make_unique<ObjectiveMetricWrapper>(std::move(metric)); +} + +void FitObjective::setObjectiveMetric(const std::string& metric) +{ + m_metric_module = std::make_unique<ObjectiveMetricWrapper>( + ObjectiveMetricUtils::createMetric(metric, ObjectiveMetricUtils::defaultNormName())); +} + void FitObjective::setObjectiveMetric(const std::string& metric, const std::string& norm) { m_metric_module = - std::make_unique<ObjectiveMetricWrapper>(ObjectiveMetric::createMetric(metric, norm)); + std::make_unique<ObjectiveMetricWrapper>(ObjectiveMetricUtils::createMetric(metric, norm)); } std::vector<double> FitObjective::composeArray(DataPairAccessor getter) const diff --git a/Core/Fitting/FitObjective.h b/Core/Fitting/FitObjective.h index e536b23991c0b87ae80dc8895adab190f0191144..717336b7345ec5686ee76aedc8e140b90ac447cf 100644 --- a/Core/Fitting/FitObjective.h +++ b/Core/Fitting/FitObjective.h @@ -25,6 +25,7 @@ class FitStatus; class IChiSquaredModule; class IMetricWrapper; +class ObjectiveMetric; class PyBuilderCallback; class PyObserverCallback; @@ -130,10 +131,15 @@ public: void setChiSquaredModule(const IChiSquaredModule& module); +#ifndef SWIG + void setObjectiveMetric(std::unique_ptr<ObjectiveMetric> metric); +#endif //SWIG + + void setObjectiveMetric(const std::string& metric); //! Sets objective metric to the FitObjective. //! @param metric: metric name - //! @param norm: metric norm name (defaults to "l2") - void setObjectiveMetric(const std::string& metric, const std::string& norm = "l2"); + //! @param norm: metric norm name (defaults to L2-norm) + void setObjectiveMetric(const std::string& metric, const std::string& norm); private: typedef std::vector<double> (SimDataPair::*DataPairAccessor)() const; diff --git a/Core/Fitting/ObjectiveMetric.cpp b/Core/Fitting/ObjectiveMetric.cpp index 60c094f97a1d6f3b4642d410625d0cd520133575..c17cb2fb6425d0202542b4773d4d505d995d3aef 100644 --- a/Core/Fitting/ObjectiveMetric.cpp +++ b/Core/Fitting/ObjectiveMetric.cpp @@ -13,6 +13,7 @@ // ************************************************************************** // #include "ObjectiveMetric.h" +#include "ObjectiveMetricUtils.h" #include "OutputData.h" #include "SimDataPair.h" #include <cmath> @@ -23,18 +24,6 @@ const double double_max = std::numeric_limits<double>::max(); const double double_min = std::numeric_limits<double>::min(); const double ln10 = std::log(10.0); -const std::map<std::string, std::function<std::unique_ptr<ObjectiveMetric>()>> metric_factory = { - {"Chi2", []() { return std::make_unique<Chi2Metric>(); }}, - {"PoissonLike", []() { return std::make_unique<PoissonLikeMetric>(); }}, - {"Log", []() { return std::make_unique<LogMetric>(); }}, - {"RelativeDifference", []() { return std::make_unique<RelativeDifferenceMetric>(); }}, - {"RQ4", []() { return std::make_unique<RQ4Metric>(); }} -}; - -const std::map<std::string, std::function<double(double)>> norm_factory = { - {"l1", ObjectiveMetric::l1_norm}, {"l2", ObjectiveMetric::l2_norm} -}; - template<class T> T* copyMetric(const T& metric) { @@ -69,37 +58,6 @@ void checkIntegrity(const std::vector<double>& sim_data, const std::vector<doubl } } -const std::function<double(double)> ObjectiveMetric::l1_norm = [](double term) { - return std::abs(term); -}; - -const std::function<double(double)> ObjectiveMetric::l2_norm = [](double term) { - return term * term; -}; - -std::unique_ptr<ObjectiveMetric> ObjectiveMetric::createMetric(const std::string& metric, - const std::string& norm) -{ - const auto metric_iter = metric_factory.find(metric); - const auto norm_iter = norm_factory.find(norm); - if (metric_iter == metric_factory.end() || norm_iter == norm_factory.end()) { - std::stringstream ss; - ss << "Error in ObjectiveMetric::createMetric: either metric (" << metric << ") or norm (" - << norm << ") name is unknown.\n"; - ss << "Available metrics:\n"; - for (auto& item: metric_factory) - ss << "\t" << item.first << "\n"; - ss << "Available norms:\n"; - for (auto& item: norm_factory) - ss << "\t" << item.first << "\n"; - throw std::runtime_error(ss.str()); - } - - auto result = metric_iter->second(); - result->setNorm(norm_iter->second); - return result; -} - ObjectiveMetric::ObjectiveMetric(std::function<double(double)> norm) : m_norm(std::move(norm)) {} @@ -126,7 +84,7 @@ void ObjectiveMetric::setNorm(std::function<double(double)> norm) // ----------------------- Chi2 metric --------------------------- Chi2Metric::Chi2Metric() - : ObjectiveMetric(l2_norm) + : ObjectiveMetric(ObjectiveMetricUtils::l2Norm()) {} Chi2Metric* Chi2Metric::clone() const @@ -197,7 +155,7 @@ double PoissonLikeMetric::computeFromArrays(std::vector<double> sim_data, // ----------------------- Log metric --------------------------- LogMetric::LogMetric() - : ObjectiveMetric(l2_norm) + : ObjectiveMetric(ObjectiveMetricUtils::l2Norm()) {} LogMetric* LogMetric::clone() const diff --git a/Core/Fitting/ObjectiveMetric.h b/Core/Fitting/ObjectiveMetric.h index 132b56c3f48b6ca79a0389c075c44f035aaae2af..59991282095ea2818f7365bcd8a0842f8394dc54 100644 --- a/Core/Fitting/ObjectiveMetric.h +++ b/Core/Fitting/ObjectiveMetric.h @@ -26,14 +26,6 @@ class SimDataPair; class BA_CORE_API_ ObjectiveMetric : public ICloneable { public: - //! L1 normalization function. - static const std::function<double(double)> l1_norm; - //! L2 normalization function. - static const std::function<double(double)> l2_norm; - - static std::unique_ptr<ObjectiveMetric> createMetric(const std::string& metric, - const std::string& norm); - ObjectiveMetric(std::function<double(double)> norm); ObjectiveMetric* clone() const override = 0; diff --git a/Core/Fitting/ObjectiveMetricUtils.cpp b/Core/Fitting/ObjectiveMetricUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5afa423e4df251cb6b143472cff1b6a86111dcc --- /dev/null +++ b/Core/Fitting/ObjectiveMetricUtils.cpp @@ -0,0 +1,117 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file Core/Fitting/ObjectiveMetricUtils.cpp +//! @brief Implements ObjectiveMetric utilities. +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "ObjectiveMetricUtils.h" +#include "ObjectiveMetric.h" +#include <algorithm> +#include <cmath> +#include <map> +#include <sstream> + +namespace { +const std::function<double(double)> l1_norm = [](double term) { return std::abs(term); }; +const std::function<double(double)> l2_norm = [](double term) { return term * term; }; + +const std::map<std::string, std::function<std::unique_ptr<ObjectiveMetric>()>> metric_factory = { + {"chi2", []() { return std::make_unique<Chi2Metric>(); }}, + {"poisson-like", []() { return std::make_unique<PoissonLikeMetric>(); }}, + {"log", []() { return std::make_unique<LogMetric>(); }}, + {"reldiff", []() { return std::make_unique<RelativeDifferenceMetric>(); }}, + {"rq4", []() { return std::make_unique<RQ4Metric>(); }} +}; +const std::string default_metric_name = "poisson-like"; + +const std::map<std::string, std::function<double(double)>> norm_factory = {{"l1", l1_norm}, + {"l2", l2_norm}}; +const std::string default_norm_name = "l2"; + +template<class U> +std::vector<std::string> keys(const std::map<std::string, U>& map) +{ + std::vector<std::string> result; + result.reserve(map.size()); + for (auto& item: map) + result.push_back(item.first); + return result; +} +} // namespace + +const std::function<double(double)> ObjectiveMetricUtils::l1Norm() +{ + return l1_norm; +} + +const std::function<double(double)> ObjectiveMetricUtils::l2Norm() +{ + return l2_norm; +} + +std::unique_ptr<ObjectiveMetric> ObjectiveMetricUtils::createMetric(const std::string& metric) +{ + return createMetric(metric, defaultNormName()); +} + +std::unique_ptr<ObjectiveMetric> ObjectiveMetricUtils::createMetric(std::string metric, + std::string norm) +{ + std::transform(metric.begin(), metric.end(), metric.begin(), ::tolower); + std::transform(norm.begin(), norm.end(), norm.begin(), ::tolower); + const auto metric_iter = metric_factory.find(metric); + const auto norm_iter = norm_factory.find(norm); + if (metric_iter == metric_factory.end() || norm_iter == norm_factory.end()) { + std::stringstream ss; + ss << "Error in ObjectiveMetricUtils::createMetric: either metric (" << metric + << ") or norm (" << norm << ") name is unknown.\n"; + ss << availableMetricOptions(); + throw std::runtime_error(ss.str()); + } + + auto result = metric_iter->second(); + result->setNorm(norm_iter->second); + return result; +} + +std::string ObjectiveMetricUtils::availableMetricOptions() +{ + std::stringstream ss; + ss << "Available metrics:\n"; + for (auto& item: metricNames()) + ss << "\t" << item << "\n"; + ss << "default metric: " << defaultMetricName() << "\n"; + ss << "Available norms:\n"; + for (auto& item: normNames()) + ss << "\t" << item << "\n"; + ss << "default norm: " << defaultNormName() << "\n"; + return ss.str(); +} + +std::vector<std::string> ObjectiveMetricUtils::normNames() +{ + return keys(norm_factory); +} + +std::vector<std::string> ObjectiveMetricUtils::metricNames() +{ + return keys(metric_factory); +} + +std::string ObjectiveMetricUtils::defaultNormName() +{ + return default_norm_name; +} + +std::string ObjectiveMetricUtils::defaultMetricName() +{ + return default_metric_name; +} diff --git a/Core/Fitting/ObjectiveMetricUtils.h b/Core/Fitting/ObjectiveMetricUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..8c57f65caec9c1a3f3ff05023934e825ae278707 --- /dev/null +++ b/Core/Fitting/ObjectiveMetricUtils.h @@ -0,0 +1,55 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file Core/Fitting/ObjectiveMetricUtils.h +//! @brief Defines ObjectiveMetric utilities and corresponding namespace. +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef OBJECTIVEMETRICUTILS_H +#define OBJECTIVEMETRICUTILS_H + +#include "WinDllMacros.h" +#include <functional> +#include <memory> +#include <string> +#include <vector> + +class ObjectiveMetric; + +namespace ObjectiveMetricUtils +{ +//! Returns L1 normalization function. +BA_CORE_API_ const std::function<double(double)> l1Norm(); +//! Returns L2 normalization function. +BA_CORE_API_ const std::function<double(double)> l2Norm(); + +//! Creates the specified metric with the default norm. +BA_CORE_API_ std::unique_ptr<ObjectiveMetric> createMetric(const std::string& metric); + +//! Creates the metric with the specified norm +BA_CORE_API_ std::unique_ptr<ObjectiveMetric> createMetric(std::string metric, std::string norm); + +//! Prints available metric options +BA_CORE_API_ std::string availableMetricOptions(); + +//! Returns the names of the norms used by ObjectiveMetric. +BA_CORE_API_ std::vector<std::string> normNames(); + +//! Returns the names of the objective metrics used. +BA_CORE_API_ std::vector<std::string> metricNames(); + +//! Returns default norm name. +BA_CORE_API_ std::string defaultNormName(); + +//! Returns default metric name. +BA_CORE_API_ std::string defaultMetricName(); +} // namespace ObjectiveMetricUtils + +#endif // OBJECTIVEMETRICUTILS_H diff --git a/Fit/Kernel/Kernel.cpp b/Fit/Kernel/Kernel.cpp index 7bb064e2d6ac7c8d3af7fb3b4f1230eebbae5105..521e58873485f92d406b834033b6bfdf1691eaec 100644 --- a/Fit/Kernel/Kernel.cpp +++ b/Fit/Kernel/Kernel.cpp @@ -47,6 +47,10 @@ MinimizerResult Kernel::minimize(fcn_scalar_t fcn, const Parameters& parameters) setParameters(parameters); m_time_interval.start(); + if (m_minimizer->requiresResiduals()) + throw std::runtime_error( + "Error in Kernel::minimize: the chosen minimizer requires residuals computation. " + "Please use FitObjective::evaluate_residuals with this minimizer."); auto result = m_minimizer->minimize_scalar(fcn, parameters); m_time_interval.stop(); diff --git a/Fit/Minimizer/IMinimizer.h b/Fit/Minimizer/IMinimizer.h index 179cd239adff86bb93643ad0af4f941294c5ba19..b77642898ec0871f5865e575fe4aabeab34e900b 100644 --- a/Fit/Minimizer/IMinimizer.h +++ b/Fit/Minimizer/IMinimizer.h @@ -55,6 +55,11 @@ public: //! Sets option string to the minimizer virtual void setOptions(const std::string& options); + +#ifndef SWIG + //! Returns true if minimizer computations are residual-based, false otherwise + virtual bool requiresResiduals() { return false; } +#endif //SWIG }; #endif // IMINIMIZER_H diff --git a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp index 218245fc8999be6af134b359ece6c1bb1322127d..d162e8967e9c136ed1fb2e5ff5b8f061ec9a9879 100644 --- a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp +++ b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp @@ -100,8 +100,6 @@ std::map<std::string, std::string> GSLLevenbergMarquardtMinimizer::statusMap() c return result; } -bool GSLLevenbergMarquardtMinimizer::isGradientBasedAgorithm() { return true; } - void GSLLevenbergMarquardtMinimizer::propagateOptions() { m_gsl_minimizer->SetTolerance(tolerance()); diff --git a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h index 9fb2d2e93f29591bf2e02d887aa54526241b4343..cc8dc32603b5d8e88867b703c1e4da11d7450c4b 100644 --- a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h +++ b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h @@ -28,7 +28,7 @@ class BA_CORE_API_ GSLLevenbergMarquardtMinimizer : public RootMinimizerAdapter { public: GSLLevenbergMarquardtMinimizer(); - ~GSLLevenbergMarquardtMinimizer(); + ~GSLLevenbergMarquardtMinimizer() override; //!< Sets tolerance on the function value at the minimum. void setTolerance(double value); @@ -47,8 +47,9 @@ public: std::string statusToString() const override; std::map<std::string, std::string> statusMap() const override; + bool requiresResiduals() override { return true; } + protected: - virtual bool isGradientBasedAgorithm() override; void propagateOptions() override; const root_minimizer_t* rootMinimizer() const override; void setParameter(unsigned int index, const Fit::Parameter& par) override; diff --git a/Fit/RootAdapter/Minuit2Minimizer.cpp b/Fit/RootAdapter/Minuit2Minimizer.cpp index 852a7d4fef4831e8505dcfa5dec880b7e3143c1d..1885f4e2993fd6d06480595797ca8768608fd3dd 100644 --- a/Fit/RootAdapter/Minuit2Minimizer.cpp +++ b/Fit/RootAdapter/Minuit2Minimizer.cpp @@ -136,12 +136,9 @@ std::map<std::string, std::string> Minuit2Minimizer::statusMap() const // Fumili algorithm can work only with gradient based objective function, while others can // work with both, gradient based and chi2 based functions. Historically however, we use // simplified approach: if not Fumili, then chi2 only. Think of refactoring TODO. -bool Minuit2Minimizer::isGradientBasedAgorithm() +bool Minuit2Minimizer::requiresResiduals() { - if (algorithmName() == AlgorithmNames::Fumili) - return true; - - return false; + return algorithmName() == AlgorithmNames::Fumili; } //! Propagate options down to ROOT's Minuit2Minimizer. diff --git a/Fit/RootAdapter/Minuit2Minimizer.h b/Fit/RootAdapter/Minuit2Minimizer.h index 6595430c539cc755a6fdea79b45d252898dd5904..071ec8cd6e1ec356736134ad7f9bb4b6ddf78e2d 100644 --- a/Fit/RootAdapter/Minuit2Minimizer.h +++ b/Fit/RootAdapter/Minuit2Minimizer.h @@ -69,8 +69,9 @@ public: std::string statusToString() const override; std::map<std::string, std::string> statusMap() const override; + bool requiresResiduals() override; + protected: - bool isGradientBasedAgorithm() override; void propagateOptions() override; const root_minimizer_t* rootMinimizer() const override; diff --git a/Fit/RootAdapter/RootMinimizerAdapter.h b/Fit/RootAdapter/RootMinimizerAdapter.h index 4318c8acd89ab815f6326648e6df6b1aeed34f98..08e2ea8b7f9fe5113157644825e975b2f6928a21 100644 --- a/Fit/RootAdapter/RootMinimizerAdapter.h +++ b/Fit/RootAdapter/RootMinimizerAdapter.h @@ -72,7 +72,6 @@ protected: void propagateResults(Fit::Parameters& parameters); - virtual bool isGradientBasedAgorithm() { return false; } virtual void setParameter(unsigned int index, const Fit::Parameter& par); size_t fitDimension() const; std::vector<double> parValuesAtMinimum() const; diff --git a/Fit/RootAdapter/SimAnMinimizer.cpp b/Fit/RootAdapter/SimAnMinimizer.cpp index f1dd4f31b7f00879ae2ac1ba167ab5b376af47e0..929233406a154ceb0f2f06246d302d5945aac987 100644 --- a/Fit/RootAdapter/SimAnMinimizer.cpp +++ b/Fit/RootAdapter/SimAnMinimizer.cpp @@ -131,11 +131,6 @@ std::map<std::string, std::string> SimAnMinimizer::statusMap() const return result; } -bool SimAnMinimizer::isGradientBasedAgorithm() -{ - return false; -} - void SimAnMinimizer::propagateOptions() { ROOT::Math::GSLSimAnParams& pars = m_siman_minimizer->getSolver().Params(); diff --git a/Fit/RootAdapter/SimAnMinimizer.h b/Fit/RootAdapter/SimAnMinimizer.h index d556d0fb7a6a5ece63f111cc1c530dc9a2525855..5bbb2ae8807ff84347c38dea98cdeccf3c27d2d9 100644 --- a/Fit/RootAdapter/SimAnMinimizer.h +++ b/Fit/RootAdapter/SimAnMinimizer.h @@ -26,7 +26,7 @@ class BA_CORE_API_ SimAnMinimizer : public RootMinimizerAdapter { public: SimAnMinimizer(); - ~SimAnMinimizer(); + ~SimAnMinimizer() override; //! Sets minimizer internal print level. //! Default value is 0 (silent). @@ -62,7 +62,6 @@ public: double boltzmannMinTemp() const; std::map<std::string, std::string> statusMap() const override; - virtual bool isGradientBasedAgorithm() override; protected: void propagateOptions() override; diff --git a/GUI/coregui/Models/ItemCatalogue.cpp b/GUI/coregui/Models/ItemCatalogue.cpp index 4721fa00d2d8203ad5af7e3fbb94cd4c1f7fb6b0..fa616a20b6c5872fa169f1c0abc8d38500bd1bba 100644 --- a/GUI/coregui/Models/ItemCatalogue.cpp +++ b/GUI/coregui/Models/ItemCatalogue.cpp @@ -64,7 +64,6 @@ #include "SpecularDataItem.h" #include "SphericalDetectorItem.h" #include "TransformationItem.h" -#include "VarianceFunctionItems.h" #include "VectorItem.h" ItemCatalogue::ItemCatalogue() @@ -244,9 +243,6 @@ ItemCatalogue::ItemCatalogue() add(Constants::ProjectionContainerType, create_new<ProjectionContainerItem>); add(Constants::DepthProbeInstrumentType, create_new<DepthProbeInstrumentItem>); - - add(Constants::VarianceConstantFunctionType, create_new<VarianceConstantFunctionItem>); - add(Constants::VarianceSimFunctionType, create_new<VarianceSimFunctionItem>); } bool ItemCatalogue::contains(const QString& modelType) const diff --git a/GUI/coregui/Models/MinimizerItem.cpp b/GUI/coregui/Models/MinimizerItem.cpp index 11e130ac802eba9e64f9128d1f1c50b60b3d4a9b..854faacbbcff81b1d9537cc9c28ef6517777fb78 100644 --- a/GUI/coregui/Models/MinimizerItem.cpp +++ b/GUI/coregui/Models/MinimizerItem.cpp @@ -13,49 +13,45 @@ // ************************************************************************** // #include "MinimizerItem.h" -#include "MinimizerItemCatalogue.h" -#include "MinimizerConstants.h" -#include "Minuit2Minimizer.h" +#include "GSLLevenbergMarquardtMinimizer.h" #include "GSLMultiMinimizer.h" #include "GeneticMinimizer.h" -#include "GSLLevenbergMarquardtMinimizer.h" +#include "MinimizerConstants.h" +#include "MinimizerItemCatalogue.h" +#include "Minuit2Minimizer.h" +#include "ObjectiveMetric.h" +#include "ObjectiveMetricUtils.h" #include "SimAnMinimizer.h" #include "TestMinimizer.h" -#include "IIntensityFunction.h" -#include "VarianceFunctions.h" -#include "VarianceFunctionItems.h" - -namespace { -const QString none_fun = "None"; -const QString sqrt_fun = "sqrt"; -const QString log10_fun = "log"; -} - -// ---------------------------------------------------------------------------- -MinimizerItem::MinimizerItem(const QString &model_type) : SessionItem(model_type) -{ -} +MinimizerItem::MinimizerItem(const QString& model_type) : SessionItem(model_type) {} // ---------------------------------------------------------------------------- const QString MinimizerContainerItem::P_MINIMIZERS = "Minimizer"; -const QString MinimizerContainerItem::P_INTENSITY_FUNCTION = "Intensity function"; -const QString MinimizerContainerItem::P_VARIANCE_FUNCTIONS = "Variance"; +const QString MinimizerContainerItem::P_METRIC = "Objective metric"; +const QString MinimizerContainerItem::P_NORM = "Norm function"; MinimizerContainerItem::MinimizerContainerItem() : MinimizerItem(Constants::MinimizerContainerType) { addGroupProperty(P_MINIMIZERS, Constants::MinimizerLibraryGroup) ->setToolTip(QStringLiteral("Minimizer library")); - ComboProperty combo = ComboProperty() << none_fun << sqrt_fun << log10_fun; - addProperty(P_INTENSITY_FUNCTION, combo.variant())->setToolTip( - "Function to apply for both simulated and experimental intensities \n" - "before calculating the value of residual."); - - addGroupProperty(P_VARIANCE_FUNCTIONS, Constants::VarianceFunctionGroup) - ->setToolTip(QStringLiteral("Variance functions for residual normalization")); + ComboProperty metric_combo; + for (auto& item : ObjectiveMetricUtils::metricNames()) + metric_combo << QString::fromStdString(item); + metric_combo.setValue(QString::fromStdString(ObjectiveMetricUtils::defaultMetricName())); + addProperty(P_METRIC, metric_combo.variant()) + ->setToolTip("Objective metric to use for estimating distance between simulated and " + "experimental data."); + ComboProperty norm_combo; + for (auto& item : ObjectiveMetricUtils::normNames()) + norm_combo << QString::fromStdString(item); + norm_combo.setValue(QString::fromStdString(ObjectiveMetricUtils::defaultNormName())); + addProperty(P_NORM, norm_combo.variant()) + ->setToolTip("Normalization to use for estimating distance between simulated and " + "experimental data."); } std::unique_ptr<IMinimizer> MinimizerContainerItem::createMinimizer() const @@ -63,23 +59,11 @@ std::unique_ptr<IMinimizer> MinimizerContainerItem::createMinimizer() const return groupItem<MinimizerItem>(P_MINIMIZERS).createMinimizer(); } -std::unique_ptr<IIntensityFunction> MinimizerContainerItem::createIntensityFunction() const -{ - QString value = getItemValue(P_INTENSITY_FUNCTION).value<ComboProperty>().getValue(); - - if (value == sqrt_fun) { - return std::make_unique<IntensityFunctionSqrt>(); - } else if(value == log10_fun) { - return std::make_unique<IntensityFunctionLog>(); - } else { - return std::unique_ptr<IIntensityFunction>(); - } -} - -std::unique_ptr<IVarianceFunction> MinimizerContainerItem::createVarianceFunction() const +std::unique_ptr<ObjectiveMetric> MinimizerContainerItem::createMetric() const { - auto& variance_item = groupItem<IVarianceFunctionItem>(P_VARIANCE_FUNCTIONS); - return variance_item.createVarianceFunction(); + QString metric = getItemValue(P_METRIC).value<ComboProperty>().getValue(); + QString norm = getItemValue(P_NORM).value<ComboProperty>().getValue(); + return ObjectiveMetricUtils::createMetric(metric.toStdString(), norm.toStdString()); } // ---------------------------------------------------------------------------- @@ -89,8 +73,8 @@ const QString MinuitMinimizerItem::P_STRATEGY = QString::fromStdString(OptionNam const QString MinuitMinimizerItem::P_ERRORDEF = QString::fromStdString(OptionNames::ErrorDef); const QString MinuitMinimizerItem::P_TOLERANCE = QString::fromStdString(OptionNames::Tolerance); const QString MinuitMinimizerItem::P_PRECISION = QString::fromStdString(OptionNames::Precision); -const QString MinuitMinimizerItem::P_MAXFUNCTIONCALLS - = QString::fromStdString(OptionNames::MaxFunctionCalls); +const QString MinuitMinimizerItem::P_MAXFUNCTIONCALLS = + QString::fromStdString(OptionNames::MaxFunctionCalls); MinuitMinimizerItem::MinuitMinimizerItem() : MinimizerItem(Constants::MinuitMinimizerType) { @@ -116,7 +100,7 @@ std::unique_ptr<IMinimizer> MinuitMinimizerItem::createMinimizer() const { QString algorithmName = getItemValue(P_ALGORITHMS).value<ComboProperty>().getValue(); - Minuit2Minimizer *domainMinimizer = new Minuit2Minimizer(algorithmName.toStdString()); + Minuit2Minimizer* domainMinimizer = new Minuit2Minimizer(algorithmName.toStdString()); domainMinimizer->setStrategy(getItemValue(P_STRATEGY).toInt()); domainMinimizer->setErrorDefinition(getItemValue(P_ERRORDEF).toDouble()); domainMinimizer->setTolerance(getItemValue(P_TOLERANCE).toDouble()); @@ -129,8 +113,8 @@ std::unique_ptr<IMinimizer> MinuitMinimizerItem::createMinimizer() const // ---------------------------------------------------------------------------- const QString GSLMultiMinimizerItem::P_ALGORITHMS = "Algorithms"; -const QString GSLMultiMinimizerItem::P_MAXITERATIONS - = QString::fromStdString(OptionNames::MaxIterations); +const QString GSLMultiMinimizerItem::P_MAXITERATIONS = + QString::fromStdString(OptionNames::MaxIterations); GSLMultiMinimizerItem::GSLMultiMinimizerItem() : MinimizerItem(Constants::GSLMultiMinimizerType) { @@ -142,7 +126,7 @@ std::unique_ptr<IMinimizer> GSLMultiMinimizerItem::createMinimizer() const { QString algorithmName = getItemValue(P_ALGORITHMS).value<ComboProperty>().getValue(); - GSLMultiMinimizer *domainMinimizer = new GSLMultiMinimizer(algorithmName.toStdString()); + GSLMultiMinimizer* domainMinimizer = new GSLMultiMinimizer(algorithmName.toStdString()); domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt()); return std::unique_ptr<IMinimizer>(domainMinimizer); } @@ -150,10 +134,10 @@ std::unique_ptr<IMinimizer> GSLMultiMinimizerItem::createMinimizer() const // ---------------------------------------------------------------------------- const QString GeneticMinimizerItem::P_TOLERANCE = QString::fromStdString(OptionNames::Tolerance); -const QString GeneticMinimizerItem::P_MAXITERATIONS - = QString::fromStdString(OptionNames::MaxIterations); -const QString GeneticMinimizerItem::P_POPULATIONSIZE - = QString::fromStdString(OptionNames::PopulationSize); +const QString GeneticMinimizerItem::P_MAXITERATIONS = + QString::fromStdString(OptionNames::MaxIterations); +const QString GeneticMinimizerItem::P_POPULATIONSIZE = + QString::fromStdString(OptionNames::PopulationSize); const QString GeneticMinimizerItem::P_RANDOMSEED = QString::fromStdString(OptionNames::RandomSeed); GeneticMinimizerItem::GeneticMinimizerItem() : MinimizerItem(Constants::GeneticMinimizerType) @@ -166,7 +150,7 @@ GeneticMinimizerItem::GeneticMinimizerItem() : MinimizerItem(Constants::GeneticM std::unique_ptr<IMinimizer> GeneticMinimizerItem::createMinimizer() const { - GeneticMinimizer *domainMinimizer = new GeneticMinimizer(); + GeneticMinimizer* domainMinimizer = new GeneticMinimizer(); domainMinimizer->setTolerance(getItemValue(P_TOLERANCE).toDouble()); domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt()); domainMinimizer->setPopulationSize(getItemValue(P_POPULATIONSIZE).toInt()); @@ -176,17 +160,17 @@ std::unique_ptr<IMinimizer> GeneticMinimizerItem::createMinimizer() const // ---------------------------------------------------------------------------- -const QString SimAnMinimizerItem::P_MAXITERATIONS - = QString::fromStdString(OptionNames::MaxIterations); -const QString SimAnMinimizerItem::P_ITERATIONSTEMP - = QString::fromStdString(OptionNames::IterationTemp); +const QString SimAnMinimizerItem::P_MAXITERATIONS = + QString::fromStdString(OptionNames::MaxIterations); +const QString SimAnMinimizerItem::P_ITERATIONSTEMP = + QString::fromStdString(OptionNames::IterationTemp); const QString SimAnMinimizerItem::P_STEPSIZE = QString::fromStdString(OptionNames::StepSize); const QString SimAnMinimizerItem::P_BOLTZMANN_K = QString::fromStdString(OptionNames::BoltzmannK); -const QString SimAnMinimizerItem::P_BOLTZMANN_TINIT - = QString::fromStdString(OptionNames::BoltzmannInitT); +const QString SimAnMinimizerItem::P_BOLTZMANN_TINIT = + QString::fromStdString(OptionNames::BoltzmannInitT); const QString SimAnMinimizerItem::P_BOLTZMANN_MU = QString::fromStdString(OptionNames::BoltzmannMu); -const QString SimAnMinimizerItem::P_BOLTZMANN_TMIN - = QString::fromStdString(OptionNames::BoltzmannTmin); +const QString SimAnMinimizerItem::P_BOLTZMANN_TMIN = + QString::fromStdString(OptionNames::BoltzmannTmin); SimAnMinimizerItem::SimAnMinimizerItem() : MinimizerItem(Constants::GSLSimAnMinimizerType) { @@ -201,7 +185,7 @@ SimAnMinimizerItem::SimAnMinimizerItem() : MinimizerItem(Constants::GSLSimAnMini std::unique_ptr<IMinimizer> SimAnMinimizerItem::createMinimizer() const { - SimAnMinimizer *domainMinimizer = new SimAnMinimizer(); + SimAnMinimizer* domainMinimizer = new SimAnMinimizer(); domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt()); domainMinimizer->setIterationsAtEachTemp(getItemValue(P_ITERATIONSTEMP).toInt()); domainMinimizer->setStepSize(getItemValue(P_STEPSIZE).toDouble()); @@ -215,11 +199,10 @@ std::unique_ptr<IMinimizer> SimAnMinimizerItem::createMinimizer() const // ---------------------------------------------------------------------------- const QString GSLLMAMinimizerItem::P_TOLERANCE = QString::fromStdString(OptionNames::Tolerance); -const QString GSLLMAMinimizerItem::P_MAXITERATIONS - = QString::fromStdString(OptionNames::MaxIterations); +const QString GSLLMAMinimizerItem::P_MAXITERATIONS = + QString::fromStdString(OptionNames::MaxIterations); -GSLLMAMinimizerItem::GSLLMAMinimizerItem() - : MinimizerItem(Constants::GSLLMAMinimizerType) +GSLLMAMinimizerItem::GSLLMAMinimizerItem() : MinimizerItem(Constants::GSLLMAMinimizerType) { addProperty(P_TOLERANCE, 0.01)->setToolTip("Tolerance on the function value at the minimum"); addProperty(P_MAXITERATIONS, 0)->setToolTip("Maximum number of iterations"); @@ -227,7 +210,7 @@ GSLLMAMinimizerItem::GSLLMAMinimizerItem() std::unique_ptr<IMinimizer> GSLLMAMinimizerItem::createMinimizer() const { - GSLLevenbergMarquardtMinimizer *domainMinimizer = new GSLLevenbergMarquardtMinimizer(); + GSLLevenbergMarquardtMinimizer* domainMinimizer = new GSLLevenbergMarquardtMinimizer(); domainMinimizer->setTolerance(getItemValue(P_TOLERANCE).toDouble()); domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt()); return std::unique_ptr<IMinimizer>(domainMinimizer); @@ -235,11 +218,7 @@ std::unique_ptr<IMinimizer> GSLLMAMinimizerItem::createMinimizer() const // ---------------------------------------------------------------------------- -TestMinimizerItem::TestMinimizerItem() - : MinimizerItem(Constants::TestMinimizerType) -{ - -} +TestMinimizerItem::TestMinimizerItem() : MinimizerItem(Constants::TestMinimizerType) {} std::unique_ptr<IMinimizer> TestMinimizerItem::createMinimizer() const { diff --git a/GUI/coregui/Models/MinimizerItem.h b/GUI/coregui/Models/MinimizerItem.h index d78e681555f0e59b9fdb4f702f51be87398de10d..c75721f22f692df9b2f71f696061402b8e7c5f2f 100644 --- a/GUI/coregui/Models/MinimizerItem.h +++ b/GUI/coregui/Models/MinimizerItem.h @@ -18,15 +18,14 @@ #include "SessionItem.h" class IMinimizer; -class IIntensityFunction; -class IVarianceFunction; +class ObjectiveMetric; //! The MinimizerItem class is the base item to hold minimizer settings. class BA_CORE_API_ MinimizerItem : public SessionItem { public: - explicit MinimizerItem(const QString &model_type); + explicit MinimizerItem(const QString& model_type); virtual std::unique_ptr<IMinimizer> createMinimizer() const = 0; }; @@ -36,13 +35,13 @@ class BA_CORE_API_ MinimizerContainerItem : public MinimizerItem { public: static const QString P_MINIMIZERS; - static const QString P_INTENSITY_FUNCTION; - static const QString P_VARIANCE_FUNCTIONS; + static const QString P_METRIC; + static const QString P_NORM; + MinimizerContainerItem(); std::unique_ptr<IMinimizer> createMinimizer() const; - std::unique_ptr<IIntensityFunction> createIntensityFunction() const; - std::unique_ptr<IVarianceFunction> createVarianceFunction() const; + std::unique_ptr<ObjectiveMetric> createMetric() const; }; //! The MinuitMinimizerItem class represents settings for ROOT Minuit2 minimizer. diff --git a/GUI/coregui/Models/VarianceFunctionItems.cpp b/GUI/coregui/Models/VarianceFunctionItems.cpp deleted file mode 100644 index 3e30be9db50e836af1a2adb5cfaeeb9e93a88ee5..0000000000000000000000000000000000000000 --- a/GUI/coregui/Models/VarianceFunctionItems.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************** // -// -// BornAgain: simulate and fit scattering at grazing incidence -// -//! @file GUI/coregui/Models/VarianceFunctionItems.cpp -//! @brief Implements classes VarianceFunctionItems -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2018 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************** // - -#include "VarianceFunctionItems.h" -#include "item_constants.h" -#include "VarianceFunctions.h" - -IVarianceFunctionItem::IVarianceFunctionItem(const QString& model_type) - : SessionItem(model_type) -{ - -} - -VarianceConstantFunctionItem::VarianceConstantFunctionItem() - : IVarianceFunctionItem(Constants::VarianceConstantFunctionType) -{ - setToolTip("Leaves residual value unnormalized"); - -} - -std::unique_ptr<IVarianceFunction> VarianceConstantFunctionItem::createVarianceFunction() const -{ - return std::make_unique<VarianceConstantFunction>(); -} - -const QString VarianceSimFunctionItem::P_EPSILON = "epsilon"; - -VarianceSimFunctionItem::VarianceSimFunctionItem() - : IVarianceFunctionItem(Constants::VarianceSimFunctionType) -{ - setToolTip("Normalizes residual on max(sim, epsilon)"); - addProperty(P_EPSILON, 1.0)->setToolTip(QStringLiteral( - "Defines threshold on simulated amplitude in max(sim, epsilon)")); -} - -std::unique_ptr<IVarianceFunction> VarianceSimFunctionItem::createVarianceFunction() const -{ - return std::make_unique<VarianceSimFunction>(getItemValue(P_EPSILON).toDouble()); -} diff --git a/GUI/coregui/Models/VarianceFunctionItems.h b/GUI/coregui/Models/VarianceFunctionItems.h deleted file mode 100644 index 1f1df68a629b2d37c0acac7cd806b5f7c349543f..0000000000000000000000000000000000000000 --- a/GUI/coregui/Models/VarianceFunctionItems.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************** // -// -// BornAgain: simulate and fit scattering at grazing incidence -// -//! @file GUI/coregui/Models/VarianceFunctionItems.h -//! @brief Defines classes VarianceFunctionItems -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2018 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************** // - -#ifndef VARIANTFUNCTIONITEMS_H -#define VARIANTFUNCTIONITEMS_H - -#include "SessionItem.h" -#include <QString> -#include <memory> - -class IVarianceFunction; - -//! Base class for variance in Minimizer's residual settings. - -class BA_CORE_API_ IVarianceFunctionItem : public SessionItem -{ -public: - virtual std::unique_ptr<IVarianceFunction> createVarianceFunction() const = 0; -protected: - IVarianceFunctionItem(const QString& model_type); -}; - -class BA_CORE_API_ VarianceConstantFunctionItem : public IVarianceFunctionItem -{ -public: - VarianceConstantFunctionItem(); - - std::unique_ptr<IVarianceFunction> createVarianceFunction() const; -}; - -class BA_CORE_API_ VarianceSimFunctionItem : public IVarianceFunctionItem -{ -public: - static const QString P_EPSILON; - VarianceSimFunctionItem(); - - std::unique_ptr<IVarianceFunction> createVarianceFunction() const; -}; - -#endif // VARIANTFUNCTIONITEMS_H diff --git a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp index 4754f4d8182c6830c5f2f99347db56205bd78198..b547dab137aa12d405c696e0a041138d99c81f77 100644 --- a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp +++ b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp @@ -13,29 +13,29 @@ // ************************************************************************** // #include "FitObjectiveBuilder.h" -#include "JobItem.h" -#include "Parameters.h" -#include "KernelTypes.h" -#include "FitObjective.h" -#include "Simulation.h" -#include "MultiLayer.h" -#include "FitParameterItems.h" -#include "RealDataItem.h" -#include "GUIHelpers.h" +#include "ChiSquaredModule.h" #include "DataItem.h" -#include "OutputData.h" #include "DomainSimulationBuilder.h" +#include "FitObjective.h" +#include "FitParameterItems.h" #include "FitSuiteItem.h" -#include "MinimizerItem.h" -#include "IMinimizer.h" -#include "Minimizer.h" #include "GUIFitObserver.h" -#include "ChiSquaredModule.h" +#include "GUIHelpers.h" #include "IIntensityFunction.h" +#include "IMinimizer.h" +#include "JobItem.h" +#include "KernelTypes.h" +#include "Minimizer.h" +#include "MinimizerItem.h" +#include "MultiLayer.h" +#include "ObjectiveMetric.h" +#include "OutputData.h" +#include "Parameters.h" +#include "RealDataItem.h" +#include "Simulation.h" #include "VarianceFunctions.h" -FitObjectiveBuilder::FitObjectiveBuilder(JobItem* jobItem) - : m_jobItem(jobItem) +FitObjectiveBuilder::FitObjectiveBuilder(JobItem* jobItem) : m_jobItem(jobItem) { Q_ASSERT(m_jobItem->fitSuiteItem()); } @@ -46,24 +46,30 @@ void FitObjectiveBuilder::runFit() { m_fit_objective = createFitObjective(); - auto module = createChiSquaredModule(); - m_fit_objective->setChiSquaredModule(*module); - - fcn_residual_t residual_func = [&](const Fit::Parameters& params) { - return m_fit_objective->evaluate_residuals(params); - }; + auto module = m_jobItem->fitSuiteItem()->minimizerContainerItem()->createMetric(); + m_fit_objective->setObjectiveMetric(std::move(module)); if (m_observer) { - fit_observer_t plot_observer = [&](const FitObjective& obj) { - m_observer->update(&obj); - }; + fit_observer_t plot_observer = [&](const FitObjective& obj) { m_observer->update(&obj); }; m_fit_objective->initPlot(1, plot_observer); } - Fit::Minimizer minimizer; - minimizer.setMinimizer(createMinimizer().release()); + auto minimizer_impl = createMinimizer(); + const bool requires_residuals = minimizer_impl->requiresResiduals(); - auto result = minimizer.minimize(residual_func, createParameters()); + Fit::Minimizer minimizer; + minimizer.setMinimizer(minimizer_impl.release()); + + auto result = + requires_residuals + ? minimizer.minimize( + [&](const Fit::Parameters& params) { + return m_fit_objective->evaluate_residuals(params); + }, + createParameters()) + : minimizer.minimize( + [&](const Fit::Parameters& params) { return m_fit_objective->evaluate(params); }, + createParameters()); m_fit_objective->finalize(result); } @@ -86,19 +92,6 @@ std::unique_ptr<IMinimizer> FitObjectiveBuilder::createMinimizer() const return fitSuiteItem->minimizerContainerItem()->createMinimizer(); } -std::unique_ptr<IChiSquaredModule> FitObjectiveBuilder::createChiSquaredModule() const -{ - std::unique_ptr<IChiSquaredModule> result = std::make_unique<ChiSquaredModule>(); - - auto fitSuiteItem = m_jobItem->fitSuiteItem(); - auto intensityFunction = fitSuiteItem->minimizerContainerItem()->createIntensityFunction(); - if (intensityFunction) - result->setIntensityFunction(*intensityFunction); - auto variaceFunction = fitSuiteItem->minimizerContainerItem()->createVarianceFunction(); - result->setVarianceFunction(*variaceFunction); - return result; -} - Fit::Parameters FitObjectiveBuilder::createParameters() const { auto fitSuiteItem = m_jobItem->fitSuiteItem(); @@ -115,20 +108,22 @@ void FitObjectiveBuilder::interruptFitting() m_fit_objective->interruptFitting(); } -std::unique_ptr<Simulation> FitObjectiveBuilder::buildSimulation(const Fit::Parameters& params) const +std::unique_ptr<Simulation> +FitObjectiveBuilder::buildSimulation(const Fit::Parameters& params) const { static std::mutex build_simulation_mutex; std::unique_lock<std::mutex> lock(build_simulation_mutex); update_fit_parameters(params); return DomainSimulationBuilder::createSimulation(m_jobItem->multiLayerItem(), - m_jobItem->instrumentItem(), m_jobItem->simulationOptionsItem()); + m_jobItem->instrumentItem(), + m_jobItem->simulationOptionsItem()); } -std::unique_ptr<OutputData<double> > FitObjectiveBuilder::createOutputData() const +std::unique_ptr<OutputData<double>> FitObjectiveBuilder::createOutputData() const { auto realDataItem = m_jobItem->realDataItem(); - if(!realDataItem) + if (!realDataItem) throw GUIHelpers::Error("FitObjectiveBuilder::createOutputData() -> No Real Data defined."); const DataItem* intensity_item = realDataItem->dataItem(); @@ -138,7 +133,6 @@ std::unique_ptr<OutputData<double> > FitObjectiveBuilder::createOutputData() con return std::unique_ptr<OutputData<double>>(intensity_item->getOutputData()->clone()); } - void FitObjectiveBuilder::update_fit_parameters(const Fit::Parameters& params) const { QVector<double> values = GUIHelpers::fromStdVector(params.values()); diff --git a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h index 58e2a7cc6501269f4a74104023d1be51d0a16b10..ecc3c444afd971dfa41ef796ed2386782226b484 100644 --- a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h +++ b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h @@ -22,7 +22,7 @@ class JobItem; class FitObjective; class Simulation; namespace Fit { class Parameters; } -template<class T> class OutputData; +template <class T> class OutputData; class IMinimizer; class GUIFitObserver; class IChiSquaredModule; @@ -44,6 +44,7 @@ public: void attachObserver(std::shared_ptr<GUIFitObserver> observer); void interruptFitting(); + private: JobItem* m_jobItem; diff --git a/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp b/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp index 60e80c32616cc605898a682cc74159b68fb11469..3168d6987c877e4e3b2c20598b3e8507b9f04960 100644 --- a/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp +++ b/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp @@ -1,5 +1,6 @@ #include "google_test.h" #include "ObjectiveMetric.h" +#include "ObjectiveMetricUtils.h" #include <cmath> class ObjectiveMetricTest : public ::testing::Test @@ -39,7 +40,7 @@ TEST_F(ObjectiveMetricTest, Chi2WellFormed) EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties_1, weight_factors), 204.0); - metric.setNorm(ObjectiveMetric::l1_norm); + metric.setNorm(ObjectiveMetricUtils::l1Norm()); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors), 26.0); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 5.0); @@ -100,7 +101,7 @@ TEST_F(ObjectiveMetricTest, PoissionLikeWellFormed) sim_data_1[0] = 0.0; EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data_1, exp_data, weight_factors), 5.25); - metric.setNorm(ObjectiveMetric::l1_norm); + metric.setNorm(ObjectiveMetricUtils::l1Norm()); std::vector<double> sim_data_2 = sim_data; sim_data_2[1] = 1.0; @@ -163,7 +164,7 @@ TEST_F(ObjectiveMetricTest, LogWellFormed) EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties_1, weight_factors), 4.0101e+6 * std::log(10.0) * std::log(10.0)); - metric.setNorm(ObjectiveMetric::l1_norm); + metric.setNorm(ObjectiveMetricUtils::l1Norm()); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors), 4.0211e+5 * std::log(10.0)); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 5.0); @@ -223,7 +224,7 @@ TEST_F(ObjectiveMetricTest, RelativeDifferenceWellFormed) exp_data_1[0] = 0.0; EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data_1, exp_data_1, weight_factors), 4.0 / 9.0); - metric.setNorm(ObjectiveMetric::l1_norm); + metric.setNorm(ObjectiveMetricUtils::l1Norm()); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 5.0 / 3.0); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors), @@ -280,7 +281,7 @@ TEST_F(ObjectiveMetricTest, RQ4WellFormed) exp_data_1[0] = 0.0; EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data_1, exp_data_1, weight_factors), 13.0); - metric.setNorm(ObjectiveMetric::l1_norm); + metric.setNorm(ObjectiveMetricUtils::l1Norm()); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 8.0); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors), @@ -313,3 +314,51 @@ TEST_F(ObjectiveMetricTest, RQ4IllFormed) 0.0); EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors_1), 0.0); } + +TEST_F(ObjectiveMetricTest, createMetric) +{ + auto result = ObjectiveMetricUtils::createMetric("Poisson-like"); + EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get())); + // Since norm functions lack equality comparison, check the equality of applying them + EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l2Norm()(2.0)); + + result = ObjectiveMetricUtils::createMetric("Poisson-Like", "L1"); + EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get())); + EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l1Norm()(2.0)); + + result = ObjectiveMetricUtils::createMetric("poisson-like", "l1"); + EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get())); + EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l1Norm()(2.0)); + + result = ObjectiveMetricUtils::createMetric("poissoN-likE", "L2"); + EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get())); + EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l2Norm()(2.0)); + + result = ObjectiveMetricUtils::createMetric("poisson-like"); + EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get())); + EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l2Norm()(2.0)); + + result = ObjectiveMetricUtils::createMetric("chi2"); + EXPECT_TRUE(dynamic_cast<Chi2Metric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("Chi2"); + EXPECT_TRUE(dynamic_cast<Chi2Metric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("Log"); + EXPECT_TRUE(dynamic_cast<LogMetric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("log"); + EXPECT_TRUE(dynamic_cast<LogMetric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("reldiff"); + EXPECT_TRUE(dynamic_cast<RelativeDifferenceMetric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("RelDiff"); + EXPECT_TRUE(dynamic_cast<RelativeDifferenceMetric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("RQ4"); + EXPECT_TRUE(dynamic_cast<RQ4Metric*>(result.get())); + + result = ObjectiveMetricUtils::createMetric("rq4"); + EXPECT_TRUE(dynamic_cast<RQ4Metric*>(result.get())); +}