FitUtil.cxx

Go to the documentation of this file.
00001 // @(#)root/mathcore:$Id: FitUtil.cxx 35511 2010-09-21 08:47:52Z moneta $
00002 // Author: L. Moneta Tue Nov 28 10:52:47 2006
00003 
00004 /**********************************************************************
00005  *                                                                    *
00006  * Copyright (c) 2006  LCG ROOT Math Team, CERN/PH-SFT                *
00007  *                                                                    *
00008  *                                                                    *
00009  **********************************************************************/
00010 
00011 // Implementation file for class FitUtil
00012 
00013 #include "Fit/FitUtil.h"
00014 
00015 #include "Fit/BinData.h"
00016 #include "Fit/UnBinData.h"
00017 //#include "Fit/BinPoint.h"
00018 
00019 #include "Math/IParamFunction.h"
00020 #include "Math/Integrator.h"
00021 #include "Math/IntegratorMultiDim.h"
00022 #include "Math/WrappedFunction.h"
00023 #include "Math/OneDimFunctionAdapter.h"
00024 #include "Math/RichardsonDerivator.h"
00025 
00026 #include "Math/Error.h"
00027 #include "Math/Util.h"  // for safe log(x)
00028 
00029 #include <limits>
00030 #include <cmath>
00031 #include <cassert> 
00032 //#include <memory>
00033 
00034 //#define DEBUG
00035 #ifdef DEBUG
00036 #define NSAMPLE 10
00037 #include <iostream> 
00038 #endif
00039 
00040 //todo: 
00041 
00042 //  need to implement integral option
00043 
00044 namespace ROOT { 
00045 
00046    namespace Fit { 
00047 
00048       namespace FitUtil { 
00049 
00050          // internal class to evaluate the function or the integral 
00051          // and cached internal integration details
00052          // if useIntegral is false no allocation is done
00053          // and this is a dummy class
00054          // class is templated on any parametric functor implementing operator()(x,p) and NDim()
00055          // contains a constant pointer to the function
00056          template <class ParamFunc = ROOT::Math::IParamMultiFunction> 
00057          class IntegralEvaluator { 
00058 
00059          public: 
00060 
00061             IntegralEvaluator(const ParamFunc & func, const double * p, bool useIntegral = true) :
00062                fDim(0),
00063                fParams(0),
00064                fFunc(0),
00065                fIg1Dim(0), 
00066                fIgNDim(0),
00067                fFunc1Dim(0),
00068                fFuncNDim(0)
00069             { 
00070                if (useIntegral) { 
00071                   SetFunction(func, p); 
00072                }
00073             }
00074 
00075             void SetFunction(const ParamFunc & func, const double * p = 0) { 
00076                // set the integrand function and create required wrapper 
00077                // to perform integral in (x) of a generic  f(x,p)
00078                fParams = p;
00079                fDim = func.NDim(); 
00080                // copy the function object to be able to modify the parameters 
00081                //fFunc = dynamic_cast<ROOT::Math::IParamMultiFunction *>( func.Clone() ); 
00082                fFunc = &func;
00083                assert(fFunc != 0); 
00084                // set parameters in function
00085                //fFunc->SetParameters(p); 
00086                if (fDim == 1) { 
00087                   fFunc1Dim = new ROOT::Math::WrappedMemFunction< IntegralEvaluator, double (IntegralEvaluator::*)(double ) const > (*this, &IntegralEvaluator::F1);
00088                   fIg1Dim = new ROOT::Math::IntegratorOneDim(); 
00089                   //fIg1Dim->SetFunction( static_cast<const ROOT::Math::IMultiGenFunction & >(*fFunc),false);
00090                   fIg1Dim->SetFunction( static_cast<const ROOT::Math::IGenFunction &>(*fFunc1Dim) );
00091                } 
00092                else if (fDim > 1) {
00093                   fFuncNDim = new ROOT::Math::WrappedMemMultiFunction< IntegralEvaluator, double (IntegralEvaluator::*)(const double *) const >  (*this, &IntegralEvaluator::FN, fDim);
00094                   fIgNDim = new ROOT::Math::IntegratorMultiDim();
00095                   fIgNDim->SetFunction(*fFuncNDim);
00096                }
00097                else
00098                   assert(fDim > 0);                
00099             }
00100             
00101             void SetParameters(const double *p) { 
00102                // copy just the pointer
00103                fParams = p; 
00104             }
00105 
00106             ~IntegralEvaluator() { 
00107                if (fIg1Dim) delete fIg1Dim; 
00108                if (fIgNDim) delete fIgNDim; 
00109                if (fFunc1Dim) delete fFunc1Dim; 
00110                if (fFuncNDim) delete fFuncNDim; 
00111                //if (fFunc) delete fFunc; 
00112             }
00113             
00114             // evaluation of integrand function (one-dim)
00115             double F1 (double x) const { 
00116                double xx[1]; xx[0] = x;
00117                return (*fFunc)( xx, fParams);
00118             }
00119             // evaluation of integrand function (multi-dim)
00120             double FN(const double * x) const { 
00121                return (*fFunc)( x, fParams);
00122             }
00123 
00124             double operator()(const double *x1, const double * x2) { 
00125                // return normalized integral, divided by bin volume (dx1*dx...*dxn) 
00126                if (fIg1Dim) { 
00127                   double dV = *x2 - *x1;
00128                   return fIg1Dim->Integral( *x1, *x2)/dV; 
00129                }
00130                else if (fIgNDim) { 
00131                   double dV = 1; 
00132                   for (unsigned int i = 0; i < fDim; ++i) 
00133                      dV *= ( x2[i] - x1[i] );                   
00134                   return fIgNDim->Integral( x1, x2)/dV; 
00135 //                   std::cout << " do integral btw x " << x1[0] << "  " << x2[0] << " y " << x1[1] << "  " << x2[1] << " dV = " << dV << " result = " << result << std::endl;
00136 //                   return result; 
00137                }
00138                else 
00139                   assert(1.); // should never be here
00140                return 0;
00141             }
00142 
00143          private: 
00144 
00145             unsigned int fDim; 
00146             const double * fParams;
00147             //ROOT::Math::IParamMultiFunction * fFunc;  // copy of function in order to be able to change parameters    
00148             const ParamFunc * fFunc;       //  reference to a generic parametric function  
00149             ROOT::Math::IntegratorOneDim * fIg1Dim; 
00150             ROOT::Math::IntegratorMultiDim * fIgNDim; 
00151             ROOT::Math::IGenFunction * fFunc1Dim; 
00152             ROOT::Math::IMultiGenFunction * fFuncNDim; 
00153          }; 
00154 
00155 
00156          // derivative with respect of the parameter to be integrated
00157          template<class GradFunc = IGradModelFunction>
00158          struct ParamDerivFunc { 
00159             ParamDerivFunc(const GradFunc & f) : fFunc(f), fIpar(0) {}
00160             void SetDerivComponent(unsigned int ipar) { fIpar = ipar; }
00161             double operator() (const double *x, const double *p) const { 
00162                return fFunc.ParameterDerivative( x, p, fIpar ); 
00163             } 
00164             unsigned int NDim() const { return fFunc.NDim(); }
00165             const GradFunc & fFunc; 
00166             unsigned int fIpar; 
00167          };
00168 
00169          // simple gradient calculator using the 2 points rule
00170 
00171          class SimpleGradientCalculator { 
00172 
00173          public: 
00174             // construct from function and gradient dimension gdim
00175             // gdim = npar for parameter gradient
00176             // gdim = ndim for coordinate gradients
00177             // construct (the param values will be passed later)
00178             // one can choose between 2 points rule (1 extra evaluation) istrat=1
00179             // or two point rule (2 extra evaluation)
00180             // (found 2 points rule does not work correctly - minuit2FitBench fails) 
00181             SimpleGradientCalculator(int gdim, const IModelFunction & func,double eps = 2.E-8, int istrat = 1) : 
00182                fEps(eps),
00183                fPrecision(1.E-8 ), // sqrt(epsilon)
00184                fStrategy(istrat), 
00185                fN(gdim ),
00186                fFunc(func),
00187                fVec(std::vector<double>(gdim) ) // this can be probably optimized 
00188             {}
00189 
00190             // internal method to calculate single partial derivative
00191             // assume cached vector fVec is already set
00192             double DoParameterDerivative(const double *x, const double *p, double f0, int k) const {
00193                double p0 = p[k];
00194                double h = std::max( fEps* std::abs(p0), 8.0*fPrecision*(std::abs(p0) + fPrecision) );
00195                fVec[k] += h;
00196                double deriv = 0; 
00197                // t.b.d : treat case of infinities 
00198                //if (fval > - std::numeric_limits<double>::max() && fval < std::numeric_limits<double>::max() ) 
00199                double f1 = fFunc(x, &fVec.front() );
00200                if (fStrategy > 1) { 
00201                   fVec[k] = p0 - h; 
00202                   double f2 = fFunc(x, &fVec.front() );
00203                   deriv = 0.5 * ( f2 - f1 )/h;
00204                }
00205                else 
00206                   deriv = ( f1 - f0 )/h;
00207                
00208                fVec[k] = p[k]; // restore original p value
00209                return deriv; 
00210             }
00211             // number of dimension in x (needed when calculating the integrals) 
00212             unsigned int NDim() const { 
00213                return fFunc.NDim(); 
00214             }
00215             // number of parameters (needed for grad ccalculation)
00216             unsigned int NPar() const { 
00217                return fFunc.NPar(); 
00218             }
00219 
00220             double ParameterDerivative(const double *x, const double *p, int ipar) const {
00221                // fVec are the cached parameter values
00222                std::copy(p, p+fN, fVec.begin()); 
00223                double f0 = fFunc(x, p);
00224                return DoParameterDerivative(x,p,f0,ipar); 
00225             }
00226 
00227             // calculate all gradient at point (x,p) knnowing already value f0 (we gain a function eval.)
00228             void ParameterGradient(const double * x, const double * p, double f0, double * g) { 
00229                // fVec are the cached parameter values
00230                std::copy(p, p+fN, fVec.begin()); 
00231                for (unsigned int k = 0; k < fN; ++k) {
00232                   g[k] = DoParameterDerivative(x,p,f0,k);
00233                }
00234             }
00235 
00236             // calculate gradient w.r coordinate values
00237             void Gradient(const double * x, const double * p, double f0, double * g) { 
00238                // fVec are the cached coordinate values
00239                std::copy(x, x+fN, fVec.begin()); 
00240                for (unsigned int k = 0; k < fN; ++k) {
00241                   double x0 = x[k]; 
00242                   double h = std::max( fEps* std::abs(x0), 8.0*fPrecision*(std::abs(x0) + fPrecision) );
00243                   fVec[k] += h;
00244                   // t.b.d : treat case of infinities 
00245                   //if (fval > - std::numeric_limits<double>::max() && fval < std::numeric_limits<double>::max() ) 
00246                   double f1 = fFunc( &fVec.front(), p );
00247                   if (fStrategy > 1) { 
00248                      fVec[k] = x0 - h; 
00249                      double f2 = fFunc( &fVec.front(), p  );
00250                      g[k] = 0.5 * ( f2 - f1 )/h;
00251                   }
00252                   else 
00253                      g[k] = ( f1 - f0 )/h;
00254 
00255                   fVec[k] = x[k]; // restore original x value
00256                }
00257             }
00258 
00259          private:
00260 
00261             double fEps; 
00262             double fPrecision;
00263             int fStrategy; // strategy in calculation ( =1 use 2 point rule( 1 extra func) , = 2 use r point rule) 
00264             unsigned int fN; // gradient dimension
00265             const IModelFunction & fFunc; 
00266             mutable std::vector<double> fVec; // cached coordinates (or parameter values in case of gradientpar)
00267          };
00268 
00269 
00270          // function to avoid infinities or nan
00271          double CorrectValue(double rval) { 
00272             // avoid infinities or nan in  rval
00273             if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() ) 
00274                return rval; 
00275             else if (rval < 0)
00276                // case -inf
00277                return -std::numeric_limits<double>::max(); 
00278             else 
00279                // case + inf or nan
00280                return  + std::numeric_limits<double>::max(); 
00281          }
00282          bool CheckValue(double & rval) { 
00283             if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() ) 
00284                return true; 
00285             else if (rval < 0) { 
00286                // case -inf
00287                rval =  -std::numeric_limits<double>::max(); 
00288                return false; 
00289             }
00290             else { 
00291                // case + inf or nan
00292                rval =  + std::numeric_limits<double>::max(); 
00293                return false; 
00294             }
00295          }
00296 
00297 
00298          // calculation of the integral of the gradient functions
00299          // for a function providing derivative w.r.t parameters
00300          // x1 and x2 defines the integration interval , p the parameters
00301          template <class GFunc> 
00302          void CalculateGradientIntegral(const GFunc & gfunc, 
00303                                         const double *x1, const double * x2, const double * p, double *g) { 
00304 
00305             // needs to calculate the integral for each partial derivative
00306             ParamDerivFunc<GFunc> pfunc( gfunc); 
00307             IntegralEvaluator<ParamDerivFunc<GFunc> > igDerEval( pfunc, p, true);
00308             // loop on the parameters         
00309             unsigned int npar = gfunc.NPar();  
00310             for (unsigned int k = 0; k < npar; ++k ) { 
00311                pfunc.SetDerivComponent(k); 
00312                g[k] = igDerEval( x1, x2 ); 
00313             }
00314          }
00315 
00316 
00317 
00318       } // end namespace  FitUtil      
00319 
00320 
00321 
00322 //___________________________________________________________________________________________________________________________
00323 // for chi2 functions
00324 //___________________________________________________________________________________________________________________________
00325 
00326 double FitUtil::EvaluateChi2(const IModelFunction & func, const BinData & data, const double * p, unsigned int & nPoints) {  
00327    // evaluate the chi2 given a  function reference  , the data and returns the value and also in nPoints 
00328    // the actual number of used points
00329    // normal chi2 using only error on values (from fitting histogram)
00330    // optionally the integral of function in the bin is used 
00331    
00332    unsigned int n = data.Size();
00333 
00334  
00335 #ifdef DEBUG
00336    std::cout << "\n\nFit data size = " << n << std::endl;
00337    std::cout << "evaluate chi2 using function " << &func << "  " << p << std::endl; 
00338 #endif
00339 
00340 
00341    double chi2 = 0;
00342    //int nRejected = 0; 
00343    
00344    // do not cache parameter values (it is not thread safe)
00345    //func.SetParameters(p); 
00346 
00347    
00348    // get fit option and check case if using integral of bins
00349    const DataOptions & fitOpt = data.Opt();
00350    bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges(); 
00351    bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
00352 
00353    IntegralEvaluator<> igEval( func, p, useBinIntegral); 
00354 
00355    double maxResValue = std::numeric_limits<double>::max() /n;
00356    double wrefVolume = 1.0; 
00357    std::vector<double> xc; 
00358    if (useBinVolume) { 
00359       wrefVolume /= data.RefVolume();
00360       xc.resize(data.NDim() );
00361    }
00362 
00363    for (unsigned int i = 0; i < n; ++ i) { 
00364 
00365 
00366       double y, invError; 
00367       // in case of no error in y invError=1 is returned
00368       const double * x1 = data.GetPoint(i,y, invError);
00369 
00370       double fval = 0;
00371 
00372       double binVolume = 1.0; 
00373       if (useBinVolume) { 
00374          unsigned int ndim = data.NDim(); 
00375          const double * x2 = data.BinUpEdge(i);  
00376          for (unsigned int j = 0; j < ndim; ++j) {
00377             binVolume *= std::abs( x2[j]-x1[j] );
00378             xc[j] = 0.5*(x2[j]+ x1[j]);
00379          }
00380          // normalize the bin volume using a reference value
00381          binVolume *= wrefVolume;
00382       }
00383 
00384       const double * x = (useBinVolume) ? &xc.front() : x1;
00385 
00386       if (!useBinIntegral) {
00387          fval = func ( x, p );
00388       }
00389       else {
00390          // calculate integral normalized by bin volume
00391          // need to set function and parameters here in case loop is parallelized
00392          fval = igEval( x1, data.BinUpEdge(i)) ; 
00393       }
00394       // normalize result if requested according to bin volume
00395       if (useBinVolume) fval *= binVolume;
00396 
00397 //#define DEBUG
00398 #ifdef DEBUG      
00399       std::cout << x[0] << "  " << y << "  " << 1./invError << " params : "; 
00400       for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) 
00401          std::cout << p[ipar] << "\t";
00402       std::cout << "\tfval = " << fval << " bin volume " << binVolume << " ref " << wrefVolume << std::endl; 
00403 #endif
00404 //#undef DEBUG
00405 
00406 
00407       double tmp = ( y -fval )* invError;         
00408       double resval = tmp * tmp;
00409 
00410 
00411       // avoid inifinity or nan in chi2 values due to wrong function values 
00412       if ( resval < maxResValue )  
00413          chi2 += resval; 
00414       else {  
00415          //nRejected++; 
00416          chi2 += maxResValue;
00417       }
00418 
00419       
00420    }
00421    
00422    // reset the number of fitting data points
00423    nPoints = n;  // no points are rejected 
00424    //if (nRejected != 0)  nPoints = n - nRejected;   
00425 
00426 #ifdef DEBUG
00427    std::cout << "chi2 = " << chi2 << " n = " << nPoints  /*<< " rejected = " << nRejected */ << std::endl;
00428 #endif
00429 
00430 
00431    return chi2;
00432 }
00433 
00434 
00435 //___________________________________________________________________________________________________________________________
00436 
00437 double FitUtil::EvaluateChi2Effective(const IModelFunction & func, const BinData & data, const double * p, unsigned int & nPoints) {  
00438    // evaluate the chi2 given a  function reference  , the data and returns the value and also in nPoints 
00439    // the actual number of used points
00440    // method using the error in the coordinates
00441    // integral of bin does not make sense in this case
00442    
00443    unsigned int n = data.Size();
00444 
00445 #ifdef DEBUG
00446    std::cout << "\n\nFit data size = " << n << std::endl;
00447    std::cout << "evaluate effective chi2 using function " << &func << "  " << p << std::endl; 
00448 #endif
00449 
00450    assert(data.HaveCoordErrors() ); 
00451 
00452    double chi2 = 0;
00453    //int nRejected = 0; 
00454    
00455 
00456    //func.SetParameters(p); 
00457 
00458    unsigned int ndim = func.NDim();
00459 
00460    // use Richardson derivator
00461    ROOT::Math::RichardsonDerivator derivator;
00462 
00463    double maxResValue = std::numeric_limits<double>::max() /n;
00464 
00465 
00466 
00467    for (unsigned int i = 0; i < n; ++ i) { 
00468 
00469 
00470       double y = 0;
00471       const double * x = data.GetPoint(i,y);
00472 
00473       double fval = func( x, p );
00474 
00475       double delta_y_func = y - fval; 
00476 
00477 
00478       double ey = 0;
00479       const double * ex = 0; 
00480       if (!data.HaveAsymErrors() )
00481          ex = data.GetPointError(i, ey); 
00482       else { 
00483          double eylow, eyhigh = 0; 
00484          ex = data.GetPointError(i, eylow, eyhigh); 
00485          if ( delta_y_func < 0) 
00486             ey = eyhigh; // function is higher than points 
00487          else
00488             ey = eylow; 
00489       }
00490       double e2 = ey * ey; 
00491       // before calculating the gradient check that all error in x are not zero
00492       unsigned int j = 0; 
00493       while ( j < ndim && ex[j] == 0.)  { j++; } 
00494       // if j is less ndim some elements are not zero
00495       if (j < ndim) { 
00496          // need an adapter from a multi-dim function to a one-dimensional
00497          ROOT::Math::OneDimMultiFunctionAdapter<const IModelFunction &> f1D(func,x,0,p);
00498          // select optimal step size  (use 10--3 by default as was done in TF1:
00499          double kEps = 0.001;
00500          double kPrecision = 1.E-8;
00501          for (unsigned int icoord = 0; icoord < ndim; ++icoord) { 
00502             // calculate derivative for each coordinate 
00503             if (ex[icoord] > 0) {               
00504                //gradCalc.Gradient(x, p, fval, &grad[0]);
00505                f1D.SetCoord(icoord);
00506                // optimal spep size (maybe should take average or  range in points) 
00507                double x0= x[icoord];
00508                double h = std::max( kEps* std::abs(x0), 8.0*kPrecision*(std::abs(x0) + kPrecision) );
00509                double deriv = derivator.Derivative1(f1D, x[icoord], h);  
00510                double edx = ex[icoord] * deriv; 
00511                e2 += edx * edx;  
00512             }
00513          } 
00514       }
00515       double w2 = (e2 > 0) ? 1.0/e2 : 0;  
00516       double resval = w2 * ( y - fval ) *  ( y - fval); 
00517 
00518 #ifdef DEBUG      
00519       std::cout << x[0] << "  " << y << " ex " << e2 << " ey  " << ey << " params : "; 
00520       for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) 
00521          std::cout << p[ipar] << "\t";
00522       std::cout << "\tfval = " << fval << "\tresval = " << resval << std::endl; 
00523 #endif
00524       
00525       // avoid (infinity and nan ) in the chi2 sum 
00526       // eventually add possibility of excluding some points (like singularity) 
00527       if ( resval < maxResValue )  
00528          chi2 += resval; 
00529       else 
00530          chi2 += maxResValue;
00531       //nRejected++; 
00532       
00533    }
00534    
00535    // reset the number of fitting data points
00536    nPoints = n;  // no points are rejected 
00537    //if (nRejected != 0)  nPoints = n - nRejected;   
00538 
00539 #ifdef DEBUG
00540    std::cout << "chi2 = " << chi2 << " n = " << nPoints << /* " rejected = " << nRejected */ << std::endl;
00541 #endif
00542 
00543    return chi2;
00544 
00545 }
00546 
00547 
00548 //___________________________________________________________________________________________________________________________
00549 double FitUtil::EvaluateChi2Residual(const IModelFunction & func, const BinData & data, const double * p, unsigned int i, double * g) {  
00550    // evaluate the chi2 contribution (residual term) only for data with no coord-errors
00551    // This function is used in the specialized least square algorithms like FUMILI or L.M.
00552    // if we have error on the coordinates the method is not yet implemented 
00553    //  integral option is also not yet implemented
00554    //  one can use in that case normal chi2 method
00555   
00556    if (data.GetErrorType() == BinData::kCoordError && data.Opt().fCoordErrors ) { 
00557       MATH_ERROR_MSG("FitUtil::EvaluateChi2Residual","Error on the coordinates are not used in calculating Chi2 residual");
00558       return 0; // it will assert otherwise later in GetPoint
00559    }
00560 
00561 
00562    //func.SetParameters(p);
00563 
00564    double y, invError = 0; 
00565 
00566    const DataOptions & fitOpt = data.Opt();
00567    bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges(); 
00568    bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
00569 
00570    const double * x1 = data.GetPoint(i,y, invError);
00571 
00572    IntegralEvaluator<> igEval( func, p, useBinIntegral); 
00573    double fval = 0; 
00574    unsigned int ndim = data.NDim(); 
00575    double binVolume = 1.0; 
00576    const double * x2 = 0; 
00577    if (useBinVolume || useBinIntegral) x2 = data.BinUpEdge(i);  
00578 
00579    double * xc = 0;
00580  
00581    if (useBinVolume) { 
00582       xc = new double[ndim];
00583       for (unsigned int j = 0; j < ndim; ++j) {
00584          binVolume *= std::abs( x2[j]-x1[j] );
00585          xc[j] = 0.5*(x2[j]+ x1[j]);         
00586       }
00587       // normalize the bin volume using a reference value
00588       binVolume /= data.RefVolume();
00589    }
00590 
00591    const double * x = (useBinVolume) ? xc : x1;
00592 
00593    if (!useBinIntegral) {
00594       fval = func ( x, p );
00595    }
00596    else {
00597       // calculate integral (normalized by bin volume) 
00598       // need to set function and parameters here in case loop is parallelized
00599       fval = igEval( x1, x2) ; 
00600    }
00601    // normalize result if requested according to bin volume
00602    if (useBinVolume) fval *= binVolume;
00603 
00604    double resval =   ( y -fval )* invError;        
00605 
00606    // avoid infinities or nan in  resval
00607    resval = CorrectValue(resval);
00608    
00609    // estimate gradient 
00610    if (g != 0) {  
00611 
00612       unsigned int npar = func.NPar(); 
00613       const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func); 
00614 
00615       if (gfunc != 0) { 
00616          //case function provides gradient
00617          if (!useBinIntegral ) {
00618             gfunc->ParameterGradient(  x , p, g );  
00619          }
00620          else { 
00621             // needs to calculate the integral for each partial derivative
00622             CalculateGradientIntegral( *gfunc, x1, x2, p, g); 
00623          }
00624       }
00625       else { 
00626          SimpleGradientCalculator  gc( npar, func); 
00627          if (!useBinIntegral ) 
00628             gc.ParameterGradient(x, p, fval, g); 
00629          else { 
00630             // needs to calculate the integral for each partial derivative
00631             CalculateGradientIntegral( gc, x1, x2, p, g); 
00632          }
00633       }
00634       // mutiply by - 1 * weight
00635       for (unsigned int k = 0; k < npar; ++k) {
00636          g[k] *= - invError;
00637          if (useBinVolume) g[k] *= binVolume;      
00638       }       
00639    }
00640 
00641    if (useBinVolume) delete [] xc;
00642 
00643    return resval; 
00644 
00645 }
00646 
00647 void FitUtil::EvaluateChi2Gradient(const IModelFunction & f, const BinData & data, const double * p, double * grad, unsigned int & nPoints) { 
00648    // evaluate the gradient of the chi2 function
00649    // this function is used when the model function knows how to calculate the derivative and we can  
00650    // avoid that the minimizer re-computes them 
00651    //
00652    // case of chi2 effective (errors on coordinate) is not supported
00653 
00654    if ( data.HaveCoordErrors() ) {
00655       MATH_ERROR_MSG("FitUtil::EvaluateChi2Residual","Error on the coordinates are not used in calculating Chi2 gradient");            return; // it will assert otherwise later in GetPoint
00656    }
00657 
00658    unsigned int nRejected = 0; 
00659 
00660    const IGradModelFunction * fg = dynamic_cast<const IGradModelFunction *>( &f); 
00661    assert (fg != 0); // must be called by a gradient function
00662 
00663    const IGradModelFunction & func = *fg; 
00664    unsigned int n = data.Size();
00665 
00666 
00667 #ifdef DEBUG
00668    std::cout << "\n\nFit data size = " << n << std::endl;
00669    std::cout << "evaluate chi2 using function gradient " << &func << "  " << p << std::endl; 
00670 #endif
00671 
00672    const DataOptions & fitOpt = data.Opt();
00673    bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges(); 
00674    bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
00675 
00676    double wrefVolume = 1.0; 
00677    std::vector<double> xc; 
00678    if (useBinVolume) { 
00679       wrefVolume /= data.RefVolume();
00680       xc.resize(data.NDim() );
00681    }
00682 
00683    IntegralEvaluator<> igEval( func, p, useBinIntegral); 
00684 
00685    //int nRejected = 0; 
00686    // set values of parameters 
00687 
00688    unsigned int npar = func.NPar(); 
00689    //   assert (npar == NDim() );  // npar MUST be  Chi2 dimension
00690    std::vector<double> gradFunc( npar ); 
00691    // set all vector values to zero
00692    std::vector<double> g( npar); 
00693 
00694    for (unsigned int i = 0; i < n; ++ i) { 
00695 
00696 
00697       double y, invError = 0; 
00698       const double * x1 = data.GetPoint(i,y, invError);
00699 
00700       double fval = 0; 
00701       const double * x2 = 0; 
00702 
00703       double binVolume = 1; 
00704       if (useBinVolume) { 
00705          unsigned int ndim = data.NDim(); 
00706          x2 = data.BinUpEdge(i);  
00707          for (unsigned int j = 0; j < ndim; ++j) {
00708             binVolume *= std::abs( x2[j]-x1[j] );
00709             xc[j] = 0.5*(x2[j]+ x1[j]);
00710          }
00711          // normalize the bin volume using a reference value
00712          binVolume *= wrefVolume;
00713       }
00714 
00715       const double * x = (useBinVolume) ? &xc.front() : x1;
00716 
00717       if (!useBinIntegral ) {
00718          fval = func ( x, p ); 
00719          func.ParameterGradient(  x , p, &gradFunc[0] ); 
00720       }
00721       else { 
00722          x2 = data.BinUpEdge(i); 
00723          // calculate normalized integral and gradient (divided by bin volume)
00724          // need to set function and parameters here in case loop is parallelized 
00725          fval = igEval( x1, x2 ) ; 
00726          CalculateGradientIntegral( func, x1, x2, p, &gradFunc[0]); 
00727       }
00728       if (useBinVolume) fval *= binVolume;
00729 
00730 #ifdef DEBUG      
00731       std::cout << x[0] << "  " << y << "  " << 1./invError << " params : "; 
00732       for (unsigned int ipar = 0; ipar < npar; ++ipar) 
00733          std::cout << p[ipar] << "\t";
00734       std::cout << "\tfval = " << fval << std::endl; 
00735 #endif
00736       if ( !CheckValue(fval) ) { 
00737          nRejected++; 
00738          continue;
00739       } 
00740 
00741       // loop on the parameters
00742       unsigned int ipar = 0; 
00743       for ( ; ipar < npar ; ++ipar) { 
00744 
00745          // correct gradient for bin volumes
00746          if (useBinVolume) gradFunc[ipar] *= binVolume;
00747 
00748          // avoid singularity in the function (infinity and nan ) in the chi2 sum 
00749          // eventually add possibility of excluding some points (like singularity) 
00750          double dfval = gradFunc[ipar];
00751          if ( !CheckValue(dfval) ) { 
00752                break; // exit loop on parameters
00753          } 
00754  
00755          // calculate derivative point contribution
00756          double tmp = - 2.0 * ( y -fval )* invError * invError * gradFunc[ipar];          
00757          g[ipar] += tmp;
00758       
00759       }
00760 
00761       if ( ipar < npar ) { 
00762           // case loop was broken for an overflow in the gradient calculation  
00763          nRejected++; 
00764          continue;
00765       } 
00766 
00767 
00768    } 
00769 
00770    // correct the number of points
00771    nPoints = n; 
00772    if (nRejected != 0)  {
00773       assert(nRejected <= n);
00774       nPoints = n - nRejected;
00775       if (nPoints < npar)  MATH_ERROR_MSG("FitUtil::EvaluateChi2Gradient","Error - too many points rejected for overflow in gradient calculation");
00776    } 
00777 
00778    // copy result 
00779    std::copy(g.begin(), g.end(), grad);
00780 
00781 }
00782 
00783 //______________________________________________________________________________________________________
00784 //
00785 //  Log Likelihood functions       
00786 //_______________________________________________________________________________________________________
00787 
00788 // utility function used by the likelihoods 
00789 
00790 // for LogLikelihood functions
00791 
00792 double FitUtil::EvaluatePdf(const IModelFunction & func, const UnBinData & data, const double * p, unsigned int i, double * g) {  
00793    // evaluate the pdf contribution to the generic logl function in case of bin data
00794    // return actually the log of the pdf and its derivatives 
00795 
00796    //func.SetParameters(p);
00797 
00798 
00799    const double * x = data.Coords(i);
00800    double fval = func ( x, p ); 
00801    double logPdf = ROOT::Math::Util::EvalLog(fval);
00802    //return 
00803    if (g == 0) return logPdf;
00804 
00805    const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func); 
00806 
00807    // gradient  calculation
00808    if (gfunc != 0) { 
00809       //case function provides gradient
00810       gfunc->ParameterGradient(  x , p, g );  
00811    }
00812    else { 
00813       // estimate gradieant numerically with simple 2 point rule 
00814       // should probably calculate gradient of log(pdf) is more stable numerically
00815       SimpleGradientCalculator gc(func.NPar(), func); 
00816       gc.ParameterGradient(x, p, fval, g ); 
00817    }       
00818    // divide gradient by function value since returning the logs
00819    for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) {
00820       g[ipar] /= fval; // this should be checked against infinities
00821    }
00822 
00823 #ifdef DEBUG
00824    std::cout << x[i] << "\t"; 
00825    std::cout << "\tpar = [ " << func.NPar() << " ] =  "; 
00826    for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) 
00827       std::cout << p[ipar] << "\t";
00828    std::cout << "\tfval = " << fval;
00829    std::cout << "\tgrad = [ "; 
00830    for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) 
00831       std::cout << g[ipar] << "\t";
00832    std::cout << " ] "   << std::endl; 
00833 #endif
00834 
00835 
00836    return logPdf;
00837 }
00838 
00839 double FitUtil::EvaluateLogL(const IModelFunction & func, const UnBinData & data, const double * p, unsigned int &nPoints) {  
00840    // evaluate the LogLikelihood 
00841 
00842    unsigned int n = data.Size();
00843 
00844 #ifdef DEBUG
00845    std::cout << "\n\nFit data size = " << n << std::endl;
00846    std::cout << "func pointer is " << typeid(func).name() << std::endl;
00847 #endif
00848 
00849    double logl = 0;
00850    //unsigned int nRejected = 0; 
00851 
00852    for (unsigned int i = 0; i < n; ++ i) { 
00853       const double * x = data.Coords(i);
00854       double fval = func ( x, p ); 
00855 
00856 #ifdef DEBUG      
00857       std::cout << "x [ " << data.PointSize() << " ] = "; 
00858       for (unsigned int j = 0; j < data.PointSize(); ++j)
00859          std::cout << x[j] << "\t"; 
00860       std::cout << "\tpar = [ " << func.NPar() << " ] =  "; 
00861       for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) 
00862          std::cout << p[ipar] << "\t";
00863       std::cout << "\tfval = " << fval << std::endl; 
00864 #endif
00865       // function EvalLog protects against negative or too small values of fval
00866       logl += ROOT::Math::Util::EvalLog( fval);       
00867    }
00868    
00869    // reset the number of fitting data points
00870    nPoints = n; 
00871 //    if (nRejected != 0)  { 
00872 //       assert(nRejected <= n);
00873 //       nPoints = n - nRejected;
00874 //       if ( nPoints < func.NPar() ) 
00875 //          MATH_ERROR_MSG("FitUtil::EvaluateLogL","Error too many points rejected because of bad pdf values");
00876 
00877 //    }
00878 
00879 #ifdef DEBUG
00880    std::cout << "Logl = " << logl << " np = " << nPoints << std::endl;
00881 #endif
00882    
00883    return -logl;
00884 }
00885 
00886 void FitUtil::EvaluateLogLGradient(const IModelFunction & f, const UnBinData & data, const double * p, double * grad, unsigned int & ) { 
00887    // evaluate the gradient of the log likelihood function
00888 
00889    const IGradModelFunction * fg = dynamic_cast<const IGradModelFunction *>( &f); 
00890    assert (fg != 0); // must be called by a grad function
00891    const IGradModelFunction & func = *fg; 
00892 
00893    unsigned int n = data.Size();
00894    //int nRejected = 0; 
00895 
00896    unsigned int npar = func.NPar(); 
00897    std::vector<double> gradFunc( npar ); 
00898    std::vector<double> g( npar); 
00899 
00900    for (unsigned int i = 0; i < n; ++ i) { 
00901       const double * x = data.Coords(i);
00902       double fval = func ( x , p); 
00903       func.ParameterGradient( x, p, &gradFunc[0] );
00904       for (unsigned int kpar = 0; kpar < npar; ++ kpar) { 
00905          if (fval > 0)  
00906             g[kpar] -= 1./fval * gradFunc[ kpar ]; 
00907          else if (gradFunc [ kpar] != 0) { 
00908             const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
00909             const double kdmax2 = std::numeric_limits<double>::max() / (4*n);
00910             double gg = kdmax1 * gradFunc[ kpar ];  
00911             if ( gg > 0) gg = std::min( gg, kdmax2);
00912             else gg = std::max(gg, - kdmax2);
00913             g[kpar] -= gg;
00914          }
00915          // if func derivative is zero term is also zero so do not add in g[kpar]
00916       }
00917             
00918     // copy result 
00919    std::copy(g.begin(), g.end(), grad);
00920    }
00921 }
00922 //_________________________________________________________________________________________________
00923 // for binned log likelihood functions      
00924 //------------------------------------------------------------------------------------------------
00925 double FitUtil::EvaluatePoissonBinPdf(const IModelFunction & func, const BinData & data, const double * p, unsigned int i, double * g ) {  
00926    // evaluate the pdf (Poisson) contribution to the logl (return actually log of pdf)
00927    // and its gradient
00928 
00929 
00930    double y = 0; 
00931    const double * x1 = data.GetPoint(i,y);
00932 
00933    const DataOptions & fitOpt = data.Opt();
00934    bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges(); 
00935    bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
00936 
00937    IntegralEvaluator<> igEval( func, p, useBinIntegral); 
00938    double fval = 0; 
00939    const double * x2 = 0; 
00940    // calculate the bin volume
00941    double binVolume = 1; 
00942    std::vector<double> xc; 
00943    if (useBinVolume) { 
00944       unsigned int ndim = data.NDim(); 
00945       xc.resize(ndim);
00946       x2 = data.BinUpEdge(i);  
00947       for (unsigned int j = 0; j < ndim; ++j) {
00948          binVolume *= std::abs( x2[j]-x1[j] );
00949          xc[j] = 0.5*(x2[j]+ x1[j]);
00950       }
00951       // normalize the bin volume using a reference value
00952       binVolume /= data.RefVolume();
00953    }
00954 
00955    const double * x = (useBinVolume) ? &xc.front() : x1;
00956 
00957    if (!useBinIntegral ) {
00958       fval = func ( x, p ); 
00959    }
00960    else { 
00961       // calculate integral normalized (divided by bin volume) 
00962       x2 =  data.BinUpEdge(i);
00963       fval = igEval( x1, x2 ) ; 
00964    }
00965    if (useBinVolume) fval *= binVolume;
00966 
00967    // logPdf for Poisson: ignore constant term depending on N
00968    fval = std::max(fval, 0.0);  // avoid negative or too small values 
00969    double logPdf =   y * ROOT::Math::Util::EvalLog( fval) - fval;  
00970    // need to return the pdf contribution (not the log)
00971 
00972    //double pdfval =  std::exp(logPdf);
00973 
00974   //if (g == 0) return pdfval; 
00975    if (g == 0) return logPdf; 
00976 
00977    unsigned int npar = func.NPar(); 
00978    const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func); 
00979 
00980    // gradient  calculation
00981    if (gfunc != 0) { 
00982       //case function provides gradient
00983       if (!useBinIntegral ) 
00984          gfunc->ParameterGradient(  x , p, g );  
00985       else { 
00986          // needs to calculate the integral for each partial derivative
00987          CalculateGradientIntegral( *gfunc, x1, x2, p, g); 
00988       }
00989 
00990    }
00991    else {       
00992       SimpleGradientCalculator  gc(func.NPar(), func); 
00993       if (!useBinIntegral ) 
00994          gc.ParameterGradient(x, p, fval, g); 
00995       else { 
00996         // needs to calculate the integral for each partial derivative
00997          CalculateGradientIntegral( gc, x1, x2, p, g);  
00998       } 
00999    }
01000    // correct g[] do be derivative of poisson term 
01001    for (unsigned int k = 0; k < npar; ++k) {
01002       // apply bin volume correction
01003       if (useBinVolume) g[k] *= binVolume;  
01004 
01005       // correct for Poisson term
01006       if ( fval > 0) 
01007          g[k] *= ( y/fval - 1.) ;//* pdfval; 
01008       else if (y > 0) { 
01009          const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
01010          g[k] *= kdmax1; 
01011       }
01012       else   // y == 0 cannot have  negative y
01013          g[k] *= -1;         
01014    }       
01015      
01016 
01017 #ifdef DEBUG
01018    std::cout << "x = " << x[0] << " logPdf = " << logPdf << " grad"; 
01019    for (unsigned int ipar = 0; ipar < npar; ++ipar) 
01020       std::cout << g[ipar] << "\t";
01021    std::cout << std::endl;
01022 #endif 
01023 
01024 //   return pdfval;
01025    return logPdf;
01026 }
01027 
01028 double FitUtil::EvaluatePoissonLogL(const IModelFunction & func, const BinData & data, const double * p, unsigned int &   nPoints ) {  
01029    // evaluate the Poisson Log Likelihood
01030    // for binned likelihood fits
01031    // this is Sum ( f(x_i)  -  y_i * log( f (x_i) ) )
01032 
01033    unsigned int n = data.Size();
01034 #ifdef DEBUG
01035    std::cout << "Evaluate PoissonLogL for params = [ "; 
01036    for (unsigned int j=0; j < func.NPar(); ++j) std::cout << p[j] << " , ";
01037    std::cout << "]  - data size = " << n << std::endl;
01038 #endif
01039    
01040    double loglike = 0;
01041    //int nRejected = 0; 
01042 
01043    // get fit option and check case of using integral of bins
01044    const DataOptions & fitOpt = data.Opt();
01045    bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges(); 
01046    bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
01047 
01048    double wrefVolume = 1.0; 
01049    std::vector<double> xc; 
01050    if (useBinVolume) { 
01051       wrefVolume /= data.RefVolume();
01052       xc.resize(data.NDim() );
01053    }
01054 
01055    IntegralEvaluator<> igEval( func, p, fitOpt.fIntegral); 
01056 
01057    for (unsigned int i = 0; i < n; ++ i) { 
01058       const double * x1 = data.Coords(i);
01059       double y = data.Value(i);
01060 
01061       double fval = 0;   
01062       double binVolume = 1.0; 
01063 
01064       if (useBinVolume) { 
01065          unsigned int ndim = data.NDim(); 
01066          const double * x2 = data.BinUpEdge(i);  
01067          for (unsigned int j = 0; j < ndim; ++j) {
01068             binVolume *= std::abs( x2[j]-x1[j] );
01069             xc[j] = 0.5*(x2[j]+ x1[j]);
01070          }
01071          // normalize the bin volume using a reefrence value
01072          binVolume *= wrefVolume;
01073       }
01074 
01075       const double * x = (useBinVolume) ? &xc.front() : x1;
01076 
01077       if (!useBinIntegral) {
01078          fval = func ( x, p );
01079       }
01080       else {
01081          // calculate integral (normalized by bin volume) 
01082          // need to set function and parameters here in case loop is parallelized
01083          fval = igEval( x1, data.BinUpEdge(i)) ; 
01084       }
01085       if (useBinVolume) fval *= binVolume;
01086 
01087 
01088 #ifdef DEBUG
01089       int NSAMPLE = 100;
01090       if (i%NSAMPLE == 0) { 
01091          std::cout << "evt " << i << " x1 = [ "; 
01092          for (unsigned int j=0; j < func.NDim(); ++j) std::cout << x[j] << " , ";
01093          std::cout << "]  ";
01094          if (fitOpt.fIntegral) { 
01095             std::cout << "x2 = [ "; 
01096             for (unsigned int j=0; j < func.NDim(); ++j) std::cout << data.BinUpEdge(i)[j] << " , ";
01097             std::cout << "] ";
01098          }
01099          std::cout << "  y = " << y << " fval = " << fval << std::endl;
01100       }
01101 #endif
01102 
01103 
01104       // EvalLog protects against 0 values of fval but don't want to add in the -log sum 
01105       // negative values of fval 
01106       fval = std::max(fval, 0.0);
01107       loglike +=  fval - y * ROOT::Math::Util::EvalLog( fval);  
01108 
01109    }
01110    
01111    // reset the number of fitting data points
01112    nPoints = n; 
01113    //if (nRejected != 0)  nPoints = n - nRejected;
01114 
01115 #ifdef DEBUG
01116    std::cout << "Loglikelihood  = " << loglike << std::endl;
01117 #endif
01118    
01119    return loglike;  
01120 }
01121 
01122 void FitUtil::EvaluatePoissonLogLGradient(const IModelFunction & f, const BinData & data, const double * p, double * grad ) { 
01123    // evaluate the gradient of the Poisson log likelihood function
01124 
01125    const IGradModelFunction * fg = dynamic_cast<const IGradModelFunction *>( &f); 
01126    assert (fg != 0); // must be called by a grad function
01127    const IGradModelFunction & func = *fg; 
01128 
01129    unsigned int n = data.Size();
01130 
01131    const DataOptions & fitOpt = data.Opt();
01132    bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges(); 
01133    bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
01134 
01135    double wrefVolume = 1.0;
01136    std::vector<double> xc;  
01137    if (useBinVolume) {
01138       wrefVolume /= data.RefVolume();
01139       xc.resize(data.NDim() );
01140    }
01141 
01142    IntegralEvaluator<> igEval( func, p, useBinIntegral); 
01143 
01144    unsigned int npar = func.NPar(); 
01145    std::vector<double> gradFunc( npar ); 
01146    std::vector<double> g( npar); 
01147 
01148    for (unsigned int i = 0; i < n; ++ i) { 
01149       const double * x1 = data.Coords(i);
01150       double y = data.Value(i);
01151       double fval = 0; 
01152       const double * x2 = 0; 
01153 
01154       double binVolume = 1.0; 
01155       if (useBinVolume) { 
01156          x2 = data.BinUpEdge(i);  
01157          unsigned int ndim = data.NDim(); 
01158          for (unsigned int j = 0; j < ndim; ++j) { 
01159             binVolume *= std::abs( x2[j]-x1[j] );
01160             xc[j] = 0.5*(x2[j]+ x1[j]);
01161          }
01162          // normalize the bin volume using a reference value
01163          binVolume *= wrefVolume;
01164       }
01165 
01166       const double * x = (useBinVolume) ? &xc.front() : x1;
01167 
01168       if (!useBinIntegral) {
01169          fval = func ( x, p );
01170          func.ParameterGradient(  x , p, &gradFunc[0] ); 
01171       }
01172       else {
01173          // calculate integral (normalized by bin volume) 
01174          // need to set function and parameters here in case loop is parallelized
01175          x2 = data.BinUpEdge(i);
01176          fval = igEval( x1, x2) ; 
01177          CalculateGradientIntegral( func, x1, x2, p, &gradFunc[0]); 
01178       }
01179       if (useBinVolume) fval *= binVolume;
01180       
01181       // correct the gradient
01182       for (unsigned int kpar = 0; kpar < npar; ++ kpar) { 
01183 
01184          // correct gradient for bin volumes
01185          if (useBinVolume) gradFunc[kpar] *= binVolume; 
01186 
01187          // df/dp * (1.  - y/f )
01188          if (fval > 0)  
01189             g[kpar] += gradFunc[ kpar ] * ( 1. - y/fval ); 
01190          else if (gradFunc [ kpar] != 0) { 
01191             const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
01192             const double kdmax2 = std::numeric_limits<double>::max() / (4*n);
01193             double gg = kdmax1 * gradFunc[ kpar ];  
01194             if ( gg > 0) gg = std::min( gg, kdmax2);
01195             else gg = std::max(gg, - kdmax2);
01196             g[kpar] -= gg;
01197          }
01198       }            
01199 
01200       // copy result 
01201       std::copy(g.begin(), g.end(), grad);
01202    }
01203 }
01204    
01205 }
01206 
01207 } // end namespace ROOT
01208 

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