Logo ROOT   6.08/07
Reference Guide
FitUtil.cxx
Go to the documentation of this file.
1 // @(#)root/mathcore:$Id$
2 // Author: L. Moneta Tue Nov 28 10:52:47 2006
3 
4 /**********************************************************************
5  * *
6  * Copyright (c) 2006 LCG ROOT Math Team, CERN/PH-SFT *
7  * *
8  * *
9  **********************************************************************/
10 
11 // Implementation file for class FitUtil
12 
13 #include "Fit/FitUtil.h"
14 
15 #include "Fit/BinData.h"
16 #include "Fit/UnBinData.h"
17 //#include "Fit/BinPoint.h"
18 
19 #include "Math/IFunctionfwd.h"
20 #include "Math/IParamFunction.h"
21 #include "Math/IParamFunctionfwd.h"
22 #include "Math/Integrator.h"
24 #include "Math/WrappedFunction.h"
27 
28 #include "Math/Error.h"
29 #include "Math/Util.h" // for safe log(x)
30 
31 #include <limits>
32 #include <cmath>
33 #include <cassert>
34 #include <algorithm>
35 //#include <memory>
36 
37 //#define DEBUG
38 #ifdef DEBUG
39 #define NSAMPLE 10
40 #include <iostream>
41 #endif
42 
43 // using parameter cache is not thread safe but needed for normalizing the functions
44 #define USE_PARAMCACHE
45 
46 // need to implement integral option
47 
48 namespace ROOT {
49 
50  namespace Fit {
51 
52  namespace FitUtil {
53 
54  // internal class to evaluate the function or the integral
55  // and cached internal integration details
56  // if useIntegral is false no allocation is done
57  // and this is a dummy class
58  // class is templated on any parametric functor implementing operator()(x,p) and NDim()
59  // contains a constant pointer to the function
60  template <class ParamFunc = ROOT::Math::IParamMultiFunction>
61  class IntegralEvaluator {
62 
63  public:
64 
65  IntegralEvaluator(const ParamFunc & func, const double * p, bool useIntegral = true) :
66  fDim(0),
67  fParams(0),
68  fFunc(0),
69  fIg1Dim(0),
70  fIgNDim(0),
71  fFunc1Dim(0),
72  fFuncNDim(0)
73  {
74  if (useIntegral) {
75  SetFunction(func, p);
76  }
77  }
78 
79  void SetFunction(const ParamFunc & func, const double * p = 0) {
80  // set the integrand function and create required wrapper
81  // to perform integral in (x) of a generic f(x,p)
82  fParams = p;
83  fDim = func.NDim();
84  // copy the function object to be able to modify the parameters
85  //fFunc = dynamic_cast<ROOT::Math::IParamMultiFunction *>( func.Clone() );
86  fFunc = &func;
87  assert(fFunc != 0);
88  // set parameters in function
89  //fFunc->SetParameters(p);
90  if (fDim == 1) {
92  fIg1Dim = new ROOT::Math::IntegratorOneDim();
93  //fIg1Dim->SetFunction( static_cast<const ROOT::Math::IMultiGenFunction & >(*fFunc),false);
94  fIg1Dim->SetFunction( static_cast<const ROOT::Math::IGenFunction &>(*fFunc1Dim) );
95  }
96  else if (fDim > 1) {
98  fIgNDim = new ROOT::Math::IntegratorMultiDim();
99  fIgNDim->SetFunction(*fFuncNDim);
100  }
101  else
102  assert(fDim > 0);
103  }
104 
105  void SetParameters(const double *p) {
106  // copy just the pointer
107  fParams = p;
108  }
109 
110  ~IntegralEvaluator() {
111  if (fIg1Dim) delete fIg1Dim;
112  if (fIgNDim) delete fIgNDim;
113  if (fFunc1Dim) delete fFunc1Dim;
114  if (fFuncNDim) delete fFuncNDim;
115  //if (fFunc) delete fFunc;
116  }
117 
118  // evaluation of integrand function (one-dim)
119  double F1 (double x) const {
120  double xx[1]; xx[0] = x;
121  return (*fFunc)( xx, fParams);
122  }
123  // evaluation of integrand function (multi-dim)
124  double FN(const double * x) const {
125  return (*fFunc)( x, fParams);
126  }
127 
128  double Integral(const double *x1, const double * x2) {
129  // return unormalized integral
130  return (fIg1Dim) ? fIg1Dim->Integral( *x1, *x2) : fIgNDim->Integral( x1, x2);
131  }
132  double operator()(const double *x1, const double * x2) {
133  // return normalized integral, divided by bin volume (dx1*dx...*dxn)
134  if (fIg1Dim) {
135  double dV = *x2 - *x1;
136  return fIg1Dim->Integral( *x1, *x2)/dV;
137  }
138  else if (fIgNDim) {
139  double dV = 1;
140  for (unsigned int i = 0; i < fDim; ++i)
141  dV *= ( x2[i] - x1[i] );
142  return fIgNDim->Integral( x1, x2)/dV;
143 // std::cout << " do integral btw x " << x1[0] << " " << x2[0] << " y " << x1[1] << " " << x2[1] << " dV = " << dV << " result = " << result << std::endl;
144 // return result;
145  }
146  else
147  assert(1.); // should never be here
148  return 0;
149  }
150 
151  private:
152 
153  // objects of this class are not meant to be copied / assigned
154  IntegralEvaluator(const IntegralEvaluator& rhs);
155  IntegralEvaluator& operator=(const IntegralEvaluator& rhs);
156 
157  unsigned int fDim;
158  const double * fParams;
159  //ROOT::Math::IParamMultiFunction * fFunc; // copy of function in order to be able to change parameters
160  const ParamFunc * fFunc; // reference to a generic parametric function
163  ROOT::Math::IGenFunction * fFunc1Dim;
164  ROOT::Math::IMultiGenFunction * fFuncNDim;
165  };
166 
167 
168  // derivative with respect of the parameter to be integrated
169  template<class GradFunc = IGradModelFunction>
170  struct ParamDerivFunc {
171  ParamDerivFunc(const GradFunc & f) : fFunc(f), fIpar(0) {}
172  void SetDerivComponent(unsigned int ipar) { fIpar = ipar; }
173  double operator() (const double *x, const double *p) const {
174  return fFunc.ParameterDerivative( x, p, fIpar );
175  }
176  unsigned int NDim() const { return fFunc.NDim(); }
177  const GradFunc & fFunc;
178  unsigned int fIpar;
179  };
180 
181  // simple gradient calculator using the 2 points rule
182 
183  class SimpleGradientCalculator {
184 
185  public:
186  // construct from function and gradient dimension gdim
187  // gdim = npar for parameter gradient
188  // gdim = ndim for coordinate gradients
189  // construct (the param values will be passed later)
190  // one can choose between 2 points rule (1 extra evaluation) istrat=1
191  // or two point rule (2 extra evaluation)
192  // (found 2 points rule does not work correctly - minuit2FitBench fails)
193  SimpleGradientCalculator(int gdim, const IModelFunction & func,double eps = 2.E-8, int istrat = 1) :
194  fEps(eps),
195  fPrecision(1.E-8 ), // sqrt(epsilon)
196  fStrategy(istrat),
197  fN(gdim ),
198  fFunc(func),
199  fVec(std::vector<double>(gdim) ) // this can be probably optimized
200  {}
201 
202  // internal method to calculate single partial derivative
203  // assume cached vector fVec is already set
204  double DoParameterDerivative(const double *x, const double *p, double f0, int k) const {
205  double p0 = p[k];
206  double h = std::max( fEps* std::abs(p0), 8.0*fPrecision*(std::abs(p0) + fPrecision) );
207  fVec[k] += h;
208  double deriv = 0;
209  // t.b.d : treat case of infinities
210  //if (fval > - std::numeric_limits<double>::max() && fval < std::numeric_limits<double>::max() )
211  double f1 = fFunc(x, &fVec.front() );
212  if (fStrategy > 1) {
213  fVec[k] = p0 - h;
214  double f2 = fFunc(x, &fVec.front() );
215  deriv = 0.5 * ( f2 - f1 )/h;
216  }
217  else
218  deriv = ( f1 - f0 )/h;
219 
220  fVec[k] = p[k]; // restore original p value
221  return deriv;
222  }
223  // number of dimension in x (needed when calculating the integrals)
224  unsigned int NDim() const {
225  return fFunc.NDim();
226  }
227  // number of parameters (needed for grad ccalculation)
228  unsigned int NPar() const {
229  return fFunc.NPar();
230  }
231 
232  double ParameterDerivative(const double *x, const double *p, int ipar) const {
233  // fVec are the cached parameter values
234  std::copy(p, p+fN, fVec.begin());
235  double f0 = fFunc(x, p);
236  return DoParameterDerivative(x,p,f0,ipar);
237  }
238 
239  // calculate all gradient at point (x,p) knnowing already value f0 (we gain a function eval.)
240  void ParameterGradient(const double * x, const double * p, double f0, double * g) {
241  // fVec are the cached parameter values
242  std::copy(p, p+fN, fVec.begin());
243  for (unsigned int k = 0; k < fN; ++k) {
244  g[k] = DoParameterDerivative(x,p,f0,k);
245  }
246  }
247 
248  // calculate gradient w.r coordinate values
249  void Gradient(const double * x, const double * p, double f0, double * g) {
250  // fVec are the cached coordinate values
251  std::copy(x, x+fN, fVec.begin());
252  for (unsigned int k = 0; k < fN; ++k) {
253  double x0 = x[k];
254  double h = std::max( fEps* std::abs(x0), 8.0*fPrecision*(std::abs(x0) + fPrecision) );
255  fVec[k] += h;
256  // t.b.d : treat case of infinities
257  //if (fval > - std::numeric_limits<double>::max() && fval < std::numeric_limits<double>::max() )
258  double f1 = fFunc( &fVec.front(), p );
259  if (fStrategy > 1) {
260  fVec[k] = x0 - h;
261  double f2 = fFunc( &fVec.front(), p );
262  g[k] = 0.5 * ( f2 - f1 )/h;
263  }
264  else
265  g[k] = ( f1 - f0 )/h;
266 
267  fVec[k] = x[k]; // restore original x value
268  }
269  }
270 
271  private:
272 
273  double fEps;
274  double fPrecision;
275  int fStrategy; // strategy in calculation ( =1 use 2 point rule( 1 extra func) , = 2 use r point rule)
276  unsigned int fN; // gradient dimension
277  const IModelFunction & fFunc;
278  mutable std::vector<double> fVec; // cached coordinates (or parameter values in case of gradientpar)
279  };
280 
281 
282  // function to avoid infinities or nan
283  double CorrectValue(double rval) {
284  // avoid infinities or nan in rval
285  if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() )
286  return rval;
287  else if (rval < 0)
288  // case -inf
289  return -std::numeric_limits<double>::max();
290  else
291  // case + inf or nan
292  return + std::numeric_limits<double>::max();
293  }
294  bool CheckValue(double & rval) {
295  if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() )
296  return true;
297  else if (rval < 0) {
298  // case -inf
299  rval = -std::numeric_limits<double>::max();
300  return false;
301  }
302  else {
303  // case + inf or nan
304  rval = + std::numeric_limits<double>::max();
305  return false;
306  }
307  }
308 
309 
310  // calculation of the integral of the gradient functions
311  // for a function providing derivative w.r.t parameters
312  // x1 and x2 defines the integration interval , p the parameters
313  template <class GFunc>
314  void CalculateGradientIntegral(const GFunc & gfunc,
315  const double *x1, const double * x2, const double * p, double *g) {
316 
317  // needs to calculate the integral for each partial derivative
318  ParamDerivFunc<GFunc> pfunc( gfunc);
319  IntegralEvaluator<ParamDerivFunc<GFunc> > igDerEval( pfunc, p, true);
320  // loop on the parameters
321  unsigned int npar = gfunc.NPar();
322  for (unsigned int k = 0; k < npar; ++k ) {
323  pfunc.SetDerivComponent(k);
324  g[k] = igDerEval( x1, x2 );
325  }
326  }
327 
328 
329 
330  } // end namespace FitUtil
331 
332 
333 
334 //___________________________________________________________________________________________________________________________
335 // for chi2 functions
336 //___________________________________________________________________________________________________________________________
337 
338 double FitUtil::EvaluateChi2(const IModelFunction & func, const BinData & data, const double * p, unsigned int & nPoints) {
339  // evaluate the chi2 given a function reference , the data and returns the value and also in nPoints
340  // the actual number of used points
341  // normal chi2 using only error on values (from fitting histogram)
342  // optionally the integral of function in the bin is used
343 
344  unsigned int n = data.Size();
345 
346  double chi2 = 0;
347  nPoints = 0; // count the effective non-zero points
348  // set parameters of the function to cache integral value
349 #ifdef USE_PARAMCACHE
350  (const_cast<IModelFunction &>(func)).SetParameters(p);
351 #endif
352  // do not cache parameter values (it is not thread safe)
353  //func.SetParameters(p);
354 
355 
356  // get fit option and check case if using integral of bins
357  const DataOptions & fitOpt = data.Opt();
358  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
359  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
360  bool useExpErrors = (fitOpt.fExpErrors);
361 
362 #ifdef DEBUG
363  std::cout << "\n\nFit data size = " << n << std::endl;
364  std::cout << "evaluate chi2 using function " << &func << " " << p << std::endl;
365  std::cout << "use empty bins " << fitOpt.fUseEmpty << std::endl;
366  std::cout << "use integral " << fitOpt.fIntegral << std::endl;
367  std::cout << "use all error=1 " << fitOpt.fErrors1 << std::endl;
368 #endif
369 
370 #ifdef USE_PARAMCACHE
371  IntegralEvaluator<> igEval( func, 0, useBinIntegral);
372 #else
373  IntegralEvaluator<> igEval( func, p, useBinIntegral);
374 #endif
375  double maxResValue = std::numeric_limits<double>::max() /n;
376  double wrefVolume = 1.0;
377  std::vector<double> xc;
378  if (useBinVolume) {
379  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
380  xc.resize(data.NDim() );
381  }
382 
383  (const_cast<IModelFunction &>(func)).SetParameters(p);
384  for (unsigned int i = 0; i < n; ++ i) {
385 
386  double y = 0, invError = 1.;
387 
388  // in case of no error in y invError=1 is returned
389  const double * x1 = data.GetPoint(i,y, invError);
390 
391  double fval = 0;
392 
393  double binVolume = 1.0;
394  if (useBinVolume) {
395  unsigned int ndim = data.NDim();
396  const double * x2 = data.BinUpEdge(i);
397  for (unsigned int j = 0; j < ndim; ++j) {
398  binVolume *= std::abs( x2[j]-x1[j] );
399  xc[j] = 0.5*(x2[j]+ x1[j]);
400  }
401  // normalize the bin volume using a reference value
402  binVolume *= wrefVolume;
403  }
404 
405  const double * x = (useBinVolume) ? &xc.front() : x1;
406 
407  if (!useBinIntegral) {
408 #ifdef USE_PARAMCACHE
409  fval = func ( x );
410 #else
411  fval = func ( x, p );
412 #endif
413  }
414  else {
415  // calculate integral normalized by bin volume
416  // need to set function and parameters here in case loop is parallelized
417  fval = igEval( x1, data.BinUpEdge(i)) ;
418  }
419  // normalize result if requested according to bin volume
420  if (useBinVolume) fval *= binVolume;
421 
422  // expected errors
423  if (useExpErrors) {
424  // we need first to check if a weight factor needs to be applied
425  // weight = sumw2/sumw = error**2/content
426  double invWeight = y * invError * invError;
427  if (invError == 0) invWeight = (data.SumOfError2() > 0) ? data.SumOfContent()/ data.SumOfError2() : 1.0;
428  // compute expected error as f(x) / weight
429  double invError2 = (fval > 0) ? invWeight / fval : 0.0;
430  invError = std::sqrt(invError2);
431  }
432 
433 //#define DEBUG
434 #ifdef DEBUG
435  std::cout << x[0] << " " << y << " " << 1./invError << " params : ";
436  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
437  std::cout << p[ipar] << "\t";
438  std::cout << "\tfval = " << fval << " bin volume " << binVolume << " ref " << wrefVolume << std::endl;
439 #endif
440 //#undef DEBUG
441 
442 
443  if (invError > 0) {
444  nPoints++;
445 
446  double tmp = ( y -fval )* invError;
447  double resval = tmp * tmp;
448 
449 
450  // avoid inifinity or nan in chi2 values due to wrong function values
451  if ( resval < maxResValue )
452  chi2 += resval;
453  else {
454  //nRejected++;
455  chi2 += maxResValue;
456  }
457  }
458 
459 
460  }
461  nPoints=n;
462 
463 #ifdef DEBUG
464  std::cout << "chi2 = " << chi2 << " n = " << nPoints /*<< " rejected = " << nRejected */ << std::endl;
465 #endif
466 
467 
468  return chi2;
469 }
470 
471 
472 //___________________________________________________________________________________________________________________________
473 
474 double FitUtil::EvaluateChi2Effective(const IModelFunction & func, const BinData & data, const double * p, unsigned int & nPoints) {
475  // evaluate the chi2 given a function reference , the data and returns the value and also in nPoints
476  // the actual number of used points
477  // method using the error in the coordinates
478  // integral of bin does not make sense in this case
479 
480  unsigned int n = data.Size();
481 
482 #ifdef DEBUG
483  std::cout << "\n\nFit data size = " << n << std::endl;
484  std::cout << "evaluate effective chi2 using function " << &func << " " << p << std::endl;
485 #endif
486 
487  assert(data.HaveCoordErrors() );
488 
489  double chi2 = 0;
490  //int nRejected = 0;
491 
492 
493  //func.SetParameters(p);
494 
495  unsigned int ndim = func.NDim();
496 
497  // use Richardson derivator
499 
500  double maxResValue = std::numeric_limits<double>::max() /n;
501 
502 
503 
504  for (unsigned int i = 0; i < n; ++ i) {
505 
506 
507  double y = 0;
508  const double * x = data.GetPoint(i,y);
509 
510  double fval = func( x, p );
511 
512  double delta_y_func = y - fval;
513 
514 
515  double ey = 0;
516  const double * ex = 0;
517  if (!data.HaveAsymErrors() )
518  ex = data.GetPointError(i, ey);
519  else {
520  double eylow, eyhigh = 0;
521  ex = data.GetPointError(i, eylow, eyhigh);
522  if ( delta_y_func < 0)
523  ey = eyhigh; // function is higher than points
524  else
525  ey = eylow;
526  }
527  double e2 = ey * ey;
528  // before calculating the gradient check that all error in x are not zero
529  unsigned int j = 0;
530  while ( j < ndim && ex[j] == 0.) { j++; }
531  // if j is less ndim some elements are not zero
532  if (j < ndim) {
533  // need an adapter from a multi-dim function to a one-dimensional
535  // select optimal step size (use 10--2 by default as was done in TF1:
536  double kEps = 0.01;
537  double kPrecision = 1.E-8;
538  for (unsigned int icoord = 0; icoord < ndim; ++icoord) {
539  // calculate derivative for each coordinate
540  if (ex[icoord] > 0) {
541  //gradCalc.Gradient(x, p, fval, &grad[0]);
542  f1D.SetCoord(icoord);
543  // optimal spep size (take ex[] as scale for the points and 1% of it
544  double x0= x[icoord];
545  double h = std::max( kEps* std::abs(ex[icoord]), 8.0*kPrecision*(std::abs(x0) + kPrecision) );
546  double deriv = derivator.Derivative1(f1D, x[icoord], h);
547  double edx = ex[icoord] * deriv;
548  e2 += edx * edx;
549 #ifdef DEBUG
550  std::cout << "error for coord " << icoord << " = " << ex[icoord] << " deriv " << deriv << std::endl;
551 #endif
552  }
553  }
554  }
555  double w2 = (e2 > 0) ? 1.0/e2 : 0;
556  double resval = w2 * ( y - fval ) * ( y - fval);
557 
558 #ifdef DEBUG
559  std::cout << x[0] << " " << y << " ex " << ex[0] << " ey " << ey << " params : ";
560  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
561  std::cout << p[ipar] << "\t";
562  std::cout << "\tfval = " << fval << "\tresval = " << resval << std::endl;
563 #endif
564 
565  // avoid (infinity and nan ) in the chi2 sum
566  // eventually add possibility of excluding some points (like singularity)
567  if ( resval < maxResValue )
568  chi2 += resval;
569  else
570  chi2 += maxResValue;
571  //nRejected++;
572 
573  }
574 
575  // reset the number of fitting data points
576  nPoints = n; // no points are rejected
577  //if (nRejected != 0) nPoints = n - nRejected;
578 
579 #ifdef DEBUG
580  std::cout << "chi2 = " << chi2 << " n = " << nPoints << std::endl;
581 #endif
582 
583  return chi2;
584 
585 }
586 
587 
588 ////////////////////////////////////////////////////////////////////////////////
589 /// evaluate the chi2 contribution (residual term) only for data with no coord-errors
590 /// This function is used in the specialized least square algorithms like FUMILI or L.M.
591 /// if we have error on the coordinates the method is not yet implemented
592 /// integral option is also not yet implemented
593 /// one can use in that case normal chi2 method
594 
595 double FitUtil::EvaluateChi2Residual(const IModelFunction & func, const BinData & data, const double * p, unsigned int i, double * g) {
596  if (data.GetErrorType() == BinData::kCoordError && data.Opt().fCoordErrors ) {
597  MATH_ERROR_MSG("FitUtil::EvaluateChi2Residual","Error on the coordinates are not used in calculating Chi2 residual");
598  return 0; // it will assert otherwise later in GetPoint
599  }
600 
601 
602  //func.SetParameters(p);
603 
604  double y, invError = 0;
605 
606  const DataOptions & fitOpt = data.Opt();
607  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
608  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
609  bool useExpErrors = (fitOpt.fExpErrors);
610 
611  const double * x1 = data.GetPoint(i,y, invError);
612 
613  IntegralEvaluator<> igEval( func, p, useBinIntegral);
614  double fval = 0;
615  unsigned int ndim = data.NDim();
616  double binVolume = 1.0;
617  const double * x2 = 0;
618  if (useBinVolume || useBinIntegral) x2 = data.BinUpEdge(i);
619 
620  double * xc = 0;
621 
622  if (useBinVolume) {
623  xc = new double[ndim];
624  for (unsigned int j = 0; j < ndim; ++j) {
625  binVolume *= std::abs( x2[j]-x1[j] );
626  xc[j] = 0.5*(x2[j]+ x1[j]);
627  }
628  // normalize the bin volume using a reference value
629  binVolume /= data.RefVolume();
630  }
631 
632  const double * x = (useBinVolume) ? xc : x1;
633 
634  if (!useBinIntegral) {
635  fval = func ( x, p );
636  }
637  else {
638  // calculate integral (normalized by bin volume)
639  // need to set function and parameters here in case loop is parallelized
640  fval = igEval( x1, x2) ;
641  }
642  // normalize result if requested according to bin volume
643  if (useBinVolume) fval *= binVolume;
644 
645  // expected errors
646  if (useExpErrors) {
647  // we need first to check if a weight factor needs to be applied
648  // weight = sumw2/sumw = error**2/content
649  double invWeight = y * invError * invError;
650  if (invError == 0) invWeight = (data.SumOfError2() > 0) ? data.SumOfContent()/ data.SumOfError2() : 1.0;
651  // compute expected error as f(x) / weight
652  double invError2 = (fval > 0) ? invWeight / fval : 0.0;
653  invError = std::sqrt(invError2);
654  }
655 
656 
657  double resval = ( y -fval )* invError;
658 
659  // avoid infinities or nan in resval
660  resval = CorrectValue(resval);
661 
662  // estimate gradient
663  if (g != 0) {
664 
665  unsigned int npar = func.NPar();
666  const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func);
667 
668  if (gfunc != 0) {
669  //case function provides gradient
670  if (!useBinIntegral ) {
671  gfunc->ParameterGradient( x , p, g );
672  }
673  else {
674  // needs to calculate the integral for each partial derivative
675  CalculateGradientIntegral( *gfunc, x1, x2, p, g);
676  }
677  }
678  else {
679  SimpleGradientCalculator gc( npar, func);
680  if (!useBinIntegral )
681  gc.ParameterGradient(x, p, fval, g);
682  else {
683  // needs to calculate the integral for each partial derivative
684  CalculateGradientIntegral( gc, x1, x2, p, g);
685  }
686  }
687  // mutiply by - 1 * weight
688  for (unsigned int k = 0; k < npar; ++k) {
689  g[k] *= - invError;
690  if (useBinVolume) g[k] *= binVolume;
691  }
692  }
693 
694  if (useBinVolume) delete [] xc;
695 
696  return resval;
697 
698 }
699 
700 void FitUtil::EvaluateChi2Gradient(const IModelFunction & f, const BinData & data, const double * p, double * grad, unsigned int & nPoints) {
701  // evaluate the gradient of the chi2 function
702  // this function is used when the model function knows how to calculate the derivative and we can
703  // avoid that the minimizer re-computes them
704  //
705  // case of chi2 effective (errors on coordinate) is not supported
706 
707  if ( data.HaveCoordErrors() ) {
708  MATH_ERROR_MSG("FitUtil::EvaluateChi2Residual","Error on the coordinates are not used in calculating Chi2 gradient"); return; // it will assert otherwise later in GetPoint
709  }
710 
711  unsigned int nRejected = 0;
712 
713  const IGradModelFunction * fg = dynamic_cast<const IGradModelFunction *>( &f);
714  assert (fg != 0); // must be called by a gradient function
715 
716  const IGradModelFunction & func = *fg;
717  unsigned int n = data.Size();
718 
719 
720 #ifdef DEBUG
721  std::cout << "\n\nFit data size = " << n << std::endl;
722  std::cout << "evaluate chi2 using function gradient " << &func << " " << p << std::endl;
723 #endif
724 
725  const DataOptions & fitOpt = data.Opt();
726  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
727  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
728 
729  double wrefVolume = 1.0;
730  std::vector<double> xc;
731  if (useBinVolume) {
732  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
733  xc.resize(data.NDim() );
734  }
735 
736  IntegralEvaluator<> igEval( func, p, useBinIntegral);
737 
738  //int nRejected = 0;
739  // set values of parameters
740 
741  unsigned int npar = func.NPar();
742  // assert (npar == NDim() ); // npar MUST be Chi2 dimension
743  std::vector<double> gradFunc( npar );
744  // set all vector values to zero
745  std::vector<double> g( npar);
746 
747  for (unsigned int i = 0; i < n; ++ i) {
748 
749 
750  double y, invError = 0;
751  const double * x1 = data.GetPoint(i,y, invError);
752 
753  double fval = 0;
754  const double * x2 = 0;
755 
756  double binVolume = 1;
757  if (useBinVolume) {
758  unsigned int ndim = data.NDim();
759  x2 = data.BinUpEdge(i);
760  for (unsigned int j = 0; j < ndim; ++j) {
761  binVolume *= std::abs( x2[j]-x1[j] );
762  xc[j] = 0.5*(x2[j]+ x1[j]);
763  }
764  // normalize the bin volume using a reference value
765  binVolume *= wrefVolume;
766  }
767 
768  const double * x = (useBinVolume) ? &xc.front() : x1;
769 
770  if (!useBinIntegral ) {
771  fval = func ( x, p );
772  func.ParameterGradient( x , p, &gradFunc[0] );
773  }
774  else {
775  x2 = data.BinUpEdge(i);
776  // calculate normalized integral and gradient (divided by bin volume)
777  // need to set function and parameters here in case loop is parallelized
778  fval = igEval( x1, x2 ) ;
779  CalculateGradientIntegral( func, x1, x2, p, &gradFunc[0]);
780  }
781  if (useBinVolume) fval *= binVolume;
782 
783 #ifdef DEBUG
784  std::cout << x[0] << " " << y << " " << 1./invError << " params : ";
785  for (unsigned int ipar = 0; ipar < npar; ++ipar)
786  std::cout << p[ipar] << "\t";
787  std::cout << "\tfval = " << fval << std::endl;
788 #endif
789  if ( !CheckValue(fval) ) {
790  nRejected++;
791  continue;
792  }
793 
794  // loop on the parameters
795  unsigned int ipar = 0;
796  for ( ; ipar < npar ; ++ipar) {
797 
798  // correct gradient for bin volumes
799  if (useBinVolume) gradFunc[ipar] *= binVolume;
800 
801  // avoid singularity in the function (infinity and nan ) in the chi2 sum
802  // eventually add possibility of excluding some points (like singularity)
803  double dfval = gradFunc[ipar];
804  if ( !CheckValue(dfval) ) {
805  break; // exit loop on parameters
806  }
807 
808  // calculate derivative point contribution
809  double tmp = - 2.0 * ( y -fval )* invError * invError * gradFunc[ipar];
810  g[ipar] += tmp;
811 
812  }
813 
814  if ( ipar < npar ) {
815  // case loop was broken for an overflow in the gradient calculation
816  nRejected++;
817  continue;
818  }
819 
820 
821  }
822 
823  // correct the number of points
824  nPoints = n;
825  if (nRejected != 0) {
826  assert(nRejected <= n);
827  nPoints = n - nRejected;
828  if (nPoints < npar) MATH_ERROR_MSG("FitUtil::EvaluateChi2Gradient","Error - too many points rejected for overflow in gradient calculation");
829  }
830 
831  // copy result
832  std::copy(g.begin(), g.end(), grad);
833 
834 }
835 
836 //______________________________________________________________________________________________________
837 //
838 // Log Likelihood functions
839 //_______________________________________________________________________________________________________
840 
841 // utility function used by the likelihoods
842 
843 // for LogLikelihood functions
844 
845 double FitUtil::EvaluatePdf(const IModelFunction & func, const UnBinData & data, const double * p, unsigned int i, double * g) {
846  // evaluate the pdf contribution to the generic logl function in case of bin data
847  // return actually the log of the pdf and its derivatives
848 
849 
850  //func.SetParameters(p);
851 
852 
853  const double * x = data.Coords(i);
854  double fval = func ( x, p );
855  double logPdf = ROOT::Math::Util::EvalLog(fval);
856  //return
857  if (g == 0) return logPdf;
858 
859  const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func);
860 
861  // gradient calculation
862  if (gfunc != 0) {
863  //case function provides gradient
864  gfunc->ParameterGradient( x , p, g );
865  }
866  else {
867  // estimate gradieant numerically with simple 2 point rule
868  // should probably calculate gradient of log(pdf) is more stable numerically
869  SimpleGradientCalculator gc(func.NPar(), func);
870  gc.ParameterGradient(x, p, fval, g );
871  }
872  // divide gradient by function value since returning the logs
873  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) {
874  g[ipar] /= fval; // this should be checked against infinities
875  }
876 
877 #ifdef DEBUG
878  std::cout << x[i] << "\t";
879  std::cout << "\tpar = [ " << func.NPar() << " ] = ";
880  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
881  std::cout << p[ipar] << "\t";
882  std::cout << "\tfval = " << fval;
883  std::cout << "\tgrad = [ ";
884  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
885  std::cout << g[ipar] << "\t";
886  std::cout << " ] " << std::endl;
887 #endif
888 
889 
890  return logPdf;
891 }
892 
893 double FitUtil::EvaluateLogL(const IModelFunction & func, const UnBinData & data, const double * p,
894  int iWeight, bool extended, unsigned int &nPoints) {
895  // evaluate the LogLikelihood
896 
897  unsigned int n = data.Size();
898 
899 #ifdef DEBUG
900  std::cout << "\n\nFit data size = " << n << std::endl;
901  std::cout << "func pointer is " << typeid(func).name() << std::endl;
902 #endif
903 
904  double logl = 0;
905  //unsigned int nRejected = 0;
906 
907  // set parameters of the function to cache integral value
908 #ifdef USE_PARAMCACHE
909  (const_cast<IModelFunction &>(func)).SetParameters(p);
910 #endif
911 
912  // this is needed if function must be normalized
913  bool normalizeFunc = false;
914  double norm = 1.0;
915  if (normalizeFunc) {
916  // compute integral of the function
917  std::vector<double> xmin(data.NDim());
918  std::vector<double> xmax(data.NDim());
919  IntegralEvaluator<> igEval( func, p, true);
920  data.Range().GetRange(&xmin[0],&xmax[0]);
921  norm = igEval.Integral(&xmin[0],&xmax[0]);
922  }
923 
924  // needed to compue effective global weight in case of extended likelihood
925  double sumW = 0;
926  double sumW2 = 0;
927 
928  for (unsigned int i = 0; i < n; ++ i) {
929  const double * x = data.Coords(i);
930 #ifdef USE_PARAMCACHE
931  double fval = func ( x );
932 #else
933  double fval = func ( x, p );
934 #endif
935  if (normalizeFunc) fval = fval / norm;
936 
937 #ifdef DEBUG
938  std::cout << "x [ " << data.NDim() << " ] = ";
939  for (unsigned int j = 0; j < data.NDim(); ++j)
940  std::cout << x[j] << "\t";
941  std::cout << "\tpar = [ " << func.NPar() << " ] = ";
942  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
943  std::cout << p[ipar] << "\t";
944  std::cout << "\tfval = " << fval << std::endl;
945 #endif
946  // function EvalLog protects against negative or too small values of fval
947  double logval = ROOT::Math::Util::EvalLog( fval);
948  if (iWeight > 0) {
949  double weight = data.Weight(i);
950  logval *= weight;
951  if (iWeight ==2) {
952  logval *= weight; // use square of weights in likelihood
953  if (extended) {
954  // needed sum of weights and sum of weight square if likelkihood is extended
955  sumW += weight;
956  sumW2 += weight*weight;
957  }
958  }
959  }
960  logl += logval;
961  }
962 
963  if (extended) {
964  // add Poisson extended term
965  double extendedTerm = 0; // extended term in likelihood
966  double nuTot = 0;
967  // nuTot is integral of function in the range
968  // if function has been normalized integral has been already computed
969  if (!normalizeFunc) {
970  IntegralEvaluator<> igEval( func, p, true);
971  std::vector<double> xmin(data.NDim());
972  std::vector<double> xmax(data.NDim());
973  data.Range().GetRange(&xmin[0],&xmax[0]);
974  nuTot = igEval.Integral( &xmin[0], &xmax[0]);
975  // force to be last parameter value
976  //nutot = p[func.NDim()-1];
977  if (iWeight != 2)
978  extendedTerm = - nuTot; // no need to add in this case n log(nu) since is already computed before
979  else {
980  // case use weight square in likelihood : compute total effective weight = sw2/sw
981  // ignore for the moment case when sumW is zero
982  extendedTerm = - (sumW2 / sumW) * nuTot;
983  }
984 
985  }
986  else {
987  nuTot = norm;
988  extendedTerm = - nuTot + double(n) * ROOT::Math::Util::EvalLog( nuTot);
989  // in case of weights need to use here sum of weights (to be done)
990  }
991  logl += extendedTerm;
992 
993 #ifdef DEBUG
994  // for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
995  // std::cout << p[ipar] << "\t";
996  // std::cout << std::endl;
997  std::cout << "fit is extended n = " << n << " nutot " << nuTot << " extended LL term = " << extendedTerm << " logl = " << logl
998  << std::endl;
999 #endif
1000  }
1001 
1002  // reset the number of fitting data points
1003  nPoints = n;
1004 // if (nRejected != 0) {
1005 // assert(nRejected <= n);
1006 // nPoints = n - nRejected;
1007 // if ( nPoints < func.NPar() )
1008 // MATH_ERROR_MSG("FitUtil::EvaluateLogL","Error too many points rejected because of bad pdf values");
1009 
1010 // }
1011 #ifdef DEBUG
1012  std::cout << "Logl = " << logl << " np = " << nPoints << std::endl;
1013 #endif
1014 
1015  return -logl;
1016 }
1017 
1018 void FitUtil::EvaluateLogLGradient(const IModelFunction & f, const UnBinData & data, const double * p, double * grad, unsigned int & ) {
1019  // evaluate the gradient of the log likelihood function
1020 
1021  const IGradModelFunction * fg = dynamic_cast<const IGradModelFunction *>( &f);
1022  assert (fg != 0); // must be called by a grad function
1023  const IGradModelFunction & func = *fg;
1024 
1025  unsigned int n = data.Size();
1026  //int nRejected = 0;
1027 
1028  unsigned int npar = func.NPar();
1029  std::vector<double> gradFunc( npar );
1030  std::vector<double> g( npar);
1031 
1032  for (unsigned int i = 0; i < n; ++ i) {
1033  const double * x = data.Coords(i);
1034  double fval = func ( x , p);
1035  func.ParameterGradient( x, p, &gradFunc[0] );
1036  for (unsigned int kpar = 0; kpar < npar; ++ kpar) {
1037  if (fval > 0)
1038  g[kpar] -= 1./fval * gradFunc[ kpar ];
1039  else if (gradFunc [ kpar] != 0) {
1040  const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
1041  const double kdmax2 = std::numeric_limits<double>::max() / (4*n);
1042  double gg = kdmax1 * gradFunc[ kpar ];
1043  if ( gg > 0) gg = std::min( gg, kdmax2);
1044  else gg = std::max(gg, - kdmax2);
1045  g[kpar] -= gg;
1046  }
1047  // if func derivative is zero term is also zero so do not add in g[kpar]
1048  }
1049 
1050  // copy result
1051  std::copy(g.begin(), g.end(), grad);
1052  }
1053 }
1054 //_________________________________________________________________________________________________
1055 // for binned log likelihood functions
1056 ////////////////////////////////////////////////////////////////////////////////
1057 /// evaluate the pdf (Poisson) contribution to the logl (return actually log of pdf)
1058 /// and its gradient
1059 
1060 double FitUtil::EvaluatePoissonBinPdf(const IModelFunction & func, const BinData & data, const double * p, unsigned int i, double * g ) {
1061  double y = 0;
1062  const double * x1 = data.GetPoint(i,y);
1063 
1064  const DataOptions & fitOpt = data.Opt();
1065  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1066  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1067 
1068  IntegralEvaluator<> igEval( func, p, useBinIntegral);
1069  double fval = 0;
1070  const double * x2 = 0;
1071  // calculate the bin volume
1072  double binVolume = 1;
1073  std::vector<double> xc;
1074  if (useBinVolume) {
1075  unsigned int ndim = data.NDim();
1076  xc.resize(ndim);
1077  x2 = data.BinUpEdge(i);
1078  for (unsigned int j = 0; j < ndim; ++j) {
1079  binVolume *= std::abs( x2[j]-x1[j] );
1080  xc[j] = 0.5*(x2[j]+ x1[j]);
1081  }
1082  // normalize the bin volume using a reference value
1083  binVolume /= data.RefVolume();
1084  }
1085 
1086  const double * x = (useBinVolume) ? &xc.front() : x1;
1087 
1088  if (!useBinIntegral ) {
1089  fval = func ( x, p );
1090  }
1091  else {
1092  // calculate integral normalized (divided by bin volume)
1093  x2 = data.BinUpEdge(i);
1094  fval = igEval( x1, x2 ) ;
1095  }
1096  if (useBinVolume) fval *= binVolume;
1097 
1098  // logPdf for Poisson: ignore constant term depending on N
1099  fval = std::max(fval, 0.0); // avoid negative or too small values
1100  double logPdf = - fval;
1101  if (y > 0.0) {
1102  // include also constants due to saturate model (see Baker-Cousins paper)
1103  logPdf += y * ROOT::Math::Util::EvalLog( fval / y) + y;
1104  }
1105  // need to return the pdf contribution (not the log)
1106 
1107  //double pdfval = std::exp(logPdf);
1108 
1109  //if (g == 0) return pdfval;
1110  if (g == 0) return logPdf;
1111 
1112  unsigned int npar = func.NPar();
1113  const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func);
1114 
1115  // gradient calculation
1116  if (gfunc != 0) {
1117  //case function provides gradient
1118  if (!useBinIntegral )
1119  gfunc->ParameterGradient( x , p, g );
1120  else {
1121  // needs to calculate the integral for each partial derivative
1122  CalculateGradientIntegral( *gfunc, x1, x2, p, g);
1123  }
1124 
1125  }
1126  else {
1127  SimpleGradientCalculator gc(func.NPar(), func);
1128  if (!useBinIntegral )
1129  gc.ParameterGradient(x, p, fval, g);
1130  else {
1131  // needs to calculate the integral for each partial derivative
1132  CalculateGradientIntegral( gc, x1, x2, p, g);
1133  }
1134  }
1135  // correct g[] do be derivative of poisson term
1136  for (unsigned int k = 0; k < npar; ++k) {
1137  // apply bin volume correction
1138  if (useBinVolume) g[k] *= binVolume;
1139 
1140  // correct for Poisson term
1141  if ( fval > 0)
1142  g[k] *= ( y/fval - 1.) ;//* pdfval;
1143  else if (y > 0) {
1144  const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
1145  g[k] *= kdmax1;
1146  }
1147  else // y == 0 cannot have negative y
1148  g[k] *= -1;
1149  }
1150 
1151 
1152 #ifdef DEBUG
1153  std::cout << "x = " << x[0] << " logPdf = " << logPdf << " grad";
1154  for (unsigned int ipar = 0; ipar < npar; ++ipar)
1155  std::cout << g[ipar] << "\t";
1156  std::cout << std::endl;
1157 #endif
1158 
1159 // return pdfval;
1160  return logPdf;
1161 }
1162 
1164  const double * p, int iWeight, bool extended, unsigned int & nPoints ) {
1165  // evaluate the Poisson Log Likelihood
1166  // for binned likelihood fits
1167  // this is Sum ( f(x_i) - y_i * log( f (x_i) ) )
1168  // add as well constant term for saturated model to make it like a Chi2/2
1169  // by default is etended. If extended is false the fit is not extended and
1170  // the global poisson term is removed (i.e is a binomial fit)
1171  // (remember that in this case one needs to have a function with a fixed normalization
1172  // like in a non extended binned fit)
1173  //
1174  // if use Weight use a weighted dataset
1175  // iWeight = 1 ==> logL = Sum( w f(x_i) )
1176  // case of iWeight==1 is actually identical to weight==0
1177  // iWeight = 2 ==> logL = Sum( w*w * f(x_i) )
1178  //
1179  // nPoints returns the points where bin content is not zero
1180 
1181 
1182  unsigned int n = data.Size();
1183 
1184 #ifdef USE_PARAMCACHE
1185  (const_cast<IModelFunction &>(func)).SetParameters(p);
1186 #endif
1187 
1188  double nloglike = 0; // negative loglikelihood
1189  nPoints = 0; // npoints
1190 
1191 
1192  // get fit option and check case of using integral of bins
1193  const DataOptions & fitOpt = data.Opt();
1194  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1195  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1196  bool useW2 = (iWeight == 2);
1197 
1198  // normalize if needed by a reference volume value
1199  double wrefVolume = 1.0;
1200  std::vector<double> xc;
1201  if (useBinVolume) {
1202  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
1203  xc.resize(data.NDim() );
1204  }
1205 
1206 #ifdef DEBUG
1207  std::cout << "Evaluate PoissonLogL for params = [ ";
1208  for (unsigned int j=0; j < func.NPar(); ++j) std::cout << p[j] << " , ";
1209  std::cout << "] - data size = " << n << " useBinIntegral " << useBinIntegral << " useBinVolume "
1210  << useBinVolume << " useW2 " << useW2 << " wrefVolume = " << wrefVolume << std::endl;
1211 #endif
1212 
1213 #ifdef USE_PARAMCACHE
1214  IntegralEvaluator<> igEval( func, 0, useBinIntegral);
1215 #else
1216  IntegralEvaluator<> igEval( func, p, useBinIntegral);
1217 #endif
1218  // double nuTot = 0; // total number of expected events (needed for non-extended fits)
1219  // double wTot = 0; // sum of all weights
1220  // double w2Tot = 0; // sum of weight squared (these are needed for useW2)
1221 
1222 
1223  for (unsigned int i = 0; i < n; ++ i) {
1224  const double * x1 = data.Coords(i);
1225  double y = data.Value(i);
1226 
1227  double fval = 0;
1228  double binVolume = 1.0;
1229 
1230  if (useBinVolume) {
1231  unsigned int ndim = data.NDim();
1232  const double * x2 = data.BinUpEdge(i);
1233  for (unsigned int j = 0; j < ndim; ++j) {
1234  binVolume *= std::abs( x2[j]-x1[j] );
1235  xc[j] = 0.5*(x2[j]+ x1[j]);
1236  }
1237  // normalize the bin volume using a reference value
1238  binVolume *= wrefVolume;
1239  }
1240 
1241  const double * x = (useBinVolume) ? &xc.front() : x1;
1242 
1243  if (!useBinIntegral) {
1244 #ifdef USE_PARAMCACHE
1245  fval = func ( x );
1246 #else
1247  fval = func ( x, p );
1248 #endif
1249  }
1250  else {
1251  // calculate integral (normalized by bin volume)
1252  // need to set function and parameters here in case loop is parallelized
1253  fval = igEval( x1, data.BinUpEdge(i)) ;
1254  }
1255  if (useBinVolume) fval *= binVolume;
1256 
1257 
1258 
1259 #ifdef DEBUG
1260  int NSAMPLE = 100;
1261  if (i%NSAMPLE == 0) {
1262  std::cout << "evt " << i << " x1 = [ ";
1263  for (unsigned int j=0; j < func.NDim(); ++j) std::cout << x[j] << " , ";
1264  std::cout << "] ";
1265  if (fitOpt.fIntegral) {
1266  std::cout << "x2 = [ ";
1267  for (unsigned int j=0; j < func.NDim(); ++j) std::cout << data.BinUpEdge(i)[j] << " , ";
1268  std::cout << "] ";
1269  }
1270  std::cout << " y = " << y << " fval = " << fval << std::endl;
1271  }
1272 #endif
1273 
1274 
1275  // EvalLog protects against 0 values of fval but don't want to add in the -log sum
1276  // negative values of fval
1277  fval = std::max(fval, 0.0);
1278 
1279 
1280  double tmp = 0;
1281  if (useW2) {
1282  // apply weight correction . Effective weight is error^2/ y
1283  // and expected events in bins is fval/weight
1284  // can apply correction only when y is not zero otherwise weight is undefined
1285  // (in case of weighted likelihood I don't care about the constant term due to
1286  // the saturated model)
1287  if (y != 0) {
1288  double error = data.Error(i);
1289  double weight = (error*error)/y; // this is the bin effective weight
1290  if (extended) {
1291  tmp = fval * weight;
1292  // wTot += weight;
1293  // w2Tot += weight*weight;
1294  }
1295  tmp -= weight * y * ROOT::Math::Util::EvalLog( fval);
1296  }
1297 
1298  // need to compute total weight and weight-square
1299  // if (extended ) {
1300  // nuTot += fval;
1301  // }
1302 
1303  }
1304  else {
1305  // standard case no weights or iWeight=1
1306  // this is needed for Poisson likelihood (which are extened and not for multinomial)
1307  // the formula below include constant term due to likelihood of saturated model (f(x) = y)
1308  // (same formula as in Baker-Cousins paper, page 439 except a factor of 2
1309  if (extended) tmp = fval -y ;
1310  if (y > 0) {
1311  tmp += y * (ROOT::Math::Util::EvalLog( y) - ROOT::Math::Util::EvalLog(fval));
1312  nPoints++;
1313  }
1314  }
1315 
1316 
1317  nloglike += tmp;
1318  }
1319 
1320  // if (notExtended) {
1321  // // not extended : remove from the Likelihood the global Poisson term
1322  // if (!useW2)
1323  // nloglike -= nuTot - yTot * ROOT::Math::Util::EvalLog( nuTot);
1324  // else {
1325  // // this needs to be checked
1326  // nloglike -= wTot* nuTot - wTot* yTot * ROOT::Math::Util::EvalLog( nuTot);
1327  // }
1328 
1329  // }
1330 
1331  // if (extended && useW2) {
1332  // // effective total weight is total sum of weight square / sum of weights
1333  // //nloglike += (w2Tot/wTot) * nuTot;
1334  // }
1335 
1336 
1337 #ifdef DEBUG
1338  std::cout << "Loglikelihood = " << nloglike << std::endl;
1339 #endif
1340 
1341  return nloglike;
1342 }
1343 
1344 void FitUtil::EvaluatePoissonLogLGradient(const IModelFunction & f, const BinData & data, const double * p, double * grad ) {
1345  // evaluate the gradient of the Poisson log likelihood function
1346 
1347  const IGradModelFunction * fg = dynamic_cast<const IGradModelFunction *>( &f);
1348  assert (fg != 0); // must be called by a grad function
1349  const IGradModelFunction & func = *fg;
1350 
1351  unsigned int n = data.Size();
1352 
1353  const DataOptions & fitOpt = data.Opt();
1354  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1355  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1356 
1357  double wrefVolume = 1.0;
1358  std::vector<double> xc;
1359  if (useBinVolume) {
1360  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
1361  xc.resize(data.NDim() );
1362  }
1363 
1364  IntegralEvaluator<> igEval( func, p, useBinIntegral);
1365 
1366  unsigned int npar = func.NPar();
1367  std::vector<double> gradFunc( npar );
1368  std::vector<double> g( npar);
1369 
1370  for (unsigned int i = 0; i < n; ++ i) {
1371  const double * x1 = data.Coords(i);
1372  double y = data.Value(i);
1373  double fval = 0;
1374  const double * x2 = 0;
1375 
1376  double binVolume = 1.0;
1377  if (useBinVolume) {
1378  x2 = data.BinUpEdge(i);
1379  unsigned int ndim = data.NDim();
1380  for (unsigned int j = 0; j < ndim; ++j) {
1381  binVolume *= std::abs( x2[j]-x1[j] );
1382  xc[j] = 0.5*(x2[j]+ x1[j]);
1383  }
1384  // normalize the bin volume using a reference value
1385  binVolume *= wrefVolume;
1386  }
1387 
1388  const double * x = (useBinVolume) ? &xc.front() : x1;
1389 
1390  if (!useBinIntegral) {
1391  fval = func ( x, p );
1392  func.ParameterGradient( x , p, &gradFunc[0] );
1393  }
1394  else {
1395  // calculate integral (normalized by bin volume)
1396  // need to set function and parameters here in case loop is parallelized
1397  x2 = data.BinUpEdge(i);
1398  fval = igEval( x1, x2) ;
1399  CalculateGradientIntegral( func, x1, x2, p, &gradFunc[0]);
1400  }
1401  if (useBinVolume) fval *= binVolume;
1402 
1403  // correct the gradient
1404  for (unsigned int kpar = 0; kpar < npar; ++ kpar) {
1405 
1406  // correct gradient for bin volumes
1407  if (useBinVolume) gradFunc[kpar] *= binVolume;
1408 
1409  // df/dp * (1. - y/f )
1410  if (fval > 0)
1411  g[kpar] += gradFunc[ kpar ] * ( 1. - y/fval );
1412  else if (gradFunc [ kpar] != 0) {
1413  const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
1414  const double kdmax2 = std::numeric_limits<double>::max() / (4*n);
1415  double gg = kdmax1 * gradFunc[ kpar ];
1416  if ( gg > 0) gg = std::min( gg, kdmax2);
1417  else gg = std::max(gg, - kdmax2);
1418  g[kpar] -= gg;
1419  }
1420  }
1421 
1422  // copy result
1423  std::copy(g.begin(), g.end(), grad);
1424  }
1425 }
1426 
1427 }
1428 
1429 } // end namespace ROOT
1430 
unsigned int Size() const
return number of fit points
Definition: BinData.h:447
bool CheckValue(double &rval)
Definition: FitUtil.cxx:294
float xmin
Definition: THbookFile.cxx:93
Interface (abstract class) for generic functions objects of one-dimension Provides a method to evalua...
Definition: IFunction.h:133
bool HaveAsymErrors() const
flag to control if data provides asymmetric errors on the value
Definition: BinData.h:174
unsigned int NDim() const
return coordinate data dimension
Definition: UnBinData.h:321
bool HasBinEdges() const
query if the data store the bin edges instead of the center
Definition: BinData.h:477
void EvaluateChi2Gradient(const IModelFunction &func, const BinData &data, const double *x, double *grad, unsigned int &nPoints)
evaluate the Chi2 gradient given a model function and the data at the point x.
Definition: FitUtil.cxx:700
This namespace contains pre-defined functions to be used in conjuction with TExecutor::Map and TExecu...
Definition: StringConv.hxx:21
double EvaluatePoissonBinPdf(const IModelFunction &func, const BinData &data, const double *x, unsigned int ipoint, double *g=0)
evaluate the pdf contribution to the Poisson LogL given a model function and the BinPoint data...
Definition: FitUtil.cxx:1060
void CalculateGradientIntegral(const GFunc &gfunc, const double *x1, const double *x2, const double *p, double *g)
Definition: FitUtil.cxx:314
ErrorType GetErrorType() const
Definition: BinData.h:75
TH1 * h
Definition: legend2.C:5
Class describing the unbinned data sets (just x coordinates values) of any dimensions.
Definition: UnBinData.h:47
double SumOfError2() const
compute the total sum of the error square (sum of weight square in case of a weighted data set) ...
Definition: BinData.h:510
const double * GetPoint(unsigned int ipoint, double &value) const
retrieve at the same time a pointer to the coordinate data and the fit value More efficient than call...
Definition: BinData.h:304
double sqrt(double)
static const double x2[5]
void EvaluatePoissonLogLGradient(const IModelFunction &func, const BinData &data, const double *x, double *grad)
evaluate the Poisson LogL given a model function and the data at the point x.
Definition: FitUtil.cxx:1344
void SetParameters(TFitEditor::FuncParams_t &pars, TF1 *func)
Restore the parameters from pars into the function.
Definition: TFitEditor.cxx:287
Double_t x[n]
Definition: legend1.C:17
OneDimMultiFunctionAdapter class to wrap a multidimensional function in one dimensional one...
double RefVolume() const
retrieve the reference volume used to normalize the data when the option bin volume is set ...
Definition: BinData.h:492
double Derivative1(double x)
Returns the first derivative of the function at point x, computed by Richardson&#39;s extrapolation metho...
void EvaluateLogLGradient(const IModelFunction &func, const UnBinData &data, const double *x, double *grad, unsigned int &nPoints)
evaluate the LogL gradient given a model function and the data at the point x.
Definition: FitUtil.cxx:1018
#define F1(x, y, z)
Definition: TMD5.cxx:267
double Error(unsigned int ipoint) const
return error on the value for the given fit point Safe (but slower) method returning correctly the er...
Definition: BinData.h:249
#define MATH_ERROR_MSG(loc, str)
Definition: Error.h:50
const double * BinUpEdge(unsigned int icoord) const
return an array containing the upper edge of the bin for coordinate i In case of empty bin they could...
Definition: BinData.h:469
virtual void ParameterGradient(const double *x, const double *p, double *grad) const
Evaluate the all the derivatives (gradient vector) of the function with respect to the parameters at ...
virtual unsigned int NPar() const =0
Return the number of Parameters.
double Weight(unsigned int ipoint) const
Definition: UnBinData.h:293
DataOptions : simple structure holding the options on how the data are filled.
Definition: DataOptions.h:28
IParamFunction interface (abstract class) describing multi-dimensional parameteric functions It is a ...
double EvaluateChi2Effective(const IModelFunction &func, const BinData &data, const double *x, unsigned int &nPoints)
evaluate the effective Chi2 given a model function and the data at the point x.
Definition: FitUtil.cxx:474
virtual unsigned int NDim() const =0
Retrieve the dimension of the function.
double EvaluateLogL(const IModelFunction &func, const UnBinData &data, const double *x, int iWeight, bool extended, unsigned int &nPoints)
evaluate the LogL given a model function and the data at the point x.
Definition: FitUtil.cxx:893
double EvaluateChi2Residual(const IModelFunction &func, const BinData &data, const double *x, unsigned int ipoint, double *g=0)
Parallel evaluate the Chi2 given a model function and the data at the point x.
Definition: FitUtil.cxx:595
const double * Coords(unsigned int ipoint) const
return a pointer to the coordinates data for the given fit point
Definition: BinData.h:226
User Class for performing numerical integration of a function in one dimension.
Definition: Integrator.h:102
Double_t E()
Definition: TMath.h:54
const DataOptions & Opt() const
access to options
Definition: DataVector.h:97
Class describing the binned data sets : vectors of x coordinates, y values and optionally error on y ...
Definition: BinData.h:61
float xmax
Definition: THbookFile.cxx:93
double EvaluatePoissonLogL(const IModelFunction &func, const BinData &data, const double *x, int iWeight, bool extended, unsigned int &nPoints)
evaluate the Poisson LogL given a model function and the data at the point x.
Definition: FitUtil.cxx:1163
bool HaveCoordErrors() const
flag to control if data provides error on the coordinates
Definition: BinData.h:166
ROOT::Math::IParamMultiFunction IModelFunction
Definition: FitUtil.h:40
double EvaluateChi2(const IModelFunction &func, const BinData &data, const double *x, unsigned int &nPoints)
Chi2 Functions.
Definition: FitUtil.cxx:338
double CorrectValue(double rval)
Definition: FitUtil.cxx:283
const double * Coords(unsigned int ipoint) const
return pointer to coordinate data
Definition: UnBinData.h:282
static const double x1[5]
double f(double x)
TFitResultPtr Fit(FitObject *h1, TF1 *f1, Foption_t &option, const ROOT::Math::MinimizerOptions &moption, const char *goption, ROOT::Fit::DataRange &range)
Definition: HFitImpl.cxx:134
Interface (abstract class) for parametric gradient multi-dimensional functions providing in addition ...
double EvaluatePdf(const IModelFunction &func, const UnBinData &data, const double *x, unsigned int ipoint, double *g=0)
evaluate the pdf contribution to the LogL given a model function and the BinPoint data...
Definition: FitUtil.cxx:845
Double_t y[n]
Definition: legend1.C:17
double func(double *x, double *p)
Definition: stressTF1.cxx:213
Double_t ey[n]
Definition: legend1.C:17
void GetRange(unsigned int icoord, double &xmin, double &xmax) const
get the first range for given coordinate.
Definition: DataRange.h:103
double Value(unsigned int ipoint) const
return the value for the given fit point
Definition: BinData.h:236
double SumOfContent() const
compute the total sum of the data content (sum of weights in cse of weighted data set) ...
Definition: BinData.h:504
unsigned int NDim() const
return coordinate data dimension
Definition: BinData.h:452
double EvalLog(double x)
safe evaluation of log(x) with a protections against negative or zero argument to the log smooth line...
Definition: Util.h:55
User class for performing multidimensional integration.
double f2(const double *x)
const double * GetPointError(unsigned int ipoint, double &errvalue) const
Retrieve the errors on the point (coordinate and value) for the given fit point It must be called onl...
Definition: BinData.h:349
TF1 * f1
Definition: legend1.C:11
const DataRange & Range() const
access to range
Definition: DataVector.h:103
const int NPar
Documentation for the abstract class IBaseFunctionMultiDim.
Definition: IFunction.h:63
double norm(double *x, double *p)
Definition: unuranDistr.cxx:40
Double_t ex[n]
Definition: legend1.C:17
const Int_t n
Definition: legend1.C:16
Template class to wrap any member function of a class taking a double and returning a double in a 1D ...
unsigned int Size() const
return number of contained points
Definition: UnBinData.h:316
User class for calculating the derivatives of a function.
char name[80]
Definition: TGX11.cxx:109