MnFunctionCross.cxx

Go to the documentation of this file.
00001 // @(#)root/minuit2:$Id: MnFunctionCross.cxx 30097 2009-09-09 16:47:06Z 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/MnFunctionCross.h"
00011 #include "Minuit2/FunctionMinimum.h"
00012 #include "Minuit2/MnMigrad.h"
00013 #include "Minuit2/FCNBase.h"
00014 #include "Minuit2/MnParabola.h"
00015 #include "Minuit2/MnParabolaPoint.h"
00016 #include "Minuit2/MnParabolaFactory.h"
00017 #include "Minuit2/MnCross.h"
00018 #include "Minuit2/MnMachinePrecision.h"
00019 
00020 //#define DEBUG
00021 
00022 #if defined(DEBUG) || defined(WARNINGMSG)
00023 #include "Minuit2/MnPrint.h" 
00024 #endif
00025 
00026 
00027 namespace ROOT {
00028 
00029    namespace Minuit2 {
00030 
00031 
00032 
00033 MnCross MnFunctionCross::operator()(const std::vector<unsigned int>& par, const std::vector<double>& pmid, 
00034                                     const std::vector<double>& pdir, double tlr, unsigned int maxcalls) const {
00035    // evaluate crossing point where function is equal to MIN + UP, 
00036    // with direction pdir from values pmid
00037    // tlr indicate tolerance and maxcalls maximum number of calls
00038                                     
00039 //   double edmmax = 0.5*0.001*toler*fFCN.Up();
00040   
00041    
00042    unsigned int npar = par.size();
00043    unsigned int nfcn = 0;
00044    const MnMachinePrecision& prec = fState.Precision();
00045    //   double tlr = 0.01;
00046    // convergence when F is within tlf of aim and next prediction 
00047    // of aopt is within tla of previous value of aopt
00048    double up = fFCN.Up();
00049    double tlf = tlr*up;
00050    // tolerance used when calling Migrad
00051    double mgr_tlr = 0.05 * up;   // to be consistent with F77 version 
00052    double tla = tlr;
00053    unsigned int maxitr = 15;
00054    unsigned int ipt = 0;
00055    double aminsv = fFval;
00056    double aim = aminsv + up;
00057    //std::cout<<"aim= "<<aim<<std::endl;
00058    double aopt = 0.;
00059    bool limset = false;
00060    std::vector<double> alsb(3, 0.), flsb(3, 0.);
00061 
00062 #ifdef DEBUG
00063    std::cout<<"MnFunctionCross for parameter  "<<par.front()<< "fmin = " << aminsv 
00064             << " contur value aim = (fmin + up) = " << aim << std::endl;
00065 #endif
00066 
00067    
00068    // find the largest allowed aulim
00069 
00070    double aulim = 100.;
00071    for(unsigned int i = 0; i < par.size(); i++) {
00072       unsigned int kex = par[i];
00073       if(fState.Parameter(kex).HasLimits()) {
00074          double zmid = pmid[i]; 
00075          double zdir = pdir[i];
00076          if(fabs(zdir) < fState.Precision().Eps()) continue;
00077          //       double zlim = 0.;
00078          if(zdir > 0. && fState.Parameter(kex).HasUpperLimit()) {
00079             double zlim = fState.Parameter(kex).UpperLimit();
00080             aulim = std::min(aulim, (zlim-zmid)/zdir);
00081          }
00082          else if(zdir < 0. && fState.Parameter(kex).HasLowerLimit()) {
00083             double zlim = fState.Parameter(kex).LowerLimit();
00084             aulim = std::min(aulim, (zlim-zmid)/zdir);
00085          }
00086       }
00087    }
00088 
00089 #ifdef DEBUG
00090    std::cout<<"Largest allowed aulim "<< aulim << std::endl;
00091 #endif
00092    
00093    if(aulim  < aopt+tla) limset = true;
00094 
00095    
00096    MnMigrad migrad(fFCN, fState, MnStrategy(std::max(0, int(fStrategy.Strategy()-1))));
00097    
00098    for(unsigned int i = 0; i < npar; i++) {
00099 #ifdef DEBUG
00100       std::cout << "MnFunctionCross: Set value for " << par[i] <<  " to " << pmid[i] << std::endl; 
00101 #endif
00102       migrad.SetValue(par[i], pmid[i]);
00103    }
00104    // find minimum with respect all the other parameters (n- npar) (npar are the fixed ones)
00105    
00106    FunctionMinimum min0 = migrad(maxcalls, mgr_tlr);
00107    nfcn += min0.NFcn();
00108 
00109 #ifdef DEBUG
00110    std::cout << "MnFunctionCross: after Migrad on n-1  minimum is " << min0 << std::endl; 
00111 #endif
00112    
00113    if(min0.HasReachedCallLimit()) 
00114       return MnCross(min0.UserState(), nfcn, MnCross::CrossFcnLimit());
00115    if(!min0.IsValid()) return MnCross(fState, nfcn);
00116    if(limset == true && min0.Fval() < aim) 
00117       return MnCross(min0.UserState(), nfcn, MnCross::CrossParLimit());
00118    
00119    ipt++;
00120    alsb[0] = 0.;
00121    flsb[0] = min0.Fval();
00122    flsb[0] = std::max(flsb[0], aminsv + 0.1*up);
00123    aopt = sqrt(up/(flsb[0]-aminsv)) - 1.;
00124    if(fabs(flsb[0] - aim) < tlf) return MnCross(aopt, min0.UserState(), nfcn);
00125    
00126    if(aopt > 1.) aopt = 1.;
00127    if(aopt < -0.5) aopt = -0.5;
00128    limset = false;
00129    if(aopt > aulim) {
00130       aopt = aulim;
00131       limset = true;
00132    }
00133 #ifdef DEBUG
00134    std::cout << "MnFunctionCross: flsb[0] = " << flsb[0] << " aopt =  " << aopt  << std::endl;
00135 #endif
00136    
00137    for(unsigned int i = 0; i < npar; i++) {
00138 #ifdef DEBUG
00139       std::cout << "MnFunctionCross: Set new value for " << par[i] <<  " from " << pmid[i] << " to " << pmid[i] + (aopt)*pdir[i] << " aopt = " << aopt << std::endl; 
00140 #endif
00141       migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
00142    }
00143    
00144    FunctionMinimum min1 = migrad(maxcalls, mgr_tlr);
00145    nfcn += min1.NFcn();
00146 
00147 #ifdef DEBUG
00148    std::cout << "MnFunctionCross: after Migrad on n-1  minimum is " << min1 << std::endl; 
00149 #endif
00150    
00151    if(min1.HasReachedCallLimit()) 
00152       return MnCross(min1.UserState(), nfcn, MnCross::CrossFcnLimit());
00153    if(!min1.IsValid()) return MnCross(fState, nfcn);
00154    if(limset == true && min1.Fval() < aim) 
00155       return MnCross(min1.UserState(), nfcn, MnCross::CrossParLimit());
00156    
00157    ipt++;
00158    alsb[1] = aopt;
00159    flsb[1] = min1.Fval();
00160    double dfda = (flsb[1] - flsb[0])/(alsb[1] - alsb[0]);
00161 
00162 #ifdef DEBUG
00163    std::cout << "aopt = " << aopt << " min1Val = " << flsb[1] << " dfda = " << dfda << std::endl; 
00164 #endif   
00165 
00166    
00167 L300:
00168       if(dfda < 0.) {
00169          // looking for slope of the right sign
00170 #ifdef DEBUG
00171          std::cout << "MnFunctionCross: dfda < 0 - iterate from " << ipt << " to max of " << maxitr << std::endl;
00172 #endif
00173          // iterate (max times is maxitr) incrementing aopt 
00174 
00175          unsigned int maxlk = maxitr - ipt;
00176          for(unsigned int it = 0; it < maxlk; it++) {
00177             alsb[0] = alsb[1];
00178             flsb[0] = flsb[1];
00179             // LM: Add + 1, looking at Fortran code it starts from 1 ( see bug #8396)
00180             aopt = alsb[0] + 0.2*(it+1);   
00181             limset = false;
00182             if(aopt > aulim) {
00183                aopt = aulim;
00184                limset = true;
00185             }
00186             for(unsigned int i = 0; i < npar; i++) {
00187 #ifdef DEBUG
00188       std::cout << "MnFunctionCross: Set new value for " << par[i] <<  " to " << pmid[i] + (aopt)*pdir[i] << " aopt = " << aopt << std::endl; 
00189 #endif
00190                migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
00191             }
00192             min1 = migrad(maxcalls, mgr_tlr);
00193             nfcn += min1.NFcn();
00194 
00195 #ifdef DEBUG
00196    std::cout << "MnFunctionCross: after Migrad on n-1  minimum is " << min1 << std::endl; 
00197    std::cout << "nfcn = " << nfcn << std::endl;
00198 #endif
00199             
00200             if(min1.HasReachedCallLimit()) 
00201                return MnCross(min1.UserState(), nfcn, MnCross::CrossFcnLimit());
00202             if(!min1.IsValid()) return MnCross(fState, nfcn);
00203             if(limset == true && min1.Fval() < aim) 
00204                return MnCross(min1.UserState(), nfcn, MnCross::CrossParLimit());
00205             ipt++;
00206             alsb[1] = aopt;
00207             flsb[1] = min1.Fval();
00208             dfda = (flsb[1] - flsb[0])/(alsb[1] - alsb[0]);
00209             //       if(dfda > 0.) goto L460;
00210 
00211 #ifdef DEBUG
00212    std::cout << "aopt = " << aopt << " min1Val = " << flsb[1] << " dfda = " << dfda << std::endl; 
00213 #endif   
00214 
00215             if(dfda > 0.) break;
00216          } 
00217          if(ipt > maxitr) return MnCross(fState, nfcn);
00218       } //if(dfda < 0.)
00219    
00220 L460:
00221 
00222       // dfda > 0: we have two points with the right slope 
00223       
00224       aopt = alsb[1] + (aim-flsb[1])/dfda;
00225 
00226 #ifdef DEBUG
00227       std::cout << "MnFunctionCross: dfda > 0 : aopt = " << aopt << std::endl;
00228 #endif
00229 
00230    double fdist = std::min(fabs(aim  - flsb[0]), fabs(aim  - flsb[1]));
00231    double adist = std::min(fabs(aopt - alsb[0]), fabs(aopt - alsb[1]));
00232    tla = tlr;
00233    if(fabs(aopt) > 1.) tla = tlr*fabs(aopt);
00234    if(adist < tla && fdist < tlf) return MnCross(aopt, min1.UserState(), nfcn);
00235    if(ipt > maxitr) return MnCross(fState, nfcn);
00236    double bmin = std::min(alsb[0], alsb[1]) - 1.;
00237    if(aopt < bmin) aopt = bmin;
00238    double bmax = std::max(alsb[0], alsb[1]) + 1.;
00239    if(aopt > bmax) aopt = bmax;
00240    
00241    limset = false;
00242    if(aopt > aulim) {
00243       aopt = aulim;
00244       limset = true;
00245    }
00246    
00247    for(unsigned int i = 0; i < npar; i++) {
00248 #ifdef DEBUG
00249       std::cout << "MnFunctionCross: Set new value for " << par[i] <<  " from " << pmid[i] << " to " << pmid[i] + (aopt)*pdir[i] << " aopt = " << aopt << std::endl; 
00250 #endif
00251       migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
00252    }
00253    FunctionMinimum min2 = migrad(maxcalls, mgr_tlr);
00254    nfcn += min2.NFcn();
00255 
00256 #ifdef DEBUG
00257    std::cout << "MnFunctionCross: after Migrad on n-1  minimum is " << min2 << std::endl; 
00258    std::cout << "nfcn = " << nfcn << std::endl;
00259 #endif
00260    
00261    if(min2.HasReachedCallLimit()) 
00262       return MnCross(min2.UserState(), nfcn, MnCross::CrossFcnLimit());
00263    if(!min2.IsValid()) return MnCross(fState, nfcn);
00264    if(limset == true && min2.Fval() < aim) 
00265       return MnCross(min2.UserState(), nfcn, MnCross::CrossParLimit());
00266    
00267    ipt++;
00268    alsb[2] = aopt;
00269    flsb[2] = min2.Fval();
00270 
00271    // now we have three points, ask how many < AIM
00272 
00273    double ecarmn = fabs(flsb[2] - aim);
00274    double ecarmx = 0.;
00275    unsigned int ibest = 2;
00276    unsigned int iworst = 0;
00277    unsigned int noless = 0;
00278    
00279    for(unsigned int i = 0; i < 3; i++) {
00280       double ecart = fabs(flsb[i] - aim);
00281       if(ecart > ecarmx) {
00282          ecarmx = ecart;
00283          iworst = i;
00284       }
00285       if(ecart < ecarmn) {
00286          ecarmn = ecart;
00287          ibest = i;
00288       }
00289       if(flsb[i] < aim) noless++;
00290    }
00291    
00292 #ifdef DEBUG
00293    std::cout << "MnFunctionCross: have three points : nless < aim  = " << noless << " ibest = " << ibest << " iworst = " << iworst << std::endl;   
00294 #endif
00295 
00296    //std::cout<<"480"<<std::endl;
00297    
00298    // at least one on each side of AIM (contour), fit a parabola 
00299    if(noless == 1 || noless == 2) goto L500;
00300    // if all three are above AIM, third point must be the closest to AIM, return it
00301    if(noless == 0 && ibest != 2) return MnCross(fState, nfcn);
00302    // if all three below and third is not best then the slope has again gone negative, 
00303    // re-iterate and look for positive slope
00304    if(noless == 3 && ibest != 2) {
00305       alsb[1] = alsb[2];
00306       flsb[1] = flsb[2];
00307 #ifdef DEBUG
00308    std::cout << "MnFunctionCross: all three points below - look again fir positive slope " << std::endl;   
00309 #endif
00310       goto L300;
00311    }
00312 
00313    // in other case new straight line thru first two points
00314    
00315    flsb[iworst] = flsb[2];
00316    alsb[iworst] = alsb[2];
00317    dfda = (flsb[1] - flsb[0])/(alsb[1] - alsb[0]);
00318 #ifdef DEBUG
00319    std::cout << "MnFunctionCross: new straight line using point 1-2 - dfda =  " << dfda << std::endl;   
00320 #endif
00321    goto L460;
00322    
00323 L500:
00324       
00325       do {
00326          // do parabola fit 
00327          MnParabola parbol = MnParabolaFactory()(MnParabolaPoint(alsb[0], flsb[0]), MnParabolaPoint(alsb[1], flsb[1]), MnParabolaPoint(alsb[2], flsb[2]));
00328          //   aopt = parbol.X_pos(aim);
00329          //std::cout<<"alsb1,2,3= "<<alsb[0]<<", "<<alsb[1]<<", "<<alsb[2]<<std::endl;
00330          //std::cout<<"flsb1,2,3= "<<flsb[0]<<", "<<flsb[1]<<", "<<flsb[2]<<std::endl;
00331 
00332 #ifdef DEBUG
00333    std::cout << "MnFunctionCross: parabola fit: iteration " << ipt  << std::endl;   
00334 #endif
00335          
00336          double coeff1 = parbol.C();
00337          double coeff2 = parbol.B(); 
00338          double coeff3 = parbol.A(); 
00339          double determ = coeff2*coeff2 - 4.*coeff3*(coeff1 - aim);
00340 
00341 #ifdef DEBUG
00342          std::cout << "MnFunctionCross: parabola fit: a =  " << coeff3  << " b = " 
00343                    << coeff2 << " c = " << coeff1 << " determ = " << determ << std::endl;   
00344 #endif
00345          // curvature is negative 
00346          if(determ < prec.Eps()) return MnCross(fState, nfcn); 
00347          double rt = sqrt(determ);
00348          double x1 = (-coeff2 + rt)/(2.*coeff3);
00349          double x2 = (-coeff2 - rt)/(2.*coeff3);
00350          double s1 = coeff2 + 2.*x1*coeff3;
00351          double s2 = coeff2 + 2.*x2*coeff3;
00352 
00353 #ifdef DEBUG
00354          std::cout << "MnFunctionCross: parabola fit: x1 =  " << x1  << " x2 = " 
00355                    << x2 << " s1 = " << s1 << " s2 = " << s2 << std::endl;   
00356 #endif
00357          
00358 #ifdef WARNINGMSG
00359          if(s1*s2 > 0.)   MN_INFO_MSG("MnFunctionCross problem 1");
00360 #endif
00361          // find with root is the right one
00362          aopt = x1;
00363          double slope = s1;
00364          if(s2 > 0.) {
00365             aopt = x2;
00366             slope = s2;
00367          }
00368 #ifdef DEBUG
00369          std::cout << "MnFunctionCross: parabola fit: aopt =  " << aopt  << " slope = " 
00370                    << slope << std::endl;   
00371 #endif
00372 
00373          // ask if converged
00374          tla = tlr;
00375          if(fabs(aopt) > 1.) tla = tlr*fabs(aopt);
00376 
00377 #ifdef DEBUG
00378          std::cout << "MnFunctionCross: Delta(aopt) =  " << fabs(aopt - alsb[ibest])  << " tla = " 
00379                    << tla << "Delta(F) = " << fabs(flsb[ibest] - aim) << " tlf = " << tlf << std::endl;   
00380 #endif
00381 
00382 
00383          if(fabs(aopt - alsb[ibest]) < tla && fabs(flsb[ibest] - aim) < tlf) 
00384             return MnCross(aopt, min2.UserState(), nfcn);
00385          
00386          //     if(ipt > maxitr) return MnCross(); 
00387          
00388          // see if proposed point is in acceptable zone between L and R 
00389          // first find ileft, iright, iout and ibest
00390 
00391          unsigned int ileft = 3;
00392          unsigned int iright = 3;
00393          unsigned int iout = 3;
00394          ibest = 0;
00395          ecarmx = 0.;
00396          ecarmn = fabs(aim-flsb[0]);
00397          for(unsigned int i = 0; i < 3; i++) {
00398             double ecart = fabs(flsb[i] - aim);
00399             if(ecart < ecarmn) {
00400                ecarmn = ecart;
00401                ibest = i;
00402             }
00403             if(ecart > ecarmx) ecarmx = ecart;
00404             if(flsb[i] > aim) {
00405                if(iright == 3) iright = i;
00406                else if(flsb[i] > flsb[iright]) iout = i;
00407                else {
00408                   iout = iright;
00409                   iright = i;
00410                }
00411             } else if(ileft == 3) ileft = i;
00412             else if(flsb[i] < flsb[ileft]) iout = i;
00413             else {
00414                iout = ileft;
00415                ileft = i;
00416             }
00417          }
00418 
00419 #ifdef DEBUG
00420          std::cout << "MnFunctionCross: ileft =  " << ileft  << " iright = " 
00421                    << iright << " iout = " << iout << " ibest = " << ibest << std::endl;   
00422 #endif
00423 
00424          // avoid keeping a bad point nest time around 
00425 
00426          if(ecarmx > 10.*fabs(flsb[iout] - aim)) 
00427             aopt = 0.5*(aopt + 0.5*(alsb[iright] + alsb[ileft]));
00428 
00429          // knowing ileft and iright, get acceptable window 
00430          double smalla = 0.1*tla;
00431          if(slope*smalla > tlf) smalla = tlf/slope;
00432          double aleft = alsb[ileft] + smalla;
00433          double aright = alsb[iright] - smalla;
00434 
00435          // move proposed point AOPT into window if necessary
00436          if(aopt < aleft) aopt = aleft;
00437          if(aopt > aright) aopt = aright;
00438          if(aleft > aright) aopt = 0.5*(aleft + aright);
00439          
00440          // see if proposed point outside limits (should be impossible)
00441          limset = false;
00442          if(aopt > aulim) {
00443             aopt = aulim;
00444             limset = true;
00445          }
00446          
00447          // evaluate at new point aopt
00448          for(unsigned int i = 0; i < npar; i++) {
00449 #ifdef DEBUG
00450       std::cout << "MnFunctionCross: Set new value for " << par[i] <<  " from " << pmid[i] << " to " << pmid[i] + (aopt)*pdir[i] << " aopt = " << aopt << std::endl; 
00451 #endif   
00452             migrad.SetValue(par[i], pmid[i] + (aopt)*pdir[i]);
00453          }
00454          min2 = migrad(maxcalls, mgr_tlr);
00455          nfcn += min2.NFcn();
00456 #ifdef DEBUG
00457    std::cout << "MnFunctionCross: after Migrad on n-1  minimum is " << min2 << std::endl; 
00458    std::cout << "nfcn = " << nfcn << std::endl;
00459 #endif
00460          
00461          if(min2.HasReachedCallLimit()) 
00462             return MnCross(min2.UserState(), nfcn, MnCross::CrossFcnLimit());
00463          if(!min2.IsValid()) return MnCross(fState, nfcn);
00464          if(limset == true && min2.Fval() < aim) 
00465             return MnCross(min2.UserState(), nfcn, MnCross::CrossParLimit());
00466          
00467          ipt++;
00468          // replace odd point with new one (which is the best of three)
00469          alsb[iout] = aopt;
00470          flsb[iout] = min2.Fval();
00471          ibest = iout;
00472       } while(ipt < maxitr);
00473    
00474    // goto L500;
00475    
00476    return MnCross(fState, nfcn);
00477 }
00478 
00479    }  // namespace Minuit2
00480 
00481 }  // namespace ROOT

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