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