Jpp 21.0.0-rc.1-88-g0130508c4
the software that should make you happy
Loading...
Searching...
No Matches
JGradient.hh
Go to the documentation of this file.
1#ifndef __JFIT__JGRADIENT__
2#define __JFIT__JGRADIENT__
3
4#include <limits>
5#include <vector>
6#include <cmath>
7#include <memory>
8#include <ostream>
9#include <iomanip>
10#include <vector>
11
12#include "JLang/JManip.hh"
13#include "JLang/JException.hh"
14#include "Jeep/JMessage.hh"
15
16
17/**
18 * \author mdejong
19 */
20
21namespace JFIT {}
22namespace JPP { using namespace JFIT; }
23
24namespace JFIT {
25
26 /**
27 * Auxiliary data structure for fit parameter.
28 */
29 struct JParameter_t {
30 /**
31 * Virtual destructor.
32 */
33 virtual ~JParameter_t()
34 {}
35
36 /**
37 * Apply step.
38 *
39 * \param step step
40 */
41 virtual void apply(const double step) = 0;
42 };
43
44
45 /**
46 * Auxiliary data structure for editable parameter.
47 */
48 struct JModifier_t :
49 public std::shared_ptr<JParameter_t>
50 {
51 /**
52 * Constructor.
53 *
54 * \param name name
55 * \param parameter parameter
56 * \param value value
57 */
58 JModifier_t(const std::string& name,
59 JParameter_t* parameter,
60 const double value) :
61 std::shared_ptr<JParameter_t>(parameter),
62 name (name),
64 {}
65
66 std::string name;
67 double value;
68 };
69
70
71 /**
72 * Conjugate gradient fit.
73 */
74 struct JGradient :
75 public std::vector<JModifier_t>
76 {
77 /**
78 * Constructor.
79 *
80 * The number of iterations and epsilon refer to the number of steps and
81 * the distance to the minimum, respectively.\n
82 * The number of extra steps can be used to overcome a possible hurdle on the way.
83 *
84 * \param Nmax maximum number of iterations
85 * \param Nextra maximum number of extra steps
86 * \param epsilon epsilon
87 * \param debug debug
88 */
89 JGradient(const size_t Nmax = std::numeric_limits<size_t>::max(),
90 const size_t Nextra = 0,
91 const double epsilon = 1.0e-4,
92 const int debug = 3) :
93 Nmax (Nmax),
94 Nextra (Nextra),
96 debug (debug)
97 {}
98
99
100 /**
101 * Fit.
102 *
103 * The template parameter should provide for the following function operator.
104 * <pre>
105 * double operator()(int option);
106 * </pre>
107 * The value of the option corresponds to the following cases.
108 * - 0 => step wise improvement of the chi2;
109 * - 1 => evaluation of the chi2 before the determination of the gradient of the chi2; and
110 * - 2 => evaluation of the derivative of the chi2 to each fit parameter.
111 *
112 * \param getChi2 chi2 function
113 * \return chi2
114 */
115 template<class T>
116 double operator()(const T& getChi2)
117 {
118 using namespace std;
119 using namespace JPP;
120
121 vector<double> chi2(5, numeric_limits<double>::max());
122
123 if (this->empty()) {
124 return numeric_limits<double>::max();
125 }
126
127 chi2[0] = this->evaluate(getChi2);
128
129 const size_t N = this->size();
130
131 vector<double> H(N);
132 vector<double> G(N);
133
134 for (size_t i = 0; i != N; ++i) {
135 G[i] = -1.0 * gradient[i];
136 H[i] = G[i];
137 gradient[i] = H[i];
138 }
139
141
143
144 DEBUG("chi2[0] " << setw(4) << numberOfIterations << ' ' << FIXED(12,5) << chi2[0] << endl);
145
146 // minimise chi2 in direction of gradient
147
148 chi2[1] = chi2[0];
149 chi2[2] = chi2[1];
150
151 size_t m = 0;
152
153 for (double ds = 1.0; ds > 1.0e-3; ) {
154
155 this->move(+1.0 * ds);
156
157 chi2[3] = getChi2(0);
158
159 DEBUG("chi2[3] " << setw(4) << m << ' ' << FIXED(12,5) << chi2[3] << ' ' << FIXED(12,5) << ds << endl);
160
161 if (chi2[3] < chi2[2]) {
162
163 chi2[1] = chi2[2];
164 chi2[2] = chi2[3];
165
166 m = 0;
167
168 continue;
169 }
170
171 if (ds == 1.0) {
172
173 if (m == 0) {
174 chi2[4] = chi2[3];
175 }
176
177 if (m != Nextra) {
178
179 ++m;
180
181 continue;
182
183 } else {
184
185 for ( ; m != 0; --m) {
186 this->move(-1.0 * ds);
187 }
188
189 chi2[3] = chi2[4];
190 }
191 }
192
193 this->move(-1.0 * ds);
194
195 if (chi2[2] < chi2[3]) {
196
197 // final step based on parabolic interpolation through following points
198 //
199 // x1 = -1 * ds -> chi2[1]
200 // x2 = 0 * ds -> chi2[2]
201 // x3 = +1 * ds -> chi2[3]
202
203 const double f21 = chi2[2] - chi2[1]; // f(x2) - f(x1)
204 const double f23 = chi2[2] - chi2[3]; // f(x2) - f(x3)
205
206 const double xs = 0.5 * (f21 - f23) / (f23 + f21);
207
208 this->move(+1.0 * xs * ds);
209
210 chi2[3] = getChi2(0);
211
212 if (chi2[3] < chi2[2]) {
213
214 chi2[2] = chi2[3];
215
216 } else {
217
218 this->move(-1.0 * xs * ds);
219
220 chi2[2] = getChi2(0);
221 }
222
223 DEBUG("chi2[2] " << setw(4) << numberOfIterations << ' ' << FIXED(12,5) << chi2[2] << ' ' << SCIENTIFIC(12,5) << ds << endl);
224
225 break;
226
227 } else {
228
229 ds *= 0.5;
230 }
231 }
232
233 if (fabs(chi2[2] - chi2[0]) < epsilon * (absolute ? 1.0 : 0.5 * (fabs(chi2[0]) + fabs(chi2[2])))) {
234
235 chi2[0] = chi2[2];
236
237 break;
238 }
239
240 chi2[0] = this->evaluate(getChi2);
241
242 double gg = 0.0;
243 double dgg = 0.0;
244
245 for (size_t i = 0; i != N; ++i){
246 gg += G[i]*G[i];
247 dgg += (gradient[i] + G[i]) * gradient[i];
248 }
249
250 if (gg == 0.0) {
251 break;
252 }
253
254 dgg /= gg;
255
256 for (size_t i = 0; i != N; ++i){
257 G[i] = -1.0 * gradient[i];
258 H[i] = G[i] + dgg * H[i];
259 gradient[i] = H[i];
260 }
261 }
262
263 DEBUG("chi2[0] " << setw(4) << numberOfIterations << ' ' << FIXED(12,5) << chi2[0] << endl);
264
265 return chi2[0];
266 }
267
268
269 /**
270 * Make scan at current position.
271 *
272 * \param out output stream
273 * \param getChi2 chi2 function
274 * \param xs set of steps
275 */
276 template<class T>
277 void operator()(std::ostream& out, const T& getChi2, const std::vector<double>& xs) const
278 {
279 using namespace std;
280
281 out << "scan: ";
282
283 for (const auto& i : *this) {
284 out << i.name << ' ';
285 }
286
287 out << "chi2" << endl;
288
290
291 scan(out, getChi2, xs, ys);
292 }
293
294
295 size_t Nmax; //!< maximum number of iterations
296 size_t Nextra; //!< maximum number of extra steps
297 double epsilon; //!< epsilon
298 int debug; //!< debug
299
300 bool absolute = false; //!< absolute chi2
301 bool normalise = false; //!< normalise gradient
302
304
305 private:
306 /**
307 * Evaluate gradient.
308 *
309 * \return chi2
310 */
311 template<class T>
312 double evaluate(const T& getChi2)
313 {
314 using namespace std;
315 using namespace JPP;
316
317 const size_t N = this->size();
318
319 gradient.resize(N);
320
321 for (std::vector<double>::iterator i = gradient.begin(); i != gradient.end(); ++i) {
322 *i = 0.0;
323 }
324
325 const double x0 = getChi2(1);
326
327 size_t width = 1;
328
329 for (size_t i = 0; i != N; ++i) {
330 if ((*this)[i].name.size() > width) {
331 width = (*this)[i].name.size();
332 }
333 }
334
335 double V = 0.0;
336
337 for (size_t i = 0; i != N; ++i) {
338
339 if ((*this)[i].value != 0.0) {
340
341 (*this)[i]->apply(+0.5 * (*this)[i].value);
342
343 const double x1 = getChi2(2);
344
345 (*this)[i]->apply(-0.5 * (*this)[i].value);
346 (*this)[i]->apply(-0.5 * (*this)[i].value);
347
348 const double x2 = getChi2(2);
349
350 gradient[i] = x1 - x2;
351
352 (*this)[i]->apply(+0.5 * (*this)[i].value);
353
354 DEBUG(setw(width) << left << (*this)[i].name << right << ' ' << FIXED(12,5) << (*this)[i].value << ' ' << FIXED(12,5) << gradient[i] << endl);
355
356 } else {
357
358 gradient[i] = 0.0;
359 }
360
361 V += gradient[i] * gradient[i];
362 }
363
364 V = sqrt(V);
365
366 DEBUG(setw(width) << left << "|gradient|" << right << ' ' << FIXED(12,5) << V << endl);
367
368 if (normalise) {
369 for (size_t i = 0; i != N; ++i) {
370 gradient[i] /= V;
371 }
372 }
373
374 return x0;
375 }
376
377
378 /**
379 * Move.
380 *
381 * \param factor factor
382 */
383 void move(const double factor)
384 {
385 if (factor > 0.0) {
386 for (size_t i = 0; i != this->size(); ++i) {
387 (*this)[ i ]->apply((*this)[ i ].value * gradient[ i ] * factor);
388 }
389 } else if (factor < 0.0) {
390 for (size_t i = this->size(); i != 0; --i) {
391 (*this)[i-1]->apply((*this)[i-1].value * gradient[i-1] * factor);
392 }
393 }
394 }
395
396
397 /**
398 * Scan.
399 *
400 * \param out output stream
401 * \param getChi2 chi2 function
402 * \param xs set of steps
403 * \param ys set of current steps
404 */
405 template<class T>
406 void scan(std::ostream& out, const T& getChi2, const std::vector<double>& xs, std::vector<double>& ys) const
407 {
408 using namespace std;
409
410 if (ys.size() == this->size()) {
411
412 for (size_t i = 0; i != this->size(); ++i) {
413 (*this)[ i ]->apply(+ys[ i ]);
414 }
415
416 for (const double y : ys) {
417 out << FIXED(12,5) << y << ' ';
418 }
419
420 out << FIXED(12,5) << getChi2(1) << endl;
421
422 for (size_t i = this->size(); i != 0; --i) {
423 (*this)[i-1]->apply(-ys[i-1]);
424 }
425
426 } else {
427
428 for (const double x : xs) {
429
430 ys.push_back(x * (*this)[ys.size()].value);
431
432 scan(out, getChi2, xs, ys);
433
434 ys.pop_back();
435 }
436 }
437 }
438
440 };
441}
442
443#endif
Exceptions.
I/O manipulators.
General purpose messaging.
#define DEBUG(A)
Message macros.
Definition JMessage.hh:62
Auxiliary classes and methods for linear and iterative data regression.
double getChi2(const double P)
Get chi2 corresponding to given probability.
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
Conjugate gradient fit.
Definition JGradient.hh:76
size_t Nmax
maximum number of iterations
Definition JGradient.hh:295
bool normalise
normalise gradient
Definition JGradient.hh:301
void operator()(std::ostream &out, const T &getChi2, const std::vector< double > &xs) const
Make scan at current position.
Definition JGradient.hh:277
void move(const double factor)
Move.
Definition JGradient.hh:383
void scan(std::ostream &out, const T &getChi2, const std::vector< double > &xs, std::vector< double > &ys) const
Scan.
Definition JGradient.hh:406
std::vector< double > gradient
Definition JGradient.hh:439
bool absolute
absolute chi2
Definition JGradient.hh:300
double evaluate(const T &getChi2)
Evaluate gradient.
Definition JGradient.hh:312
double operator()(const T &getChi2)
Fit.
Definition JGradient.hh:116
double epsilon
epsilon
Definition JGradient.hh:297
JGradient(const size_t Nmax=std::numeric_limits< size_t >::max(), const size_t Nextra=0, const double epsilon=1.0e-4, const int debug=3)
Constructor.
Definition JGradient.hh:89
size_t numberOfIterations
Definition JGradient.hh:303
size_t Nextra
maximum number of extra steps
Definition JGradient.hh:296
Auxiliary data structure for editable parameter.
Definition JGradient.hh:50
std::string name
Definition JGradient.hh:66
JModifier_t(const std::string &name, JParameter_t *parameter, const double value)
Constructor.
Definition JGradient.hh:58
Auxiliary data structure for fit parameter.
Definition JGradient.hh:29
virtual void apply(const double step)=0
Apply step.
virtual ~JParameter_t()
Virtual destructor.
Definition JGradient.hh:33
Auxiliary data structure for floating point format specification.
Definition JManip.hh:488