Mantid
Loading...
Searching...
No Matches
CostFuncFitting.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2019 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//----------------------------------------------------------------------
16
17#include <gsl/gsl_multifit_nlin.h>
18#include <limits>
19#include <utility>
20
22
27 : m_dirtyVal(true), m_dirtyDeriv(true), m_dirtyHessian(true), m_includePenalty(true), m_value(0), m_pushed(false),
28 m_pushedValue(false), m_ignoreInvalidData(false) {}
29
34 m_dirtyVal = true;
35 m_dirtyDeriv = true;
36 m_dirtyHessian = true;
37}
38
42double CostFuncFitting::getParameter(size_t i) const {
44 return m_function->activeParameter(m_indexMap[i]);
45}
46
50void CostFuncFitting::setParameter(size_t i, const double &value) {
52 m_function->setActiveParameter(m_indexMap[i], value);
53 setDirty();
54}
55
61std::string CostFuncFitting::parameterName(size_t i) const {
63 return m_function->parameterName(m_indexMap[i]);
64}
65
69 return m_indexMap.size();
70}
71
82 m_function = std::move(function);
83 m_domain = std::move(domain);
84 m_values = std::move(values);
86 reset();
87}
88
94 return false;
95 }
96 if (m_function->nParams() != m_numberFunParams) {
97 reset();
98 }
99 auto nActive = m_indexMap.size();
100 for (size_t i = 0, j = 0; i < m_numberFunParams; ++i) {
101 if (m_function->isActive(i)) {
102 if (j >= nActive || m_indexMap[j] != i) {
103 reset();
104 break;
105 }
106 ++j;
107 }
108 }
109 return true;
110}
111
116 if (!isValid()) {
117 throw std::runtime_error("Fitting cost function isn't set");
118 }
119}
120
125 // construct the jacobian
126 EigenJacobian J(*m_function, m_values->size());
127 size_t na = this->nParams(); // number of active parameters
128 const auto &j = J.getJ();
129 assert(static_cast<size_t>(j.cols()) == na);
130 covar.resize(na, na);
131
132 // calculate the derivatives
133 m_function->functionDeriv(*m_domain, J);
134
135 // let the GSL to compute the covariance matrix
136 EigenMatrix covarTr(covar.tr());
137 EigenMatrix tempJTr;
138 tempJTr = j.transpose();
139 gsl_matrix_const_view tempJTrGSL = getGSLMatrixView_const(tempJTr.inspector());
140 gsl_matrix_view covarTrGSL = getGSLMatrixView(covarTr.mutator());
141
142 gsl_multifit_covar(&tempJTrGSL.matrix, epsrel, &covarTrGSL.matrix);
143 covar = covarTr.tr();
144}
145
153 EigenMatrix c;
154 calActiveCovarianceMatrix(c, epsrel);
155
156 size_t np = m_function->nParams();
157
158 bool isTransformationIdentity = true;
159 for (size_t i = 0; i < np; ++i) {
160 if (!m_function->isActive(i))
161 continue;
162 isTransformationIdentity =
163 isTransformationIdentity && (m_function->activeParameter(i) == m_function->getParameter(i));
164 }
165
166 if (isTransformationIdentity) {
167 // if the transformation is identity simply copy the matrix
168 covar = c;
169 } else {
170 // else do the transformation
171 EigenMatrix tm;
173 covar = tm.tr() * c * tm;
174 }
175}
176
183void CostFuncFitting::calFittingErrors(const EigenMatrix &covar, double chi2) {
185 size_t np = m_function->nParams();
186 auto covarMatrix = std::shared_ptr<Kernel::Matrix<double>>(new Kernel::Matrix<double>(np, np));
187 m_function->setCovarianceMatrix(covarMatrix);
188 size_t ia = 0;
189 for (size_t i = 0; i < np; ++i) {
190 if (!m_function->isActive(i)) {
191 m_function->setError(i, 0);
192 } else {
193 size_t ja = 0;
194 for (size_t j = 0; j < np; ++j) {
195 if (m_function->isActive(j)) {
196 (*covarMatrix)[i][j] = covar.get(ia, ja);
197 ++ja;
198
199 if (ja >= covar.size2())
200 break;
201 }
202 }
203 double err = sqrt(covar.get(ia, ia));
204 m_function->setError(i, err);
205 ++ia;
206
207 if (ia >= covar.size1())
208 break;
209 }
210 }
211 m_function->setCovarianceMatrix(covarMatrix);
212 m_function->setReducedChiSquared(chi2 / static_cast<double>((m_values->size() - np)));
213}
214
220 const double epsilon = std::numeric_limits<double>::epsilon() * 100;
221 size_t np = m_function->nParams();
222 size_t na = nParams();
223 tm.resize(na, na);
224 size_t ia = 0;
225 for (size_t i = 0; i < np; ++i) {
226 if (!m_function->isActive(i))
227 continue;
228 double p0 = m_function->getParameter(i);
229 for (size_t j = 0; j < na; ++j) {
230 double ap = getParameter(j);
231 double step = ap == 0.0 ? epsilon : ap * epsilon;
232 setParameter(j, ap + step);
233 tm.set(ia, j, (m_function->getParameter(i) - p0) / step);
234 setParameter(j, ap);
235 }
236 ++ia;
237 }
238}
239
242 if (m_function) {
243 m_function->applyTies();
244 }
245}
246
249 m_numberFunParams = m_function->nParams();
250 m_indexMap.clear();
251 for (size_t i = 0; i < m_numberFunParams; ++i) {
252 if (m_function->isActive(i)) {
253 m_indexMap.emplace_back(i);
254 }
255 API::IConstraint *c = m_function->getConstraint(i);
256 if (c) {
258 }
259 }
260 m_dirtyDeriv = true;
261 m_dirtyHessian = true;
262}
263
269 auto np = nParams();
270 if (np != params.size()) {
271 throw Kernel::Exception::FitSizeWarning(params.size(), np);
272 }
273 for (size_t i = 0; i < np; ++i) {
274 setParameter(i, params.get(i));
275 }
276 m_function->applyTies();
277}
278
284 auto np = nParams();
285 if (params.size() != np) {
286 params.resize(np);
287 }
288 for (size_t i = 0; i < np; ++i) {
289 params.set(i, getParameter(i));
290 }
291}
292
296double CostFuncFitting::val() const {
297 if (!m_dirtyVal) {
298 return m_value;
299 }
300
302
303 m_value = 0.0;
304
305 auto seqDomain = std::dynamic_pointer_cast<SeqDomain>(m_domain);
306
307 if (seqDomain) {
308 seqDomain->additiveCostFunctionVal(*this);
309 } else {
310 if (!m_values) {
311 throw std::runtime_error("CostFunction: undefined FunctionValues.");
312 }
314 }
315
316 // add penalty
317 if (m_includePenalty) {
318 for (size_t i = 0; i < m_function->nParams(); ++i) {
319 if (!m_function->isActive(i))
320 continue;
321 API::IConstraint *c = m_function->getConstraint(i);
322 if (c) {
323 m_value += c->check();
324 }
325 }
326 }
327
328 m_dirtyVal = false;
329 return m_value;
330}
331
335void CostFuncFitting::deriv(std::vector<double> &der) const {
336 valDerivHessian(true, false);
337
338 if (der.size() != nParams()) {
339 der.resize(nParams());
340 }
341 for (size_t i = 0; i < nParams(); ++i) {
342 der[i] = m_der.get(i);
343 }
344}
345
350double CostFuncFitting::valAndDeriv(std::vector<double> &der) const {
351 valDerivHessian(true, false);
352
353 if (der.size() != nParams()) {
354 der.resize(nParams());
355 }
356 for (size_t i = 0; i < nParams(); ++i) {
357 der[i] = m_der.get(i);
358 }
359 return m_value;
360}
361
367double CostFuncFitting::valDerivHessian(bool evalDeriv, bool evalHessian) const {
368 if (m_pushed || !evalDeriv) {
369 return val();
370 }
371
373 return m_value;
374 }
375
376 const size_t numParams = nParams();
377
379
380 m_value = 0.0;
381
382 m_der.resize(numParams);
383 m_der.zero();
384 if (evalHessian) {
385 m_hessian.resize(numParams, numParams);
386 m_hessian.zero();
387 }
388
389 auto seqDomain = std::dynamic_pointer_cast<SeqDomain>(m_domain);
390
391 if (seqDomain) {
392 seqDomain->additiveCostFunctionValDerivHessian(*this, evalDeriv, evalHessian);
393 } else {
394 if (!m_values) {
395 throw std::runtime_error("CostFunction: undefined FunctionValues.");
396 }
397 addValDerivHessian(m_function, m_domain, m_values, evalDeriv, evalHessian);
398 }
399
400 // Add constraints penalty
401 size_t np = m_function->nParams();
402 if (m_includePenalty) {
403 for (size_t i = 0; i < np; ++i) {
404 API::IConstraint *c = m_function->getConstraint(i);
405 if (c) {
406 m_value += c->check();
407 }
408 }
409 }
410 m_dirtyVal = false;
411
412 if (evalDeriv) {
413 if (m_includePenalty) {
414 size_t i = 0;
415 for (size_t ip = 0; ip < np; ++ip) {
416 if (!m_function->isActive(ip))
417 continue;
418 API::IConstraint *c = m_function->getConstraint(ip);
419 if (c) {
420 double d = m_der.get(i) + c->checkDeriv();
421 m_der.set(i, d);
422 }
423 ++i;
424 }
425 }
426 m_dirtyDeriv = false;
427
428 if (m_includePenalty) {
429 size_t i = 0;
430 for (size_t ip = 0; ip < np; ++ip) {
431 if (!m_function->isActive(ip))
432 continue;
433 API::IConstraint *c = m_function->getConstraint(ip);
434 if (c && !m_hessian.isEmpty()) {
435 double d = m_hessian.get(i, i) + c->checkDeriv2();
436 m_hessian.set(i, i, d);
437 }
438 ++i;
439 }
440 }
441 // clear the dirty flag if hessian was actually calculated
443 }
444
445 return m_value;
446}
447
452 if (m_pushed) {
453 return m_der;
454 }
457 }
458 return m_der;
459}
460
465 if (m_pushed) {
466 return m_hessian;
467 }
470 }
471 return m_hessian;
472}
473
478 if (m_pushed) {
479 throw std::runtime_error("Cost Function: double push.");
480 }
481 // make sure we are not dirty
484 m_pushed = true;
485}
486
491 if (!m_pushed) {
492 throw std::runtime_error("Cost Function: empty stack.");
493 }
496 m_pushed = false;
497 m_dirtyVal = false;
498 m_dirtyDeriv = false;
499 m_dirtyHessian = false;
500}
501
506 if (!m_pushed) {
507 throw std::runtime_error("Cost Function: empty stack.");
508 }
509 m_pushed = false;
510 setDirty();
511}
512
518 return;
519 }
520
521 for (size_t i = 0; i < m_values->size(); i++) {
522 if (m_values->getFitWeight(i) < 0) {
523 throw std::runtime_error("Invalid data found at point=" + std::to_string(i) + " in fit weight.");
524 }
525 }
526}
527
528} // namespace Mantid::CurveFitting::CostFunctions
const std::string & m_value
Definition Algorithm.cpp:71
double value
The value of the point.
Definition FitMW.cpp:51
An interface to a constraint.
Definition IConstraint.h:26
virtual double checkDeriv()=0
Returns the derivative of the penalty for each active parameter.
virtual double checkDeriv2()=0
Returns the derivative of the penalty for each active parameter.
virtual double check()=0
Returns a penalty number which is bigger than or equal to zero If zero it means that the constraint i...
virtual void setParamToSatisfyConstraint()=0
Set the parameters of IFitFunction to satisfy constraint.
bool m_includePenalty
Flag to include constraint in cost function value.
API::FunctionValues_sptr m_values
Shared poinetr to the function values.
virtual void deriv(std::vector< double > &der) const override
Calculate the derivatives of the cost function.
void setParameter(size_t i, const double &value) override
Set i-th parameter.
virtual void calFittingErrors(const EigenMatrix &covar, double chi2)
Calculate fitting errors.
virtual double valDerivHessian(bool evalDeriv=true, bool evalHessian=true) const
Calculate the value, the first and the second derivatives of the cost function.
void pop()
Restore saved parameters, derivatives and hessian.
void reset() const
Reset the fitting function (neccessary if parameters get fixed/unfixed)
std::vector< size_t > m_indexMap
maps the cost function's parameters to the ones of the fitting function.
API::FunctionDomain_sptr m_domain
Shared pointer to the function domain.
virtual void updateValidateFitWeights()
Update or validate the fit weights in m_values when necessary.
void setParameters(const EigenVector &params)
Set all parameters.
void push()
Save current parameters, derivatives and hessian.
void validateNegativeFitWeights()
Functionality to validate negative fit weights.
virtual void setFittingFunction(API::IFunction_sptr function, API::FunctionDomain_sptr domain, API::FunctionValues_sptr values)
Set fitting function.
void getParameters(EigenVector &params) const
Get all parameters into a GSLVector.
const EigenMatrix & getHessian() const
Return cached or calculate the Hessian.
virtual void addVal(API::FunctionDomain_sptr domain, API::FunctionValues_sptr values) const =0
Increment to the cost function by evaluating it on a domain.
size_t nParams() const override
Number of parameters.
void calTransformationMatrixNumerically(EigenMatrix &tm)
Calculate the transformation matrix T by numeric differentiation.
API::IFunction_sptr m_function
Shared pointer to the fitting function.
virtual double val() const override
Calculate value of cost function.
virtual double valAndDeriv(std::vector< double > &der) const override
Calculate the value and the derivatives of the cost function.
std::string parameterName(size_t i) const
Get name of i-th parameter.
bool isValid() const
Is the function set and valid?
virtual void calActiveCovarianceMatrix(EigenMatrix &covar, double epsrel=1e-8)
Calculates covariance matrix for fitting function's active parameters.
double getParameter(size_t i) const override
Get i-th parameter.
virtual void addValDerivHessian(API::IFunction_sptr function, API::FunctionDomain_sptr domain, API::FunctionValues_sptr values, bool evalDeriv=true, bool evalHessian=true) const =0
Increments the cost function and its derivatives by evaluating them on a domain.
size_t m_numberFunParams
Number of all parameters in the fitting function.
void drop()
Discard saved parameters, derivatives and hessian.
void checkValidity() const
Throw a runtime_error if function is invalid.
const EigenVector & getDeriv() const
Return cached or calculate the drivatives.
virtual void calCovarianceMatrix(EigenMatrix &covar, double epsrel=1e-8)
Calculates covariance matrix.
void applyTies()
Apply ties in the fitting function.
Two implementations of Jacobian.
map_type & getJ()
Get the map to the jacobian.
A wrapper around Eigen::Matrix.
Definition EigenMatrix.h:33
bool isEmpty() const
Is matrix empty.
void set(size_t i, size_t j, double value)
Set an element.
double get(size_t i, size_t j) const
Get an element.
size_t size1() const
First size of the matrix.
EigenMatrix tr() const
Calculate the eigensystem of a symmetric matrix.
map_type & mutator()
Get the map to Eigen matrix.
Definition EigenMatrix.h:56
void zero()
Set all elements to zero.
size_t size2() const
Second size of the matrix.
const map_type inspector() const
Get a const copy of the Eigen matrix.
Definition EigenMatrix.h:58
void resize(const size_t nx, const size_t ny)
Resize the matrix.
A wrapper around Eigen::Vector.
Definition EigenVector.h:27
void set(const size_t i, const double value)
Set an element.
double get(const size_t i) const
Get an element.
void resize(const size_t n)
Resize the vector.
size_t size() const
Size of the vector.
Exception thrown when a fitting function changes number of parameters during fit.
Definition Exception.h:336
Numerical Matrix class.
Definition Matrix.h:42
std::shared_ptr< FunctionValues > FunctionValues_sptr
typedef for a shared pointer
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition IFunction.h:743
std::shared_ptr< FunctionDomain > FunctionDomain_sptr
typedef for a shared pointer
gsl_matrix_const_view const getGSLMatrixView_const(const map_type m)
take data from a constEigen Matrix and return a transposed gsl view.
gsl_matrix_view getGSLMatrixView(map_type &tr)
take data from an Eigen Matrix and return a transposed a gsl view.
std::string to_string(const wide_integer< Bits, Signed > &n)