MnHesse.cxx

Go to the documentation of this file.
00001 // @(#)root/minuit2:$Id: MnHesse.cxx 37557 2010-12-13 10:42:24Z moneta $
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/MnHesse.h"
00011 #include "Minuit2/MnUserParameterState.h"
00012 #include "Minuit2/MnUserFcn.h"
00013 #include "Minuit2/FCNBase.h"
00014 #include "Minuit2/MnPosDef.h"
00015 #include "Minuit2/HessianGradientCalculator.h"
00016 #include "Minuit2/Numerical2PGradientCalculator.h"
00017 #include "Minuit2/InitialGradientCalculator.h"
00018 #include "Minuit2/MinimumState.h"
00019 #include "Minuit2/VariableMetricEDMEstimator.h"
00020 #include "Minuit2/FunctionMinimum.h"
00021 
00022 //#define DEBUG
00023 
00024 #if defined(DEBUG) || defined(WARNINGMSG)
00025 #include "Minuit2/MnPrint.h"
00026 #endif
00027 
00028 #include "Minuit2/MPIProcess.h"
00029 
00030 namespace ROOT {
00031 
00032    namespace Minuit2 {
00033 
00034 
00035 MnUserParameterState MnHesse::operator()(const FCNBase& fcn, const std::vector<double>& par, const std::vector<double>& err, unsigned int maxcalls) const { 
00036    // interface from vector of params and errors
00037    return (*this)(fcn, MnUserParameterState(par, err), maxcalls);
00038 }
00039 
00040 MnUserParameterState MnHesse::operator()(const FCNBase& fcn, const std::vector<double>& par, unsigned int nrow, const std::vector<double>& cov,  unsigned int maxcalls) const {
00041    // interface from vector of params and covariance
00042    return (*this)(fcn, MnUserParameterState(par, cov, nrow), maxcalls);
00043 }
00044 
00045 MnUserParameterState MnHesse::operator()(const FCNBase& fcn, const std::vector<double>& par, const MnUserCovariance& cov, unsigned int maxcalls) const {
00046     // interface from vector of params and covariance
00047    return (*this)(fcn, MnUserParameterState(par, cov), maxcalls);
00048 }
00049 
00050 MnUserParameterState MnHesse::operator()(const FCNBase& fcn, const MnUserParameters& par, unsigned int maxcalls) const {
00051    // interface from MnUserParameters
00052    return (*this)(fcn, MnUserParameterState(par), maxcalls);
00053 }
00054 
00055 MnUserParameterState MnHesse::operator()(const FCNBase& fcn, const MnUserParameters& par, const MnUserCovariance& cov, unsigned int maxcalls) const {
00056    // interface from MnUserParameters and MnUserCovariance
00057    return (*this)(fcn, MnUserParameterState(par, cov), maxcalls);
00058 }
00059 
00060 MnUserParameterState MnHesse::operator()(const FCNBase& fcn, const MnUserParameterState& state, unsigned int maxcalls) const {
00061    // interface from MnUserParameterState 
00062    // create a new Minimum state and use that interface
00063    unsigned int n = state.VariableParameters();
00064    MnUserFcn mfcn(fcn, state.Trafo(),state.NFcn());
00065    MnAlgebraicVector x(n);
00066    for(unsigned int i = 0; i < n; i++) x(i) = state.IntParameters()[i];
00067    double amin = mfcn(x);
00068    Numerical2PGradientCalculator gc(mfcn, state.Trafo(), fStrategy);
00069    MinimumParameters par(x, amin);
00070    FunctionGradient gra = gc(par);
00071    MinimumState tmp = (*this)(mfcn, MinimumState(par, MinimumError(MnAlgebraicSymMatrix(n), 1.), gra, state.Edm(), state.NFcn()), state.Trafo(), maxcalls);
00072    
00073    return MnUserParameterState(tmp, fcn.Up(), state.Trafo());
00074 }
00075 
00076 void MnHesse::operator()(const FCNBase& fcn, FunctionMinimum& min, unsigned int maxcalls) const {
00077    // interface from FunctionMinimum to be used after minimization 
00078    // use last state from the minimization without the need to re-create a new state
00079    // do not reset function calls and keep updating them
00080    MnUserFcn mfcn(fcn, min.UserState().Trafo(),min.NFcn());
00081    MinimumState st = (*this)( mfcn, min.State(), min.UserState().Trafo(), maxcalls); 
00082    min.Add(st); 
00083 }
00084 
00085 MinimumState MnHesse::operator()(const MnFcn& mfcn, const MinimumState& st, const MnUserTransformation& trafo, unsigned int maxcalls) const {
00086    // internal interface from MinimumState and MnUserTransformation
00087    // Function who does the real Hessian calculations
00088    
00089    const MnMachinePrecision& prec = trafo.Precision();
00090    // make sure starting at the right place
00091    double amin = mfcn(st.Vec());
00092    double aimsag = sqrt(prec.Eps2())*(fabs(amin)+mfcn.Up());
00093    
00094    // diagonal Elements first
00095    
00096    unsigned int n = st.Parameters().Vec().size();
00097    if(maxcalls == 0) maxcalls = 200 + 100*n + 5*n*n;
00098    
00099    MnAlgebraicSymMatrix vhmat(n);
00100    MnAlgebraicVector g2 = st.Gradient().G2();
00101    MnAlgebraicVector gst = st.Gradient().Gstep();
00102    MnAlgebraicVector grd = st.Gradient().Grad();
00103    MnAlgebraicVector dirin = st.Gradient().Gstep();
00104    MnAlgebraicVector yy(n);
00105 
00106 
00107    // case gradient is not numeric (could be analytical or from FumiliGradientCalculator)
00108 
00109    if(st.Gradient().IsAnalytical()  ) {
00110       Numerical2PGradientCalculator igc(mfcn, trafo, fStrategy);
00111       FunctionGradient tmp = igc(st.Parameters());
00112       gst = tmp.Gstep();
00113       dirin = tmp.Gstep();
00114       g2 = tmp.G2();
00115    }
00116    
00117    MnAlgebraicVector x = st.Parameters().Vec(); 
00118 
00119 #ifdef DEBUG
00120    std::cout << "\nMnHesse " << std::endl;
00121    std::cout << " x " << x << std::endl;
00122    std::cout << " amin " << amin << "  " << st.Fval() << std::endl;
00123    std::cout << " grd " << grd << std::endl;
00124    std::cout << " gst " << gst << std::endl;
00125    std::cout << " g2  " << g2 << std::endl;
00126    std::cout << " Gradient is analytical  " << st.Gradient().IsAnalytical() << std::endl;
00127 #endif
00128 
00129    
00130    for(unsigned int i = 0; i < n; i++) {
00131       
00132       double xtf = x(i);
00133       double dmin = 8.*prec.Eps2()*(fabs(xtf) + prec.Eps2());
00134       double d = fabs(gst(i));
00135       if(d < dmin) d = dmin;
00136 
00137 #ifdef DEBUG
00138       std::cout << "\nDerivative parameter  " << i << " d = " << d << " dmin = " << dmin << std::endl;
00139 #endif
00140 
00141       
00142       for(unsigned int icyc = 0; icyc < Ncycles(); icyc++) {
00143          double sag = 0.;
00144          double fs1 = 0.;
00145          double fs2 = 0.;
00146          for(unsigned int multpy = 0; multpy < 5; multpy++) {
00147             x(i) = xtf + d;
00148             fs1 = mfcn(x);
00149             x(i) = xtf - d;
00150             fs2 = mfcn(x);
00151             x(i) = xtf;
00152             sag = 0.5*(fs1+fs2-2.*amin);
00153 
00154 #ifdef DEBUG
00155             std::cout << "cycle " << icyc << " mul " << multpy << "\t sag = " << sag << " d = " << d << std::endl; 
00156 #endif
00157             //  Now as F77 Minuit - check taht sag is not zero
00158             if (sag != 0) goto L30; // break
00159             if(trafo.Parameter(i).HasLimits()) {
00160                if(d > 0.5) goto L26;
00161                d *= 10.;
00162                if(d > 0.5) d = 0.51;
00163                continue;
00164             }
00165             d *= 10.;
00166          }
00167          
00168 L26:  
00169 #ifdef WARNINGMSG
00170          MN_INFO_VAL2("MnHesse: 2nd derivative zero for Parameter ",i);
00171          MN_INFO_MSG("MnHesse fails and will return diagonal matrix ");
00172 #endif
00173          
00174          for(unsigned int j = 0; j < n; j++) {
00175             double tmp = g2(j) < prec.Eps2() ? 1. : 1./g2(j);
00176             vhmat(j,j) = tmp < prec.Eps2() ? 1. : tmp;
00177          }
00178          
00179          return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed()), st.Gradient(), st.Edm(), mfcn.NumOfCalls());
00180          
00181 L30:      
00182             double g2bfor = g2(i);
00183          g2(i) = 2.*sag/(d*d);
00184          grd(i) = (fs1-fs2)/(2.*d);
00185          gst(i) = d;
00186          dirin(i) = d;
00187          yy(i) = fs1;
00188          double dlast = d;
00189          d = sqrt(2.*aimsag/fabs(g2(i)));
00190          if(trafo.Parameter(i).HasLimits()) d = std::min(0.5, d);
00191          if(d < dmin) d = dmin;
00192 
00193 #ifdef DEBUG
00194          std::cout << "\t g1 = " << grd(i) << " g2 = " << g2(i) << " step = " << gst(i) << " d = " << d 
00195                    << " diffd = " <<  fabs(d-dlast)/d << " diffg2 = " << fabs(g2(i)-g2bfor)/g2(i) << std::endl;
00196 #endif
00197 
00198          
00199          // see if converged
00200          if(fabs((d-dlast)/d) < Tolerstp()) break;
00201          if(fabs((g2(i)-g2bfor)/g2(i)) < TolerG2()) break; 
00202          d = std::min(d, 10.*dlast);
00203          d = std::max(d, 0.1*dlast);   
00204       }
00205       vhmat(i,i) = g2(i);
00206       if(mfcn.NumOfCalls()  > maxcalls) {
00207          
00208 #ifdef WARNINGMSG
00209          //std::cout<<"maxcalls " << maxcalls << " " << mfcn.NumOfCalls() << "  " <<   st.NFcn() << std::endl;
00210          MN_INFO_MSG("MnHesse: maximum number of allowed function calls exhausted.");  
00211          MN_INFO_MSG("MnHesse fails and will return diagonal matrix ");
00212 #endif
00213          
00214          for(unsigned int j = 0; j < n; j++) {
00215             double tmp = g2(j) < prec.Eps2() ? 1. : 1./g2(j);
00216             vhmat(j,j) = tmp < prec.Eps2() ? 1. : tmp;
00217          }
00218          
00219          return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed()), st.Gradient(), st.Edm(), mfcn.NumOfCalls());
00220       }
00221       
00222    }
00223 
00224 #ifdef DEBUG
00225    std::cout << "\n Second derivatives " << g2 << std::endl; 
00226 #endif   
00227    
00228    if(fStrategy.Strategy() > 0) {
00229       // refine first derivative
00230       HessianGradientCalculator hgc(mfcn, trafo, fStrategy);
00231       FunctionGradient gr = hgc(st.Parameters(), FunctionGradient(grd, g2, gst));
00232       // update gradient and step values 
00233       grd = gr.Grad();
00234       gst = gr.Gstep();
00235    }
00236    
00237    //off-diagonal Elements  
00238    // initial starting values
00239    MPIProcess mpiprocOffDiagonal(n*(n-1)/2,0);
00240    unsigned int startParIndexOffDiagonal = mpiprocOffDiagonal.StartElementIndex();
00241    unsigned int endParIndexOffDiagonal = mpiprocOffDiagonal.EndElementIndex();
00242 
00243    unsigned int offsetVect = 0;
00244    for (unsigned int in = 0; in<startParIndexOffDiagonal; in++)
00245       if ((in+offsetVect)%(n-1)==0) offsetVect += (in+offsetVect)/(n-1);
00246 
00247    for (unsigned int in = startParIndexOffDiagonal;
00248         in<endParIndexOffDiagonal; in++) {
00249 
00250       int i = (in+offsetVect)/(n-1);
00251       if ((in+offsetVect)%(n-1)==0) offsetVect += i;
00252       int j = (in+offsetVect)%(n-1)+1;
00253 
00254       if ((i+1)==j || in==startParIndexOffDiagonal)
00255          x(i) += dirin(i);
00256       
00257       x(j) += dirin(j);
00258       
00259       double fs1 = mfcn(x);
00260       double elem = (fs1 + amin - yy(i) - yy(j))/(dirin(i)*dirin(j));
00261       vhmat(i,j) = elem;
00262       
00263       x(j) -= dirin(j);
00264       
00265       if (j%(n-1)==0 || in==endParIndexOffDiagonal-1)
00266          x(i) -= dirin(i);
00267       
00268    }
00269    
00270    mpiprocOffDiagonal.SyncSymMatrixOffDiagonal(vhmat);
00271 
00272    //verify if matrix pos-def (still 2nd derivative)
00273 
00274 #ifdef DEBUG
00275    std::cout << "Original error matrix " << vhmat << std::endl;
00276 #endif
00277 
00278    MinimumError tmpErr = MnPosDef()(MinimumError(vhmat,1.), prec);
00279 
00280 #ifdef DEBUG
00281    std::cout << "Original error matrix " << vhmat << std::endl;
00282 #endif
00283 
00284    vhmat = tmpErr.InvHessian();
00285 
00286 #ifdef DEBUG
00287    std::cout << "PosDef error matrix " << vhmat << std::endl;
00288 #endif
00289 
00290 
00291    int ifail = Invert(vhmat);
00292    if(ifail != 0) {
00293       
00294 #ifdef WARNINGMSG
00295       MN_INFO_MSG("MnHesse: matrix inversion fails!");
00296       MN_INFO_MSG("MnHesse fails and will return diagonal matrix.");
00297 #endif
00298       
00299       MnAlgebraicSymMatrix tmpsym(vhmat.Nrow());
00300       for(unsigned int j = 0; j < n; j++) {
00301          double tmp = g2(j) < prec.Eps2() ? 1. : 1./g2(j);
00302          tmpsym(j,j) = tmp < prec.Eps2() ? 1. : tmp;
00303       }
00304       
00305       return MinimumState(st.Parameters(), MinimumError(tmpsym, MinimumError::MnInvertFailed()), st.Gradient(), st.Edm(), mfcn.NumOfCalls());
00306    }
00307    
00308    FunctionGradient gr(grd, g2, gst);
00309    VariableMetricEDMEstimator estim;
00310    
00311    // if matrix is made pos def returns anyway edm
00312    if(tmpErr.IsMadePosDef()) {
00313       MinimumError err(vhmat, MinimumError::MnMadePosDef() );
00314       double edm = estim.Estimate(gr, err);
00315 #ifdef WARNINGMSG
00316       MN_INFO_MSG("MnHesse: matrix was forced pos. def. ");
00317 #endif
00318       return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls());
00319    }
00320    
00321    //calculate edm for good errors
00322    MinimumError err(vhmat, 0.);
00323    double edm = estim.Estimate(gr, err);
00324 
00325 #ifdef DEBUG
00326    std::cout << "\nNew state from MnHesse " << std::endl;
00327    std::cout << "Gradient " << grd << std::endl; 
00328    std::cout << "Second Deriv " << g2 << std::endl; 
00329    std::cout << "Gradient step " << gst << std::endl; 
00330    std::cout << "Error  " << vhmat  << std::endl; 
00331    std::cout << "edm  " << edm  << std::endl; 
00332 #endif
00333 
00334    
00335    return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls());
00336 }
00337 
00338 /*
00339  MinimumError MnHesse::Hessian(const MnFcn& mfcn, const MinimumState& st, const MnUserTransformation& trafo) const {
00340     
00341     const MnMachinePrecision& prec = trafo.Precision();
00342     // make sure starting at the right place
00343     double amin = mfcn(st.Vec());
00344     //   if(fabs(amin - st.Fval()) > prec.Eps2()) std::cout<<"function Value differs from amin  by "<<amin - st.Fval()<<std::endl;
00345     
00346     double aimsag = sqrt(prec.Eps2())*(fabs(amin)+mfcn.Up());
00347     
00348     // diagonal Elements first
00349     
00350     unsigned int n = st.Parameters().Vec().size();
00351     MnAlgebraicSymMatrix vhmat(n);
00352     MnAlgebraicVector g2 = st.Gradient().G2();
00353     MnAlgebraicVector gst = st.Gradient().Gstep();
00354     MnAlgebraicVector grd = st.Gradient().Grad();
00355     MnAlgebraicVector dirin = st.Gradient().Gstep();
00356     MnAlgebraicVector yy(n);
00357     MnAlgebraicVector x = st.Parameters().Vec(); 
00358     
00359     for(unsigned int i = 0; i < n; i++) {
00360        
00361        double xtf = x(i);
00362        double dmin = 8.*prec.Eps2()*fabs(xtf);
00363        double d = fabs(gst(i));
00364        if(d < dmin) d = dmin;
00365        for(int icyc = 0; icyc < Ncycles(); icyc++) {
00366           double sag = 0.;
00367           double fs1 = 0.;
00368           double fs2 = 0.;
00369           for(int multpy = 0; multpy < 5; multpy++) {
00370              x(i) = xtf + d;
00371              fs1 = mfcn(x);
00372              x(i) = xtf - d;
00373              fs2 = mfcn(x);
00374              x(i) = xtf;
00375              sag = 0.5*(fs1+fs2-2.*amin);
00376              if(sag > prec.Eps2()) break;
00377              if(trafo.Parameter(i).HasLimits()) {
00378                 if(d > 0.5) {
00379                    std::cout<<"second derivative zero for Parameter "<<i<<std::endl;
00380                    std::cout<<"return diagonal matrix "<<std::endl;
00381                    for(unsigned int j = 0; j < n; j++) {
00382                       vhmat(j,j) = (g2(j) < prec.Eps2() ? 1. : 1./g2(j));
00383                       return MinimumError(vhmat, 1., false);
00384                    }
00385                 }
00386                 d *= 10.;
00387                 if(d > 0.5) d = 0.51;
00388                 continue;
00389              }
00390              d *= 10.;
00391           }
00392           if(sag < prec.Eps2()) {
00393              std::cout<<"MnHesse: internal loop exhausted, return diagonal matrix."<<std::endl;
00394              for(unsigned int i = 0; i < n; i++)
00395                 vhmat(i,i) = (g2(i) < prec.Eps2() ? 1. : 1./g2(i));
00396              return MinimumError(vhmat, 1., false);
00397           }
00398           double g2bfor = g2(i);
00399           g2(i) = 2.*sag/(d*d);
00400           grd(i) = (fs1-fs2)/(2.*d);
00401           gst(i) = d;
00402           dirin(i) = d;
00403           yy(i) = fs1;
00404           double dlast = d;
00405           d = sqrt(2.*aimsag/fabs(g2(i)));
00406           if(trafo.Parameter(i).HasLimits()) d = std::min(0.5, d);
00407           if(d < dmin) d = dmin;
00408           
00409           // see if converged
00410           if(fabs((d-dlast)/d) < Tolerstp()) break;
00411           if(fabs((g2(i)-g2bfor)/g2(i)) < TolerG2()) break; 
00412           d = std::min(d, 10.*dlast);
00413           d = std::max(d, 0.1*dlast);
00414        }
00415        vhmat(i,i) = g2(i);
00416     }
00417     
00418     //off-diagonal Elements  
00419     for(unsigned int i = 0; i < n; i++) {
00420        x(i) += dirin(i);
00421        for(unsigned int j = i+1; j < n; j++) {
00422           x(j) += dirin(j);
00423           double fs1 = mfcn(x);
00424           double elem = (fs1 + amin - yy(i) - yy(j))/(dirin(i)*dirin(j));
00425           vhmat(i,j) = elem;
00426           x(j) -= dirin(j);
00427        }
00428        x(i) -= dirin(i);
00429     }
00430     
00431     return MinimumError(vhmat, 0.);
00432  }
00433  */
00434 
00435   }  // namespace Minuit2
00436 
00437 }  // namespace ROOT

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