Jpp master_rocky-44-g75b7c4f75
the software that should make you happy
Loading...
Searching...
No Matches
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
23namespace JFIT {}
24namespace JPP { using namespace JFIT; }
25
26namespace JFIT {
27
28 using JEEP::JMessage;
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),
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
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) {
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 {
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>
541
542
543 /**
544 * minimal value diagonal element of matrix
545 */
546 template<class JModel_t>
547 double JGandalf<JModel_t>::PIVOT = std::numeric_limits<double>::epsilon();
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.
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.
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 int index)
Read/write access to parameter value by index.
Definition JGandalf.hh:477
struct JFIT::JGandalf::@12 current
void reset()
Reset current parameters.
Definition JGandalf.hh:349
static double LAMBDA_DOWN
multiplication factor control parameter
Definition JGandalf.hh:335
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
static double & get(JModel_t &model, double JModel_t::*parameter)
Read/write access to parameter value by data member.
Definition JGandalf.hh:425
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
static double & get(JModel_t &model, const size_t index)
Read/write access to parameter value by index.
Definition JGandalf.hh:451
General exception.
Definition JException.hh:24
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
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).
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
Nx1 matrix.
Definition JVectorND.hh:23