VariableMetricBuilder.cxx

Go to the documentation of this file.
00001 // @(#)root/minuit2:$Id: VariableMetricBuilder.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/VariableMetricBuilder.h"
00011 #include "Minuit2/GradientCalculator.h"
00012 #include "Minuit2/MinimumState.h"
00013 #include "Minuit2/MinimumError.h"
00014 #include "Minuit2/FunctionGradient.h"
00015 #include "Minuit2/FunctionMinimum.h"
00016 #include "Minuit2/MnLineSearch.h"
00017 #include "Minuit2/MinimumSeed.h"
00018 #include "Minuit2/MnFcn.h"
00019 #include "Minuit2/MnMachinePrecision.h"
00020 #include "Minuit2/MnPosDef.h"
00021 #include "Minuit2/MnParabolaPoint.h"
00022 #include "Minuit2/LaSum.h"
00023 #include "Minuit2/LaProd.h"
00024 #include "Minuit2/MnStrategy.h"
00025 #include "Minuit2/MnHesse.h"
00026 
00027 //#define DEBUG 
00028 
00029 #if defined(DEBUG) || defined(WARNINGMSG)
00030 #include "Minuit2/MnPrint.h" 
00031 #endif
00032 
00033 
00034 
00035 namespace ROOT {
00036 
00037    namespace Minuit2 {
00038 
00039 
00040 double inner_product(const LAVector&, const LAVector&);
00041 
00042 FunctionMinimum VariableMetricBuilder::Minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, const MnStrategy& strategy, unsigned int maxfcn, double edmval) const {   
00043    // top level function to find minimum from a given initial seed 
00044    // iterate on a minimum search in case of first attempt is not succesfull
00045    
00046    // to be consistent with F77 Minuit
00047    // in Minuit2 edm is correct and is ~ a factor of 2 smaller than F77Minuit
00048    // There are also a check for convergence if (edm < 0.1 edmval for exiting the loop) 
00049    edmval *= 0.0002; 
00050    
00051    
00052 #ifdef DEBUG
00053    std::cout<<"VariableMetricBuilder convergence when edm < "<<edmval<<std::endl;
00054 #endif
00055    
00056    if(seed.Parameters().Vec().size() == 0) {
00057       return FunctionMinimum(seed, fcn.Up());
00058    }
00059    
00060    
00061    //   double edm = Estimator().Estimate(seed.Gradient(), seed.Error());
00062    double edm = seed.State().Edm();
00063    
00064    FunctionMinimum min(seed, fcn.Up() );
00065    
00066    if(edm < 0.) {
00067 #ifdef WARNINGMSG
00068       MN_INFO_MSG("VariableMetricBuilder: initial matrix not pos.def.");
00069 #endif
00070       //assert(!seed.Error().IsPosDef());
00071       return min;
00072    }
00073    
00074    std::vector<MinimumState> result;
00075    //   result.reserve(1);
00076    result.reserve(8);
00077    
00078    result.push_back( seed.State() );
00079    
00080    // do actual iterations
00081    
00082    
00083    // try first with a maxfxn = 80% of maxfcn 
00084    int maxfcn_eff = maxfcn;
00085    int ipass = 0;
00086    bool iterate = false; 
00087    
00088    do { 
00089       
00090       iterate = false; 
00091 
00092 #ifdef DEBUG
00093       std::cout << "start iterating... " << std::endl; 
00094       if (ipass > 0)  std::cout << "continue iterating... " << std::endl; 
00095 #endif
00096       
00097       min = Minimum(fcn, gc, seed, result, maxfcn_eff, edmval);
00098       // second time check for validity of function Minimum 
00099       if (ipass > 0) { 
00100          if(!min.IsValid()) {
00101 #ifdef WARNINGMSG
00102             MN_INFO_MSG("FunctionMinimum is invalid.");
00103 #endif
00104             return min;
00105          }
00106       }
00107       
00108       // resulting edm of minimization
00109       edm = result.back().Edm();
00110       
00111       if( (strategy.Strategy() == 2) || 
00112           (strategy.Strategy() == 1 && min.Error().Dcovar() > 0.05) ) {
00113          
00114 #ifdef DEBUG
00115          std::cout<<"MnMigrad will verify convergence and Error matrix. "<< std::endl;
00116          std::cout<<"dcov is =  "<<  min.Error().Dcovar() << std::endl;
00117 #endif
00118          
00119          MinimumState st = MnHesse(strategy)(fcn, min.State(), min.Seed().Trafo(),maxfcn);
00120          result.push_back( st );
00121          
00122          // check edm 
00123          edm = st.Edm();
00124 #ifdef DEBUG
00125          std::cout << "edm after Hesse calculation " << edm << " requested " << edmval << std::endl;
00126 #endif
00127          if (edm > edmval) { 
00128 #ifdef WARNINGMSG
00129             MN_INFO_MSG("VariableMetricBuilder: Tolerance is not sufficient, continue the minimization");
00130             MN_INFO_VAL(edm);
00131             MN_INFO_VAL2("required",edmval);
00132 #endif
00133             // be careful with machine precision and avoid too small edm
00134             if (edm >= fabs(seed.Precision().Eps2()*result.back().Fval())) 
00135                iterate = true; 
00136 
00137          }
00138       }
00139       
00140       
00141       // end loop on iterations
00142       // ? need a maximum here (or max of function calls is enough ? ) 
00143       // continnue iteration (re-calculate function Minimum if edm IS NOT sufficient) 
00144       // no need to check that hesse calculation is done (if isnot done edm is OK anyway)
00145       // count the pass to exit second time when function Minimum is invalid
00146       // increase by 20% maxfcn for doing some more tests
00147       if (ipass == 0) maxfcn_eff = int(maxfcn*1.3);
00148       ipass++;
00149    }  while ( iterate );
00150    
00151    
00152    
00153    // Add latest state (Hessian calculation)
00154    min.Add( result.back() );
00155    
00156    return min;
00157 }
00158 
00159 FunctionMinimum VariableMetricBuilder::Minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, std::vector<MinimumState>& result, unsigned int maxfcn, double edmval) const {
00160    // function performing the minimum searches using the Variable Metric  algorithm (MIGRAD) 
00161    // perform first a line search in the - Vg direction and then update using the Davidon formula (Davidon Error updator)
00162    // stop when edm reached is less than required (edmval)
00163    
00164    // after the modification when I iterate on this functions, so it can be called many times, 
00165    //  the seed is used here only to get precision and construct the returned FunctionMinimum object 
00166    
00167 
00168    
00169    const MnMachinePrecision& prec = seed.Precision();
00170    
00171    
00172    //   result.push_back(MinimumState(seed.Parameters(), seed.Error(), seed.Gradient(), edm, fcn.NumOfCalls()));
00173    const MinimumState & initialState = result.back();
00174    
00175    
00176    double edm = initialState.Edm();
00177    
00178    
00179 #ifdef DEBUG
00180    std::cout << "\n\nDEBUG Variable Metric Builder  \nInitial State: "  
00181              << " Parameter " << initialState.Vec()       
00182              << " Gradient " << initialState.Gradient().Vec() 
00183              << " Inv Hessian " << initialState.Error().InvHessian()  
00184              << " edm = " << initialState.Edm() << std::endl;
00185 #endif
00186    
00187    
00188    
00189    // iterate until edm is small enough or max # of iterations reached
00190    edm *= (1. + 3.*initialState.Error().Dcovar());
00191    MnLineSearch lsearch;
00192    MnAlgebraicVector step(initialState.Gradient().Vec().size());
00193    // keep also prevStep
00194    MnAlgebraicVector prevStep(initialState.Gradient().Vec().size());
00195    
00196    do {   
00197       
00198       //     const MinimumState& s0 = result.back();
00199       MinimumState s0 = result.back();
00200       
00201       step = -1.*s0.Error().InvHessian()*s0.Gradient().Vec();
00202       
00203 #ifdef DEBUG
00204       std::cout << "\n\n---> Iteration - " << result.size() 
00205                 << "\nFval = " << s0.Fval() << " numOfCall = " << fcn.NumOfCalls() 
00206                 << "\nInternal Parameter values " << s0.Vec() 
00207                 << " Newton step " << step << std::endl; 
00208 #endif
00209       
00210       // check if derivatives are not zero
00211       if ( inner_product(s0.Gradient().Vec(),s0.Gradient().Vec() )  <= 0 )  { 
00212 #ifdef DEBUG
00213          std::cout << "VariableMetricBuilder: all derivatives are zero - return current status" << std::endl;
00214 #endif
00215          break;
00216       }
00217       
00218 
00219       double gdel = inner_product(step, s0.Gradient().Grad());
00220 
00221 #ifdef DEBUG
00222       std::cout << " gdel = " << gdel << std::endl;
00223 #endif
00224 
00225 
00226       if(gdel > 0.) {
00227 #ifdef WARNINGMSG
00228          MN_INFO_MSG("VariableMetricBuilder: matrix not pos.def, gdel > 0");
00229          MN_INFO_VAL(gdel);
00230 #endif
00231          MnPosDef psdf;
00232          s0 = psdf(s0, prec);
00233          step = -1.*s0.Error().InvHessian()*s0.Gradient().Vec();
00234          // #ifdef DEBUG
00235          //       std::cout << "After MnPosdef - Error  " << s0.Error().InvHessian() << " Gradient " << s0.Gradient().Vec() << " step " << step << std::endl;      
00236          // #endif
00237          gdel = inner_product(step, s0.Gradient().Grad());
00238 #ifdef WARNINGMSG
00239          MN_INFO_VAL(gdel);
00240 #endif
00241          if(gdel > 0.) {
00242             result.push_back(s0);
00243             return FunctionMinimum(seed, result, fcn.Up());
00244          }
00245       }
00246       MnParabolaPoint pp = lsearch(fcn, s0.Parameters(), step, gdel, prec);
00247 
00248       // <= needed for case 0 <= 0
00249       if(fabs(pp.Y() - s0.Fval()) <=  fabs(s0.Fval())*prec.Eps() ) {
00250 #ifdef WARNINGMSG
00251          MN_INFO_MSG("VariableMetricBuilder: no improvement in line search");
00252 #endif
00253          // no improvement exit   (is it really needed LM ? in vers. 1.22 tried alternative )
00254          // add new state where only fcn changes
00255          result.push_back(MinimumState(s0.Parameters(), s0.Error(), s0.Gradient(), s0.Edm(), fcn.NumOfCalls()) );
00256          break; 
00257          
00258          
00259       }
00260       
00261 #ifdef DEBUG
00262       std::cout << "Result after line search : \nx = " << pp.X() 
00263                 << "\nOld Fval = " << s0.Fval() 
00264                 << "\nNew Fval = " << pp.Y() 
00265                 << "\nNFcalls = " << fcn.NumOfCalls() << std::endl; 
00266 #endif
00267       
00268       MinimumParameters p(s0.Vec() + pp.X()*step, pp.Y());
00269       
00270       
00271       FunctionGradient g = gc(p, s0.Gradient());
00272       
00273       
00274       edm = Estimator().Estimate(g, s0.Error());
00275       
00276       
00277       if(edm < 0.) {
00278 #ifdef WARNINGMSG
00279          MN_INFO_MSG("VariableMetricBuilder: matrix not pos.def. : edm is < 0. Make pos def...");
00280 #endif
00281          MnPosDef psdf;
00282          s0 = psdf(s0, prec);
00283          edm = Estimator().Estimate(g, s0.Error());
00284          if(edm < 0.) {
00285 #ifdef WARNINGMSG
00286             MN_INFO_MSG("VariableMetricBuilder: matrix still not pos.def. : exit iterations ");
00287 #endif
00288             result.push_back(s0);
00289             return FunctionMinimum(seed, result, fcn.Up());
00290          }
00291       } 
00292       MinimumError e = ErrorUpdator().Update(s0, p, g);
00293       
00294 #ifdef DEBUG
00295       std::cout << "Updated new point: \n " 
00296                 << " Parameter " << p.Vec()       
00297                 << " Gradient " << g.Vec() 
00298                 << " InvHessian " << e.Matrix() 
00299                 << " Hessian " << e.Hessian() 
00300                 << " edm = " << edm << std::endl << std::endl;
00301 #endif
00302       
00303       
00304       result.push_back(MinimumState(p, e, g, edm, fcn.NumOfCalls())); 
00305       
00306       // correct edm 
00307       edm *= (1. + 3.*e.Dcovar());
00308       
00309 #ifdef DEBUG
00310       std::cout << "edm corrected = " << edm << std::endl;
00311 #endif
00312       
00313       
00314       
00315    } while(edm > edmval && fcn.NumOfCalls() < maxfcn);  // end of iteration loop
00316    
00317    if(fcn.NumOfCalls() >= maxfcn) {
00318 #ifdef WARNINGMSG
00319       MN_INFO_MSG("VariableMetricBuilder: call limit exceeded.");
00320 #endif
00321       return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnReachedCallLimit());
00322    }
00323    
00324    if(edm > edmval) {
00325       if(edm < fabs(prec.Eps2()*result.back().Fval())) {
00326 #ifdef WARNINGMSG
00327          MN_INFO_MSG("VariableMetricBuilder: machine accuracy limits further improvement.");
00328 #endif
00329          return FunctionMinimum(seed, result, fcn.Up());
00330       } else if(edm < 10.*edmval) {
00331          return FunctionMinimum(seed, result, fcn.Up());
00332       } else {
00333 #ifdef WARNINGMSG
00334          MN_INFO_MSG("VariableMetricBuilder: iterations finish without convergence.");
00335          MN_INFO_VAL2("VariableMetricBuilder",edm);
00336          MN_INFO_VAL2("            requested",edmval);
00337 #endif
00338          return FunctionMinimum(seed, result, fcn.Up(), FunctionMinimum::MnAboveMaxEdm());
00339       }
00340    }
00341    //   std::cout<<"result.back().Error().Dcovar()= "<<result.back().Error().Dcovar()<<std::endl;
00342    
00343 #ifdef DEBUG
00344    std::cout << "Exiting succesfully Variable Metric Builder \n" 
00345              << "NFCalls = " << fcn.NumOfCalls() 
00346              << "\nFval = " <<  result.back().Fval() 
00347              << "\nedm = " << edm << " requested = " << edmval << std::endl; 
00348 #endif
00349    
00350    return FunctionMinimum(seed, result, fcn.Up());
00351 }
00352 
00353    }  // namespace Minuit2
00354 
00355 }  // namespace ROOT

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