Jpp  master_rocky
the software that should make you happy
JGandalf.hh
Go to the documentation of this file.
1 #ifndef __JFIT__JGANDALF__
2 #define __JFIT__JGANDALF__
3 
4 #include <limits>
5 #include <vector>
6 #include <cmath>
7 #include <ostream>
8 #include <iomanip>
9 #include <type_traits>
10 
11 #include "Jeep/JMessage.hh"
12 #include "JMath/JVectorND.hh"
13 #include "JMath/JMatrixNS.hh"
14 #include "JMath/JZero.hh"
15 #include "JLang/JManip.hh"
16 #include "JLang/JException.hh"
17 
18 
19 /**
20  * \author mdejong
21  */
22 
23 namespace JFIT {}
24 namespace JPP { using namespace JFIT; }
25 
26 namespace JFIT {
27 
28  using JEEP::JMessage;
29  using JLANG::JException;
30 
31  namespace JFIT_LOCAL {
32 
33  template<class T>
34  class JTypedef {
35  template<class U> static auto parameter_type(U*) -> decltype(std::declval<typename U::parameter_type>());
36  template<typename> static auto parameter_type(...) -> std::false_type;
37 
38  public:
39  static const bool has_parameter_type = !std::is_same<std::false_type, decltype(parameter_type<T> (0))>::value;
40  };
41 
42  template<class T, bool has_parameter_type = JTypedef<T>::has_parameter_type>
43  struct JTypedef_t;
44 
45  template<class T> struct JTypedef_t<T, true> { typedef typename T::parameter_type parameter_type; };
46  template<class T> struct JTypedef_t<T, false> { typedef double T::*parameter_type; };
47  }
48 
49 
50  /**
51  * Auxiliary function to constrain model during fit.
52  *
53  * \param value model (I/O)
54  */
55  template<class JModel_t>
56  inline void model(JModel_t& value)
57  {}
58 
59 
60  /**
61  * Fit method based on the Levenberg-Marquardt method.
62  *
63  * The template argument refers to the model that should be fitted to the data.\n
64  * This data structure should have arithmetic capabilities.
65  *
66  * The data member JGandalf::value corresponds to the start c.q.\ final value of
67  * the model of the fit procedure and JGandalf::error to the uncertainties.\n
68  * The co-variance matrix is stored in data member JGandalf::V.\n
69  *
70  * The data member JGandalf::parameters constitutes a list of those parameters of the model that should actually be fitted.\n
71  * For this, the model should contain the type definition for <tt>parameter_type</tt>.\n
72  * Normally, this type definition corresponds to a pointer to a data member of the model.\n
73  * If not defined, the parameters are assumed to be data members of type <tt>double</tt>.\n
74  * Alternatively, the type definition can be <tt>size_t</tt> or <tt>int</tt>.\n
75  * In that case, the model should provide for the element access <tt>operator[]</tt>.\n
76  *
77  * The first template parameter in the function operator should provide for an implementation of the actual fit function.\n
78  * This function should return the data type JGandalf::result_type.\n
79  * This data structure comprises the values of the chi2 and the gradient for a given data point.\n
80  * The function operator returns the minimal chi2 and summed gradient of all data points.
81  */
82 
83  template<class JModel_t>
84  class JGandalf :
85  public JMessage< JGandalf<JModel_t> >
86  {
87  public:
88 
90 
91 
92  /**
93  * Data type of fit parameter.
94  */
96 
97 
98  /**
99  * Data structure for return value of fit function.
100  */
101  struct result_type {
102  /**
103  * Default constructor.
104  */
106  chi2 (0.0),
107  gradient()
108  {}
109 
110 
111  /**
112  * Constructor.
113  *
114  * \param chi2 chi2
115  * \param model gradient
116  */
117  result_type(const double chi2,
118  const JModel_t& model) :
119  chi2 (chi2),
120  gradient(model)
121  {}
122 
123 
124  /**
125  * Type conversion operator.
126  *
127  * \return chi2
128  */
129  operator double() const
130  {
131  return chi2;
132  }
133 
134 
135  double chi2; //!< chi2
136  JModel_t gradient; //!< partial derivatives of chi2
137  };
138 
139 
140  /**
141  * Default constructor.
142  */
144  {}
145 
146 
147  /**
148  * Multi-dimensional fit of multiple data sets.
149  *
150  * The fit function should return the chi2 as well as the partial derivatives
151  * for the current value of the model and a given data point.
152  *
153  * \param fit fit function
154  * \param __begin begin of data
155  * \param __end end of data
156  * \param args optional data
157  * \return chi2 and gradient
158  */
159  template<class JFunction_t, class T, class ...Args>
160  result_type operator()(const JFunction_t& fit, T __begin, T __end, Args ...args)
161  {
162  using namespace std;
163  using namespace JPP;
164 
165  // note that all model values should be assigned to the start value of the model before use
166  // because the actual list of model parameters can vary from fit to fit
167  // (e.g. if model consists of a container).
168 
169  const size_t N = parameters.size();
170 
171  V.resize(N);
172  h.resize(N);
173  x.resize(N);
174 
175  previous.result.chi2 = numeric_limits<double>::max();
176 
177  current.result.chi2 = numeric_limits<double>::max();
178  current.result.gradient = value;
179  current.result.gradient = zero;
180 
181  error = value;
182  error = zero;
183 
184  lambda = LAMBDA_MIN;
185 
187 
188  DEBUG("step: " << numberOfIterations << endl);
189 
190  reset();
191 
192  update(fit, __begin, __end, args...);
193 
194  DEBUG("lambda: " << FIXED(12,5) << lambda << endl);
195  DEBUG("chi2: " << FIXED(12,5) << current.result.chi2 << endl);
196 
197  if (current.result.chi2 < previous.result.chi2) {
198 
199  if (numberOfIterations != 0) {
200 
201  const double tolerance = EPSILON * (EPSILON_ABSOLUTE ? 1.0 : fabs(previous.result.chi2));
202 
203  if (fabs(previous.result.chi2 - current.result.chi2) <= tolerance) {
204 
205  // normal end
206 
207  const result_type result = current.result;
208 
209  reset();
210 
211  update(fit, __begin, __end, args...);
212 
213  try {
214  V.invert();
215  }
216  catch (const exception& error) {}
217 
218  for (size_t i = 0; i != N; ++i) {
219  get(error, parameters[i]) = sqrt(V(i,i));
220  }
221 
222  return result;
223  }
224 
225  if (lambda > LAMBDA_MIN) {
226  lambda /= LAMBDA_DOWN;
227  }
228  }
229 
230  // store current values
231 
232  previous.value = value;
233  previous.result = current.result;
234 
235  } else {
236 
237  value = previous.value; // restore value
238 
239  lambda *= LAMBDA_UP;
240 
241  if (lambda > LAMBDA_MAX) {
242  break;
243  }
244 
245  reset();
246 
247  update(fit, __begin, __end, args...);
248  }
249 
250  DEBUG("Hesse matrix:" << endl << V << endl);
251 
252  // force definite positiveness
253 
254  for (size_t i = 0; i != N; ++i) {
255 
256  if (V(i,i) < PIVOT) {
257  V(i,i) = PIVOT;
258  }
259 
260  h[i] = 1.0 / sqrt(V(i,i));
261  }
262 
263  // normalisation
264 
265  for (size_t row = 0; row != N; ++row) {
266  for (size_t col = 0; col != row; ++col) {
267  V(row,col) *= h[row] * h[col];
268  V(col,row) = V(row,col);
269  }
270  }
271 
272  for (size_t i = 0; i != N; ++i) {
273  V(i,i) = 1.0 + lambda;
274  }
275 
276  // solve A x = b
277 
278  for (size_t col = 0; col != N; ++col) {
279  x[col] = h[col] * get(current.result.gradient, parameters[col]);
280  }
281 
282  try {
283  V.solve(x);
284  }
285  catch (const exception& error) {
286 
287  ERROR("JGandalf: " << error.what() << endl << V << endl);
288 
289  break;
290  }
291 
292  // update value
293 
294  for (size_t row = 0; row != N; ++row) {
295 
296  DEBUG("u[" << noshowpos << setw(3) << row << "] = " << showpos << FIXED(15,5) << get(value, parameters[row]));
297 
298  get(value, parameters[row]) -= h[row] * x[row];
299 
300  DEBUG(" -> " << FIXED(15,5) << get(value, parameters[row]) << noshowpos << endl);
301  }
302 
303  model(value);
304  }
305 
306  // abnormal end
307 
308  const result_type result = previous.result;
309 
310  value = previous.value; // restore value
311 
312  reset();
313 
314  update(fit, __begin, __end, args...);
315 
316  try {
317  V.invert();
318  }
319  catch (const exception& error) {}
320 
321  for (size_t i = 0; i != N; ++i) {
322  get(error, parameters[i]) = sqrt(V(i,i));
323  }
324 
325  return result;
326  }
327 
328 
329  static int MAXIMUM_ITERATIONS; //!< maximal number of iterations
330  static double EPSILON; //!< maximal distance to minimum
331  static bool EPSILON_ABSOLUTE; //!< set epsilon to absolute difference instead of relative
332  static double LAMBDA_MIN; //!< minimal value control parameter
333  static double LAMBDA_MAX; //!< maximal value control parameter
334  static double LAMBDA_UP; //!< multiplication factor control parameter
335  static double LAMBDA_DOWN; //!< multiplication factor control parameter
336  static double PIVOT; //!< minimal value diagonal element of Hesse matrix
337 
339  int numberOfIterations; //!< number of iterations
340  double lambda; //!< control parameter
341  JModel_t value; //!< value
342  JModel_t error; //!< error
343  JMATH::JMatrixNS V; //!< Hesse matrix
344 
345  private:
346  /**
347  * Reset current parameters.
348  */
349  void reset()
350  {
351  using namespace JPP;
352 
353  current.result.chi2 = 0.0;
354  current.result.gradient = zero;
355 
356  V.reset();
357  }
358 
359 
360  /**
361  * Recursive method to update current parameters.
362  *
363  * \param fit fit function
364  * \param __begin begin of data
365  * \param __end end of data
366  * \param args optional data
367  */
368  template<class JFunction_t, class T, class ...Args>
369  inline void update(const JFunction_t& fit, T __begin, T __end, Args ...args)
370  {
371  for (T i = __begin; i != __end; ++i) {
372 
373  const result_type& result = fit(value, *i);
374 
375  current.result.chi2 += result.chi2;
376  current.result.gradient += result.gradient;
377 
378  for (size_t row = 0; row != parameters.size(); ++row) {
379  for (size_t col = row; col != parameters.size(); ++col) {
380  V(row,col) += get(result.gradient, parameters[row]) * get(result.gradient, parameters[col]);
381  }
382  }
383  }
384 
385  update(fit, args...);
386  }
387 
388 
389  /**
390  * Termination method to update current parameters.
391  *
392  * \param fit fit function
393  */
394  template<class JFunction_t>
395  inline void update(const JFunction_t& fit)
396  {
397  for (size_t row = 0; row != parameters.size(); ++row) {
398  for (size_t col = 0; col != row; ++col) {
399  V(row,col) = V(col,row);
400  }
401  }
402  }
403 
404 
405  /**
406  * Read/write access to parameter value by data member.
407  *
408  * \param model model
409  * \param parameter parameter
410  * \return value
411  */
412  static inline double get(const JModel_t& model, double JModel_t::*parameter)
413  {
414  return model.*parameter;
415  }
416 
417 
418  /**
419  * Read/write access to parameter value by data member.
420  *
421  * \param model model
422  * \param parameter parameter
423  * \return value
424  */
425  static inline double& get(JModel_t& model, double JModel_t::*parameter)
426  {
427  return model.*parameter;
428  }
429 
430 
431  /**
432  * Read/write access to parameter value by index.
433  *
434  * \param model model
435  * \param index index
436  * \return value
437  */
438  static inline double get(const JModel_t& model, const size_t index)
439  {
440  return model[index];
441  }
442 
443 
444  /**
445  * Read/write access to parameter value by index.
446  *
447  * \param model model
448  * \param index index
449  * \return value
450  */
451  static inline double& get(JModel_t& model, const size_t index)
452  {
453  return model[index];
454  }
455 
456 
457  /**
458  * Read/write access to parameter value by index.
459  *
460  * \param model model
461  * \param index index
462  * \return value
463  */
464  static inline double get(const JModel_t& model, const int index)
465  {
466  return model[index];
467  }
468 
469 
470  /**
471  * Read/write access to parameter value by index.
472  *
473  * \param model model
474  * \param index index
475  * \return value
476  */
477  static inline double& get(JModel_t& model, const int index)
478  {
479  return model[index];
480  }
481 
482  std::vector<double> h; // normalisation vector
484 
485  struct {
486  result_type result; // result
488 
489  struct {
490  JModel_t value; // value
491  result_type result; // result
493  };
494 
495 
496  /**
497  * maximal number of iterations.
498  */
499  template<class JModel_t>
501 
502 
503  /**
504  * maximal distance to minimum.
505  */
506  template<class JModel_t>
507  double JGandalf<JModel_t>::EPSILON = 1.0e-3;
508 
509  /**
510  * set epsilon to absolute difference instead of relative.
511  */
512  template<class JModel_t>
514 
515  /**
516  * minimal value control parameter
517  */
518  template<class JModel_t>
519  double JGandalf<JModel_t>::LAMBDA_MIN = 0.01;
520 
521 
522  /**
523  * maximal value control parameter
524  */
525  template<class JModel_t>
526  double JGandalf<JModel_t>::LAMBDA_MAX = 100.0;
527 
528 
529  /**
530  * multiplication factor control parameter
531  */
532  template<class JModel_t>
533  double JGandalf<JModel_t>::LAMBDA_UP = 10.0;
534 
535 
536  /**
537  * multiplication factor control parameter
538  */
539  template<class JModel_t>
540  double JGandalf<JModel_t>::LAMBDA_DOWN = 10.0;
541 
542 
543  /**
544  * minimal value diagonal element of matrix
545  */
546  template<class JModel_t>
548 }
549 
550 #endif
551 
Exceptions.
I/O manipulators.
General purpose messaging.
#define DEBUG(A)
Message macros.
Definition: JMessage.hh:62
#define ERROR(A)
Definition: JMessage.hh:66
Definition of zero value for any class.
Place holder for custom implementation.
Definition: JKatoomba_t.hh:671
static auto parameter_type(...) -> std::false_type
static auto parameter_type(U *) -> decltype(std::declval< typename U::parameter_type >())
static const bool has_parameter_type
Definition: JGandalf.hh:39
Fit method based on the Levenberg-Marquardt method.
Definition: JGandalf.hh:86
void update(const JFunction_t &fit)
Termination method to update current parameters.
Definition: JGandalf.hh:395
double lambda
control parameter
Definition: JGandalf.hh:340
static double LAMBDA_MIN
minimal value control parameter
Definition: JGandalf.hh:332
JMATH::JVectorND x
Definition: JGandalf.hh:483
static double & get(JModel_t &model, const size_t index)
Read/write access to parameter value by index.
Definition: JGandalf.hh:451
struct JFIT::JGandalf::@12 current
void reset()
Reset current parameters.
Definition: JGandalf.hh:349
static double & get(JModel_t &model, double JModel_t::*parameter)
Read/write access to parameter value by data member.
Definition: JGandalf.hh:425
static double LAMBDA_DOWN
multiplication factor control parameter
Definition: JGandalf.hh:335
static double & get(JModel_t &model, const int index)
Read/write access to parameter value by index.
Definition: JGandalf.hh:477
std::vector< parameter_type > parameters
fit parameters
Definition: JGandalf.hh:338
static double get(const JModel_t &model, const size_t index)
Read/write access to parameter value by index.
Definition: JGandalf.hh:438
static double LAMBDA_UP
multiplication factor control parameter
Definition: JGandalf.hh:334
int numberOfIterations
number of iterations
Definition: JGandalf.hh:339
static bool EPSILON_ABSOLUTE
set epsilon to absolute difference instead of relative
Definition: JGandalf.hh:331
std::vector< double > h
Definition: JGandalf.hh:482
static double get(const JModel_t &model, double JModel_t::*parameter)
Read/write access to parameter value by data member.
Definition: JGandalf.hh:412
static int MAXIMUM_ITERATIONS
maximal number of iterations
Definition: JGandalf.hh:329
static double PIVOT
minimal value diagonal element of Hesse matrix
Definition: JGandalf.hh:336
result_type operator()(const JFunction_t &fit, T __begin, T __end, Args ...args)
Multi-dimensional fit of multiple data sets.
Definition: JGandalf.hh:160
void update(const JFunction_t &fit, T __begin, T __end, Args ...args)
Recursive method to update current parameters.
Definition: JGandalf.hh:369
JGandalf()
Default constructor.
Definition: JGandalf.hh:143
result_type result
Definition: JGandalf.hh:486
static double EPSILON
maximal distance to minimum
Definition: JGandalf.hh:330
JModel_t value
value
Definition: JGandalf.hh:341
JFIT_LOCAL::JTypedef_t< JModel_t >::parameter_type parameter_type
Data type of fit parameter.
Definition: JGandalf.hh:95
JMATH::JMatrixNS V
Hesse matrix.
Definition: JGandalf.hh:343
static double LAMBDA_MAX
maximal value control parameter
Definition: JGandalf.hh:333
static double get(const JModel_t &model, const int index)
Read/write access to parameter value by index.
Definition: JGandalf.hh:464
JModel_t error
error
Definition: JGandalf.hh:342
struct JFIT::JGandalf::@13 previous
General exception.
Definition: JException.hh:24
const double epsilon
Definition: JQuadrature.cc:21
Auxiliary classes and methods for linear and iterative data regression.
Definition: JEnergy.hh:15
void model(JModel_t &value)
Auxiliary function to constrain model during fit.
Definition: JGandalf.hh:56
static const JZero zero
Function object to assign zero value.
Definition: JZero.hh:105
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).
Definition: JSTDTypes.hh:14
Auxiliary data structure for floating point format specification.
Definition: JManip.hh:448
Auxiliary class for handling debug parameter within a class.
Definition: JMessage.hh:44
static int debug
debug level (default is off).
Definition: JMessage.hh:45
Data structure for return value of fit function.
Definition: JGandalf.hh:101
result_type()
Default constructor.
Definition: JGandalf.hh:105
result_type(const double chi2, const JModel_t &model)
Constructor.
Definition: JGandalf.hh:117
JModel_t gradient
partial derivatives of chi2
Definition: JGandalf.hh:136
void resize(const size_t size)
Resize matrix.
Definition: JMatrixND.hh:446
JMatrixND & reset()
Set matrix to the null matrix.
Definition: JMatrixND.hh:459
N x N symmetric matrix.
Definition: JMatrixNS.hh:30
void solve(JVectorND_t &u)
Get solution of equation A x = b.
Definition: JMatrixNS.hh:308
void invert()
Invert matrix according LDU decomposition.
Definition: JMatrixNS.hh:75
Fit model.
Definition: JMath/JModel.hh:32
Nx1 matrix.
Definition: JVectorND.hh:23