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 <limits>
18#include <utility>
19
21
26 : m_dirtyVal(true), m_dirtyDeriv(true), m_dirtyHessian(true), m_includePenalty(true), m_value(0), m_pushed(false),
27 m_pushedValue(false), m_ignoreInvalidData(false) {}
28
33 m_dirtyVal = true;
34 m_dirtyDeriv = true;
35 m_dirtyHessian = true;
36}
37
41double CostFuncFitting::getParameter(size_t i) const {
43 return m_function->activeParameter(m_indexMap[i]);
44}
45
49void CostFuncFitting::setParameter(size_t i, const double &value) {
51 m_function->setActiveParameter(m_indexMap[i], value);
52 setDirty();
53}
54
60std::string CostFuncFitting::parameterName(size_t i) const {
62 return m_function->parameterName(m_indexMap[i]);
63}
64
68 return m_indexMap.size();
69}
70
81 m_function = std::move(function);
82 m_domain = std::move(domain);
83 m_values = std::move(values);
85 reset();
86}
87
93 return false;
94 }
95 if (m_function->nParams() != m_numberFunParams) {
96 reset();
97 }
98 auto nActive = m_indexMap.size();
99 for (size_t i = 0, j = 0; i < m_numberFunParams; ++i) {
100 if (m_function->isActive(i)) {
101 if (j >= nActive || m_indexMap[j] != i) {
102 reset();
103 break;
104 }
105 ++j;
106 }
107 }
108 return true;
109}
110
115 if (!isValid()) {
116 throw std::runtime_error("Fitting cost function isn't set");
117 }
118}
119
124 // construct the jacobian
125 EigenJacobian J(*m_function, m_values->size());
126 size_t na = this->nParams(); // number of active parameters
127 const auto &j = J.getJ();
128 assert(static_cast<size_t>(j.cols()) == na);
129 covar.resize(na, na);
130
131 // calculate the derivatives
132 m_function->functionDeriv(*m_domain, J);
133
134 // let Eigen compute the covariance matrix
135 covar = covar_from_jacobian(j, epsrel);
136}
137
145 EigenMatrix c;
146 calActiveCovarianceMatrix(c, epsrel);
147
148 size_t np = m_function->nParams();
149
150 bool isTransformationIdentity = true;
151 for (size_t i = 0; i < np; ++i) {
152 if (!m_function->isActive(i))
153 continue;
154 isTransformationIdentity =
155 isTransformationIdentity && (m_function->activeParameter(i) == m_function->getParameter(i));
156 }
157
158 if (isTransformationIdentity) {
159 // if the transformation is identity simply copy the matrix
160 covar = c;
161 } else {
162 // else do the transformation
163 EigenMatrix tm;
165 covar = tm.tr() * c * tm;
166 }
167}
168
175void CostFuncFitting::calFittingErrors(const EigenMatrix &covar, double chi2) {
177 size_t np = m_function->nParams();
178 auto covarMatrix = std::shared_ptr<Kernel::Matrix<double>>(new Kernel::Matrix<double>(np, np));
179 m_function->setCovarianceMatrix(covarMatrix);
180 size_t ia = 0;
181 for (size_t i = 0; i < np; ++i) {
182 if (!m_function->isActive(i)) {
183 m_function->setError(i, 0);
184 } else {
185 size_t ja = 0;
186 for (size_t j = 0; j < np; ++j) {
187 if (m_function->isActive(j)) {
188 (*covarMatrix)[i][j] = covar.get(ia, ja);
189 ++ja;
190
191 if (ja >= covar.size2())
192 break;
193 }
194 }
195 double err = sqrt(covar.get(ia, ia));
196 m_function->setError(i, err);
197 ++ia;
198
199 if (ia >= covar.size1())
200 break;
201 }
202 }
203 m_function->setCovarianceMatrix(covarMatrix);
204 m_function->setReducedChiSquared(chi2 / static_cast<double>((m_values->size() - np)));
205}
206
212 const double epsilon = std::numeric_limits<double>::epsilon() * 100;
213 size_t np = m_function->nParams();
214 size_t na = nParams();
215 tm.resize(na, na);
216 size_t ia = 0;
217 for (size_t i = 0; i < np; ++i) {
218 if (!m_function->isActive(i))
219 continue;
220 double p0 = m_function->getParameter(i);
221 for (size_t j = 0; j < na; ++j) {
222 double ap = getParameter(j);
223 double step = ap == 0.0 ? epsilon : ap * epsilon;
224 setParameter(j, ap + step);
225 tm.set(ia, j, (m_function->getParameter(i) - p0) / step);
226 setParameter(j, ap);
227 }
228 ++ia;
229 }
230}
231
234 if (m_function) {
235 m_function->applyTies();
236 }
237}
238
241 m_numberFunParams = m_function->nParams();
242 m_indexMap.clear();
243 for (size_t i = 0; i < m_numberFunParams; ++i) {
244 if (m_function->isActive(i)) {
245 m_indexMap.emplace_back(i);
246 }
247 API::IConstraint *c = m_function->getConstraint(i);
248 if (c) {
250 }
251 }
252 m_dirtyDeriv = true;
253 m_dirtyHessian = true;
254}
255
261 auto np = nParams();
262 if (np != params.size()) {
263 throw Kernel::Exception::FitSizeWarning(params.size(), np);
264 }
265 for (size_t i = 0; i < np; ++i) {
266 setParameter(i, params.get(i));
267 }
268 m_function->applyTies();
269}
270
276 auto np = nParams();
277 if (params.size() != np) {
278 params.resize(np);
279 }
280 for (size_t i = 0; i < np; ++i) {
281 params.set(i, getParameter(i));
282 }
283}
284
288double CostFuncFitting::val() const {
289 if (!m_dirtyVal) {
290 return m_value;
291 }
292
294
295 m_value = 0.0;
296
297 auto seqDomain = std::dynamic_pointer_cast<SeqDomain>(m_domain);
298
299 if (seqDomain) {
300 seqDomain->additiveCostFunctionVal(*this);
301 } else {
302 if (!m_values) {
303 throw std::runtime_error("CostFunction: undefined FunctionValues.");
304 }
306 }
307
308 // add penalty
309 if (m_includePenalty) {
310 for (size_t i = 0; i < m_function->nParams(); ++i) {
311 if (!m_function->isActive(i))
312 continue;
313 API::IConstraint *c = m_function->getConstraint(i);
314 if (c) {
315 m_value += c->check();
316 }
317 }
318 }
319
320 m_dirtyVal = false;
321 return m_value;
322}
323
327void CostFuncFitting::deriv(std::vector<double> &der) const {
328 valDerivHessian(true, false);
329
330 if (der.size() != nParams()) {
331 der.resize(nParams());
332 }
333 for (size_t i = 0; i < nParams(); ++i) {
334 der[i] = m_der.get(i);
335 }
336}
337
342double CostFuncFitting::valAndDeriv(std::vector<double> &der) const {
343 valDerivHessian(true, false);
344
345 if (der.size() != nParams()) {
346 der.resize(nParams());
347 }
348 for (size_t i = 0; i < nParams(); ++i) {
349 der[i] = m_der.get(i);
350 }
351 return m_value;
352}
353
359double CostFuncFitting::valDerivHessian(bool evalDeriv, bool evalHessian) const {
360 if (m_pushed || !evalDeriv) {
361 return val();
362 }
363
365 return m_value;
366 }
367
368 const size_t numParams = nParams();
369
371
372 m_value = 0.0;
373
374 m_der.resize(numParams);
375 m_der.zero();
376 if (evalHessian) {
377 m_hessian.resize(numParams, numParams);
378 m_hessian.zero();
379 }
380
381 auto seqDomain = std::dynamic_pointer_cast<SeqDomain>(m_domain);
382
383 if (seqDomain) {
384 seqDomain->additiveCostFunctionValDerivHessian(*this, evalDeriv, evalHessian);
385 } else {
386 if (!m_values) {
387 throw std::runtime_error("CostFunction: undefined FunctionValues.");
388 }
389 addValDerivHessian(m_function, m_domain, m_values, evalDeriv, evalHessian);
390 }
391
392 // Add constraints penalty
393 size_t np = m_function->nParams();
394 if (m_includePenalty) {
395 for (size_t i = 0; i < np; ++i) {
396 API::IConstraint *c = m_function->getConstraint(i);
397 if (c) {
398 m_value += c->check();
399 }
400 }
401 }
402 m_dirtyVal = false;
403
404 if (evalDeriv) {
405 if (m_includePenalty) {
406 size_t i = 0;
407 for (size_t ip = 0; ip < np; ++ip) {
408 if (!m_function->isActive(ip))
409 continue;
410 API::IConstraint *c = m_function->getConstraint(ip);
411 if (c) {
412 double d = m_der.get(i) + c->checkDeriv();
413 m_der.set(i, d);
414 }
415 ++i;
416 }
417 }
418 m_dirtyDeriv = false;
419
420 if (m_includePenalty) {
421 size_t i = 0;
422 for (size_t ip = 0; ip < np; ++ip) {
423 if (!m_function->isActive(ip))
424 continue;
425 API::IConstraint *c = m_function->getConstraint(ip);
426 if (c && !m_hessian.isEmpty()) {
427 double d = m_hessian.get(i, i) + c->checkDeriv2();
428 m_hessian.set(i, i, d);
429 }
430 ++i;
431 }
432 }
433 // clear the dirty flag if hessian was actually calculated
435 }
436
437 return m_value;
438}
439
444 if (m_pushed) {
445 return m_der;
446 }
449 }
450 return m_der;
451}
452
457 if (m_pushed) {
458 return m_hessian;
459 }
462 }
463 return m_hessian;
464}
465
470 if (m_pushed) {
471 throw std::runtime_error("Cost Function: double push.");
472 }
473 // make sure we are not dirty
476 m_pushed = true;
477}
478
483 if (!m_pushed) {
484 throw std::runtime_error("Cost Function: empty stack.");
485 }
488 m_pushed = false;
489 m_dirtyVal = false;
490 m_dirtyDeriv = false;
491 m_dirtyHessian = false;
492}
493
498 if (!m_pushed) {
499 throw std::runtime_error("Cost Function: empty stack.");
500 }
501 m_pushed = false;
502 setDirty();
503}
504
510 return;
511 }
512
513 for (size_t i = 0; i < m_values->size(); i++) {
514 if (m_values->getFitWeight(i) < 0) {
515 throw std::runtime_error("Invalid data found at point=" + std::to_string(i) + " in fit weight.");
516 }
517 }
518}
519
520} // 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.
void zero()
Set all elements to zero.
size_t size2() const
Second size of the matrix.
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
MANTID_CURVEFITTING_DLL Eigen::MatrixXd covar_from_jacobian(const map_type &J, double epsrel)
Mimics gsl_multifit_covar(J, epsrel, covar)
std::string to_string(const wide_integer< Bits, Signed > &n)