FumiliBuilder.cxx

Go to the documentation of this file.
00001 // @(#)root/minuit2:$Id: FumiliBuilder.cxx 30749 2009-10-15 16:33:04Z brun $
00002 // Authors: M. Winkler, F. James, L. Moneta, A. Zsenei   2003-2005  
00003 
00004 /**********************************************************************
00005  *                                                                    *
00006  * Copyright (c) 2005 LCG ROOT Math team,  CERN/PH-SFT                *
00007  *                                                                    *
00008  **********************************************************************/
00009 
00010 #include "Minuit2/FumiliBuilder.h"
00011 #include "Minuit2/FumiliStandardMaximumLikelihoodFCN.h"
00012 #include "Minuit2/GradientCalculator.h"
00013 //#include "Minuit2/Numerical2PGradientCalculator.h"
00014 #include "Minuit2/MinimumState.h"
00015 #include "Minuit2/MinimumError.h"
00016 #include "Minuit2/FunctionGradient.h"
00017 #include "Minuit2/FunctionMinimum.h"
00018 #include "Minuit2/MnLineSearch.h"
00019 #include "Minuit2/MinimumSeed.h"
00020 #include "Minuit2/MnFcn.h"
00021 #include "Minuit2/MnMachinePrecision.h"
00022 #include "Minuit2/MnPosDef.h"
00023 #include "Minuit2/MnParabolaPoint.h"
00024 #include "Minuit2/LaSum.h"
00025 #include "Minuit2/LaProd.h"
00026 #include "Minuit2/MnStrategy.h"
00027 #include "Minuit2/MnHesse.h"
00028 
00029 
00030 
00031 //#define DEBUG 1
00032 #if defined(DEBUG) || defined(WARNINGMSG)
00033 #include "Minuit2/MnPrint.h" 
00034 #endif
00035 
00036 
00037 
00038 namespace ROOT {
00039 
00040    namespace Minuit2 {
00041 
00042 
00043 
00044 
00045 double inner_product(const LAVector&, const LAVector&);
00046 
00047 FunctionMinimum FumiliBuilder::Minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, const MnStrategy& strategy, unsigned int maxfcn, double edmval) const {
00048    // top level function to find minimum from a given initial seed 
00049    // iterate on a minimum search in case of first attempt is not succesfull
00050    
00051    edmval *= 0.0001;
00052    //edmval *= 0.1; // use small factor for Fumili
00053    
00054    
00055 #ifdef DEBUG
00056    std::cout<<"FumiliBuilder convergence when edm < "<<edmval<<std::endl;
00057 #endif
00058    
00059    if(seed.Parameters().Vec().size() == 0) {
00060       return FunctionMinimum(seed, fcn.Up());
00061    }
00062    
00063    
00064    //   double edm = Estimator().Estimate(seed.Gradient(), seed.Error());
00065    double edm = seed.State().Edm();
00066    
00067    FunctionMinimum min(seed, fcn.Up() );
00068    
00069    if(edm < 0.) {
00070 #ifdef WARNINGMSG
00071       MN_INFO_MSG("FumiliBuilder: initial matrix not pos.def.");
00072 #endif
00073       return min;
00074    }
00075    
00076    std::vector<MinimumState> result;
00077    //   result.reserve(1);
00078    result.reserve(8);
00079    
00080    result.push_back( seed.State() );
00081    
00082    // do actual iterations
00083    
00084    
00085    // try first with a maxfxn = 50% of maxfcn 
00086    // Fumili in principle needs much less iterations
00087    int maxfcn_eff = int(0.5*maxfcn);
00088    int ipass = 0;
00089    double edmprev = 1;
00090    
00091    do { 
00092       
00093       
00094       min = Minimum(fcn, gc, seed, result, maxfcn_eff, edmval);
00095 
00096 
00097       // second time check for validity of function Minimum 
00098       if (ipass > 0) { 
00099          if(!min.IsValid()) {
00100 #ifdef WARNINGMSG
00101             MN_INFO_MSG("FumiliBuilder: FunctionMinimum is invalid.");
00102 #endif
00103             return min;
00104          }
00105       }
00106       
00107       // resulting edm of minimization
00108       edm = result.back().Edm();
00109 
00110 #ifdef DEBUG
00111       std::cout << "approximate edm is  " << edm << std::endl;
00112       std::cout << "npass is " << ipass << std::endl;
00113 #endif
00114       
00115       // call always Hesse (error matrix from Fumili is never accurate since is approximate) 
00116          
00117 #ifdef DEBUG
00118       std::cout << "FumiliBuilder will verify convergence and Error matrix. " << std::endl;
00119       std::cout << "dcov is =  "<<  min.Error().Dcovar() << std::endl;
00120 #endif
00121 
00122 //       // recalculate the gradient using the numerical gradient calculator
00123 //       Numerical2PGradientCalculator ngc(fcn, min.Seed().Trafo(), strategy);
00124 //       FunctionGradient ng = ngc( min.State().Parameters() );
00125 //       MinimumState tmp( min.State().Parameters(), min.State().Error(), ng, min.State().Edm(), min.State().NFcn() ); 
00126          
00127       MinimumState st = MnHesse(strategy)(fcn, min.State(), min.Seed().Trafo(),maxfcn);
00128       result.push_back( st );
00129          
00130 
00131       // check edm 
00132       edm = st.Edm();
00133 #ifdef DEBUG
00134       std::cout << "edm after Hesse calculation " << edm << std::endl;
00135       std::cout << "state after Hessian calculation  " << st << std::endl;
00136 #endif
00137          
00138       // break the loop if edm is NOT getting smaller 
00139       if (ipass > 0 && edm >= edmprev) { 
00140 #ifdef WARNINGMSG
00141          MN_INFO_MSG("FumiliBuilder: Exit iterations, no improvements after Hesse ");
00142          MN_INFO_VAL2("current edm is ", edm); 
00143          MN_INFO_VAL2("previous value ",edmprev);
00144 #endif
00145          break; 
00146       } 
00147       if (edm > edmval) { 
00148 #ifdef DEBUG
00149          std::cout << "FumiliBuilder: Tolerance is not sufficient - edm is " << edm << " requested " << edmval 
00150                    << " continue the minimization" << std::endl;
00151 #endif
00152       }
00153       else { 
00154          // Case when edm < edmval after Heasse but min is flagged eith a  bad edm: 
00155          // make then a new Function minimum since now edm is ok 
00156          if (min.IsAboveMaxEdm() ) {
00157             min = FunctionMinimum( min.Seed(), min.States(), min.Up() );
00158             break;
00159          }
00160          
00161       } 
00162       
00163       // end loop on iterations
00164       // ? need a maximum here (or max of function calls is enough ? ) 
00165       // continnue iteration (re-calculate function Minimum if edm IS NOT sufficient) 
00166       // no need to check that hesse calculation is done (if isnot done edm is OK anyway)
00167       // count the pass to exit second time when function Minimum is invalid
00168       // increase by 20% maxfcn for doing some more tests
00169       if (ipass == 0) maxfcn_eff = maxfcn;
00170       ipass++;
00171       edmprev = edm; 
00172       
00173    }  while (edm > edmval );
00174    
00175    
00176    
00177    // Add latest state (Hessian calculation)
00178    min.Add( result.back() );
00179    
00180    return min;
00181    
00182 }
00183 
00184 FunctionMinimum FumiliBuilder::Minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, std::vector<MinimumState>& result, unsigned int maxfcn, double edmval) const {
00185 
00186    // function performing the minimum searches using the FUMILI algorithm 
00187    // after the modification when I iterate on this functions, so it can be called many times, 
00188    //  the seed is used here only to get precision and construct the returned FunctionMinimum object 
00189    
00190    
00191    /*
00192     Three options were possible:
00193     
00194     1) create two parallel and completely separate hierarchies, in which case
00195     the FumiliMinimizer would NOT inherit from ModularFunctionMinimizer, 
00196     FumiliBuilder would not inherit from MinimumBuilder etc
00197     
00198     2) Use the inheritance (base classes of ModularFunctionMinimizer,
00199                             MinimumBuilder etc), but recreate the member functions Minimize() and 
00200     Minimum() respectively (naming them for example minimize2() and 
00201                             minimum2()) so that they can take FumiliFCNBase as Parameter instead FCNBase
00202     (otherwise one wouldn't be able to call the Fumili-specific methods).
00203     
00204     3) Cast in the daughter classes derived from ModularFunctionMinimizer,
00205     MinimumBuilder.
00206     
00207     The first two would mean to duplicate all the functionality already existent,
00208     which is a very bad practice and Error-prone. The third one is the most
00209     elegant and effective solution, where the only constraint is that the user
00210     must know that he has to pass a subclass of FumiliFCNBase to the FumiliMinimizer 
00211     and not just a subclass of FCNBase.
00212     BTW, the first two solutions would have meant to recreate also a parallel
00213     structure for MnFcn...
00214     **/
00215    //  const FumiliFCNBase* tmpfcn =  dynamic_cast<const FumiliFCNBase*>(&(fcn.Fcn()));
00216    
00217    const MnMachinePrecision& prec = seed.Precision();
00218    
00219    const MinimumState & initialState = result.back();
00220    
00221    double edm = initialState.Edm();
00222    
00223    
00224 #ifdef DEBUG
00225    std::cout << "\n\nDEBUG FUMILI Builder  \nInitial State: "  
00226       << " Parameter " << initialState.Vec()       
00227       << " Gradient " << initialState.Gradient().Vec() 
00228       << " Inv Hessian " << initialState.Error().InvHessian()  
00229       << " edm = " << initialState.Edm() 
00230       << " maxfcn = " << maxfcn 
00231       << " tolerance = " << edmval 
00232       << std::endl; 
00233 #endif
00234    
00235    
00236    // iterate until edm is small enough or max # of iterations reached
00237    edm *= (1. + 3.*initialState.Error().Dcovar());
00238    MnAlgebraicVector step(initialState.Gradient().Vec().size());
00239    
00240    // initial lambda Value
00241    double lambda = 0.001; 
00242    
00243    
00244    do {   
00245       
00246       //     const MinimumState& s0 = result.back();
00247       MinimumState s0 = result.back();
00248       
00249       step = -1.*s0.Error().InvHessian()*s0.Gradient().Vec();
00250       
00251       
00252 #ifdef DEBUG
00253       std::cout << "\n\n---> Iteration - " << result.size() 
00254       << "\nFval = " << s0.Fval() << " numOfCall = " << fcn.NumOfCalls() 
00255       << "\nInternal Parameter values " << s0.Vec() 
00256       << " Newton step " << step << std::endl; 
00257 #endif
00258       
00259       double gdel = inner_product(step, s0.Gradient().Grad());
00260       if(gdel > 0.) {
00261 #ifdef WARNINGMSG
00262          MN_INFO_MSG("FumiliBuilder: matrix not pos.def, gdel > 0");
00263          MN_INFO_VAL(gdel);
00264 #endif
00265          MnPosDef psdf;
00266          s0 = psdf(s0, prec);
00267          step = -1.*s0.Error().InvHessian()*s0.Gradient().Vec();
00268          gdel = inner_product(step, s0.Gradient().Grad());
00269 #ifdef WARNINGMSG
00270          MN_INFO_VAL2("After correction ",gdel);
00271 #endif
00272          if(gdel > 0.) {
00273             result.push_back(s0);
00274             return FunctionMinimum(seed, result, fcn.Up());
00275          }
00276       }
00277       
00278       
00279       //     MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec);
00280       
00281       //     if(fabs(pp.Y() - s0.Fval()) < prec.Eps()) {
00282       //       std::cout<<"FumiliBuilder: no improvement"<<std::endl;
00283       //       break; //no improvement
00284       //     }
00285       
00286       
00287       //     MinimumParameters p(s0.Vec() + pp.X()*step, pp.Y());
00288       
00289       // if taking a full step 
00290       
00291       // take a full step
00292       
00293       MinimumParameters p(s0.Vec() + step,  fcn( s0.Vec() + step ) );
00294       
00295       // check that taking the full step does not deteriorate minimum
00296       // in that case do a line search  
00297       if ( p.Fval() >= s0.Fval()  ) {
00298          MnLineSearch lsearch;   
00299          MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec);
00300          
00301          if(fabs(pp.Y() - s0.Fval()) < prec.Eps()) {
00302             //std::cout<<"FumiliBuilder: no improvement"<<std::endl;
00303             break; //no improvement
00304          }
00305          p =  MinimumParameters(s0.Vec() + pp.X()*step, pp.Y() );
00306       }
00307       
00308 #ifdef DEBUG
00309       std::cout << "Before Gradient " << fcn.NumOfCalls() << std::endl; 
00310 #endif
00311       
00312       FunctionGradient g = gc(p, s0.Gradient());
00313       
00314 #ifdef DEBUG   
00315       std::cout << "After Gradient " << fcn.NumOfCalls() << std::endl; 
00316 #endif
00317       
00318       //FunctionGradient g = gc(s0.Parameters(), s0.Gradient()); 
00319       
00320       
00321       // move Error updator after Gradient since the Value is cached inside
00322       
00323       MinimumError e = ErrorUpdator().Update(s0, p, gc, lambda);
00324       
00325       
00326       edm = Estimator().Estimate(g, s0.Error());
00327       
00328       
00329 #ifdef DEBUG
00330       std::cout << "Updated new point: \n " 
00331          << " FVAL      " << p.Fval()       
00332          << " Parameter " << p.Vec()       
00333          << " Gradient " << g.Vec() 
00334          << " InvHessian " << e.Matrix() 
00335          << " Hessian " << e.Hessian() 
00336          << " edm = " << edm << std::endl << std::endl;
00337 #endif
00338       
00339       if(edm < 0.) {
00340 #ifdef WARNINGMSG
00341          MN_INFO_MSG("FumiliBuilder: matrix not pos.def., edm < 0");
00342 #endif
00343          MnPosDef psdf;
00344          s0 = psdf(s0, prec);
00345          edm = Estimator().Estimate(g, s0.Error());
00346          if(edm < 0.) {
00347             result.push_back(s0);
00348             return FunctionMinimum(seed, result, fcn.Up());
00349          }
00350       } 
00351       
00352       // check lambda according to step 
00353       if ( p.Fval() < s0.Fval()  ) 
00354          // fcn is decreasing along the step
00355          lambda *= 0.1;
00356       else { 
00357          lambda *= 10;
00358          // if close to minimum stop to avoid oscillations around minimum value 
00359          if ( edm < 0.1 ) break;
00360       }
00361       
00362 #ifdef DEBUG
00363       std::cout <<  " finish iteration- " << result.size() << " lambda =  "  << lambda << " f1 = " << p.Fval() << " f0 = " << s0.Fval() << " num of calls = " << fcn.NumOfCalls() << " edm " << edm << std::endl; 
00364 #endif
00365             
00366       result.push_back(MinimumState(p, e, g, edm, fcn.NumOfCalls())); 
00367       
00368       
00369       edm *= (1. + 3.*e.Dcovar());
00370             
00371       
00372    } while(edm > edmval && fcn.NumOfCalls() < maxfcn);
00373    
00374    
00375    
00376    if(fcn.NumOfCalls() >= maxfcn) {
00377 #ifdef WARNINGMSG
00378       MN_INFO_MSG("FumiliBuilder: call limit exceeded.");
00379 #endif
00380       return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnReachedCallLimit());
00381    }
00382    
00383    if(edm > edmval) {
00384       if(edm < fabs(prec.Eps2()*result.back().Fval())) {
00385 #ifdef WARNINGMSG
00386          MN_INFO_MSG("FumiliBuilder: machine accuracy limits further improvement.");
00387 #endif
00388          return FunctionMinimum(seed, result, fcn.Up());
00389       } else if(edm < 10.*edmval) {
00390          return FunctionMinimum(seed, result, fcn.Up());
00391       } else {
00392 #ifdef WARNINGMSG
00393          MN_INFO_MSG("FumiliBuilder: finishes without convergence.");
00394          MN_INFO_VAL2("FumiliBuilder: ",edm);
00395          MN_INFO_VAL2("    requested: ",edmval);
00396 #endif
00397          return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnAboveMaxEdm());
00398       }
00399    }
00400    //   std::cout<<"result.back().Error().Dcovar()= "<<result.back().Error().Dcovar()<<std::endl;
00401    
00402 #ifdef DEBUG
00403    std::cout << "Exiting succesfully FumiliBuilder \n" 
00404       << "NFCalls = " << fcn.NumOfCalls() 
00405       << "\nFval = " <<  result.back().Fval() 
00406       << "\nedm = " << edm << " requested = " << edmval << std::endl; 
00407 #endif
00408    
00409    return FunctionMinimum(seed, result, fcn.Up());
00410 }
00411 
00412    }  // namespace Minuit2
00413 
00414 }  // namespace ROOT

Generated on Tue Jul 5 14:36:48 2011 for ROOT_528-00b_version by  doxygen 1.5.1