Mantid
Loading...
Searching...
No Matches
LevenbergMarquardtMinimizer.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
12
16
18#include "MantidKernel/Logger.h"
19
20#include <gsl/gsl_blas.h>
21#include <gsl/gsl_version.h>
22
24
26namespace {
27// Get a reference to the logger
28Kernel::Logger g_log("LevenbergMarquardtMinimizer");
29
30bool cannotReachSpecifiedToleranceInF(int errorCode) { return errorCode == GSL_ETOLF; }
31bool cannotReachSpecifiedToleranceInX(int errorCode) { return errorCode == GSL_ETOLX; }
32} // namespace
33
34// clang-format off
35DECLARE_FUNCMINIMIZER(LevenbergMarquardtMinimizer, Levenberg-Marquardt)
36// clang-format on
37
39 : m_data(nullptr), gslContainer(), m_gslSolver(nullptr), m_function(), m_absError(1e-4), m_relError(1e-4) {
40 declareProperty("AbsError", m_absError,
41 "Absolute error allowed for "
42 "parameters - a stopping parameter "
43 "in success.");
44 declareProperty("RelError", m_relError,
45 "Relative error allowed for "
46 "parameters - a stopping parameter "
47 "in success.");
48}
49
50void LevenbergMarquardtMinimizer::initialize(API::ICostFunction_sptr costFunction, size_t /*maxIterations*/) {
51 // set-up GSL container to be used with GSL simplex algorithm
52 if (const auto ps = std::dynamic_pointer_cast<CostFunctions::CostFuncPoisson>(costFunction); ps) {
53 m_data = std::make_unique<GSL_FitData>(ps);
54 } else if (const auto ls = std::dynamic_pointer_cast<CostFunctions::CostFuncLeastSquares>(costFunction); ls) {
55 m_data = std::make_unique<GSL_FitData>(ls);
56 } else {
57 throw std::runtime_error("LevenbergMarquardt can only be used with Least "
58 "squares or Deviance (Poisson) cost functions.");
59 }
60 // specify the type of GSL solver to use
61 const gsl_multifit_fdfsolver_type *T = gsl_multifit_fdfsolver_lmsder;
62
63 // setup GSL container
65 gslContainer.df = &gsl_df;
66 gslContainer.fdf = &gsl_fdf;
67 gslContainer.n = m_data->n;
68 gslContainer.p = m_data->p;
69 gslContainer.params = m_data.get();
70
71 // setup GSL solver
72 m_gslSolver = gsl_multifit_fdfsolver_alloc(T, m_data->n, m_data->p);
73 if (!m_gslSolver) {
74 throw std::runtime_error("Levenberg-Marquardt minimizer failed to initialize. \n" + std::to_string(m_data->n) +
75 " data points, " + std::to_string(m_data->p) + " fitting parameters. ");
76 }
77 gsl_multifit_fdfsolver_set(m_gslSolver, &gslContainer, m_data->initFuncParams);
78
79 m_function = m_data->costFunction->getFittingFunction();
80}
81
83 if (m_gslSolver) {
84 gsl_multifit_fdfsolver_free(m_gslSolver);
85 }
86}
87
88bool LevenbergMarquardtMinimizer::iterate(size_t /*iteration*/) {
89 m_absError = getProperty("AbsError");
90 m_relError = getProperty("RelError");
91
92 int retVal = gsl_multifit_fdfsolver_iterate(m_gslSolver);
93
94 // From experience it is found that gsl_multifit_fdfsolver_iterate
95 // occasionally get
96 // stock - even after having achieved a sensible fit. This seem in particular
97 // to be a
98 // problem on Linux. However, to force GSL not to return ga ga have to do
99 // stuff in the
100 // if statement below
101 // GSL 1.14 changed return value from GSL_CONTINUE->GSL_ENOPROG for
102 // non-converging fits at 10 iterations
103 if (retVal == GSL_CONTINUE || retVal == GSL_ENOPROG) {
104 size_t ia = 0;
105 for (size_t i = 0; i < m_function->nParams(); i++) {
106 if (m_function->isActive(i)) {
107 m_function->setActiveParameter(i, gsl_vector_get(m_gslSolver->x, ia));
108 ++ia;
109 }
110 }
111 m_function->applyTies();
112 retVal = GSL_CONTINUE;
113 }
114
115 if (retVal && retVal != GSL_CONTINUE) {
116 m_errorString = gsl_strerror(retVal);
117 if (cannotReachSpecifiedToleranceInF(retVal)) {
119 } else if (cannotReachSpecifiedToleranceInX(retVal)) {
121 }
122 return false;
123 }
124
125 retVal = hasConverged();
126 return retVal != GSL_SUCCESS;
127}
128
130 return gsl_multifit_test_delta(m_gslSolver->dx, m_gslSolver->x, m_absError, m_relError);
131}
132
134 double chi = gsl_blas_dnrm2(m_gslSolver->f);
135 return chi * chi;
136}
137
138/* Calculates covariance matrix
139 *
140 * @param epsrel :: Is used to remove linear-dependent columns
141 * @param covar :: Returned covariance matrix, here as
142 */
143void LevenbergMarquardtMinimizer::calCovarianceMatrix(double epsrel, gsl_matrix *covar) {
144#if GSL_MAJOR_VERSION < 2
145 gsl_multifit_covar(m_gslSolver->J, epsrel, covar);
146#else
147 gsl_matrix *J = gsl_matrix_alloc(gslContainer.n, gslContainer.p);
148 gsl_multifit_fdfsolver_jac(m_gslSolver, J);
149 gsl_multifit_covar(J, epsrel, covar);
150 gsl_matrix_free(J);
151#endif
152}
153
154} // namespace Mantid::CurveFitting::FuncMinimisers
#define DECLARE_FUNCMINIMIZER(classname, username)
Macro for declaring a new type of minimizers to be used with the FuncMinimizerFactory.
const std::vector< Type > & m_data
std::string m_errorString
Error string.
Implementing Levenberg-Marquardt by wrapping the IFuncMinimizer interface around the GSL implementati...
gsl_multifit_fdfsolver * m_gslSolver
pointer to the GSL solver doing the work
double costFunctionVal() override
Return current value of the cost function.
API::IFunction_sptr m_function
Stored to access IFunction interface in iterate()
void initialize(API::ICostFunction_sptr costFunction, size_t maxIterations=0) override
Initialize minimizer, i.e. pass a function to minimize.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
const std::string CHANGES_IN_FUNCTION_TOO_SMALL
Reported by Levenberg-Marquardt when the change in the cost function between iterations has fallen be...
const std::string CHANGES_IN_PARAMETER_TOO_SMALL
Reported by Levenberg-Marquardt when the change in the parameter values between iterations has fallen...
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< ICostFunction > ICostFunction_sptr
define a shared pointer to a cost function
int gsl_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *J)
Fit derivatives and function GSL wrapper.
int gsl_f(const gsl_vector *x, void *params, gsl_vector *f)
Fit GSL function wrapper.
int gsl_df(const gsl_vector *x, void *params, gsl_matrix *J)
Fit GSL derivative function wrapper.
std::string to_string(const wide_integer< Bits, Signed > &n)