Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:10:23

0001 // @(#)root/mathmore:$Id$
0002 // Author: L. Moneta Wed Sep  6 09:52:26 2006
0003 
0004 /**********************************************************************
0005  *                                                                    *
0006  * Copyright (c) 2006  LCG ROOT Math Team, CERN/PH-SFT                *
0007  *                                                                    *
0008  *                                                                    *
0009  **********************************************************************/
0010 
0011 // Header file for class WrappedTFunction
0012 
0013 #ifndef ROOT_Math_WrappedMultiTF1
0014 #define ROOT_Math_WrappedMultiTF1
0015 
0016 
0017 #include "Math/IParamFunction.h"
0018 
0019 #include "TF1.h"
0020 #include <string>
0021 #include <vector>
0022 #include <algorithm>
0023 
0024 namespace ROOT {
0025 
0026    namespace Math {
0027 
0028       namespace Internal {
0029          double DerivPrecision(double eps);
0030          TF1 *CopyTF1Ptr(const TF1 *funcToCopy);
0031       };
0032 
0033       /**
0034          Class to Wrap a ROOT Function class (like TF1)  in a IParamMultiFunction interface
0035          of multi-dimensions to be used in the ROOT::Math numerical algorithm.
0036          This wrapper class does not own the TF1 pointer, so it assumes it exists during the wrapper lifetime.
0037          The class copy the TF1 pointer only when it owns it.
0038 
0039          The class from ROOT version 6.03 does not contain anymore a copy of the parameters. The parameters are
0040          stored in the TF1 class.
0041 
0042          @ingroup CppFunctions
0043       */
0044 
0045       //LM note: are there any issues when cloning the class for the parameters that are not copied anymore ??
0046 
0047       template<class T>
0048       class WrappedMultiTF1Templ: virtual public ROOT::Math::IParametricGradFunctionMultiDimTempl<T> {
0049 
0050       public:
0051 
0052          typedef  ROOT::Math::IParametricGradFunctionMultiDimTempl<T>  BaseParamFunc;
0053          typedef  typename ROOT::Math::IParametricFunctionMultiDimTempl<T>::BaseFunc  BaseFunc;
0054 
0055          /**
0056             constructor from a function pointer to a TF1
0057             If dim = 0 dimension is taken from TF1::GetNdim().
0058             In case of multi-dimensional function created using directly TF1 object the dimension
0059             returned by TF1::GetNdim is always 1. The user must then pass the correct value of dim
0060          */
0061          WrappedMultiTF1Templ(TF1 &f, unsigned int dim = 0);
0062 
0063          /**
0064             Destructor (no operations). Function pointer is not owned
0065          */
0066          ~WrappedMultiTF1Templ() override
0067          {
0068             if (fOwnFunc && fFunc) delete fFunc;
0069          }
0070 
0071          /**
0072             Copy constructor
0073          */
0074          WrappedMultiTF1Templ(const WrappedMultiTF1Templ<T> &rhs);
0075 
0076          /**
0077             Assignment operator
0078          */
0079          WrappedMultiTF1Templ &operator = (const WrappedMultiTF1Templ<T> &rhs);
0080 
0081          /** @name interface inherited from IParamFunction */
0082 
0083          /**
0084              Clone the wrapper but not the original function
0085          */
0086          IMultiGenFunctionTempl<T> *Clone() const override
0087          {
0088             return new WrappedMultiTF1Templ<T>(*this);
0089          }
0090 
0091          /**
0092               Retrieve the dimension of the function
0093           */
0094          unsigned int NDim() const override
0095          {
0096             return fDim;
0097          }
0098 
0099          /// get the parameter values (return values from TF1)
0100          const double *Parameters() const override
0101          {
0102             //return  (fParams.size() > 0) ? &fParams.front() : 0;
0103             return  fFunc->GetParameters();
0104          }
0105 
0106          /// set parameter values (only the cached one in this class,leave unchanges those of TF1)
0107          void SetParameters(const double *p) override
0108          {
0109             //std::copy(p,p+fParams.size(),fParams.begin());
0110             fFunc->SetParameters(p);
0111          }
0112 
0113          /// return number of parameters
0114          unsigned int NPar() const override
0115          {
0116             // return fParams.size();
0117             return fFunc->GetNpar();
0118          }
0119 
0120          /// return parameter name (from TF1)
0121          std::string ParameterName(unsigned int i) const override {
0122             return std::string(fFunc->GetParName(i));
0123          }
0124 
0125          // evaluate the derivative of the function with respect to the parameters
0126          void ParameterGradient(const T *x, const double *par, T *grad) const override;
0127 
0128          // evaluate the hessian of the function with respect to the parameters
0129          bool ParameterHessian(const T *x, const double *par, T *h) const override;
0130 
0131          // return capability of computing parameter Hessian
0132          bool HasParameterHessian() const override;
0133 
0134          // evaluate the 2nd derivatives of the function with respect to the parameters
0135          bool ParameterG2(const T *, const double *, T *) const override {
0136             return false; // not yet implemented
0137          }
0138 
0139          /// precision value used for calculating the derivative step-size
0140          /// h = eps * |x|. The default is 0.001, give a smaller in case function changes rapidly
0141          static void SetDerivPrecision(double eps);
0142 
0143          /// get precision value used for calculating the derivative step-size
0144          static double GetDerivPrecision();
0145 
0146          /// method to retrieve the internal function pointer
0147          const TF1 *GetFunction() const
0148          {
0149             return fFunc;
0150          }
0151 
0152          /// method to set a new function pointer and copy it inside.
0153          /// By calling this method the class manages now the passed TF1 pointer
0154          void SetAndCopyFunction(const TF1 *f = nullptr);
0155 
0156       private:
0157          /// evaluate function passing coordinates x and vector of parameters
0158          T DoEvalPar(const T *x, const double *p) const override
0159          {
0160             return fFunc->EvalPar(x, p);
0161          }
0162 
0163          /// evaluate function using the cached parameter values (of TF1)
0164          /// re-implement for better efficiency
0165          T DoEvalVec(const T *x) const
0166          {
0167             return fFunc->EvalPar(x, 0);
0168          }
0169 
0170          /// evaluate function using the cached parameter values (of TF1)
0171          /// re-implement for better efficiency
0172          T DoEval(const T *x) const override
0173          {
0174             // no need to call InitArg for interpreted functions (done in ctor)
0175 
0176             //const double * p = (fParams.size() > 0) ? &fParams.front() : 0;
0177 
0178             return fFunc->EvalPar(x, nullptr);
0179          }
0180 
0181          /// evaluate the partial derivative with respect to the parameter
0182          T DoParameterDerivative(const T *x, const double *p, unsigned int ipar) const override;
0183 
0184          bool fLinear;                 // flag for linear functions
0185          bool fPolynomial;             // flag for polynomial functions
0186          bool fOwnFunc;                 // flag to indicate we own the TF1 function pointer
0187          TF1 *fFunc;                    // pointer to ROOT function
0188          unsigned int fDim;             // cached value of dimension
0189          //std::vector<double> fParams;   // cached vector with parameter values
0190 
0191       };
0192 
0193       /**
0194        * Auxiliar class to bypass the (provisional) lack of vectorization in TFormula::EvalPar.
0195        *
0196        * WrappedMultiTF1Templ::DoParameterDerivation calls TFormula::EvalPar in the case of a general linear function
0197        * built with TFormula using ++; as EvalPar is not vectorized, in order to generalize  DoParameterDerivative with
0198        * a general type T, we use this auxiliar class to branch the code in compile time with the double
0199        * specialization (that can call EvalPar) and the general implementation (that throws an error in the case of
0200        * general linear function).
0201        */
0202       template <class T>
0203       struct GeneralLinearFunctionDerivation {
0204          static T DoParameterDerivative(const WrappedMultiTF1Templ<T> *, const T *, unsigned int)
0205          {
0206             Error("DoParameterDerivative", "The vectorized implementation of DoParameterDerivative does not support"
0207                                            "general linear functions built in TFormula with ++");
0208 
0209             return TMath::SignalingNaN();
0210          }
0211       };
0212 
0213       template <>
0214       struct GeneralLinearFunctionDerivation<double> {
0215          static double
0216          DoParameterDerivative(const WrappedMultiTF1Templ<double> *wrappedFunc, const double *x, unsigned int ipar)
0217          {
0218             const TFormula *df = dynamic_cast<const TFormula *>(wrappedFunc->GetFunction()->GetLinearPart(ipar));
0219             assert(df != nullptr);
0220             return (const_cast<TFormula *>(df))->EvalPar(x); // derivatives should not depend on parameters since
0221             // function  is linear
0222          }
0223       };
0224 
0225       // implementations for WrappedMultiTF1Templ<T>
0226       template<class T>
0227       WrappedMultiTF1Templ<T>::WrappedMultiTF1Templ(TF1 &f, unsigned int dim)  :
0228          fLinear(false),
0229          fPolynomial(false),
0230          fOwnFunc(false),
0231          fFunc(&f),
0232          fDim(dim)
0233          //fParams(f.GetParameters(),f.GetParameters()+f.GetNpar())
0234       {
0235          // constructor of WrappedMultiTF1Templ<T>
0236          // pass a dimension if dimension specified in TF1 does not correspond to real dimension
0237          // for example in case of multi-dimensional TF1 objects defined as TF1 (i.e. for functions with dims > 3 )
0238          if (fDim == 0) fDim = fFunc->GetNdim();
0239 
0240          // check that in case function is linear the linear terms are not zero
0241          // function is linear when is a TFormula created with "++"
0242          // hyperplane are not yet existing in TFormula
0243          if (fFunc->IsLinear()) {
0244             int ip = 0;
0245             fLinear = true;
0246             while (fLinear && ip < fFunc->GetNpar())  {
0247                fLinear &= (fFunc->GetLinearPart(ip) != nullptr);
0248                ip++;
0249             }
0250          }
0251          // distinguish case of polynomial functions and linear functions
0252          if (fDim == 1 && fFunc->GetNumber() >= 300 && fFunc->GetNumber() < 310) {
0253             fLinear = true;
0254             fPolynomial = true;
0255          }
0256       }
0257 
0258       template<class T>
0259       WrappedMultiTF1Templ<T>::WrappedMultiTF1Templ(const WrappedMultiTF1Templ<T> &rhs) :
0260          BaseParamFunc(rhs),
0261          fLinear(rhs.fLinear),
0262          fPolynomial(rhs.fPolynomial),
0263          fOwnFunc(rhs.fOwnFunc),
0264          fFunc(rhs.fFunc),
0265          fDim(rhs.fDim)
0266          //fParams(rhs.fParams)
0267       {
0268          // copy constructor
0269          if (fOwnFunc) SetAndCopyFunction(rhs.fFunc);
0270       }
0271 
0272       template<class T>
0273       WrappedMultiTF1Templ<T> &WrappedMultiTF1Templ<T>::operator= (const WrappedMultiTF1Templ<T> &rhs)
0274       {
0275          // Assignment operator
0276          if (this == &rhs) return *this;  // time saving self-test
0277          fLinear = rhs.fLinear;
0278          fPolynomial = rhs.fPolynomial;
0279          fOwnFunc = rhs.fOwnFunc;
0280          fDim = rhs.fDim;
0281          //fParams = rhs.fParams;
0282          return *this;
0283       }
0284 
0285       template <class T>
0286       void WrappedMultiTF1Templ<T>::ParameterGradient(const T *x, const double *par, T *grad) const
0287       {
0288          // evaluate the gradient of the function with respect to the parameters
0289          //IMPORTANT NOTE: TF1::GradientPar returns 0 for fixed parameters to avoid computing useless derivatives
0290          //  BUT the TLinearFitter wants to have the derivatives also for fixed parameters.
0291          //  so in case of fLinear (or fPolynomial) a non-zero value will be returned for fixed parameters
0292 
0293          if (!fLinear) {
0294             // need to set parameter values
0295             fFunc->SetParameters(par);
0296             // no need to call InitArgs (it is called in TF1::GradientPar)
0297             double prec = this->GetDerivPrecision();
0298             fFunc->GradientPar(x, grad, prec);
0299          } else { // case of linear functions
0300             unsigned int np = NPar();
0301             for (unsigned int i = 0; i < np; ++i)
0302                grad[i] = DoParameterDerivative(x, par, i);
0303          }
0304       }
0305 
0306       // struct for dealing of generic Hessian computation, since it is available only in TFormula
0307       template <class T>
0308       struct GeneralHessianCalc {
0309          static bool Hessian(TF1 *, const T *, const double *, T *)
0310          {
0311             Error("Hessian", "The vectorized implementation of ParameterHessian is not supported");
0312             return false;
0313          }
0314          static bool IsAvailable(TF1 * ) { return false;}
0315       };
0316 
0317       template <>
0318       struct GeneralHessianCalc<double> {
0319          static bool Hessian(TF1 * func, const double *x, const double * par, double * h)
0320          {
0321             // compute Hessian if TF1 is a formula based
0322             unsigned int np = func->GetNpar();
0323             auto formula = func->GetFormula();
0324             if (!formula) return false;
0325             std::vector<double> h2(np*np);
0326             func->SetParameters(par);
0327             formula->HessianPar(x,h2);
0328             for (unsigned int i = 0; i < np; i++) {
0329                for (unsigned int j = 0; j <= i; j++) {
0330                   unsigned int ih = j + i *(i+1)/2;  // formula for j <= i
0331                   unsigned int im = i*np + j;
0332                   h[ih] = h2[im];
0333                }
0334             }
0335             return true;
0336          }
0337          static bool IsAvailable(TF1 * func) {
0338             auto formula = func->GetFormula();
0339             if (!formula) return false;
0340             return formula->GenerateHessianPar();
0341          }
0342       };
0343 
0344 
0345 
0346       template <class T>
0347       bool WrappedMultiTF1Templ<T>::ParameterHessian(const T *x, const double *par, T *h) const
0348       {
0349          if (fLinear) {
0350             std::fill(h, h + NPar()*(NPar()+1)/2, 0.0);
0351             return true;
0352          }
0353          return GeneralHessianCalc<T>::Hessian(fFunc, x, par, h);
0354       }
0355 
0356       template <class T>
0357       bool WrappedMultiTF1Templ<T>::HasParameterHessian() const
0358       {
0359          return GeneralHessianCalc<T>::IsAvailable(fFunc);
0360       }
0361 
0362       template <class T>
0363       T WrappedMultiTF1Templ<T>::DoParameterDerivative(const T *x, const double *p, unsigned int ipar) const
0364       {
0365          // evaluate the derivative of the function with respect to parameter ipar
0366          // see note above concerning the fixed parameters
0367          if (!fLinear) {
0368             fFunc->SetParameters(p);
0369             double prec = this->GetDerivPrecision();
0370             return fFunc->GradientPar(ipar, x, prec);
0371          }
0372          if (fPolynomial) {
0373             // case of polynomial function (no parameter dependency)  (case for dim = 1)
0374             assert(fDim == 1);
0375             if (ipar == 0) return 1.0;
0376 #ifdef R__HAS_VECCORE
0377             return vecCore::math::Pow(x[0], static_cast<T>(ipar));
0378 #else
0379             return std::pow(x[0], static_cast<int>(ipar));
0380 #endif
0381          } else {
0382             // case of general linear function (built in TFormula with ++ )
0383             return GeneralLinearFunctionDerivation<T>::DoParameterDerivative(this, x, ipar);
0384          }
0385       }
0386       template<class T>
0387       void WrappedMultiTF1Templ<T>::SetDerivPrecision(double eps)
0388       {
0389          ::ROOT::Math::Internal::DerivPrecision(eps);
0390       }
0391 
0392       template<class T>
0393       double WrappedMultiTF1Templ<T>::GetDerivPrecision()
0394       {
0395          return ::ROOT::Math::Internal::DerivPrecision(-1);
0396       }
0397 
0398       template<class T>
0399       void WrappedMultiTF1Templ<T>::SetAndCopyFunction(const TF1 *f)
0400       {
0401          const TF1 *funcToCopy = (f) ? f : fFunc;
0402          fFunc = ::ROOT::Math::Internal::CopyTF1Ptr(funcToCopy);
0403          fOwnFunc = true;
0404       }
0405 
0406       using WrappedMultiTF1 = WrappedMultiTF1Templ<double>;
0407 
0408    } // end namespace Math
0409 
0410 } // end namespace ROOT
0411 
0412 
0413 #endif /* ROOT_Fit_WrappedMultiTF1 */