Logo ROOT   6.13/01
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 
18 #include "Math/IFunctionfwd.h"
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 <numeric>
34 //#include <memory>
35 
36 #include "TROOT.h"
37 
38 //#define DEBUG
39 #ifdef DEBUG
40 #define NSAMPLE 10
41 #include <iostream>
42 #endif
43 
44 // need to implement integral option
45 
46 namespace ROOT {
47 
48  namespace Fit {
49 
50  namespace FitUtil {
51 
52  // derivative with respect of the parameter to be integrated
53  template<class GradFunc = IGradModelFunction>
54  struct ParamDerivFunc {
55  ParamDerivFunc(const GradFunc & f) : fFunc(f), fIpar(0) {}
56  void SetDerivComponent(unsigned int ipar) { fIpar = ipar; }
57  double operator() (const double *x, const double *p) const {
58  return fFunc.ParameterDerivative( x, p, fIpar );
59  }
60  unsigned int NDim() const { return fFunc.NDim(); }
61  const GradFunc & fFunc;
62  unsigned int fIpar;
63  };
64 
65 // simple gradient calculator using the 2 points rule
66 
67  class SimpleGradientCalculator {
68 
69  public:
70  // construct from function and gradient dimension gdim
71  // gdim = npar for parameter gradient
72  // gdim = ndim for coordinate gradients
73  // construct (the param values will be passed later)
74  // one can choose between 2 points rule (1 extra evaluation) istrat=1
75  // or two point rule (2 extra evaluation)
76  // (found 2 points rule does not work correctly - minuit2FitBench fails)
77  SimpleGradientCalculator(int gdim, const IModelFunction & func,double eps = 2.E-8, int istrat = 1) :
78  fEps(eps),
79  fPrecision(1.E-8 ), // sqrt(epsilon)
80  fStrategy(istrat),
81  fN(gdim ),
82  fFunc(func),
83  fVec(std::vector<double>(gdim) ) // this can be probably optimized
84  {}
85 
86  // internal method to calculate single partial derivative
87  // assume cached vector fVec is already set
88  double DoParameterDerivative(const double *x, const double *p, double f0, int k) const {
89  double p0 = p[k];
90  double h = std::max( fEps* std::abs(p0), 8.0*fPrecision*(std::abs(p0) + fPrecision) );
91  fVec[k] += h;
92  double deriv = 0;
93  // t.b.d : treat case of infinities
94  //if (fval > - std::numeric_limits<double>::max() && fval < std::numeric_limits<double>::max() )
95  double f1 = fFunc(x, &fVec.front() );
96  if (fStrategy > 1) {
97  fVec[k] = p0 - h;
98  double f2 = fFunc(x, &fVec.front() );
99  deriv = 0.5 * ( f2 - f1 )/h;
100  }
101  else
102  deriv = ( f1 - f0 )/h;
103 
104  fVec[k] = p[k]; // restore original p value
105  return deriv;
106  }
107  // number of dimension in x (needed when calculating the integrals)
108  unsigned int NDim() const {
109  return fFunc.NDim();
110  }
111  // number of parameters (needed for grad ccalculation)
112  unsigned int NPar() const {
113  return fFunc.NPar();
114  }
115 
116  double ParameterDerivative(const double *x, const double *p, int ipar) const {
117  // fVec are the cached parameter values
118  std::copy(p, p+fN, fVec.begin());
119  double f0 = fFunc(x, p);
120  return DoParameterDerivative(x,p,f0,ipar);
121  }
122 
123  // calculate all gradient at point (x,p) knnowing already value f0 (we gain a function eval.)
124  void ParameterGradient(const double * x, const double * p, double f0, double * g) {
125  // fVec are the cached parameter values
126  std::copy(p, p+fN, fVec.begin());
127  for (unsigned int k = 0; k < fN; ++k) {
128  g[k] = DoParameterDerivative(x,p,f0,k);
129  }
130  }
131 
132  // calculate gradient w.r coordinate values
133  void Gradient(const double * x, const double * p, double f0, double * g) {
134  // fVec are the cached coordinate values
135  std::copy(x, x+fN, fVec.begin());
136  for (unsigned int k = 0; k < fN; ++k) {
137  double x0 = x[k];
138  double h = std::max( fEps* std::abs(x0), 8.0*fPrecision*(std::abs(x0) + fPrecision) );
139  fVec[k] += h;
140  // t.b.d : treat case of infinities
141  //if (fval > - std::numeric_limits<double>::max() && fval < std::numeric_limits<double>::max() )
142  double f1 = fFunc( &fVec.front(), p );
143  if (fStrategy > 1) {
144  fVec[k] = x0 - h;
145  double f2 = fFunc( &fVec.front(), p );
146  g[k] = 0.5 * ( f2 - f1 )/h;
147  }
148  else
149  g[k] = ( f1 - f0 )/h;
150 
151  fVec[k] = x[k]; // restore original x value
152  }
153  }
154 
155  private:
156 
157  double fEps;
158  double fPrecision;
159  int fStrategy; // strategy in calculation ( =1 use 2 point rule( 1 extra func) , = 2 use r point rule)
160  unsigned int fN; // gradient dimension
161  const IModelFunction & fFunc;
162  mutable std::vector<double> fVec; // cached coordinates (or parameter values in case of gradientpar)
163  };
164 
165 
166  // function to avoid infinities or nan
167  double CorrectValue(double rval) {
168  // avoid infinities or nan in rval
169  if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() )
170  return rval;
171  else if (rval < 0)
172  // case -inf
173  return -std::numeric_limits<double>::max();
174  else
175  // case + inf or nan
176  return + std::numeric_limits<double>::max();
177  }
178 
179  // Check if the value is a finite number. The argument rval is updated if it is infinite or NaN,
180  // setting it to the maximum finite value (preserving the sign).
181  bool CheckInfNaNValue(double &rval)
182  {
183  if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() )
184  return true;
185  else if (rval < 0) {
186  // case -inf
187  rval = -std::numeric_limits<double>::max();
188  return false;
189  }
190  else {
191  // case + inf or nan
192  rval = + std::numeric_limits<double>::max();
193  return false;
194  }
195  }
196 
197 
198  // calculation of the integral of the gradient functions
199  // for a function providing derivative w.r.t parameters
200  // x1 and x2 defines the integration interval , p the parameters
201  template <class GFunc>
202  void CalculateGradientIntegral(const GFunc & gfunc,
203  const double *x1, const double * x2, const double * p, double *g) {
204 
205  // needs to calculate the integral for each partial derivative
206  ParamDerivFunc<GFunc> pfunc( gfunc);
207  IntegralEvaluator<ParamDerivFunc<GFunc> > igDerEval( pfunc, p, true);
208  // loop on the parameters
209  unsigned int npar = gfunc.NPar();
210  for (unsigned int k = 0; k < npar; ++k ) {
211  pfunc.SetDerivComponent(k);
212  g[k] = igDerEval( x1, x2 );
213  }
214  }
215 
216 
217 
218  } // end namespace FitUtil
219 
220 
221 
222 //___________________________________________________________________________________________________________________________
223 // for chi2 functions
224 //___________________________________________________________________________________________________________________________
225 
226  double FitUtil::EvaluateChi2(const IModelFunction &func, const BinData &data, const double *p, unsigned int &,
227  ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks)
228  {
229  // evaluate the chi2 given a function reference , the data and returns the value and also in nPoints
230  // the actual number of used points
231  // normal chi2 using only error on values (from fitting histogram)
232  // optionally the integral of function in the bin is used
233 
234  unsigned int n = data.Size();
235 
236  // set parameters of the function to cache integral value
237 #ifdef USE_PARAMCACHE
238  (const_cast<IModelFunction &>(func)).SetParameters(p);
239 #endif
240  // do not cache parameter values (it is not thread safe)
241  //func.SetParameters(p);
242 
243 
244  // get fit option and check case if using integral of bins
245  const DataOptions & fitOpt = data.Opt();
246  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
247  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
248  bool useExpErrors = (fitOpt.fExpErrors);
249  bool isWeighted = data.IsWeighted();
250 
251 #ifdef DEBUG
252  std::cout << "\n\nFit data size = " << n << std::endl;
253  std::cout << "evaluate chi2 using function " << &func << " " << p << std::endl;
254  std::cout << "use empty bins " << fitOpt.fUseEmpty << std::endl;
255  std::cout << "use integral " << fitOpt.fIntegral << std::endl;
256  std::cout << "use all error=1 " << fitOpt.fErrors1 << std::endl;
257  if (isWeighted) std::cout << "Weighted data set - sumw = " << data.SumOfContent() << " sumw2 = " << data.SumOfError2() << std::endl;
258 #endif
259 
260 #ifdef USE_PARAMCACHE
261  IntegralEvaluator<> igEval( func, 0, useBinIntegral);
262 #else
263  IntegralEvaluator<> igEval( func, p, useBinIntegral);
264 #endif
265  double maxResValue = std::numeric_limits<double>::max() /n;
266  double wrefVolume = 1.0;
267  if (useBinVolume) {
268  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
269  }
270 
271  (const_cast<IModelFunction &>(func)).SetParameters(p);
272 
273  auto mapFunction = [&](const unsigned i){
274 
275  double chi2{};
276  double fval{};
277 
278  const auto x1 = data.GetCoordComponent(i, 0);
279  const auto y = data.Value(i);
280  auto invError = data.InvError(i);
281 
282  //invError = (invError!= 0.0) ? 1.0/invError :1;
283 
284  const double * x = nullptr;
285  std::vector<double> xc;
286  double binVolume = 1.0;
287  if (useBinVolume) {
288  unsigned int ndim = data.NDim();
289  const double * x2 = data.BinUpEdge(i);
290  xc.resize(data.NDim());
291  for (unsigned int j = 0; j < ndim; ++j) {
292  auto xx = *data.GetCoordComponent(i, j);
293  binVolume *= std::abs(x2[j]- xx);
294  xc[j] = 0.5*(x2[j]+ xx);
295  }
296  x = xc.data();
297  // normalize the bin volume using a reference value
298  binVolume *= wrefVolume;
299  } else if(data.NDim() > 1) {
300  xc.resize(data.NDim());
301  xc[0] = *x1;
302  for (unsigned int j = 1; j < data.NDim(); ++j)
303  xc[j] = *data.GetCoordComponent(i, j);
304  x = xc.data();
305  } else {
306  x = x1;
307  }
308 
309 
310  if (!useBinIntegral) {
311 #ifdef USE_PARAMCACHE
312  fval = func ( x );
313 #else
314  fval = func ( x, p );
315 #endif
316  }
317  else {
318  // calculate integral normalized by bin volume
319  // need to set function and parameters here in case loop is parallelized
320  fval = igEval( x, data.BinUpEdge(i)) ;
321  }
322  // normalize result if requested according to bin volume
323  if (useBinVolume) fval *= binVolume;
324 
325  // expected errors
326  if (useExpErrors) {
327  double invWeight = 1.0;
328  if (isWeighted) {
329  // we need first to check if a weight factor needs to be applied
330  // weight = sumw2/sumw = error**2/content
331  //invWeight = y * invError * invError;
332  // we use always the global weight and not the observed one in the bin
333  // for empty bins use global weight (if it is weighted data.SumError2() is not zero)
334  invWeight = data.SumOfContent()/ data.SumOfError2();
335  //if (invError > 0) invWeight = y * invError * invError;
336  }
337 
338  // if (invError == 0) invWeight = (data.SumOfError2() > 0) ? data.SumOfContent()/ data.SumOfError2() : 1.0;
339  // compute expected error as f(x) / weight
340  double invError2 = (fval > 0) ? invWeight / fval : 0.0;
341  invError = std::sqrt(invError2);
342  //std::cout << "using Pearson chi2 " << x[0] << " " << 1./invError2 << " " << fval << std::endl;
343  }
344 
345 //#define DEBUG
346 #ifdef DEBUG
347  std::cout << x[0] << " " << y << " " << 1./invError << " params : ";
348  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
349  std::cout << p[ipar] << "\t";
350  std::cout << "\tfval = " << fval << " bin volume " << binVolume << " ref " << wrefVolume << std::endl;
351 #endif
352 //#undef DEBUG
353 
354  if (invError > 0) {
355 
356  double tmp = ( y -fval )* invError;
357  double resval = tmp * tmp;
358 
359 
360  // avoid inifinity or nan in chi2 values due to wrong function values
361  if ( resval < maxResValue )
362  chi2 += resval;
363  else {
364  //nRejected++;
365  chi2 += maxResValue;
366  }
367  }
368  return chi2;
369  };
370 
371 #ifdef R__USE_IMT
372  auto redFunction = [](const std::vector<double> & objs){
373  return std::accumulate(objs.begin(), objs.end(), double{});
374  };
375 #else
376  (void)nChunks;
377 
378  // If IMT is disabled, force the execution policy to the serial case
379  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
380  Warning("FitUtil::EvaluateChi2", "Multithread execution policy requires IMT, which is disabled. Changing "
381  "to ROOT::Fit::ExecutionPolicy::kSerial.");
382  executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
383  }
384 #endif
385 
386  double res{};
387  if(executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial){
388  for (unsigned int i=0; i<n; ++i) {
389  res += mapFunction(i);
390  }
391 #ifdef R__USE_IMT
392  } else if(executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
393  auto chunks = nChunks !=0? nChunks: setAutomaticChunking(data.Size());
394  ROOT::TThreadExecutor pool;
395  res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction, chunks);
396 #endif
397 // } else if(executionPolicy == ROOT::Fit::kMultitProcess){
398  // ROOT::TProcessExecutor pool;
399  // res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction);
400  } else{
401  Error("FitUtil::EvaluateChi2","Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
402  }
403 
404  return res;
405 }
406 
407 
408 //___________________________________________________________________________________________________________________________
409 
410 double FitUtil::EvaluateChi2Effective(const IModelFunction & func, const BinData & data, const double * p, unsigned int & nPoints) {
411  // evaluate the chi2 given a function reference , the data and returns the value and also in nPoints
412  // the actual number of used points
413  // method using the error in the coordinates
414  // integral of bin does not make sense in this case
415 
416  unsigned int n = data.Size();
417 
418 #ifdef DEBUG
419  std::cout << "\n\nFit data size = " << n << std::endl;
420  std::cout << "evaluate effective chi2 using function " << &func << " " << p << std::endl;
421 #endif
422 
423  assert(data.HaveCoordErrors() || data.HaveAsymErrors());
424 
425  double chi2 = 0;
426  //int nRejected = 0;
427 
428 
429  //func.SetParameters(p);
430 
431  unsigned int ndim = func.NDim();
432 
433  // use Richardson derivator
435 
436  double maxResValue = std::numeric_limits<double>::max() /n;
437 
438 
439 
440  for (unsigned int i = 0; i < n; ++ i) {
441 
442 
443  double y = 0;
444  const double * x = data.GetPoint(i,y);
445 
446  double fval = func( x, p );
447 
448  double delta_y_func = y - fval;
449 
450 
451  double ey = 0;
452  const double * ex = 0;
453  if (!data.HaveAsymErrors() )
454  ex = data.GetPointError(i, ey);
455  else {
456  double eylow, eyhigh = 0;
457  ex = data.GetPointError(i, eylow, eyhigh);
458  if ( delta_y_func < 0)
459  ey = eyhigh; // function is higher than points
460  else
461  ey = eylow;
462  }
463  double e2 = ey * ey;
464  // before calculating the gradient check that all error in x are not zero
465  unsigned int j = 0;
466  while ( j < ndim && ex[j] == 0.) { j++; }
467  // if j is less ndim some elements are not zero
468  if (j < ndim) {
469  // need an adapter from a multi-dim function to a one-dimensional
471  // select optimal step size (use 10--2 by default as was done in TF1:
472  double kEps = 0.01;
473  double kPrecision = 1.E-8;
474  for (unsigned int icoord = 0; icoord < ndim; ++icoord) {
475  // calculate derivative for each coordinate
476  if (ex[icoord] > 0) {
477  //gradCalc.Gradient(x, p, fval, &grad[0]);
478  f1D.SetCoord(icoord);
479  // optimal spep size (take ex[] as scale for the points and 1% of it
480  double x0= x[icoord];
481  double h = std::max( kEps* std::abs(ex[icoord]), 8.0*kPrecision*(std::abs(x0) + kPrecision) );
482  double deriv = derivator.Derivative1(f1D, x[icoord], h);
483  double edx = ex[icoord] * deriv;
484  e2 += edx * edx;
485 #ifdef DEBUG
486  std::cout << "error for coord " << icoord << " = " << ex[icoord] << " deriv " << deriv << std::endl;
487 #endif
488  }
489  }
490  }
491  double w2 = (e2 > 0) ? 1.0/e2 : 0;
492  double resval = w2 * ( y - fval ) * ( y - fval);
493 
494 #ifdef DEBUG
495  std::cout << x[0] << " " << y << " ex " << ex[0] << " ey " << ey << " params : ";
496  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
497  std::cout << p[ipar] << "\t";
498  std::cout << "\tfval = " << fval << "\tresval = " << resval << std::endl;
499 #endif
500 
501  // avoid (infinity and nan ) in the chi2 sum
502  // eventually add possibility of excluding some points (like singularity)
503  if ( resval < maxResValue )
504  chi2 += resval;
505  else
506  chi2 += maxResValue;
507  //nRejected++;
508 
509  }
510 
511  // reset the number of fitting data points
512  nPoints = n; // no points are rejected
513  //if (nRejected != 0) nPoints = n - nRejected;
514 
515 #ifdef DEBUG
516  std::cout << "chi2 = " << chi2 << " n = " << nPoints << std::endl;
517 #endif
518 
519  return chi2;
520 
521 }
522 
523 
524 ////////////////////////////////////////////////////////////////////////////////
525 /// evaluate the chi2 contribution (residual term) only for data with no coord-errors
526 /// This function is used in the specialized least square algorithms like FUMILI or L.M.
527 /// if we have error on the coordinates the method is not yet implemented
528 /// integral option is also not yet implemented
529 /// one can use in that case normal chi2 method
530 
531 double FitUtil::EvaluateChi2Residual(const IModelFunction & func, const BinData & data, const double * p, unsigned int i, double * g) {
532  if (data.GetErrorType() == BinData::kCoordError && data.Opt().fCoordErrors ) {
533  MATH_ERROR_MSG("FitUtil::EvaluateChi2Residual","Error on the coordinates are not used in calculating Chi2 residual");
534  return 0; // it will assert otherwise later in GetPoint
535  }
536 
537 
538  //func.SetParameters(p);
539 
540  double y, invError = 0;
541 
542  const DataOptions & fitOpt = data.Opt();
543  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
544  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
545  bool useExpErrors = (fitOpt.fExpErrors);
546 
547  const double * x1 = data.GetPoint(i,y, invError);
548 
549  IntegralEvaluator<> igEval( func, p, useBinIntegral);
550  double fval = 0;
551  unsigned int ndim = data.NDim();
552  double binVolume = 1.0;
553  const double * x2 = 0;
554  if (useBinVolume || useBinIntegral) x2 = data.BinUpEdge(i);
555 
556  double * xc = 0;
557 
558  if (useBinVolume) {
559  xc = new double[ndim];
560  for (unsigned int j = 0; j < ndim; ++j) {
561  binVolume *= std::abs( x2[j]-x1[j] );
562  xc[j] = 0.5*(x2[j]+ x1[j]);
563  }
564  // normalize the bin volume using a reference value
565  binVolume /= data.RefVolume();
566  }
567 
568  const double * x = (useBinVolume) ? xc : x1;
569 
570  if (!useBinIntegral) {
571  fval = func ( x, p );
572  }
573  else {
574  // calculate integral (normalized by bin volume)
575  // need to set function and parameters here in case loop is parallelized
576  fval = igEval( x1, x2) ;
577  }
578  // normalize result if requested according to bin volume
579  if (useBinVolume) fval *= binVolume;
580 
581  // expected errors
582  if (useExpErrors) {
583  // we need first to check if a weight factor needs to be applied
584  // weight = sumw2/sumw = error**2/content
585  //NOTE: assume histogram is not weighted
586  // don't know how to do with bins with weight = 0
587  //double invWeight = y * invError * invError;
588  // if (invError == 0) invWeight = (data.SumOfError2() > 0) ? data.SumOfContent()/ data.SumOfError2() : 1.0;
589  // compute expected error as f(x) / weight
590  double invError2 = (fval > 0) ? 1.0 / fval : 0.0;
591  invError = std::sqrt(invError2);
592  }
593 
594 
595  double resval = ( y -fval )* invError;
596 
597  // avoid infinities or nan in resval
598  resval = CorrectValue(resval);
599 
600  // estimate gradient
601  if (g != 0) {
602 
603  unsigned int npar = func.NPar();
604  const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func);
605 
606  if (gfunc != 0) {
607  //case function provides gradient
608  if (!useBinIntegral ) {
609  gfunc->ParameterGradient( x , p, g );
610  }
611  else {
612  // needs to calculate the integral for each partial derivative
613  CalculateGradientIntegral( *gfunc, x1, x2, p, g);
614  }
615  }
616  else {
617  SimpleGradientCalculator gc( npar, func);
618  if (!useBinIntegral )
619  gc.ParameterGradient(x, p, fval, g);
620  else {
621  // needs to calculate the integral for each partial derivative
622  CalculateGradientIntegral( gc, x1, x2, p, g);
623  }
624  }
625  // mutiply by - 1 * weight
626  for (unsigned int k = 0; k < npar; ++k) {
627  g[k] *= - invError;
628  if (useBinVolume) g[k] *= binVolume;
629  }
630  }
631 
632  if (useBinVolume) delete [] xc;
633 
634  return resval;
635 
636 }
637 
638 void FitUtil::EvaluateChi2Gradient(const IModelFunction &f, const BinData &data, const double *p, double *grad,
639  unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks)
640 {
641  // evaluate the gradient of the chi2 function
642  // this function is used when the model function knows how to calculate the derivative and we can
643  // avoid that the minimizer re-computes them
644  //
645  // case of chi2 effective (errors on coordinate) is not supported
646 
647  if (data.HaveCoordErrors()) {
648  MATH_ERROR_MSG("FitUtil::EvaluateChi2Gradient",
649  "Error on the coordinates are not used in calculating Chi2 gradient");
650  return; // it will assert otherwise later in GetPoint
651  }
652 
653  const IGradModelFunction *fg = dynamic_cast<const IGradModelFunction *>(&f);
654  assert(fg != nullptr); // must be called by a gradient function
655 
656  const IGradModelFunction &func = *fg;
657 
658 #ifdef DEBUG
659  std::cout << "\n\nFit data size = " << n << std::endl;
660  std::cout << "evaluate chi2 using function gradient " << &func << " " << p << std::endl;
661 #endif
662 
663  const DataOptions &fitOpt = data.Opt();
664  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
665  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
666 
667  double wrefVolume = 1.0;
668  if (useBinVolume) {
669  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
670  }
671 
672  IntegralEvaluator<> igEval(func, p, useBinIntegral);
673 
674  unsigned int npar = func.NPar();
675  unsigned initialNPoints = data.Size();
676 
677  std::vector<bool> isPointRejected(initialNPoints);
678 
679  auto mapFunction = [&](const unsigned int i) {
680  // set all vector values to zero
681  std::vector<double> gradFunc(npar);
682  std::vector<double> pointContribution(npar);
683 
684  const auto x1 = data.GetCoordComponent(i, 0);
685  const auto y = data.Value(i);
686  auto invError = data.Error(i);
687 
688  invError = (invError != 0.0) ? 1.0 / invError : 1;
689 
690  double fval = 0;
691 
692  const double *x = nullptr;
693  std::vector<double> xc;
694 
695  unsigned int ndim = data.NDim();
696  double binVolume = 1;
697  if (useBinVolume) {
698  const double *x2 = data.BinUpEdge(i);
699 
700  xc.resize(ndim);
701  for (unsigned int j = 0; j < ndim; ++j) {
702  auto x1_j = *data.GetCoordComponent(i, j);
703  binVolume *= std::abs(x2[j] - x1_j);
704  xc[j] = 0.5 * (x2[j] + x1_j);
705  }
706 
707  x = xc.data();
708 
709  // normalize the bin volume using a reference value
710  binVolume *= wrefVolume;
711  } else if (ndim > 1) {
712  xc.resize(ndim);
713  xc[0] = *x1;
714  for (unsigned int j = 1; j < ndim; ++j)
715  xc[j] = *data.GetCoordComponent(i, j);
716  x = xc.data();
717  } else {
718  x = x1;
719  }
720 
721  if (!useBinIntegral) {
722  fval = func(x, p);
723  func.ParameterGradient(x, p, &gradFunc[0]);
724  } else {
725  auto x2 = data.BinUpEdge(i);
726  // calculate normalized integral and gradient (divided by bin volume)
727  // need to set function and parameters here in case loop is parallelized
728  fval = igEval(x, x2);
729  CalculateGradientIntegral(func, x, x2, p, &gradFunc[0]);
730  }
731  if (useBinVolume)
732  fval *= binVolume;
733 
734 #ifdef DEBUG
735  std::cout << x[0] << " " << y << " " << 1. / invError << " params : ";
736  for (unsigned int ipar = 0; ipar < npar; ++ipar)
737  std::cout << p[ipar] << "\t";
738  std::cout << "\tfval = " << fval << std::endl;
739 #endif
740  if (!CheckInfNaNValue(fval)) {
741  isPointRejected[i] = true;
742  // Return a zero contribution to all partial derivatives on behalf of the current point
743  return pointContribution;
744  }
745 
746  // loop on the parameters
747  unsigned int ipar = 0;
748  for (; ipar < npar; ++ipar) {
749 
750  // correct gradient for bin volumes
751  if (useBinVolume)
752  gradFunc[ipar] *= binVolume;
753 
754  // avoid singularity in the function (infinity and nan ) in the chi2 sum
755  // eventually add possibility of excluding some points (like singularity)
756  double dfval = gradFunc[ipar];
757  if (!CheckInfNaNValue(dfval)) {
758  break; // exit loop on parameters
759  }
760 
761  // calculate derivative point contribution
762  pointContribution[ipar] = -2.0 * (y - fval) * invError * invError * gradFunc[ipar];
763  }
764 
765  if (ipar < npar) {
766  // case loop was broken for an overflow in the gradient calculation
767  isPointRejected[i] = true;
768  }
769 
770  return pointContribution;
771  };
772 
773  // Vertically reduce the set of vectors by summing its equally-indexed components
774  auto redFunction = [&](const std::vector<std::vector<double>> &pointContributions) {
775  std::vector<double> result(npar);
776 
777  for (auto const &pointContribution : pointContributions) {
778  for (unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
779  result[parameterIndex] += pointContribution[parameterIndex];
780  }
781 
782  return result;
783  };
784 
785  std::vector<double> g(npar);
786 
787 #ifndef R__USE_IMT
788  // If IMT is disabled, force the execution policy to the serial case
789  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
790  Warning("FitUtil::EvaluateChi2Gradient", "Multithread execution policy requires IMT, which is disabled. Changing "
791  "to ROOT::Fit::ExecutionPolicy::kSerial.");
792  executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
793  }
794 #endif
795 
796  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
797  std::vector<std::vector<double>> allGradients(initialNPoints);
798  for (unsigned int i = 0; i < initialNPoints; ++i) {
799  allGradients[i] = mapFunction(i);
800  }
801  g = redFunction(allGradients);
802  }
803 #ifdef R__USE_IMT
804  else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
805  auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(initialNPoints);
806  ROOT::TThreadExecutor pool;
807  g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, initialNPoints), redFunction, chunks);
808  }
809 #endif
810  // else if(executionPolicy == ROOT::Fit::kMultiprocess){
811  // ROOT::TProcessExecutor pool;
812  // g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction);
813  // }
814  else {
815  Error("FitUtil::EvaluateChi2Gradient",
816  "Execution policy unknown. Avalaible choices:\n 0: Serial (default)\n 1: MultiThread (requires IMT)\n");
817  }
818 
819 #ifndef R__USE_IMT
820  //to fix compiler warning
821  (void)nChunks;
822 #endif
823 
824  // correct the number of points
825  nPoints = initialNPoints;
826 
827  if (std::any_of(isPointRejected.begin(), isPointRejected.end(), [](bool point) { return point; })) {
828  unsigned nRejected = std::accumulate(isPointRejected.begin(), isPointRejected.end(), 0);
829  assert(nRejected <= initialNPoints);
830  nPoints = initialNPoints - nRejected;
831 
832  if (nPoints < npar)
833  MATH_ERROR_MSG("FitUtil::EvaluateChi2Gradient",
834  "Error - too many points rejected for overflow in gradient calculation");
835  }
836 
837  // copy result
838  std::copy(g.begin(), g.end(), grad);
839 }
840 
841 //______________________________________________________________________________________________________
842 //
843 // Log Likelihood functions
844 //_______________________________________________________________________________________________________
845 
846 // utility function used by the likelihoods
847 
848 // for LogLikelihood functions
849 
850 double FitUtil::EvaluatePdf(const IModelFunction & func, const UnBinData & data, const double * p, unsigned int i, double * g) {
851  // evaluate the pdf contribution to the generic logl function in case of bin data
852  // return actually the log of the pdf and its derivatives
853 
854 
855  //func.SetParameters(p);
856 
857 
858  const double * x = data.Coords(i);
859  double fval = func ( x, p );
860  double logPdf = ROOT::Math::Util::EvalLog(fval);
861  //return
862  if (g == 0) return logPdf;
863 
864  const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func);
865 
866  // gradient calculation
867  if (gfunc != 0) {
868  //case function provides gradient
869  gfunc->ParameterGradient( x , p, g );
870  }
871  else {
872  // estimate gradieant numerically with simple 2 point rule
873  // should probably calculate gradient of log(pdf) is more stable numerically
874  SimpleGradientCalculator gc(func.NPar(), func);
875  gc.ParameterGradient(x, p, fval, g );
876  }
877  // divide gradient by function value since returning the logs
878  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar) {
879  g[ipar] /= fval; // this should be checked against infinities
880  }
881 
882 #ifdef DEBUG
883  std::cout << x[i] << "\t";
884  std::cout << "\tpar = [ " << func.NPar() << " ] = ";
885  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
886  std::cout << p[ipar] << "\t";
887  std::cout << "\tfval = " << fval;
888  std::cout << "\tgrad = [ ";
889  for (unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
890  std::cout << g[ipar] << "\t";
891  std::cout << " ] " << std::endl;
892 #endif
893 
894 
895  return logPdf;
896 }
897 
898 double FitUtil::EvaluateLogL(const IModelFunctionTempl<double> &func, const UnBinData &data, const double *p,
899  int iWeight, bool extended, unsigned int &nPoints,
900  ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks)
901 {
902  // evaluate the LogLikelihood
903 
904  unsigned int n = data.Size();
905 
906  //unsigned int nRejected = 0;
907 
908  bool normalizeFunc = false;
909 
910  // set parameters of the function to cache integral value
911 #ifdef USE_PARAMCACHE
912  (const_cast<IModelFunctionTempl<double> &>(func)).SetParameters(p);
913 #endif
914 #ifdef R__USE_IMT
915  // in case parameter needs to be propagated to user function use trick to set parameters by calling one time the function
916  // this will be done in sequential mode and parameters can be set in a thread safe manner
917  if (!normalizeFunc) {
918  if (data.NDim() == 1) {
919  const double * x = data.GetCoordComponent(0,0);
920  func( x, p);
921  }
922  else {
923  std::vector<double> x(data.NDim());
924  for (unsigned int j = 0; j < data.NDim(); ++j)
925  x[j] = *data.GetCoordComponent(0, j);
926  func( x.data(), p);
927  }
928  }
929 #endif
930 
931  double norm = 1.0;
932  if (normalizeFunc) {
933  // compute integral of the function
934  std::vector<double> xmin(data.NDim());
935  std::vector<double> xmax(data.NDim());
936  IntegralEvaluator<> igEval(func, p, true);
937  // compute integral in the ranges where is defined
938  if (data.Range().Size() > 0) {
939  norm = 0;
940  for (unsigned int ir = 0; ir < data.Range().Size(); ++ir) {
941  data.Range().GetRange(&xmin[0], &xmax[0], ir);
942  norm += igEval.Integral(xmin.data(), xmax.data());
943  }
944  } else {
945  // use (-inf +inf)
946  data.Range().GetRange(&xmin[0], &xmax[0]);
947  // check if funcition is zero at +- inf
948  if (func(xmin.data(), p) != 0 || func(xmax.data(), p) != 0) {
949  MATH_ERROR_MSG("FitUtil::EvaluateLogLikelihood",
950  "A range has not been set and the function is not zero at +/- inf");
951  return 0;
952  }
953  norm = igEval.Integral(&xmin[0], &xmax[0]);
954  }
955  }
956 
957  // needed to compue effective global weight in case of extended likelihood
958 
959  auto mapFunction = [&](const unsigned i) {
960  double W = 0;
961  double W2 = 0;
962  double fval = 0;
963 
964  if (data.NDim() > 1) {
965  std::vector<double> x(data.NDim());
966  for (unsigned int j = 0; j < data.NDim(); ++j)
967  x[j] = *data.GetCoordComponent(i, j);
968 #ifdef USE_PARAMCACHE
969  fval = func(x.data());
970 #else
971  fval = func(x.data(), p);
972 #endif
973 
974  // one -dim case
975  } else {
976  const auto x = data.GetCoordComponent(i, 0);
977 #ifdef USE_PARAMCACHE
978  fval = func(x);
979 #else
980  fval = func(x, p);
981 #endif
982  }
983 
984  if (normalizeFunc)
985  fval = fval * (1 / norm);
986 
987  // function EvalLog protects against negative or too small values of fval
988  double logval = ROOT::Math::Util::EvalLog(fval);
989  if (iWeight > 0) {
990  double weight = data.Weight(i);
991  logval *= weight;
992  if (iWeight == 2) {
993  logval *= weight; // use square of weights in likelihood
994  if (!extended) {
995  // needed sum of weights and sum of weight square if likelkihood is extended
996  W = weight;
997  W2 = weight * weight;
998  }
999  }
1000  }
1001  nPoints++;
1002  // {
1003  // R__LOCKGUARD(gROOTMutex);
1004  // std::cout << "compute Log-l for point " << i << " nPoints " << nPoints << " = " << logval <<
1005  // std::endl;
1006  // }
1007  return LikelihoodAux<double>(logval, W, W2);
1008  };
1009 
1010 #ifdef R__USE_IMT
1011  // auto redFunction = [](const std::vector<LikelihoodAux<double>> & objs){
1012  // return std::accumulate(objs.begin(), objs.end(), LikelihoodAux<double>(0.0,0.0,0.0),
1013  // [](const LikelihoodAux<double> &l1, const LikelihoodAux<double> &l2){
1014  // return l1+l2;
1015  // });
1016  // };
1017  // do not use std::accumulate to be sure to maintain always the same order
1018  auto redFunction = [](const std::vector<LikelihoodAux<double>> & objs){
1019  auto l0 = LikelihoodAux<double>(0.0,0.0,0.0);
1020  for ( auto & l : objs ) {
1021  l0 = l0 + l;
1022  }
1023  return l0;
1024  };
1025 #else
1026  (void)nChunks;
1027 
1028  // If IMT is disabled, force the execution policy to the serial case
1029  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1030  Warning("FitUtil::EvaluateLogL", "Multithread execution policy requires IMT, which is disabled. Changing "
1031  "to ROOT::Fit::ExecutionPolicy::kSerial.");
1032  executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1033  }
1034 #endif
1035 
1036  double logl{};
1037  double sumW{};
1038  double sumW2{};
1039  if(executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial){
1040  for (unsigned int i=0; i<n; ++i) {
1041  auto resArray = mapFunction(i);
1042  logl+=resArray.logvalue;
1043  sumW+=resArray.weight;
1044  sumW2+=resArray.weight2;
1045  }
1046 #ifdef R__USE_IMT
1047  } else if(executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1048  auto chunks = nChunks !=0? nChunks: setAutomaticChunking(data.Size());
1049  ROOT::TThreadExecutor pool;
1050  auto resArray = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction, chunks);
1051  logl=resArray.logvalue;
1052  sumW=resArray.weight;
1053  sumW2=resArray.weight2;
1054 #endif
1055 // } else if(executionPolicy == ROOT::Fit::kMultitProcess){
1056  // ROOT::TProcessExecutor pool;
1057  // res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction);
1058  } else{
1059  Error("FitUtil::EvaluateLogL","Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1060  }
1061 
1062  if (extended) {
1063  // add Poisson extended term
1064  double extendedTerm = 0; // extended term in likelihood
1065  double nuTot = 0;
1066  // nuTot is integral of function in the range
1067  // if function has been normalized integral has been already computed
1068  if (!normalizeFunc) {
1069  IntegralEvaluator<> igEval( func, p, true);
1070  std::vector<double> xmin(data.NDim());
1071  std::vector<double> xmax(data.NDim());
1072 
1073  // compute integral in the ranges where is defined
1074  if (data.Range().Size() > 0 ) {
1075  nuTot = 0;
1076  for (unsigned int ir = 0; ir < data.Range().Size(); ++ir) {
1077  data.Range().GetRange(&xmin[0],&xmax[0],ir);
1078  nuTot += igEval.Integral(xmin.data(),xmax.data());
1079  }
1080  } else {
1081  // use (-inf +inf)
1082  data.Range().GetRange(&xmin[0],&xmax[0]);
1083  // check if funcition is zero at +- inf
1084  if (func(xmin.data(), p) != 0 || func(xmax.data(), p) != 0) {
1085  MATH_ERROR_MSG("FitUtil::EvaluateLogLikelihood","A range has not been set and the function is not zero at +/- inf");
1086  return 0;
1087  }
1088  nuTot = igEval.Integral(&xmin[0],&xmax[0]);
1089  }
1090 
1091  // force to be last parameter value
1092  //nutot = p[func.NDim()-1];
1093  if (iWeight != 2)
1094  extendedTerm = - nuTot; // no need to add in this case n log(nu) since is already computed before
1095  else {
1096  // case use weight square in likelihood : compute total effective weight = sw2/sw
1097  // ignore for the moment case when sumW is zero
1098  extendedTerm = - (sumW2 / sumW) * nuTot;
1099  }
1100 
1101  }
1102  else {
1103  nuTot = norm;
1104  extendedTerm = - nuTot + double(n) * ROOT::Math::Util::EvalLog( nuTot);
1105  // in case of weights need to use here sum of weights (to be done)
1106  }
1107  logl += extendedTerm;
1108 
1109  }
1110 
1111  // reset the number of fitting data points
1112  // nPoints = n;
1113  // std::cout<<", n: "<<nPoints<<std::endl;
1114  nPoints = 0;
1115 
1116 #ifdef DEBUG
1117  std::cout << "Evaluated log L for parameters (";
1118  for (unsigned int ip = 0; ip < func.NPar(); ++ip)
1119  std::cout << " " << p[ip];
1120  std::cout << ") fval = " << -logl << std::endl;
1121 #endif
1122 
1123  return -logl;
1124 }
1125 
1126 void FitUtil::EvaluateLogLGradient(const IModelFunction &f, const UnBinData &data, const double *p, double *grad,
1127  unsigned int &, ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks)
1128 {
1129  // evaluate the gradient of the log likelihood function
1130 
1131  const IGradModelFunction *fg = dynamic_cast<const IGradModelFunction *>(&f);
1132  assert(fg != nullptr); // must be called by a grad function
1133 
1134  const IGradModelFunction &func = *fg;
1135 
1136  unsigned int npar = func.NPar();
1137  unsigned initialNPoints = data.Size();
1138 
1139  (const_cast<IGradModelFunction &>(func)).SetParameters(p);
1140 
1141 #ifdef DEBUG
1142  std::cout << "\n===> Evaluate Gradient for parameters ";
1143  for (unsigned int ip = 0; ip < npar; ++ip)
1144  std::cout << " " << p[ip];
1145  std::cout << "\n";
1146 #endif
1147 
1148  const double kdmax1 = std::sqrt(std::numeric_limits<double>::max());
1149  const double kdmax2 = std::numeric_limits<double>::max() / (4 * initialNPoints);
1150 
1151  auto mapFunction = [&](const unsigned int i) {
1152  std::vector<double> gradFunc(npar);
1153  std::vector<double> pointContribution(npar);
1154 
1155 
1156  const double * x = nullptr;
1157  std::vector<double> xc;
1158  if (data.NDim() > 1) {
1159  xc.resize(data.NDim() );
1160  for (unsigned int j = 0; j < data.NDim(); ++j)
1161  xc[j] = *data.GetCoordComponent(i, j);
1162  x = xc.data();
1163  } else {
1164  x = data.GetCoordComponent(i, 0);
1165  }
1166 
1167  double fval = func(x, p);
1168  func.ParameterGradient(x, p, &gradFunc[0]);
1169 
1170 #ifdef DEBUG
1171  {
1172  R__LOCKGUARD(gROOTMutex);
1173  if (i < 5 || (i > data.Size()-5) ) {
1174  if (data.NDim() > 1) std::cout << i << " x " << x[0] << " y " << x[1] << " func " << fval
1175  << " gradient " << gradFunc[0] << " " << gradFunc[1] << " " << gradFunc[3] << std::endl;
1176  else std::cout << i << " x " << x[0] << " gradient " << gradFunc[0] << " " << gradFunc[1] << " " << gradFunc[3] << std::endl;
1177  }
1178  }
1179 #endif
1180 
1181  for (unsigned int kpar = 0; kpar < npar; ++kpar) {
1182  if (fval > 0)
1183  pointContribution[kpar] = -1. / fval * gradFunc[kpar];
1184  else if (gradFunc[kpar] != 0) {
1185  double gg = kdmax1 * gradFunc[kpar];
1186  if (gg > 0)
1187  gg = std::min(gg, kdmax2);
1188  else
1189  gg = std::max(gg, -kdmax2);
1190  pointContribution[kpar] = -gg;
1191  }
1192  // if func derivative is zero term is also zero so do not add in g[kpar]
1193  }
1194 
1195  return pointContribution;
1196  };
1197 
1198  // Vertically reduce the set of vectors by summing its equally-indexed components
1199  auto redFunction = [&](const std::vector<std::vector<double>> &pointContributions) {
1200  std::vector<double> result(npar);
1201 
1202  for (auto const &pointContribution : pointContributions) {
1203  for (unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
1204  result[parameterIndex] += pointContribution[parameterIndex];
1205  }
1206 
1207  return result;
1208  };
1209 
1210  std::vector<double> g(npar);
1211 
1212 #ifndef R__USE_IMT
1213  // If IMT is disabled, force the execution policy to the serial case
1214  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1215  Warning("FitUtil::EvaluateLogLGradient", "Multithread execution policy requires IMT, which is disabled. Changing "
1216  "to ROOT::Fit::ExecutionPolicy::kSerial.");
1217  executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1218  }
1219 #endif
1220 
1221  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1222  std::vector<std::vector<double>> allGradients(initialNPoints);
1223  for (unsigned int i = 0; i < initialNPoints; ++i) {
1224  allGradients[i] = mapFunction(i);
1225  }
1226  g = redFunction(allGradients);
1227  }
1228 #ifdef R__USE_IMT
1229  else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1230  auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(initialNPoints);
1231  ROOT::TThreadExecutor pool;
1232  g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, initialNPoints), redFunction, chunks);
1233  }
1234 #endif
1235 
1236  // else if(executionPolicy == ROOT::Fit::ExecutionPolicy::kMultiprocess){
1237  // ROOT::TProcessExecutor pool;
1238  // g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction);
1239  // }
1240  else {
1241  Error("FitUtil::EvaluateLogLGradient", "Execution policy unknown. Avalaible choices:\n "
1242  "ROOT::Fit::ExecutionPolicy::kSerial (default)\n "
1243  "ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1244  }
1245 
1246 #ifndef R__USE_IMT
1247  // to fix compiler warning
1248  (void)nChunks;
1249 #endif
1250 
1251  // copy result
1252  std::copy(g.begin(), g.end(), grad);
1253 
1254 #ifdef DEBUG
1255  std::cout << "FitUtil.cxx : Final gradient ";
1256  for (unsigned int param = 0; param < npar; param++) {
1257  std::cout << " " << grad[param];
1258  }
1259  std::cout << "\n";
1260 #endif
1261 }
1262 //_________________________________________________________________________________________________
1263 // for binned log likelihood functions
1264 ////////////////////////////////////////////////////////////////////////////////
1265 /// evaluate the pdf (Poisson) contribution to the logl (return actually log of pdf)
1266 /// and its gradient
1267 
1268 double FitUtil::EvaluatePoissonBinPdf(const IModelFunction & func, const BinData & data, const double * p, unsigned int i, double * g ) {
1269  double y = 0;
1270  const double * x1 = data.GetPoint(i,y);
1271 
1272  const DataOptions & fitOpt = data.Opt();
1273  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1274  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1275 
1276  IntegralEvaluator<> igEval( func, p, useBinIntegral);
1277  double fval = 0;
1278  const double * x2 = 0;
1279  // calculate the bin volume
1280  double binVolume = 1;
1281  std::vector<double> xc;
1282  if (useBinVolume) {
1283  unsigned int ndim = data.NDim();
1284  xc.resize(ndim);
1285  x2 = data.BinUpEdge(i);
1286  for (unsigned int j = 0; j < ndim; ++j) {
1287  binVolume *= std::abs( x2[j]-x1[j] );
1288  xc[j] = 0.5*(x2[j]+ x1[j]);
1289  }
1290  // normalize the bin volume using a reference value
1291  binVolume /= data.RefVolume();
1292  }
1293 
1294  const double * x = (useBinVolume) ? &xc.front() : x1;
1295 
1296  if (!useBinIntegral ) {
1297  fval = func ( x, p );
1298  }
1299  else {
1300  // calculate integral normalized (divided by bin volume)
1301  x2 = data.BinUpEdge(i);
1302  fval = igEval( x1, x2 ) ;
1303  }
1304  if (useBinVolume) fval *= binVolume;
1305 
1306  // logPdf for Poisson: ignore constant term depending on N
1307  fval = std::max(fval, 0.0); // avoid negative or too small values
1308  double logPdf = - fval;
1309  if (y > 0.0) {
1310  // include also constants due to saturate model (see Baker-Cousins paper)
1311  logPdf += y * ROOT::Math::Util::EvalLog( fval / y) + y;
1312  }
1313  // need to return the pdf contribution (not the log)
1314 
1315  //double pdfval = std::exp(logPdf);
1316 
1317  //if (g == 0) return pdfval;
1318  if (g == 0) return logPdf;
1319 
1320  unsigned int npar = func.NPar();
1321  const IGradModelFunction * gfunc = dynamic_cast<const IGradModelFunction *>( &func);
1322 
1323  // gradient calculation
1324  if (gfunc != 0) {
1325  //case function provides gradient
1326  if (!useBinIntegral )
1327  gfunc->ParameterGradient( x , p, g );
1328  else {
1329  // needs to calculate the integral for each partial derivative
1330  CalculateGradientIntegral( *gfunc, x1, x2, p, g);
1331  }
1332 
1333  }
1334  else {
1335  SimpleGradientCalculator gc(func.NPar(), func);
1336  if (!useBinIntegral )
1337  gc.ParameterGradient(x, p, fval, g);
1338  else {
1339  // needs to calculate the integral for each partial derivative
1340  CalculateGradientIntegral( gc, x1, x2, p, g);
1341  }
1342  }
1343  // correct g[] do be derivative of poisson term
1344  for (unsigned int k = 0; k < npar; ++k) {
1345  // apply bin volume correction
1346  if (useBinVolume) g[k] *= binVolume;
1347 
1348  // correct for Poisson term
1349  if ( fval > 0)
1350  g[k] *= ( y/fval - 1.) ;//* pdfval;
1351  else if (y > 0) {
1352  const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
1353  g[k] *= kdmax1;
1354  }
1355  else // y == 0 cannot have negative y
1356  g[k] *= -1;
1357  }
1358 
1359 
1360 #ifdef DEBUG
1361  std::cout << "x = " << x[0] << " logPdf = " << logPdf << " grad";
1362  for (unsigned int ipar = 0; ipar < npar; ++ipar)
1363  std::cout << g[ipar] << "\t";
1364  std::cout << std::endl;
1365 #endif
1366 
1367 // return pdfval;
1368  return logPdf;
1369 }
1370 
1371 double FitUtil::EvaluatePoissonLogL(const IModelFunction &func, const BinData &data, const double *p, int iWeight,
1372  bool extended, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy,
1373  unsigned nChunks)
1374 {
1375  // evaluate the Poisson Log Likelihood
1376  // for binned likelihood fits
1377  // this is Sum ( f(x_i) - y_i * log( f (x_i) ) )
1378  // add as well constant term for saturated model to make it like a Chi2/2
1379  // by default is etended. If extended is false the fit is not extended and
1380  // the global poisson term is removed (i.e is a binomial fit)
1381  // (remember that in this case one needs to have a function with a fixed normalization
1382  // like in a non extended unbinned fit)
1383  //
1384  // if use Weight use a weighted dataset
1385  // iWeight = 1 ==> logL = Sum( w f(x_i) )
1386  // case of iWeight==1 is actually identical to weight==0
1387  // iWeight = 2 ==> logL = Sum( w*w * f(x_i) )
1388  //
1389  // nPoints returns the points where bin content is not zero
1390 
1391 
1392  unsigned int n = data.Size();
1393 
1394 #ifdef USE_PARAMCACHE
1395  (const_cast<IModelFunction &>(func)).SetParameters(p);
1396 #endif
1397 
1398  nPoints = 0; // npoints
1399 
1400 
1401  // get fit option and check case of using integral of bins
1402  const DataOptions &fitOpt = data.Opt();
1403  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1404  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1405  bool useW2 = (iWeight == 2);
1406 
1407  // normalize if needed by a reference volume value
1408  double wrefVolume = 1.0;
1409  if (useBinVolume) {
1410  if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
1411  }
1412 
1413 #ifdef DEBUG
1414  std::cout << "Evaluate PoissonLogL for params = [ ";
1415  for (unsigned int j = 0; j < func.NPar(); ++j) std::cout << p[j] << " , ";
1416  std::cout << "] - data size = " << n << " useBinIntegral " << useBinIntegral << " useBinVolume "
1417  << useBinVolume << " useW2 " << useW2 << " wrefVolume = " << wrefVolume << std::endl;
1418 #endif
1419 
1420 #ifdef USE_PARAMCACHE
1421  IntegralEvaluator<> igEval(func, 0, useBinIntegral);
1422 #else
1423  IntegralEvaluator<> igEval(func, p, useBinIntegral);
1424 #endif
1425 
1426  auto mapFunction = [&](const unsigned i) {
1427  auto x1 = data.GetCoordComponent(i, 0);
1428  auto y = *data.ValuePtr(i);
1429 
1430  const double *x = nullptr;
1431  std::vector<double> xc;
1432  double fval = 0;
1433  double binVolume = 1.0;
1434 
1435  if (useBinVolume) {
1436  unsigned int ndim = data.NDim();
1437  const double *x2 = data.BinUpEdge(i);
1438  xc.resize(data.NDim());
1439  for (unsigned int j = 0; j < ndim; ++j) {
1440  auto xx = *data.GetCoordComponent(i, j);
1441  binVolume *= std::abs(x2[j] - xx);
1442  xc[j] = 0.5 * (x2[j] + xx);
1443  }
1444  x = xc.data();
1445  // normalize the bin volume using a reference value
1446  binVolume *= wrefVolume;
1447  } else if (data.NDim() > 1) {
1448  xc.resize(data.NDim());
1449  xc[0] = *x1;
1450  for (unsigned int j = 1; j < data.NDim(); ++j) {
1451  xc[j] = *data.GetCoordComponent(i, j);
1452  }
1453  x = xc.data();
1454  } else {
1455  x = x1;
1456  }
1457 
1458  if (!useBinIntegral) {
1459 #ifdef USE_PARAMCACHE
1460  fval = func(x);
1461 #else
1462  fval = func(x, p);
1463 #endif
1464  } else {
1465  // calculate integral (normalized by bin volume)
1466  // need to set function and parameters here in case loop is parallelized
1467  fval = igEval(x, data.BinUpEdge(i));
1468  }
1469  if (useBinVolume) fval *= binVolume;
1470 
1471 
1472 
1473 #ifdef DEBUG
1474  int NSAMPLE = 100;
1475  if (i % NSAMPLE == 0) {
1476  std::cout << "evt " << i << " x = [ ";
1477  for (unsigned int j = 0; j < func.NDim(); ++j) std::cout << x[j] << " , ";
1478  std::cout << "] ";
1479  if (fitOpt.fIntegral) {
1480  std::cout << "x2 = [ ";
1481  for (unsigned int j = 0; j < func.NDim(); ++j) std::cout << data.BinUpEdge(i)[j] << " , ";
1482  std::cout << "] ";
1483  }
1484  std::cout << " y = " << y << " fval = " << fval << std::endl;
1485  }
1486 #endif
1487 
1488 
1489  // EvalLog protects against 0 values of fval but don't want to add in the -log sum
1490  // negative values of fval
1491  fval = std::max(fval, 0.0);
1492 
1493  double nloglike = 0; // negative loglikelihood
1494  if (useW2) {
1495  // apply weight correction . Effective weight is error^2/ y
1496  // and expected events in bins is fval/weight
1497  // can apply correction only when y is not zero otherwise weight is undefined
1498  // (in case of weighted likelihood I don't care about the constant term due to
1499  // the saturated model)
1500 
1501  // use for the empty bins the global weight
1502  double weight = 1.0;
1503  if (y != 0) {
1504  double error = data.Error(i);
1505  weight = (error * error) / y; // this is the bin effective weight
1506  nloglike += weight * y * ( ROOT::Math::Util::EvalLog(y) - ROOT::Math::Util::EvalLog(fval) );
1507  }
1508  else {
1509  // for empty bin use the average weight computed from the total data weight
1510  weight = data.SumOfError2()/ data.SumOfContent();
1511  }
1512  if (extended) {
1513  nloglike += weight * ( fval - y);
1514  }
1515 
1516  } else {
1517  // standard case no weights or iWeight=1
1518  // this is needed for Poisson likelihood (which are extened and not for multinomial)
1519  // the formula below include constant term due to likelihood of saturated model (f(x) = y)
1520  // (same formula as in Baker-Cousins paper, page 439 except a factor of 2
1521  if (extended) nloglike = fval - y;
1522 
1523  if (y > 0) {
1524  nloglike += y * (ROOT::Math::Util::EvalLog(y) - ROOT::Math::Util::EvalLog(fval));
1525  nPoints++;
1526  }
1527  }
1528  return nloglike;
1529  };
1530 
1531 #ifdef R__USE_IMT
1532  auto redFunction = [](const std::vector<double> &objs) {
1533  return std::accumulate(objs.begin(), objs.end(), double{});
1534  };
1535 #else
1536  (void)nChunks;
1537 
1538  // If IMT is disabled, force the execution policy to the serial case
1539  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1540  Warning("FitUtil::EvaluatePoissonLogL", "Multithread execution policy requires IMT, which is disabled. Changing "
1541  "to ROOT::Fit::ExecutionPolicy::kSerial.");
1542  executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1543  }
1544 #endif
1545 
1546  double res{};
1547  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1548  for (unsigned int i = 0; i < n; ++i) {
1549  res += mapFunction(i);
1550  }
1551 #ifdef R__USE_IMT
1552  } else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1553  auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(data.Size());
1554  ROOT::TThreadExecutor pool;
1555  res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction, chunks);
1556 #endif
1557  // } else if(executionPolicy == ROOT::Fit::kMultitProcess){
1558  // ROOT::TProcessExecutor pool;
1559  // res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction);
1560  } else {
1561  Error("FitUtil::EvaluatePoissonLogL",
1562  "Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1563  }
1564 
1565 #ifdef DEBUG
1566  std::cout << "Loglikelihood = " << res << std::endl;
1567 #endif
1568 
1569  return res;
1570 }
1571 
1572 void FitUtil::EvaluatePoissonLogLGradient(const IModelFunction &f, const BinData &data, const double *p, double *grad,
1573  unsigned int &, ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks)
1574 {
1575  // evaluate the gradient of the Poisson log likelihood function
1576 
1577  const IGradModelFunction *fg = dynamic_cast<const IGradModelFunction *>(&f);
1578  assert(fg != nullptr); // must be called by a grad function
1579 
1580  const IGradModelFunction &func = *fg;
1581 
1582 #ifdef USE_PARAMCACHE
1583  (const_cast<IGradModelFunction &>(func)).SetParameters(p);
1584 #endif
1585 
1586  const DataOptions &fitOpt = data.Opt();
1587  bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1588  bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1589 
1590  double wrefVolume = 1.0;
1591  if (useBinVolume && fitOpt.fNormBinVolume)
1592  wrefVolume /= data.RefVolume();
1593 
1594  IntegralEvaluator<> igEval(func, p, useBinIntegral);
1595 
1596  unsigned int npar = func.NPar();
1597  unsigned initialNPoints = data.Size();
1598 
1599  auto mapFunction = [&](const unsigned int i) {
1600  // set all vector values to zero
1601  std::vector<double> gradFunc(npar);
1602  std::vector<double> pointContribution(npar);
1603 
1604  const auto x1 = data.GetCoordComponent(i, 0);
1605  const auto y = data.Value(i);
1606  auto invError = data.Error(i);
1607 
1608  invError = (invError != 0.0) ? 1.0 / invError : 1;
1609 
1610  double fval = 0;
1611 
1612  const double *x = nullptr;
1613  std::vector<double> xc;
1614 
1615  unsigned ndim = data.NDim();
1616  double binVolume = 1.0;
1617  if (useBinVolume) {
1618  const double *x2 = data.BinUpEdge(i);
1619 
1620  xc.resize(ndim);
1621  for (unsigned int j = 0; j < ndim; ++j) {
1622  auto x1_j = *data.GetCoordComponent(i, j);
1623  binVolume *= std::abs(x2[j] - x1_j);
1624  xc[j] = 0.5 * (x2[j] + x1_j);
1625  }
1626 
1627  x = xc.data();
1628 
1629  // normalize the bin volume using a reference value
1630  binVolume *= wrefVolume;
1631  } else if (ndim > 1) {
1632  xc.resize(ndim);
1633  xc[0] = *x1;
1634  for (unsigned int j = 1; j < ndim; ++j)
1635  xc[j] = *data.GetCoordComponent(i, j);
1636  x = xc.data();
1637  } else {
1638  x = x1;
1639  }
1640 
1641  if (!useBinIntegral) {
1642  fval = func(x, p);
1643  func.ParameterGradient(x, p, &gradFunc[0]);
1644  } else {
1645  // calculate integral (normalized by bin volume)
1646  // need to set function and parameters here in case loop is parallelized
1647  auto x2 = data.BinUpEdge(i);
1648  fval = igEval(x, x2);
1649  CalculateGradientIntegral(func, x, x2, p, &gradFunc[0]);
1650  }
1651  if (useBinVolume)
1652  fval *= binVolume;
1653 
1654 #ifdef DEBUG
1655  {
1656  R__LOCKGUARD(gROOTMutex);
1657  if (i < 5 || (i > data.Size()-5) ) {
1658  if (data.NDim() > 1) std::cout << i << " x " << x[0] << " y " << x[1] << " func " << fval
1659  << " gradient " << gradFunc[0] << " " << gradFunc[1] << " " << gradFunc[3] << std::endl;
1660  else std::cout << i << " x " << x[0] << " gradient " << gradFunc[0] << " " << gradFunc[1] << " " << gradFunc[3] << std::endl;
1661  }
1662  }
1663 #endif
1664 
1665  // correct the gradient
1666  for (unsigned int ipar = 0; ipar < npar; ++ipar) {
1667 
1668  // correct gradient for bin volumes
1669  if (useBinVolume)
1670  gradFunc[ipar] *= binVolume;
1671 
1672  // df/dp * (1. - y/f )
1673  if (fval > 0)
1674  pointContribution[ipar] = gradFunc[ipar] * (1. - y / fval);
1675  else if (gradFunc[ipar] != 0) {
1676  const double kdmax1 = std::sqrt(std::numeric_limits<double>::max());
1677  const double kdmax2 = std::numeric_limits<double>::max() / (4 * initialNPoints);
1678  double gg = kdmax1 * gradFunc[ipar];
1679  if (gg > 0)
1680  gg = std::min(gg, kdmax2);
1681  else
1682  gg = std::max(gg, -kdmax2);
1683  pointContribution[ipar] = -gg;
1684  }
1685  }
1686 
1687 
1688  return pointContribution;
1689  };
1690 
1691  // Vertically reduce the set of vectors by summing its equally-indexed components
1692  auto redFunction = [&](const std::vector<std::vector<double>> &pointContributions) {
1693  std::vector<double> result(npar);
1694 
1695  for (auto const &pointContribution : pointContributions) {
1696  for (unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
1697  result[parameterIndex] += pointContribution[parameterIndex];
1698  }
1699 
1700  return result;
1701  };
1702 
1703  std::vector<double> g(npar);
1704 
1705 #ifndef R__USE_IMT
1706  // If IMT is disabled, force the execution policy to the serial case
1707  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1708  Warning("FitUtil::EvaluatePoissonLogLGradient",
1709  "Multithread execution policy requires IMT, which is disabled. Changing "
1710  "to ROOT::Fit::ExecutionPolicy::kSerial.");
1711  executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1712  }
1713 #endif
1714 
1715  if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1716  std::vector<std::vector<double>> allGradients(initialNPoints);
1717  for (unsigned int i = 0; i < initialNPoints; ++i) {
1718  allGradients[i] = mapFunction(i);
1719  }
1720  g = redFunction(allGradients);
1721  }
1722 #ifdef R__USE_IMT
1723  else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1724  auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(initialNPoints);
1725  ROOT::TThreadExecutor pool;
1726  g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, initialNPoints), redFunction, chunks);
1727  }
1728 #endif
1729 
1730  // else if(executionPolicy == ROOT::Fit::kMultiprocess){
1731  // ROOT::TProcessExecutor pool;
1732  // g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction);
1733  // }
1734  else {
1735  Error("FitUtil::EvaluatePoissonLogLGradient",
1736  "Execution policy unknown. Avalaible choices:\n 0: Serial (default)\n 1: MultiThread (requires IMT)\n");
1737  }
1738 
1739 #ifndef R__USE_IMT
1740  //to fix compiler warning
1741  (void)nChunks;
1742 #endif
1743 
1744  // copy result
1745  std::copy(g.begin(), g.end(), grad);
1746 
1747 #ifdef DEBUG
1748  std::cout << "***** Final gradient : ";
1749  for (unsigned int ii = 0; ii< npar; ++ii) std::cout << grad[ii] << " ";
1750  std::cout << "\n";
1751 #endif
1752 
1753 }
1754 
1755 
1756 unsigned FitUtil::setAutomaticChunking(unsigned nEvents){
1757  SysInfo_t s;
1758  gSystem->GetSysInfo(&s);
1759  auto ncpu = s.fCpus;
1760  if (nEvents/ncpu < 1000) return ncpu;
1761  return nEvents/1000;
1762  //return ((nEvents/ncpu + 1) % 1000) *40 ; //arbitrary formula
1763 }
1764 
1765 }
1766 
1767 } // end namespace ROOT
bool HaveAsymErrors() const
flag to control if data provides asymmetric errors on the value
Definition: BinData.h:143
unsigned int NDim() const
return coordinate data dimension
Definition: UnBinData.h:280
bool HasBinEdges() const
query if the data store the bin edges instead of the center
Definition: BinData.h:528
Namespace for new ROOT classes and functions.
Definition: TFoamSampler.h:19
double EvaluateChi2(const IModelFunction &func, const BinData &data, const double *x, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks=0)
Chi2 Functions.
Definition: FitUtil.cxx:226
void EvaluateLogLGradient(const IModelFunction &func, const UnBinData &data, const double *x, double *grad, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy=ROOT::Fit::ExecutionPolicy::kSerial, unsigned nChunks=0)
evaluate the LogL gradient given a model function and the data at the point x.
Definition: FitUtil.cxx:1126
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:1268
void CalculateGradientIntegral(const GFunc &gfunc, const double *x1, const double *x2, const double *p, double *g)
Definition: FitUtil.cxx:202
ErrorType GetErrorType() const
retrieve the errortype
Definition: BinData.h:545
const double * ValuePtr(unsigned int ipoint) const
return a pointer to the value for the given fit point
Definition: BinData.h:229
unsigned setAutomaticChunking(unsigned nEvents)
Definition: FitUtil.cxx:1756
Class describing the unbinned data sets (just x coordinates values) of any dimensions.
Definition: UnBinData.h:42
Interface (abstract class) for parametric gradient multi-dimensional functions providing in addition ...
STL namespace.
you should not use this method at all Int_t y
Definition: TRolke.cxx:630
bool CheckInfNaNValue(double &rval)
Definition: FitUtil.cxx:181
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:560
const double * Coords(unsigned int ipoint) const
return a pointer to the coordinates data for the given fit point
Definition: FitData.h:246
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:370
double sqrt(double)
void GetRange(unsigned int irange, unsigned int icoord, double &xmin, double &xmax) const
get the i-th range for given coordinate.
Definition: DataRange.h:103
OneDimMultiFunctionAdapter class to wrap a multidimensional function in one dimensional one...
const double * GetCoordComponent(unsigned int ipoint, unsigned int icoord) const
returns a single coordinate component of a point.
Definition: FitData.h:229
void EvaluatePoissonLogLGradient(const IModelFunction &func, const BinData &data, const double *x, double *grad, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy=ROOT::Fit::ExecutionPolicy::kSerial, unsigned nChunks=0)
evaluate the Poisson LogL given a model function and the data at the point x.
Definition: FitUtil.cxx:1572
double RefVolume() const
retrieve the reference volume used to normalize the data when the option bin volume is set ...
Definition: BinData.h:535
double Derivative1(double x)
Returns the first derivative of the function at point x, computed by Richardson&#39;s extrapolation metho...
double EvaluatePoissonLogL(const IModelFunction &func, const BinData &data, const double *x, int iWeight, bool extended, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks=0)
evaluate the Poisson LogL given a model function and the data at the point x.
Definition: FitUtil.cxx:1371
unsigned int Size() const
return number of fit points
Definition: FitData.h:303
double Error(unsigned int ipoint) const
Definition: BinData.h:251
#define MATH_ERROR_MSG(loc, str)
Definition: Error.h:50
T EvalLog(T x)
safe evaluation of log(x) with a protections against negative or zero argument to the log smooth line...
Definition: Util.h:56
void EvaluateChi2Gradient(const IModelFunction &func, const BinData &data, const double *x, double *grad, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy=ROOT::Fit::ExecutionPolicy::kSerial, unsigned nChunks=0)
evaluate the Chi2 gradient given a model function and the data at the point x.
Definition: FitUtil.cxx:638
virtual unsigned int NPar() const =0
Return the number of Parameters.
double Weight(unsigned int ipoint) const
return weight
Definition: UnBinData.h:257
DataOptions : simple structure holding the options on how the data are filled.
Definition: DataOptions.h:28
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:410
const double * BinUpEdge(unsigned int ipoint) const
return an array containing the upper edge of the bin for coordinate i In case of empty bin they could...
Definition: BinData.h:508
unsigned int Size(unsigned int icoord=0) const
return range size for coordinate icoord (starts from zero) Size == 0 indicates no range is present [-...
Definition: DataRange.h:70
double EvaluateChi2Residual(const IModelFunction &func, const BinData &data, const double *x, unsigned int ipoint, double *g=0)
evaluate the residual contribution to the Chi2 given a model function and the BinPoint data and if th...
Definition: FitUtil.cxx:531
const DataOptions & Opt() const
access to options
Definition: FitData.h:319
virtual void ParameterGradient(const T *x, const double *p, T *grad) const
Evaluate the all the derivatives (gradient vector) of the function with respect to the parameters at ...
Class describing the binned data sets : vectors of x coordinates, y values and optionally error on y ...
Definition: BinData.h:53
bool HaveCoordErrors() const
flag to control if data provides error on the coordinates
Definition: BinData.h:131
* x
Deprecated and error prone model selection interface.
Definition: TRolke.cxx:630
constexpr Double_t E()
Base of natural log: .
Definition: TMath.h:97
ROOT::Math::IParamMultiFunction IModelFunction
Definition: FitUtil.h:65
double CorrectValue(double rval)
Definition: FitUtil.cxx:167
virtual void SetParameters(const double *p)=0
Set the parameter values.
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:850
double EvaluateLogL(const IModelFunction &func, const UnBinData &data, const double *p, int iWeight, bool extended, unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy, unsigned nChunks=0)
evaluate the LogL given a model function and the data at the point x.
double Value(unsigned int ipoint) const
return the value for the given fit point
Definition: BinData.h:217
double SumOfContent() const
compute the total sum of the data content (sum of weights in case of weighted data set) ...
Definition: BinData.h:554
bool IsWeighted() const
return true if the data set is weighted We cannot compute ourselfs because sometimes errors are fille...
Definition: BinData.h:567
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:451
double InvError(unsigned int ipoint) const
Return the inverse of error on the value for the given fit point useful when error in the coordinates...
Definition: BinData.h:314
unsigned int NDim() const
return coordinate data dimension
Definition: FitData.h:311
const DataRange & Range() const
access to range
Definition: FitData.h:331
double Integral(const double *x1, const double *x2)
Definition: FitUtil.h:197
User class for calculating the derivatives of a function.
virtual unsigned int NDim() const =0
Retrieve the dimension of the function.