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