Logo ROOT   6.14/05
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());
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);
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  {
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  {
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
float xmin
Definition: THbookFile.cxx:93
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: StringConv.hxx:21
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
#define g(i)
Definition: RSha256.hxx:105
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
#define f(i)
Definition: RSha256.hxx:104
Interface (abstract class) for parametric gradient multi-dimensional functions providing in addition ...
R__EXTERN TVirtualMutex * gROOTMutex
Definition: TROOT.h:57
STL namespace.
bool CheckInfNaNValue(double &rval)
Definition: FitUtil.cxx:181
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: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)
static const double x2[5]
Double_t x[n]
Definition: legend1.C:17
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
virtual int GetSysInfo(SysInfo_t *info) const
Returns static system info, like OS type, CPU type, number of CPUs RAM size, etc into the SysInfo_t s...
Definition: TSystem.cxx:2487
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:63
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
This class provides a simple interface to execute the same task multiple times in parallel...
virtual unsigned int NPar() const =0
Return the number of Parameters.
double Weight(unsigned int ipoint) const
return weight
Definition: UnBinData.h:257
Int_t fCpus
Definition: TSystem.h:155
DataOptions : simple structure holding the options on how the data are filled.
Definition: DataOptions.h:28
R__EXTERN TSystem * gSystem
Definition: TSystem.h:540
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
float xmax
Definition: THbookFile.cxx:93
bool HaveCoordErrors() const
flag to control if data provides error on the coordinates
Definition: BinData.h:131
void Warning(const char *location, const char *msgfmt,...)
constexpr Double_t E()
Base of natural log: .
Definition: TMath.h:97
#define h(i)
Definition: RSha256.hxx:106
ROOT::Math::IParamMultiFunction IModelFunction
Definition: FitUtil.h:64
double CorrectValue(double rval)
Definition: FitUtil.cxx:167
virtual void SetParameters(const double *p)=0
Set the parameter values.
static const double x1[5]
A pseudo container class which is a generator of indices.
Definition: TSeq.hxx:66
TFitResultPtr Fit(FitObject *h1, TF1 *f1, Foption_t &option, const ROOT::Math::MinimizerOptions &moption, const char *goption, ROOT::Fit::DataRange &range)
Definition: HFitImpl.cxx:134
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_t y[n]
Definition: legend1.C:17
Double_t ey[n]
Definition: legend1.C:17
static constexpr double s
#define R__LOCKGUARD(mutex)
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
typedef void((*Func_t)())
auto MapReduce(F func, unsigned nTimes, R redfunc) -> typename std::result_of< F()>::type
This method behaves just like Map, but an additional redfunc function must be provided.
bool IsWeighted() const
return true if the data set is weighted We cannot compute ourselfs because sometimes errors are fille...
Definition: BinData.h:567
auto * l
Definition: textangle.C:4
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
TF1 * f1
Definition: legend1.C:11
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:196
Double_t ex[n]
Definition: legend1.C:17
const Int_t n
Definition: legend1.C:16
User class for calculating the derivatives of a function.
void Error(ErrorHandler_t func, int code, const char *va_(fmt),...)
Write error message and call a handler, if required.
virtual unsigned int NDim() const =0
Retrieve the dimension of the function.