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