Mantid
Loading...
Searching...
No Matches
CostFuncPoisson.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#include "MantidKernel/Logger.h"
18
19#include <cmath>
20#include <limits>
21
22using namespace Mantid::API;
23
24namespace {
25// predicted < 0 is forbidden as it causes inf cost
26const double absoluteCutOff = 0.0;
27const double effectiveCutOff = 0.0001;
28
29double calculatePoissonLoss(double observedCounts, double predicted) {
30 double retVal = (predicted - observedCounts);
31 retVal += observedCounts * (log(observedCounts) - log(predicted));
32 return retVal;
33}
34
35} // namespace
36
38
39DECLARE_COSTFUNCTION(CostFuncPoisson, Poisson)
40//----------------------------------------------------------------------------------------------
44
52 m_function->function(*domain, *values);
53 size_t ny = values->size();
54
55 double retVal = 0.0;
56
57 for (size_t i = 0; i < ny; i++) {
58 const double predicted = values->getCalculated(i);
59
60 if (predicted <= absoluteCutOff) {
61 retVal = std::numeric_limits<double>::infinity();
62 break;
63 }
64
65 const double observed = values->getFitData(i);
66 if (predicted <= effectiveCutOff) {
67 retVal += (effectiveCutOff - predicted) / predicted;
68 } else if (observed == 0.0) {
69 // at observed = 0 the Poisson function reduces to simply adding predicted
70 retVal += predicted;
71 } else {
72 retVal += calculatePoissonLoss(observed, predicted);
73 }
74 }
75
77 m_value += 2.0 * retVal;
78}
79
90 API::FunctionValues_sptr values, bool evalDeriv, bool evalHessian) const {
91 const size_t numParams = nParams();
92
93 if (evalDeriv) {
94 m_der.resize(numParams);
95 m_der.zero();
96 calculateDerivative(*function, *domain, *values);
97 }
98
99 if (evalHessian) {
100 m_hessian.resize(numParams, numParams);
101 m_hessian.zero();
102 calculateHessian(*function, *domain, *values);
103 }
104}
105
107 FunctionValues &values) const {
108 const size_t numParams = function.nParams();
109 const size_t numDataPoints = domain.size();
110
111 Jacobian jacobian(numDataPoints, numParams);
112 function.function(domain, values);
113 function.functionDeriv(domain, jacobian);
114
115 size_t activeParamIndex = 0;
116 double costVal = 0.0;
117
118 for (size_t paramIndex = 0; paramIndex < numParams; ++paramIndex) {
119 if (!function.isActive(paramIndex))
120 continue;
121
122 double determinant = 0.0;
123 for (size_t i = 0; i < numDataPoints; ++i) {
124 double calc = values.getCalculated(i);
125 double obs = values.getFitData(i);
126
127 if (calc <= absoluteCutOff) {
128 if (activeParamIndex == 0) {
129 costVal += std::numeric_limits<double>::infinity();
130 }
131 determinant += std::numeric_limits<double>::infinity();
132 continue;
133 }
134
135 if (calc <= effectiveCutOff) {
136 if (activeParamIndex == 0) {
137 costVal += (effectiveCutOff - calc) / (calc - absoluteCutOff);
138 }
139 double tmp = calc - absoluteCutOff;
140 determinant += jacobian.get(i, paramIndex) * (absoluteCutOff - effectiveCutOff) / (tmp * tmp);
141
142 } else if (obs == 0.0) {
143 if (activeParamIndex == 0) {
144 costVal += calc;
145 }
146 determinant += jacobian.get(i, paramIndex);
147
148 } else {
149 if (activeParamIndex == 0) {
150 costVal += calculatePoissonLoss(obs, calc);
151 }
152 determinant += jacobian.get(i, paramIndex) * (1.0 - obs / calc);
153 }
154 }
155 PARALLEL_CRITICAL(der_set) {
156 double der = m_der.get(activeParamIndex);
157 m_der.set(activeParamIndex, der + determinant);
158 }
159 ++activeParamIndex;
160 }
161
163 m_value += 2.0 * costVal;
164}
165
167 const API::FunctionValues &values) const {
168 size_t numParams = function.nParams(); // number of parameters
169 size_t numDataPoints = domain.size(); // number of data points
170
171 Jacobian jacobian(numDataPoints, numParams);
172 function.functionDeriv(domain, jacobian);
173
174 size_t activeParamFirstIndex = 0; // The params are split into two halves and iterated through
175 for (size_t paramIndex = 0; paramIndex < numParams; ++paramIndex) {
176
177 if (!function.isActive(paramIndex))
178 continue;
179 size_t activeParamSecondIndex = 0; // The counterpart index
180 double parameter = function.getParameter(paramIndex);
181
182 double scalingFactor = 1e-4;
183 if (parameter != 0.0) {
184 scalingFactor *= parameter;
185 }
186
187 function.setParameter(paramIndex, parameter + scalingFactor);
188 Jacobian jacobian2(numDataPoints, numParams);
189 function.functionDeriv(domain, jacobian2);
190 function.setParameter(paramIndex, parameter);
191
192 for (size_t j = 0; j <= paramIndex; ++j) // over ~ half of parameters
193 {
194 if (!function.isActive(j))
195 continue;
196 double d = 0.0;
197 for (size_t k = 0; k < numDataPoints; ++k) // over fitting data
198 {
199 double d2 = (jacobian2.get(k, j) - jacobian.get(k, j)) / scalingFactor;
200
201 double calc = values.getCalculated(k);
202 double obs = values.getFitData(k);
203 if (calc <= absoluteCutOff) {
204 d += std::numeric_limits<double>::infinity();
205 } else {
206 if (calc <= effectiveCutOff) {
207 double constrainedCalc = calc - absoluteCutOff;
208 d += d2 * (absoluteCutOff - effectiveCutOff) / (constrainedCalc * constrainedCalc);
209 d += jacobian.get(k, paramIndex) * jacobian.get(k, j) * (effectiveCutOff - absoluteCutOff) * 2 /
210 (constrainedCalc * constrainedCalc * constrainedCalc);
211 } else if (obs == 0.0) {
212 d += d2;
213 } else {
214 d += d2 * (1.0 - obs / calc);
215 d += jacobian.get(k, paramIndex) * jacobian.get(k, j) * obs / (calc * calc);
216 }
217 }
218 }
219 PARALLEL_CRITICAL(hessian_set) {
220 double h = m_hessian.get(activeParamFirstIndex, activeParamSecondIndex) + d;
221 m_hessian.set(activeParamFirstIndex, activeParamSecondIndex, h);
222 if (activeParamFirstIndex != activeParamSecondIndex) {
223 m_hessian.set(activeParamSecondIndex, activeParamFirstIndex, h);
224 }
225 }
226 ++activeParamSecondIndex;
227 }
228 ++activeParamFirstIndex;
229 }
230}
231
232} // namespace Mantid::CurveFitting::CostFunctions
gsl_vector * tmp
#define DECLARE_COSTFUNCTION(classname, username)
Macro for declaring a new type of cost functions to be used with the CostFunctionFactory.
Definition: ICostFunction.h:66
#define PARALLEL_CRITICAL(name)
#define PARALLEL_ATOMIC
Base class that represents the domain of a function.
virtual size_t size() const =0
Return the number of points in the domain.
A class to store values calculated by a function.
double getFitData(size_t i) const
Get a fitting data value.
double getCalculated(size_t i) const
Get i-th calculated value.
This is an interface to a fitting function - a semi-abstarct class.
Definition: IFunction.h:163
virtual void functionDeriv(const FunctionDomain &domain, Jacobian &jacobian)
Derivatives of function with respect to active parameters.
Definition: IFunction.cpp:155
virtual size_t nParams() const =0
Total number of parameters.
bool isActive(size_t i) const
Check if an active parameter i is actually active.
Definition: IFunction.cpp:160
virtual double getParameter(size_t i) const =0
Get i-th parameter.
virtual void setParameter(size_t, const double &value, bool explicitlySet=true)=0
Set i-th parameter.
virtual void function(const FunctionDomain &domain, FunctionValues &values) const =0
Evaluates the function for all arguments in the domain.
Represents the Jacobian in IFitFunction::functionDeriv.
Definition: Jacobian.h:22
virtual double get(size_t iY, size_t iP)=0
Get the value to a Jacobian matrix element.
A semi-abstract class for a cost function for fitting functions.
size_t nParams() const override
Number of parameters.
API::IFunction_sptr m_function
Shared pointer to the fitting function.
CostFuncPoisson : Implements a cost function for fitting applications using a Poisson measure.
void calculateHessian(API::IFunction &function, const API::FunctionDomain &domain, const API::FunctionValues &values) const
Calculates the Hessian matrix for the addValDerivHessian method.
void addValDerivHessian(API::IFunction_sptr function, API::FunctionDomain_sptr domain, API::FunctionValues_sptr values, bool evalDeriv=true, bool evalHessian=true) const override
Update the cost function, derivatives and hessian by adding values calculated on a domain.
void addVal(API::FunctionDomain_sptr domain, API::FunctionValues_sptr values) const override
Add a contribution to the cost function value from the fitting function evaluated on a particular dom...
void calculateDerivative(API::IFunction &function, const API::FunctionDomain &domain, API::FunctionValues &values) const
Calculates the derivative for the addValDerivHessian method.
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.
void zero()
Set all elements to zero.
void resize(const size_t nx, const size_t ny)
Resize the matrix.
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.
Definition: EigenVector.cpp:96
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:732
std::shared_ptr< FunctionDomain > FunctionDomain_sptr
typedef for a shared pointer