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