Logo ROOT  
Reference Guide
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/LaSum.h"
25#include "Minuit2/LaProd.h"
26#include "Minuit2/MnStrategy.h"
27#include "Minuit2/MnHesse.h"
28
29
30
31// //#define DEBUG 1
32// #if defined(DEBUG) || defined(WARNINGMSG)
33#include "Minuit2/MnPrint.h"
34//#endif
35
36
37
38namespace ROOT {
39
40 namespace Minuit2 {
41
42
43
44
45double inner_product(const LAVector&, const LAVector&);
46
47FunctionMinimum FumiliBuilder::Minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, const MnStrategy& strategy, unsigned int maxfcn, double edmval) const {
48 // top level function to find minimum from a given initial seed
49 // iterate on a minimum search in case of first attempt is not successful
50
51 edmval *= 0.0001;
52 //edmval *= 0.1; // use small factor for Fumili
53
54 // synchronize print levels
55 int printLevel = PrintLevel();
56 BuilderPrintLevelConf plconf(printLevel);
57
58#ifdef DEBUG
59 std::cout<<"FumiliBuilder convergence when edm < "<<edmval<<std::endl;
60#endif
61
62 if(seed.Parameters().Vec().size() == 0) {
63 return FunctionMinimum(seed, fcn.Up());
64 }
65
66
67 // double edm = Estimator().Estimate(seed.Gradient(), seed.Error());
68 double edm = seed.State().Edm();
69
70 FunctionMinimum min(seed, fcn.Up() );
71
72 if(edm < 0.) {
73#ifdef WARNINGMSG
74 MN_INFO_MSG("FumiliBuilder: initial matrix not pos.def.");
75#endif
76 return min;
77 }
78
79 std::vector<MinimumState> result;
80 // result.reserve(1);
81 result.reserve(8);
82
83 result.push_back( seed.State() );
84
85 if (printLevel >1) {
86 std::cout << "Fumili: start iterating until Edm is < " << edmval << std::endl;
87 MnPrint::PrintState(std::cout, seed.State(), "Fumili: Initial state ");
88 }
89 if (TraceIter() ) TraceIteration(result.size()-1, result.back() );
90
91
92 // do actual iterations
93
94
95 // try first with a maxfxn = 50% of maxfcn
96 // Fumili in principle needs much less iterations
97 int maxfcn_eff = int(0.5*maxfcn);
98 int ipass = 0;
99 double edmprev = 1;
100
101 do {
102
103
104 min = Minimum(fcn, gc, seed, result, maxfcn_eff, edmval);
105
106
107 // second time check for validity of function Minimum
108 if (ipass > 0) {
109 if(!min.IsValid()) {
110#ifdef WARNINGMSG
111 MN_INFO_MSG("FumiliBuilder: FunctionMinimum is invalid.");
112#endif
113 return min;
114 }
115 }
116
117 // resulting edm of minimization
118 edm = result.back().Edm();
119
120#ifdef DEBUG
121 std::cout << "approximate edm is " << edm << std::endl;
122 std::cout << "npass is " << ipass << std::endl;
123#endif
124
125 // call always Hesse (error matrix from Fumili is never accurate since is approximate)
126
127#ifdef DEBUG
128 std::cout << "FumiliBuilder will verify convergence and Error matrix. " << std::endl;
129 std::cout << "dcov is = "<< min.Error().Dcovar() << std::endl;
130#endif
131
132// // recalculate the gradient using the numerical gradient calculator
133// Numerical2PGradientCalculator ngc(fcn, min.Seed().Trafo(), strategy);
134// FunctionGradient ng = ngc( min.State().Parameters() );
135// MinimumState tmp( min.State().Parameters(), min.State().Error(), ng, min.State().Edm(), min.State().NFcn() );
136
137 MinimumState st = MnHesse(strategy)(fcn, min.State(), min.Seed().Trafo(),maxfcn);
138 result.push_back( st );
139
140 if (printLevel > 1) {
141 MnPrint::PrintState(std::cout, st, "Fumili: After Hessian ");
142 if (TraceIter() ) TraceIteration(result.size()-1, result.back() );
143 }
144
145
146 // check edm
147 edm = st.Edm();
148#ifdef DEBUG
149 std::cout << "edm after Hesse calculation " << edm << std::endl;
150 std::cout << "state after Hessian calculation " << st << std::endl;
151#endif
152
153 // break the loop if edm is NOT getting smaller
154 if (ipass > 0 && edm >= edmprev) {
155#ifdef WARNINGMSG
156 MN_INFO_MSG("FumiliBuilder: Exit iterations, no improvements after Hesse ");
157 MN_INFO_VAL2("current edm is ", edm);
158 MN_INFO_VAL2("previous value ",edmprev);
159#endif
160 break;
161 }
162 if (edm > edmval) {
163#ifdef DEBUG
164 std::cout << "FumiliBuilder: Tolerance is not sufficient - edm is " << edm << " requested " << edmval
165 << " continue the minimization" << std::endl;
166#endif
167 }
168 else {
169 // Case when edm < edmval after Heasse but min is flagged eith a bad edm:
170 // make then a new Function minimum since now edm is ok
171 if (min.IsAboveMaxEdm() ) {
172 min = FunctionMinimum( min.Seed(), min.States(), min.Up() );
173 break;
174 }
175
176 }
177
178 // end loop on iterations
179 // ? need a maximum here (or max of function calls is enough ? )
180 // continnue iteration (re-calculate function Minimum if edm IS NOT sufficient)
181 // no need to check that hesse calculation is done (if isnot done edm is OK anyway)
182 // count the pass to exit second time when function Minimum is invalid
183 // increase by 20% maxfcn for doing some more tests
184 if (ipass == 0) maxfcn_eff = maxfcn;
185 ipass++;
186 edmprev = edm;
187
188 } while (edm > edmval );
189
190
191
192 // Add latest state (Hessian calculation)
193 min.Add( result.back() );
194
195 return min;
196
197}
198
199FunctionMinimum FumiliBuilder::Minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, std::vector<MinimumState>& result, unsigned int maxfcn, double edmval) const {
200
201 // function performing the minimum searches using the FUMILI algorithm
202 // after the modification when I iterate on this functions, so it can be called many times,
203 // the seed is used here only to get precision and construct the returned FunctionMinimum object
204
205
206 /*
207 Three options were possible:
208
209 1) create two parallel and completely separate hierarchies, in which case
210 the FumiliMinimizer would NOT inherit from ModularFunctionMinimizer,
211 FumiliBuilder would not inherit from MinimumBuilder etc
212
213 2) Use the inheritance (base classes of ModularFunctionMinimizer,
214 MinimumBuilder etc), but recreate the member functions Minimize() and
215 Minimum() respectively (naming them for example minimize2() and
216 minimum2()) so that they can take FumiliFCNBase as Parameter instead FCNBase
217 (otherwise one wouldn't be able to call the Fumili-specific methods).
218
219 3) Cast in the daughter classes derived from ModularFunctionMinimizer,
220 MinimumBuilder.
221
222 The first two would mean to duplicate all the functionality already existent,
223 which is a very bad practice and Error-prone. The third one is the most
224 elegant and effective solution, where the only constraint is that the user
225 must know that they have to pass a subclass of FumiliFCNBase to the FumiliMinimizer
226 and not just a subclass of FCNBase.
227 BTW, the first two solutions would have meant to recreate also a parallel
228 structure for MnFcn...
229 **/
230 // const FumiliFCNBase* tmpfcn = dynamic_cast<const FumiliFCNBase*>(&(fcn.Fcn()));
231
232 const MnMachinePrecision& prec = seed.Precision();
233
234 const MinimumState & initialState = result.back();
235
236 double edm = initialState.Edm();
237
238
239#ifdef DEBUG
240 std::cout << "\n\nDEBUG FUMILI Builder \nInitial State: "
241 << " Parameter " << initialState.Vec()
242 << " Gradient " << initialState.Gradient().Vec()
243 << " Inv Hessian " << initialState.Error().InvHessian()
244 << " edm = " << initialState.Edm()
245 << " maxfcn = " << maxfcn
246 << " tolerance = " << edmval
247 << std::endl;
248#endif
249
250
251 // iterate until edm is small enough or max # of iterations reached
252 edm *= (1. + 3.*initialState.Error().Dcovar());
253 MnAlgebraicVector step(initialState.Gradient().Vec().size());
254
255 // initial lambda Value
256 double lambda = 0.001;
257
258 int printLevel = MnPrint::Level();
259 do {
260
261 // const MinimumState& s0 = result.back();
262 MinimumState s0 = result.back();
263
264 step = -1.*s0.Error().InvHessian()*s0.Gradient().Vec();
265
266
267#ifdef DEBUG
268 std::cout << "\n\n---> Iteration - " << result.size()
269 << "\nFval = " << s0.Fval() << " numOfCall = " << fcn.NumOfCalls()
270 << "\nInternal Parameter values " << s0.Vec()
271 << " Newton step " << step << std::endl;
272#endif
273
274 double gdel = inner_product(step, s0.Gradient().Grad());
275 if(gdel > 0.) {
276#ifdef WARNINGMSG
277 MN_INFO_MSG("FumiliBuilder: matrix not pos.def, gdel > 0");
278 MN_INFO_VAL(gdel);
279#endif
280 MnPosDef psdf;
281 s0 = psdf(s0, prec);
282 step = -1.*s0.Error().InvHessian()*s0.Gradient().Vec();
283 gdel = inner_product(step, s0.Gradient().Grad());
284#ifdef WARNINGMSG
285 MN_INFO_VAL2("After correction ",gdel);
286#endif
287 if(gdel > 0.) {
288 result.push_back(s0);
289 return FunctionMinimum(seed, result, fcn.Up());
290 }
291 }
292
293
294 // MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec);
295
296 // if(fabs(pp.Y() - s0.Fval()) < prec.Eps()) {
297 // std::cout<<"FumiliBuilder: no improvement"<<std::endl;
298 // break; //no improvement
299 // }
300
301
302 // MinimumParameters p(s0.Vec() + pp.X()*step, pp.Y());
303
304 // if taking a full step
305
306 // take a full step
307
308 MinimumParameters p(s0.Vec() + step, fcn( s0.Vec() + step ) );
309
310 // check that taking the full step does not deteriorate minimum
311 // in that case do a line search
312 if ( p.Fval() >= s0.Fval() ) {
313 MnLineSearch lsearch;
314 MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec);
315
316 if(fabs(pp.Y() - s0.Fval()) < prec.Eps()) {
317 //std::cout<<"FumiliBuilder: no improvement"<<std::endl;
318 break; //no improvement
319 }
320 p = MinimumParameters(s0.Vec() + pp.X()*step, pp.Y() );
321 }
322
323#ifdef DEBUG
324 std::cout << "Before Gradient " << fcn.NumOfCalls() << std::endl;
325#endif
326
327 FunctionGradient g = gc(p, s0.Gradient());
328
329#ifdef DEBUG
330 std::cout << "After Gradient " << fcn.NumOfCalls() << std::endl;
331#endif
332
333 //FunctionGradient g = gc(s0.Parameters(), s0.Gradient());
334
335
336 // move Error updator after Gradient since the Value is cached inside
337
338 MinimumError e = ErrorUpdator().Update(s0, p, gc, lambda);
339
340
341 edm = Estimator().Estimate(g, s0.Error());
342
343
344#ifdef DEBUG
345 std::cout << "Updated new point: \n "
346 << " FVAL " << p.Fval()
347 << " Parameter " << p.Vec()
348 << " Gradient " << g.Vec()
349 << " InvHessian " << e.Matrix()
350 << " Hessian " << e.Hessian()
351 << " edm = " << edm << std::endl << std::endl;
352#endif
353
354 if(edm < 0.) {
355#ifdef WARNINGMSG
356 MN_INFO_MSG("FumiliBuilder: matrix not pos.def., edm < 0");
357#endif
358 MnPosDef psdf;
359 s0 = psdf(s0, prec);
360 edm = Estimator().Estimate(g, s0.Error());
361 if(edm < 0.) {
362 result.push_back(s0);
363 if (TraceIter() ) TraceIteration(result.size()-1, result.back() );
364 return FunctionMinimum(seed, result, fcn.Up());
365 }
366 }
367
368 // check lambda according to step
369 if ( p.Fval() < s0.Fval() )
370 // fcn is decreasing along the step
371 lambda *= 0.1;
372 else {
373 lambda *= 10;
374 // if close to minimum stop to avoid oscillations around minimum value
375 if ( edm < 0.1 ) break;
376 }
377
378#ifdef DEBUG
379 std::cout << " finish iteration- " << result.size() << " lambda = " << lambda << " f1 = " << p.Fval() << " f0 = " << s0.Fval() << " num of calls = " << fcn.NumOfCalls() << " edm " << edm << std::endl;
380#endif
381
382 result.push_back(MinimumState(p, e, g, edm, fcn.NumOfCalls()));
383 if (TraceIter() ) TraceIteration(result.size()-1, result.back() );
384
385 if (printLevel > 1) {
386 MnPrint::PrintState(std::cout, result.back(), "Fumili: Iteration # ",result.size());
387 }
388
389
390 edm *= (1. + 3.*e.Dcovar());
391
392
393 } while(edm > edmval && fcn.NumOfCalls() < maxfcn);
394
395
396
397 if(fcn.NumOfCalls() >= maxfcn) {
398#ifdef WARNINGMSG
399 MN_INFO_MSG("FumiliBuilder: call limit exceeded.");
400#endif
401 return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnReachedCallLimit());
402 }
403
404 if(edm > edmval) {
405 if(edm < fabs(prec.Eps2()*result.back().Fval())) {
406#ifdef WARNINGMSG
407 MN_INFO_MSG("FumiliBuilder: machine accuracy limits further improvement.");
408#endif
409 return FunctionMinimum(seed, result, fcn.Up());
410 } else if(edm < 10.*edmval) {
411 return FunctionMinimum(seed, result, fcn.Up());
412 } else {
413#ifdef WARNINGMSG
414 MN_INFO_MSG("FumiliBuilder: finishes without convergence.");
415 MN_INFO_VAL2("FumiliBuilder: ",edm);
416 MN_INFO_VAL2(" requested: ",edmval);
417#endif
418 return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnAboveMaxEdm());
419 }
420 }
421 // std::cout<<"result.back().Error().Dcovar()= "<<result.back().Error().Dcovar()<<std::endl;
422
423#ifdef DEBUG
424 std::cout << "Exiting successfully FumiliBuilder \n"
425 << "NFCalls = " << fcn.NumOfCalls()
426 << "\nFval = " << result.back().Fval()
427 << "\nedm = " << edm << " requested = " << edmval << std::endl;
428#endif
429
430 return FunctionMinimum(seed, result, fcn.Up());
431}
432
433 } // namespace Minuit2
434
435} // namespace ROOT
#define MN_INFO_VAL2(loc, x)
Definition: MnPrint.h:130
#define MN_INFO_MSG(str)
Definition: MnPrint.h:110
#define MN_INFO_VAL(x)
Definition: MnPrint.h:116
#define g(i)
Definition: RSha256.hxx:105
#define s0(x)
Definition: RSha256.hxx:90
#define e(i)
Definition: RSha256.hxx:103
virtual FunctionMinimum Minimum(const MnFcn &fMnFcn, const GradientCalculator &fGradienCalculator, const MinimumSeed &fMinimumSeed, const MnStrategy &fMnStrategy, unsigned int maxfcn, double edmval) const
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.
virtual MinimumError Update(const MinimumState &fMinimumState, const MinimumParameters &fMinimumParameters, const GradientCalculator &fGradientCalculator, double lambda) const
Member function that calculates the Error matrix (or the Hessian matrix containing the (approximate) ...
const MnAlgebraicVector & Vec() const
class holding the full result of the minimization; both internal and external (MnUserParameterState) ...
void Add(const MinimumState &state)
const std::vector< ROOT::Minuit2::MinimumState > & States() const
const MinimumError & Error() const
const MinimumState & State() const
const MinimumSeed & Seed() const
interface class for gradient calculators
unsigned int size() const
Definition: LAVector.h:198
void TraceIteration(int iter, const MinimumState &state) const
MinimumError keeps the inv.
Definition: MinimumError.h:26
const MnAlgebraicSymMatrix & InvHessian() const
Definition: MinimumError.h:60
const MnAlgebraicVector & Vec() const
MinimumSeed contains the starting values for the minimization produced by the SeedGenerator.
Definition: MinimumSeed.h:31
const MnUserTransformation & Trafo() const
Definition: MinimumSeed.h:50
const MinimumParameters & Parameters() const
Definition: MinimumSeed.h:47
const MnMachinePrecision & Precision() const
Definition: MinimumSeed.h:51
const MinimumState & State() const
Definition: MinimumSeed.h:46
MinimumState keeps the information (position, Gradient, 2nd deriv, etc) after one minimization step (...
Definition: MinimumState.h:29
const MinimumError & Error() const
Definition: MinimumState.h:62
const MnAlgebraicVector & Vec() const
Definition: MinimumState.h:59
const FunctionGradient & Gradient() const
Definition: MinimumState.h:63
Wrapper class to FCNBase interface used internally by Minuit.
Definition: MnFcn.h:33
double Up() const
Definition: MnFcn.cxx:35
unsigned int NumOfCalls() const
Definition: MnFcn.h:43
API class for calculating the numerical covariance matrix (== 2x Inverse Hessian == 2x Inverse 2nd de...
Definition: MnHesse.h:40
Implements a 1-dimensional minimization along a given direction (i.e.
Definition: MnLineSearch.h:47
Sets the relative floating point (double) arithmetic precision.
double Eps() const
eps returns the smallest possible number so that 1.+eps > 1.
double Eps2() const
eps2 returns 2*sqrt(eps)
A point of a parabola.
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:26
static void PrintState(std::ostream &os, const MinimumState &state, const char *msg, int iter=-1)
Definition: MnPrint.cxx:58
static int Level()
Definition: MnPrint.cxx:47
API class for defining three levels of strategies: low (0), medium (1), high (>=2); acts on: Migrad (...
Definition: MnStrategy.h:27
double Estimate(const FunctionGradient &, const MinimumError &) const
VecExpr< UnaryOp< Fabs< T >, VecExpr< A, T, D >, T >, T, D > fabs(const VecExpr< A, T, D > &rhs)
double inner_product(const LAVector &, const LAVector &)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Definition: StringConv.hxx:21