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