Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
FumiliBuilder.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
18#include "Minuit2/MinimumSeed.h"
19#include "Minuit2/MnFcn.h"
21#include "Minuit2/MnPosDef.h"
23#include "Minuit2/MnStrategy.h"
24#include "Minuit2/MnHesse.h"
25#include "Minuit2/MnPrint.h"
26
27namespace ROOT {
28
29namespace Minuit2 {
30
32 const MnStrategy &strategy, unsigned int maxfcn, double edmval) const
33{
34 // top level function to find minimum from a given initial seed
35 // iterate on a minimum search in case of first attempt is not successful
36
37 MnPrint print("FumiliBuilder", PrintLevel());
38
39 edmval *= 0.0001;
40 // edmval *= 0.1; // use small factor for Fumili
41
42 print.Debug("Convergence when edm <", edmval);
43
44 if (seed.Parameters().Vec().size() == 0) {
45 print.Warn("No variable parameters are defined! - Return current function value ");
46 return FunctionMinimum(seed, fcn.Up());
47 }
48
49 // estimate initial edm value
50 double edm = Estimator().Estimate(seed.Gradient(), seed.Error());
51 print.Debug("initial edm is ", edm);
52 //double edm = seed.State().Edm();
53
54 FunctionMinimum min(seed, fcn.Up());
55
56 if (edm < 0.) {
57 print.Error("Initial matrix not positive defined, edm = ",edm,"\nExit minimization ");
58 return min;
59 }
60
61 std::vector<MinimumState> result;
62 // result.reserve(1);
63 result.reserve(8);
64
65 result.push_back(seed.State());
66
67 print.Info("Start iterating until Edm is <", edmval, '\n', "Initial state", MnPrint::Oneline(seed.State()));
68
69 if (TraceIter())
70 TraceIteration(result.size() - 1, result.back());
71
72 // do actual iterations
73
74 // try first with a maxfxn = 50% of maxfcn
75 // Fumili in principle needs much less iterations
76 int maxfcn_eff = int(0.5 * maxfcn);
77 int ipass = 0;
78 double edmprev = 1;
79
80 do {
81
82 min = Minimum(fcn, gc, seed, result, maxfcn_eff, edmval);
83
84 // second time check for validity of function Minimum
85 if (ipass > 0) {
86 if (!min.IsValid()) {
87
88 print.Warn("FunctionMinimum is invalid");
89
90 return min;
91 }
92 }
93
94 // resulting edm of minimization
95 edm = result.back().Edm();
96
97 print.Debug("Approximate Edm", edm, "npass", ipass);
98
99 // call always Hesse (error matrix from Fumili is never accurate since is approximate)
100
101 if (strategy.Strategy() > 0) {
102 print.Debug("FumiliBuilder will verify convergence and Error matrix; "
103 "dcov",
104 min.Error().Dcovar());
105
106 // // recalculate the gradient using the numerical gradient calculator
107 // Numerical2PGradientCalculator ngc(fcn, min.Seed().Trafo(), strategy);
108 // FunctionGradient ng = ngc( min.State().Parameters() );
109 // MinimumState tmp( min.State().Parameters(), min.State().Error(), ng, min.State().Edm(),
110 // min.State().NFcn() );
111
112 MinimumState st = MnHesse(strategy)(fcn, min.State(), min.Seed().Trafo(), maxfcn);
113 result.push_back(st);
114
115 print.Info("After Hessian");
116 if (TraceIter())
117 TraceIteration(result.size() - 1, result.back());
118
119 // check edm
120 edm = st.Edm();
121
122 print.Debug("Edm", edm, "State", st);
123 }
124
125 // break the loop if edm is NOT getting smaller
126 if (ipass > 0 && edm >= edmprev) {
127 print.Warn("Stop iterations, no improvements after Hesse; current Edm", edm, "previous value", edmprev);
128 break;
129 }
130 if (edm > edmval) {
131 print.Debug("Tolerance not sufficient, continue minimization; Edm", edm, "Requested", edmval);
132 } else {
133 // Case when edm < edmval after Heasse but min is flagged with a bad edm:
134 // make then a new Function minimum since now edm is ok
135 if (min.IsAboveMaxEdm()) {
136 min = FunctionMinimum(min.Seed(), min.States(), min.Up());
137 break;
138 }
139 }
140
141 // end loop on iterations
142 // ? need a maximum here (or max of function calls is enough ? )
143 // continnue iteration (re-calculate function Minimum if edm IS NOT sufficient)
144 // no need to check that hesse calculation is done (if isnot done edm is OK anyway)
145 // count the pass to exit second time when function Minimum is invalid
146 // increase by 20% maxfcn for doing some more tests
147 if (ipass == 0)
149 ipass++;
150 edmprev = edm;
151
152 } while (edm > edmval);
153
154 // Add latest state (Hessian calculation)
155 min.Add(result.back());
156
157 return min;
158}
159
161 std::vector<MinimumState> &result, unsigned int maxfcn, double edmval) const
162{
163
164 // function performing the minimum searches using the FUMILI algorithm
165 // after the modification when I iterate on this functions, so it can be called many times,
166 // the seed is used here only to get precision and construct the returned FunctionMinimum object
167
168 /*
169 Three options were possible:
170
171 1) create two parallel and completely separate hierarchies, in which case
172 the FumiliMinimizer would NOT inherit from ModularFunctionMinimizer,
173 FumiliBuilder would not inherit from MinimumBuilder etc
174
175 2) Use the inheritance (base classes of ModularFunctionMinimizer,
176 MinimumBuilder etc), but recreate the member functions Minimize() and
177 Minimum() respectively (naming them for example minimize2() and
178 minimum2()) so that they can take FumiliFCNBase as Parameter instead FCNBase
179 (otherwise one wouldn't be able to call the Fumili-specific methods).
180
181 3) Cast in the daughter classes derived from ModularFunctionMinimizer,
182 MinimumBuilder.
183
184 The first two would mean to duplicate all the functionality already existent,
185 which is a very bad practice and Error-prone. The third one is the most
186 elegant and effective solution, where the only constraint is that the user
187 must know that they have to pass a subclass of FumiliFCNBase to the FumiliMinimizer
188 and not just a subclass of FCNBase.
189 BTW, the first two solutions would have meant to recreate also a parallel
190 structure for MnFcn...
191 **/
192 // const FumiliFCNBase* tmpfcn = dynamic_cast<const FumiliFCNBase*>(&(fcn.Fcn()));
193
194 const MnMachinePrecision &prec = seed.Precision();
195
196 const MinimumState &initialState = result.back();
197
198 double edm = initialState.Edm();
199
200 MnPrint print("FumiliBuilder");
201
202 print.Info("Iterating FumiliBuilder", maxfcn, edmval);
203
204 print.Debug("Initial State:", "\n Parameter", initialState.Vec(), "\n Gradient", initialState.Gradient().Vec(),
205 "\n Inv Hessian", initialState.Error().InvHessian(), "\n edm", initialState.Edm(), "\n maxfcn",
206 maxfcn, "\n tolerance", edmval);
207
208 // iterate until edm is small enough or max # of iterations reached
209 edm *= (1. + 3. * initialState.Error().Dcovar());
210 MnAlgebraicVector step(initialState.Gradient().Vec().size());
211
212 // initial lambda Value
213
214
215 const bool doLineSearch = (fMethodType == kLineSearch);
217 const bool scaleTR = (fMethodType == kTrustRegionScaled);
218 // trust region parameter
219 // use as initial value 0.3 * || x0 ||
220 auto & x0 = initialState.Vec();
221 double normX0 = std::sqrt(inner_product(x0,x0));
222 double delta = 0.3 * std::max(1.0 , normX0);
223 const double eta = 0.1;
224 // use same values as used in GSL implementation
225 const double tr_factor_up = 3;
226 const double tr_factor_down = 0.5;
227 bool acceptNewPoint = true;
228
229 if (doLineSearch)
230 print.Info("Using Fumili with a line search algorithm");
231 else if (doTrustRegion && scaleTR)
232 print.Info("Using Fumili with a scaled trust region algorithm with factors up/down",tr_factor_up,tr_factor_down);
233 else
234 print.Info("Using Fumili with a trust region algorithm with factors up/down",tr_factor_up,tr_factor_down);
235
236
237 double lambda = (doLineSearch) ? 0.001 : 0;
238
240
241 do {
242
243 // const MinimumState& s0 = result.back();
244 MinimumState s0 = result.back();
245
246 step = -1. * s0.Error().InvHessian() * s0.Gradient().Vec();
247
248 print.Debug("Iteration -", result.size(), "\n Fval", s0.Fval(), "numOfCall", fcn.NumOfCalls(),
249 "\n Internal Parameter values", s0.Vec(), "\n Newton step", step);
250
251 double gdel = inner_product(step, s0.Gradient().Grad());
252 //not sure if this is needed ??
253 if (gdel > 0.) {
254 print.Warn("Matrix not pos.def, gdel =", gdel, " > 0");
255
257 s0 = psdf(s0, prec);
258 step = -1. * s0.Error().InvHessian() * s0.Gradient().Vec();
259 gdel = inner_product(step, s0.Gradient().Grad());
260
261 print.Warn("After correction, gdel =", gdel);
262
263 if (gdel > 0.) {
264 result.push_back(s0);
265 return FunctionMinimum(seed, result, fcn.Up());
266 }
267 }
268
269
270 // take a full step
271
272 //evaluate function only if doing a line search
273 double fval2 = (doLineSearch) ? fcnCaller(s0.Vec() + step) : 0;
274 MinimumParameters p(s0.Vec() + step, fval2);
275
276 // check that taking the full step does not deteriorate minimum
277 // in that case do a line search
278 if (doLineSearch && p.Fval() >= s0.Fval()) {
279 print.Debug("Do a line search", fcn.NumOfCalls());
281 MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec);
282
283 if (std::fabs(pp.Y() - s0.Fval()) < prec.Eps()) {
284 // std::cout<<"FumiliBuilder: no improvement"<<std::endl;
285 break; // no improvement
286 }
287 p = MinimumParameters(s0.Vec() + pp.X() * step, pp.Y());
288 print.Debug("New point after Line Search :", "\n FVAL ", p.Fval(), "\n Parameter", p.Vec());
289 }
290 // use as scaling matrix diagonal of Hessian
291 auto & H = s0.Error().Hessian();
292 unsigned int n = (scaleTR) ? H.Nrow() : 0;
293
298 // set the scaling matrix to the sqrt(diagoinal hessian)
299 for (unsigned int i = 0; i < n; i++){
300 double d = std::sqrt(H(i,i));
301 D(i,i) = d;
302 Dinv(i,i) = 1./d;
303 Dinv2(i,i) = 1./(d*d);
304 }
305
306 if (doTrustRegion) {
307 // do simple trust region using some control delta and eta
308 // compute norm of Newton step
309 double norm = 0;
310 if (scaleTR) {
311 print.Debug("scaling Trust region with diagonal matrix D ",D);
312 stepScaled = D * step;
314 }
315 else
316 norm = sqrt(inner_product(step,step));
317 // some conditions
318
319 // double tr_radius = norm;
320 // if (norm < 0.1 * delta) tr_radius = 0.1*delta;
321 // else if (norm > delta) tr_radius = delta;
322
323 // update new point using reduced step
324 //step = (tr_radius/norm) * step;
325
326 // if the proposed point (newton step) is inside the trust region radius accept it
327 if (norm <= delta) {
328 p = MinimumParameters(s0.Vec() + step, fcnCaller(s0.Vec() + step));
329 print.Debug("Accept full Newton step - it is inside TR ",delta);
330 } else {
331 //step = - (delta/norm) * step;
332
333 // if Newton step is outside try to use the Cauchy point ?
334 double normGrad2 = 0;
335 double gHg = 0;
336 if (scaleTR) {
337 auto gScaled = Dinv * s0.Gradient().Grad();
339 // compute D-1 H D-1 = H(i,j) * D(i,i) * D(j,j)
341 for (unsigned int i = 0; i < n; i++) {
342 for (unsigned int j = 0; j <=i; j++) {
343 Hscaled(i,j) = H(i,j) * Dinv(i,i) * Dinv(j,j);
344 }
345 }
347 } else {
348 normGrad2 = inner_product(s0.Gradient().Grad(),s0.Gradient().Grad());
349 gHg = similarity(s0.Gradient().Grad(), s0.Error().Hessian());
350 }
351 double normGrad = sqrt(normGrad2);
352 // need to compute gTHg :
353
354
355 print.Debug("computed gdel gHg and normGN",gdel,gHg,norm*norm, normGrad2);
356 if (gHg <= 0.) {
357 // Cauchy point is at the trust region boundary)
358 step = - (delta/ normGrad) * s0.Gradient().Grad();
359 if (scaleTR) step = Dinv2 * step;
360 print.Debug("Use as new point the Cauchy point - along gradient with norm=delta ", delta);
361 } else {
362 // Cauchy point can be inside the trust region
363 double tau = std::min( (normGrad2*normGrad)/(gHg*delta), 1.0);
364
366 stepC = -tau * (delta / normGrad) * s0.Gradient().Grad();
367 if (scaleTR)
368 stepC = Dinv2 * stepC;
369
370 print.Debug("Use as new point the Cauchy point - along gradient with tau ", tau, "delta = ", delta);
371
372 // compute dog -leg step solving quadratic equation a * t^2 + b * t + c = 0
373 // where a = ||p_n - P_c ||^2
374 // b = 2 * p_c * (P_n - P_c)
375 // c = || pc ||^2 - ||Delta||^2
376 auto diffP = step - stepC;
377 double a = inner_product(diffP, diffP);
378 double b = 2. * inner_product(stepC, diffP);
379 // norm cauchy point is tau*delta
380 double c = (scaleTR) ? inner_product(stepC, stepC) - delta * delta : delta * delta * (tau * tau - 1.);
381
382 print.Debug(" dogleg equation", a, b, c);
383 // solution is
384 double t = 0;
385 // a cannot be negative or zero, otherwise point was accepted
386 if (a <= 0) {
387 // case a is zero
388 print.Warn("a is equal to zero! a = ", a);
389 print.Info(" delta ", delta, " tau ", tau, " gHg ", gHg, " normgrad2 ", normGrad2);
390 t = -b / c;
391 } else {
392 double t1 = (-b + sqrt(b * b - 4. * a * c)) / (2.0 * a);
393 double t2 = (-b - sqrt(b * b - 4. * a * c)) / (2.0 * a);
394 // if b is positive solution is
395 print.Debug(" solution dogleg equation", t1, t2);
396 if (t1 >= 0 && t1 <= 1.)
397 t = t1;
398 else
399 t = t2;
400 }
401 step = stepC + t * diffP;
402 // need to rescale point per D^-1 >
403 print.Debug("New dogleg point is t = ", t);
404 }
405 print.Debug("New accepted step is ",step);
406
407 p = MinimumParameters(s0.Vec() + step, fcnCaller(s0.Vec() + step));
408 norm = delta;
409 gdel = inner_product(step, s0.Gradient().Grad());
410 }
411
412 // compute real changes (require an evaluation)
413
414 // expected change is gdel (can correct for Hessian )
415 //double fcexp = (-gdel - 0.5 * dot(step, hess(x) * step)
416
417 double svs = 0.5 * similarity(step, s0.Error().Hessian());
418 double rho = (p.Fval()-s0.Fval())/(gdel+svs);
419 if (rho < 0.25) {
420 delta = tr_factor_down * delta;
421 } else {
422 if (rho > 0.75 && norm == delta) {
423 delta = std::min(tr_factor_up * delta, 100.*norm); // 1000. is the delta max
424 }
425 }
426 print.Debug("New point after Trust region :", "norm tr ",norm," rho ", rho," delta ", delta,
427 " FVAL ", p.Fval(), "\n Parameter", p.Vec());
428
429 // check if we need to accept the point ?
430 acceptNewPoint = (rho > eta);
431 if (acceptNewPoint) {
432 // we accept the point
433 // we have already p = s0 + step
434 print.Debug("Trust region: accept new point p = x + step since rho is larger than eta");
435 }
436 else {
437 // we keep old point
438 print.Debug("Trust region reject new point and repeat since rho is smaller than eta");
439 p = MinimumParameters(s0.Vec(), s0.Fval());
440 }
441
442 }
443
444 FunctionGradient g = s0.Gradient();
445
446 if (acceptNewPoint || result.size() == 1) {
447
448 print.Debug("Before Gradient - NCalls = ", fcn.NumOfCalls());
449
450 g = gc(p, s0.Gradient());
451
452 print.Debug("After Gradient - NCalls = ", fcn.NumOfCalls());
453
454 }
455
456 // move Error updator after Gradient since the Value is cached inside
457
458 MinimumError e = ErrorUpdator().Update(s0, p, gc, lambda);
459
460 // should I use here new error instead of old one (so.Error) ?
461 edm = Estimator().Estimate(g, s0.Error());
462
463 print.Debug("Updated new point:", "\n FVAL ", p.Fval(), "\n Parameter", p.Vec(), "\n Gradient", g.Vec(),
464 "\n InvHessian", e.InvHessian(), "\n Hessian", e.Hessian(), "\n Edm", edm);
465
466 if (edm < 0.) {
467 print.Warn("Matrix not pos.def., Edm < 0");
468
470 s0 = psdf(s0, prec);
471 edm = Estimator().Estimate(g, s0.Error());
472 if (edm < 0.) {
473 result.push_back(s0);
474 if (TraceIter())
475 TraceIteration(result.size() - 1, result.back());
476 return FunctionMinimum(seed, result, fcn.Up());
477 }
478 }
479
480 // check lambda according to step
481 if (doLineSearch) {
482 if (p.Fval() < s0.Fval())
483 // fcn is decreasing along the step
484 lambda *= 0.1;
485 else {
486 lambda *= 10;
487 // if close to minimum stop to avoid oscillations around minimum value
488 if (edm < 0.1)
489 break;
490 }
491 }
492
493 print.Debug("finish iteration -", result.size(), "lambda =", lambda, "f1 =", p.Fval(), "f0 =", s0.Fval(),
494 "num of calls =", fcn.NumOfCalls(), "edm =", edm);
495
496 result.push_back(MinimumState(p, e, g, edm, fcn.NumOfCalls()));
497 if (TraceIter())
498 TraceIteration(result.size() - 1, result.back());
499
500 print.Info(MnPrint::Oneline(result.back(), result.size()));
501
502 edm *= (1. + 3. * e.Dcovar());
503
504 //} // endif on acceptNewPoint
505
506
507 } while (edm > edmval && fcn.NumOfCalls() < maxfcn);
508
509 if (fcn.NumOfCalls() >= maxfcn) {
510 print.Warn("Call limit exceeded",fcn.NumOfCalls(), maxfcn);
511
513 }
514
515 if (edm > edmval) {
516 if (edm < std::fabs(prec.Eps2() * result.back().Fval())) {
517 print.Warn("Machine accuracy limits further improvement");
518
519 return FunctionMinimum(seed, result, fcn.Up());
520 } else if (edm < 10 * edmval) {
521 return FunctionMinimum(seed, result, fcn.Up());
522 } else {
523
524 print.Warn("No convergence; Edm", edm, "is above tolerance", 10 * edmval);
525
527 }
528 }
529
530 print.Debug("Exiting successfully", "Ncalls", fcn.NumOfCalls(), "FCN", result.back().Fval(), "Edm", edm, "Requested",
531 edmval);
532
533 return FunctionMinimum(seed, result, fcn.Up());
534}
535
536} // namespace Minuit2
537
538} // namespace ROOT
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define s0(x)
Definition RSha256.hxx:90
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void gc
FunctionMinimum Minimum(const MnFcn &fMnFcn, const GradientCalculator &fGradienCalculator, const MinimumSeed &fMinimumSeed, const MnStrategy &fMnStrategy, unsigned int maxfcn, double edmval) const override
Class the member function calculating the Minimum and verifies the result depending on the strategy.
const FumiliErrorUpdator & ErrorUpdator() const
Accessor to the Error updator of the builder.
const VariableMetricEDMEstimator & Estimator() const
Accessor to the EDM (expected vertical distance to the Minimum) estimator.
class holding the full result of the minimization; both internal and external (MnUserParameterState) ...
interface class for gradient calculators
Class describing a symmetric matrix of size n.
Definition MnMatrix.h:438
unsigned int size() const
Definition MnMatrix.h:1032
void TraceIteration(int iter, const MinimumState &state) const
MinimumError keeps the inv.
const FunctionGradient & Gradient() const
Definition MinimumSeed.h:32
const MinimumError & Error() const
Definition MinimumSeed.h:31
const MinimumParameters & Parameters() const
Definition MinimumSeed.h:30
const MnMachinePrecision & Precision() const
Definition MinimumSeed.h:34
const MinimumState & State() const
Definition MinimumSeed.h:29
MinimumState keeps the information (position, Gradient, 2nd deriv, etc) after one minimization step (...
Wrapper class to FCNBase interface used internally by Minuit.
Definition MnFcn.h:34
API class for calculating the numerical covariance matrix (== 2x Inverse Hessian == 2x Inverse 2nd de...
Definition MnHesse.h:41
Implements a 1-dimensional minimization along a given direction (i.e.
Sets the relative floating point (double) arithmetic precision.
double Y() const
Accessor to the y (second) coordinate.
double X() const
Accessor to the x (first) coordinate.
Force the covariance matrix to be positive defined by adding extra terms in the diagonal.
Definition MnPosDef.h:25
void Debug(const Ts &... args)
Definition MnPrint.h:135
void Error(const Ts &... args)
Definition MnPrint.h:117
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
const Int_t n
Definition legend1.C:16
#define H(x, y, z)
double similarity(const LAVector &, const LASymMatrix &)
Definition MnMatrix.cxx:629
double inner_product(const LAVector &, const LAVector &)
Definition MnMatrix.cxx:316
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
auto * t1
Definition textangle.C:20