ROOT  6.06/08
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 
unsigned int Size() const
return number of fit points
Definition: BinData.h:447
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
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:698
Namespace for new ROOT classes and functions.
Definition: ROOT.py:1
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
ErrorType GetErrorType() const
Definition: BinData.h:75
#define assert(cond)
Definition: unittest.h:542
TH1 * h
Definition: legend2.C:5
Class describing the unbinned data sets (just x coordinates values) of any dimensions.
Definition: UnBinData.h:47
TRObject operator()(const T1 &t1) const
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: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 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:1016
#define F1(x, y, z)
Definition: TMD5.cxx:265
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:472
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
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:1161
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:336
double CorrectValue(double rval)
Definition: FitUtil.cxx:281
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: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
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
static Vc_ALWAYS_INLINE int_v max(const int_v &x, const int_v &y)
Definition: vector.h:440
#define name(a, b)
Definition: linkTestLib0.cpp:5
Binding & operator=(OUT(*fun)(void))
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.