Logo ROOT  
Reference Guide
MnFunctionCross.cxx
Go to the documentation of this file.
1// @(#)root/minuit2:$Id$
2// Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005
3
4/**********************************************************************
5 * *
6 * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT *
7 * *
8 **********************************************************************/
9
12#include "Minuit2/MnMigrad.h"
13#include "Minuit2/FCNBase.h"
14#include "Minuit2/MnParabola.h"
17#include "Minuit2/MnCross.h"
19#include "Minuit2/MnPrint.h"
20
21namespace ROOT {
22
23namespace Minuit2 {
24
25MnCross MnFunctionCross::operator()(const std::vector<unsigned int> &par, const std::vector<double> &pmid,
26 const std::vector<double> &pdir, double tlr, unsigned int maxcalls) const
27{
28 // evaluate crossing point where function is equal to MIN + UP,
29 // with direction pdir from values pmid
30 // tlr indicate tolerance and maxcalls maximum number of calls
31
32 // double edmmax = 0.5*0.001*toler*fFCN.Up();
33
34 unsigned int npar = par.size();
35 unsigned int nfcn = 0;
36 const MnMachinePrecision &prec = fState.Precision();
37 // tolerance used when calling Migrad
38 double mgr_tlr = 0.5 * tlr; // to be consistent with F77 version (for default values of tlr which is 0.1)
39 // other olerance values are fixed at 0.01
40 tlr = 0.01;
41 // convergence when F is within tlf of aim and next prediction
42 // of aopt is within tla of previous value of aopt
43 double up = fFCN.Up();
44 // for finding the point :
45 double tlf = tlr * up;
46 double tla = tlr;
47 unsigned int maxitr = 15;
48 unsigned int ipt = 0;
49 double aminsv = fFval;
50 double aim = aminsv + up;
51 // std::cout<<"aim= "<<aim<<std::endl;
52 double aopt = 0.;
53 bool limset = false;
54 std::vector<double> alsb(3, 0.), flsb(3, 0.);
55
56 MnPrint print("MnFunctionCross");
57
58 print.Debug([&](std::ostream &os) {
59 for (unsigned int i = 0; i < par.size(); ++i)
60 os << "Parameter " << par[i] << " value " << pmid[i] << " dir " << pdir[i] << " function min = " << aminsv
61 << " contur value aim = (fmin + up) = " << aim;
62 });
63
64 // find the largest allowed aulim
65
66 double aulim = 100.;
67 for (unsigned int i = 0; i < par.size(); i++) {
68 unsigned int kex = par[i];
69 if (fState.Parameter(kex).HasLimits()) {
70 double zmid = pmid[i];
71 double zdir = pdir[i];
72 // double zlim = 0.;
73 if (zdir > 0. && fState.Parameter(kex).HasUpperLimit()) {
74 double zlim = fState.Parameter(kex).UpperLimit();
75 if (std::fabs(zdir) < fState.Precision().Eps()) {
76 // we have a limit
77 if (std::fabs(zlim - zmid) < fState.Precision().Eps())
78 limset = true;
79 continue;
80 }
81 aulim = std::min(aulim, (zlim - zmid) / zdir);
82 } else if (zdir < 0. && fState.Parameter(kex).HasLowerLimit()) {
83 double zlim = fState.Parameter(kex).LowerLimit();
84 if (std::fabs(zdir) < fState.Precision().Eps()) {
85 // we have a limit
86 if (std::fabs(zlim - zmid) < fState.Precision().Eps())
87 limset = true;
88 continue;
89 }
90 aulim = std::min(aulim, (zlim - zmid) / zdir);
91 }
92 }
93 }
94
95 print.Debug("Largest allowed aulim", aulim);
96
97 // case of a single parameter and we are at limit
98 if (limset && npar == 1) {
99 print.Warn("Parameter is at limit", pmid[0], "delta", pdir[0]);
100 return MnCross(fState, nfcn, MnCross::CrossParLimit());
101 }
102
103 if (aulim < aopt + tla)
104 limset = true;
105
106 MnMigrad migrad(fFCN, fState, MnStrategy(std::max(0, int(fStrategy.Strategy() - 1))));
107
108 print.Info([&](std::ostream &os) {
109 os << "Run Migrad with fixed parameters:";
110 for (unsigned i = 0; i < npar; ++i)
111 os << "\n Pos " << par[i] << ": " << fState.Name(par[i]) << " = " << pmid[i];
112 });
113
114 for (unsigned int i = 0; i < npar; i++)
115 migrad.SetValue(par[i], pmid[i]);
116
117 // find minimum with respect all the other parameters (n- npar) (npar are the fixed ones)
118
119 FunctionMinimum min0 = migrad(maxcalls, mgr_tlr);
120 nfcn += min0.NFcn();
121
122 print.Info("Result after Migrad", MnPrint::Oneline(min0), min0.UserState().Parameters());
123
124 // case a new minimum is found
125 if (min0.Fval() < fFval - tlf) {
126 // case of new minimum is found
127 print.Warn("New minimum found while scanning parameter", par.front(), "new value =", min0.Fval(),
128 "old value =", fFval);
129 return MnCross(min0.UserState(), nfcn, MnCross::CrossNewMin());
130 }
131 if (min0.HasReachedCallLimit())
132 return MnCross(min0.UserState(), nfcn, MnCross::CrossFcnLimit());
133 if (!min0.IsValid())
134 return MnCross(fState, nfcn);
135 if (limset == true && min0.Fval() < aim)
136 return MnCross(min0.UserState(), nfcn, MnCross::CrossParLimit());
137
138 ipt++;
139 alsb[0] = 0.;
140 flsb[0] = min0.Fval();
141 flsb[0] = std::max(flsb[0], aminsv + 0.1 * up);
142 aopt = std::sqrt(up / (flsb[0] - aminsv)) - 1.;
143 if (std::fabs(flsb[0] - aim) < tlf)
144 return MnCross(aopt, min0.UserState(), nfcn);
145
146 if (aopt > 1.)
147 aopt = 1.;
148 if (aopt < -0.5)
149 aopt = -0.5;
150 limset = false;
151 if (aopt > aulim) {
152 aopt = aulim;
153 limset = true;
154 }
155
156 print.Debug("flsb[0]", flsb[0], "aopt", aopt);
157
158 print.Info([&](std::ostream &os) {
159 os << "Run Migrad again (2nd) with fixed parameters:";
160 for (unsigned i = 0; i < npar; ++i)
161 os << "\n Pos " << par[i] << ": " << fState.Name(par[i]) << " = " << pmid[i] + (aopt)*pdir[i];
162 });
163
164 for (unsigned int i = 0; i < npar; i++)
165 migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
166
167 FunctionMinimum min1 = migrad(maxcalls, mgr_tlr);
168 nfcn += min1.NFcn();
169
170 print.Info("Result after 2nd Migrad", MnPrint::Oneline(min1), min1.UserState().Parameters());
171
172 if (min1.Fval() < fFval - tlf) // case of new minimum found
173 return MnCross(min1.UserState(), nfcn, MnCross::CrossNewMin());
174 if (min1.HasReachedCallLimit())
175 return MnCross(min1.UserState(), nfcn, MnCross::CrossFcnLimit());
176 if (!min1.IsValid())
177 return MnCross(fState, nfcn);
178 if (limset == true && min1.Fval() < aim)
179 return MnCross(min1.UserState(), nfcn, MnCross::CrossParLimit());
180
181 ipt++;
182 alsb[1] = aopt;
183 flsb[1] = min1.Fval();
184 double dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0]);
185
186 print.Debug("aopt", aopt, "min1Val", flsb[1], "dfda", dfda);
187
188L300:
189 if (dfda < 0.) {
190 // looking for slope of the right sign
191 print.Debug("dfda < 0 - iterate from", ipt, "to max of", maxitr);
192 // iterate (max times is maxitr) incrementing aopt
193
194 unsigned int maxlk = maxitr - ipt;
195 for (unsigned int it = 0; it < maxlk; it++) {
196 alsb[0] = alsb[1];
197 flsb[0] = flsb[1];
198 // LM: Add + 1, looking at Fortran code it starts from 1 ( see bug #8396)
199 aopt = alsb[0] + 0.2 * (it + 1);
200 limset = false;
201 if (aopt > aulim) {
202 aopt = aulim;
203 limset = true;
204 }
205
206 print.Info([&](std::ostream &os) {
207 os << "Run Migrad again (iteration " << it << " ) :";
208 for (unsigned i = 0; i < npar; ++i)
209 os << "\n parameter " << par[i] << " (" << fState.Name(par[i]) << ") fixed to "
210 << pmid[i] + (aopt)*pdir[i];
211 });
212
213 for (unsigned int i = 0; i < npar; i++)
214 migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
215
216 min1 = migrad(maxcalls, mgr_tlr);
217 nfcn += min1.NFcn();
218
219 print.Info("Result after Migrad", MnPrint::Oneline(min1), '\n', min1.UserState().Parameters());
220
221 if (min1.Fval() < fFval - tlf) // case of new minimum found
222 return MnCross(min1.UserState(), nfcn, MnCross::CrossNewMin());
223 if (min1.HasReachedCallLimit())
224 return MnCross(min1.UserState(), nfcn, MnCross::CrossFcnLimit());
225 if (!min1.IsValid())
226 return MnCross(fState, nfcn);
227 if (limset == true && min1.Fval() < aim)
228 return MnCross(min1.UserState(), nfcn, MnCross::CrossParLimit());
229 ipt++;
230 alsb[1] = aopt;
231 flsb[1] = min1.Fval();
232 dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0]);
233 // if(dfda > 0.) goto L460;
234
235 print.Debug("aopt", aopt, "min1Val", flsb[1], "dfda", dfda);
236
237 if (dfda > 0.)
238 break;
239 }
240 if (ipt > maxitr)
241 return MnCross(fState, nfcn);
242 } // if(dfda < 0.)
243
244L460:
245
246 // dfda > 0: we have two points with the right slope
247
248 aopt = alsb[1] + (aim - flsb[1]) / dfda;
249
250 print.Debug("dfda > 0 : aopt", aopt);
251
252 double fdist = std::min(std::fabs(aim - flsb[0]), std::fabs(aim - flsb[1]));
253 double adist = std::min(std::fabs(aopt - alsb[0]), std::fabs(aopt - alsb[1]));
254 tla = tlr;
255 if (std::fabs(aopt) > 1.)
256 tla = tlr * std::fabs(aopt);
257 if (adist < tla && fdist < tlf)
258 return MnCross(aopt, min1.UserState(), nfcn);
259 if (ipt > maxitr)
260 return MnCross(fState, nfcn);
261 double bmin = std::min(alsb[0], alsb[1]) - 1.;
262 if (aopt < bmin)
263 aopt = bmin;
264 double bmax = std::max(alsb[0], alsb[1]) + 1.;
265 if (aopt > bmax)
266 aopt = bmax;
267
268 limset = false;
269 if (aopt > aulim) {
270 aopt = aulim;
271 limset = true;
272 }
273
274 print.Info([&](std::ostream &os) {
275 os << "Run Migrad again (3rd) with fixed parameters:";
276 for (unsigned i = 0; i < npar; ++i)
277 os << "\n Pos " << par[i] << ": " << fState.Name(par[i]) << " = " << pmid[i] + (aopt)*pdir[i];
278 });
279
280 for (unsigned int i = 0; i < npar; i++)
281 migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
282
283 FunctionMinimum min2 = migrad(maxcalls, mgr_tlr);
284 nfcn += min2.NFcn();
285
286 print.Info("Result after Migrad (3rd):", MnPrint::Oneline(min2), min2.UserState().Parameters());
287
288 if (min2.Fval() < fFval - tlf) // case of new minimum found
289 return MnCross(min2.UserState(), nfcn, MnCross::CrossNewMin());
290 if (min2.HasReachedCallLimit())
291 return MnCross(min2.UserState(), nfcn, MnCross::CrossFcnLimit());
292 if (!min2.IsValid())
293 return MnCross(fState, nfcn);
294 if (limset == true && min2.Fval() < aim)
295 return MnCross(min2.UserState(), nfcn, MnCross::CrossParLimit());
296
297 ipt++;
298 alsb[2] = aopt;
299 flsb[2] = min2.Fval();
300
301 // now we have three points, ask how many < AIM
302
303 double ecarmn = std::fabs(flsb[2] - aim);
304 double ecarmx = 0.;
305 unsigned int ibest = 2;
306 unsigned int iworst = 0;
307 unsigned int noless = 0;
308
309 for (unsigned int i = 0; i < 3; i++) {
310 double ecart = std::fabs(flsb[i] - aim);
311 if (ecart > ecarmx) {
312 ecarmx = ecart;
313 iworst = i;
314 }
315 if (ecart < ecarmn) {
316 ecarmn = ecart;
317 ibest = i;
318 }
319 if (flsb[i] < aim)
320 noless++;
321 }
322
323 print.Debug("have three points : noless < aim; noless", noless, "ibest", ibest, "iworst", iworst);
324
325 // std::cout<<"480"<<std::endl;
326
327 // at least one on each side of AIM (contour), fit a parabola
328 if (noless == 1 || noless == 2)
329 goto L500;
330 // if all three are above AIM, third point must be the closest to AIM, return it
331 if (noless == 0 && ibest != 2)
332 return MnCross(fState, nfcn);
333 // if all three below and third is not best then the slope has again gone negative,
334 // re-iterate and look for positive slope
335 if (noless == 3 && ibest != 2) {
336 alsb[1] = alsb[2];
337 flsb[1] = flsb[2];
338
339 print.Debug("All three points below - look again fir positive slope");
340 goto L300;
341 }
342
343 // in other case new straight line thru first two points
344
345 flsb[iworst] = flsb[2];
346 alsb[iworst] = alsb[2];
347 dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0]);
348
349 print.Debug("New straight line using point 1-2; dfda", dfda);
350
351 goto L460;
352
353L500:
354
355 do {
356 // do parabola fit
357 MnParabola parbol = MnParabolaFactory()(MnParabolaPoint(alsb[0], flsb[0]), MnParabolaPoint(alsb[1], flsb[1]),
358 MnParabolaPoint(alsb[2], flsb[2]));
359 // aopt = parbol.X_pos(aim);
360 // std::cout<<"alsb1,2,3= "<<alsb[0]<<", "<<alsb[1]<<", "<<alsb[2]<<std::endl;
361 // std::cout<<"flsb1,2,3= "<<flsb[0]<<", "<<flsb[1]<<", "<<flsb[2]<<std::endl;
362
363 print.Debug("Parabola fit: iteration", ipt);
364
365 double coeff1 = parbol.C();
366 double coeff2 = parbol.B();
367 double coeff3 = parbol.A();
368 double determ = coeff2 * coeff2 - 4. * coeff3 * (coeff1 - aim);
369
370 print.Debug("Parabola fit: a =", coeff3, "b =", coeff2, "c =", coeff1, "determ =", determ);
371
372 // curvature is negative
373 if (determ < prec.Eps())
374 return MnCross(fState, nfcn);
375 double rt = std::sqrt(determ);
376 double x1 = (-coeff2 + rt) / (2. * coeff3);
377 double x2 = (-coeff2 - rt) / (2. * coeff3);
378 double s1 = coeff2 + 2. * x1 * coeff3;
379 double s2 = coeff2 + 2. * x2 * coeff3;
380
381 print.Debug("Parabola fit: x1", x1, "x2", x2, "s1", s1, "s2", s2);
382
383 if (s1 * s2 > 0.)
384 print.Warn("Problem 1");
385
386 // find with root is the right one
387 aopt = x1;
388 double slope = s1;
389 if (s2 > 0.) {
390 aopt = x2;
391 slope = s2;
392 }
393
394 print.Debug("Parabola fit: aopt", aopt, "slope", slope);
395
396 // ask if converged
397 tla = tlr;
398 if (std::fabs(aopt) > 1.)
399 tla = tlr * std::fabs(aopt);
400
401 print.Debug("Delta(aopt)", std::fabs(aopt - alsb[ibest]), "tla", tla, "Delta(F)", std::fabs(flsb[ibest] - aim),
402 "tlf", tlf);
403
404 if (std::fabs(aopt - alsb[ibest]) < tla && std::fabs(flsb[ibest] - aim) < tlf)
405 return MnCross(aopt, min2.UserState(), nfcn);
406
407 // if(ipt > maxitr) return MnCross();
408
409 // see if proposed point is in acceptable zone between L and R
410 // first find ileft, iright, iout and ibest
411
412 unsigned int ileft = 3;
413 unsigned int iright = 3;
414 unsigned int iout = 3;
415 ibest = 0;
416 ecarmx = 0.;
417 ecarmn = std::fabs(aim - flsb[0]);
418 for (unsigned int i = 0; i < 3; i++) {
419 double ecart = std::fabs(flsb[i] - aim);
420 if (ecart < ecarmn) {
421 ecarmn = ecart;
422 ibest = i;
423 }
424 if (ecart > ecarmx)
425 ecarmx = ecart;
426 if (flsb[i] > aim) {
427 if (iright == 3)
428 iright = i;
429 else if (flsb[i] > flsb[iright])
430 iout = i;
431 else {
432 iout = iright;
433 iright = i;
434 }
435 } else if (ileft == 3)
436 ileft = i;
437 else if (flsb[i] < flsb[ileft])
438 iout = i;
439 else {
440 iout = ileft;
441 ileft = i;
442 }
443 }
444
445 print.Debug("ileft", ileft, "iright", iright, "iout", iout, "ibest", ibest);
446
447 // avoid keeping a bad point nest time around
448
449 if (ecarmx > 10. * std::fabs(flsb[iout] - aim))
450 aopt = 0.5 * (aopt + 0.5 * (alsb[iright] + alsb[ileft]));
451
452 // knowing ileft and iright, get acceptable window
453 double smalla = 0.1 * tla;
454 if (slope * smalla > tlf)
455 smalla = tlf / slope;
456 double aleft = alsb[ileft] + smalla;
457 double aright = alsb[iright] - smalla;
458
459 // move proposed point AOPT into window if necessary
460 if (aopt < aleft)
461 aopt = aleft;
462 if (aopt > aright)
463 aopt = aright;
464 if (aleft > aright)
465 aopt = 0.5 * (aleft + aright);
466
467 // see if proposed point outside limits (should be impossible)
468 limset = false;
469 if (aopt > aulim) {
470 aopt = aulim;
471 limset = true;
472 }
473
474 // evaluate at new point aopt
475 print.Info([&](std::ostream &os) {
476 os << "Run Migrad again at new point (#iter = " << ipt+1 << " ):";
477 for (unsigned i = 0; i < npar; ++i)
478 os << "\n\t - parameter " << par[i] << " fixed to " << pmid[i] + (aopt)*pdir[i];
479 });
480
481 for (unsigned int i = 0; i < npar; i++)
482 migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
483
484 min2 = migrad(maxcalls, mgr_tlr);
485 nfcn += min2.NFcn();
486
487 print.Info("Result after new Migrad:", MnPrint::Oneline(min2), min2.UserState().Parameters());
488
489 if (min2.Fval() < fFval - tlf) // case of new minimum found
490 return MnCross(min2.UserState(), nfcn, MnCross::CrossNewMin());
491 if (min2.HasReachedCallLimit())
492 return MnCross(min2.UserState(), nfcn, MnCross::CrossFcnLimit());
493 if (!min2.IsValid())
494 return MnCross(fState, nfcn);
495 if (limset == true && min2.Fval() < aim)
496 return MnCross(min2.UserState(), nfcn, MnCross::CrossParLimit());
497
498 ipt++;
499 // replace odd point with new one (which is the best of three)
500 alsb[iout] = aopt;
501 flsb[iout] = min2.Fval();
502 ibest = iout;
503 } while (ipt < maxitr);
504
505 // goto L500;
506
507 return MnCross(fState, nfcn);
508}
509
510} // namespace Minuit2
511
512} // namespace ROOT
#define s1(x)
Definition: RSha256.hxx:91
static const double x2[5]
static const double x1[5]
double sqrt(double)
virtual double Up() const =0
Error definition of the function.
class holding the full result of the minimization; both internal and external (MnUserParameterState) ...
const MnUserParameterState & UserState() const
void SetValue(unsigned int, double)
const MnUserParameterState & fState
MnCross operator()(const std::vector< unsigned int > &, const std::vector< double > &, const std::vector< double > &, double, unsigned int) const
Sets the relative floating point (double) arithmetic precision.
double Eps() const
eps returns the smallest possible number so that 1.+eps > 1.
API class for minimization using Variable Metric technology ("MIGRAD"); allows for user interaction: ...
Definition: MnMigrad.h:32
A point of a parabola.
This class defines a parabola of the form a*x*x + b*x + c.
Definition: MnParabola.h:30
double B() const
Accessor to the coefficient of the linear term.
Definition: MnParabola.h:137
double C() const
Accessor to the coefficient of the constant term.
Definition: MnParabola.h:147
double A() const
Accessor to the coefficient of the quadratic term.
Definition: MnParabola.h:127
void Debug(const Ts &... args)
Definition: MnPrint.h:138
void Info(const Ts &... args)
Definition: MnPrint.h:132
void Warn(const Ts &... args)
Definition: MnPrint.h:126
API class for defining three levels of strategies: low (0), medium (1), high (>=2); acts on: Migrad (...
Definition: MnStrategy.h:27
unsigned int Strategy() const
Definition: MnStrategy.h:38
const MnMachinePrecision & Precision() const
const MnUserParameters & Parameters() const
const MinuitParameter & Parameter(unsigned int i) const
const char * Name(unsigned int) const
VecExpr< UnaryOp< Fabs< T >, VecExpr< A, T, D >, T >, T, D > fabs(const VecExpr< A, T, D > &rhs)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Definition: RNumpyDS.hxx:30