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 );
60 unsigned int NDim()
const {
return fFunc.NDim(); }
61 const GradFunc & fFunc;
67 class SimpleGradientCalculator {
77 SimpleGradientCalculator(
int gdim,
const IModelFunction & func,
double eps = 2.E-8,
int istrat = 1) :
83 fVec(std::vector<double>(gdim) )
88 double DoParameterDerivative(
const double *x,
const double *p,
double f0,
int k)
const {
90 double h = std::max( fEps* std::abs(p0), 8.0*fPrecision*(std::abs(p0) + fPrecision) );
95 double f1 = fFunc(x, &fVec.front() );
98 double f2 = fFunc(x, &fVec.front() );
99 deriv = 0.5 * ( f2 - f1 )/h;
102 deriv = ( f1 - f0 )/h;
108 unsigned int NDim()
const {
112 unsigned int NPar()
const {
116 double ParameterDerivative(
const double *x,
const double *p,
int ipar)
const {
118 std::copy(p, p+fN, fVec.begin());
119 double f0 = fFunc(x, p);
120 return DoParameterDerivative(x,p,f0,ipar);
124 void ParameterGradient(
const double * x,
const double * p,
double f0,
double * g) {
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);
133 void Gradient(
const double * x,
const double * p,
double f0,
double * g) {
135 std::copy(x, x+fN, fVec.begin());
136 for (
unsigned int k = 0; k < fN; ++k) {
138 double h = std::max( fEps* std::abs(x0), 8.0*fPrecision*(std::abs(x0) + fPrecision) );
142 double f1 = fFunc( &fVec.front(), p );
145 double f2 = fFunc( &fVec.front(), p );
146 g[k] = 0.5 * ( f2 - f1 )/h;
149 g[k] = ( f1 - f0 )/h;
161 const IModelFunction & fFunc;
162 mutable std::vector<double> fVec;
167 double CorrectValue(
double rval) {
169 if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() )
173 return -std::numeric_limits<double>::max();
176 return + std::numeric_limits<double>::max();
181 bool CheckInfNaNValue(
double &rval)
183 if (rval > - std::numeric_limits<double>::max() && rval < std::numeric_limits<double>::max() )
187 rval = -std::numeric_limits<double>::max();
192 rval = + std::numeric_limits<double>::max();
201 template <
class GFunc>
202 void CalculateGradientIntegral(
const GFunc & gfunc,
203 const double *x1,
const double * x2,
const double * p,
double *g) {
206 ParamDerivFunc<GFunc> pfunc( gfunc);
207 IntegralEvaluator<ParamDerivFunc<GFunc> > igDerEval( pfunc, p,
true);
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 );
226 double FitUtil::EvaluateChi2(
const IModelFunction &func,
const BinData &data,
const double *p,
unsigned int &,
227 ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks)
234 unsigned int n = data.Size();
237 #ifdef USE_PARAMCACHE
238 (
const_cast<IModelFunction &
>(func)).SetParameters(p);
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();
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;
260 ROOT::Math::IntegrationOneDim::Type igType = ROOT::Math::IntegrationOneDim::kDEFAULT;
261 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
263 igType = ROOT::Math::IntegrationOneDim::kGAUSS;
265 #ifdef USE_PARAMCACHE
266 IntegralEvaluator<> igEval( func, 0, useBinIntegral, igType);
268 IntegralEvaluator<> igEval( func, p, useBinIntegral, igType);
270 double maxResValue = std::numeric_limits<double>::max() /n;
271 double wrefVolume = 1.0;
273 if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
276 (
const_cast<IModelFunction &
>(func)).SetParameters(p);
278 auto mapFunction = [&](
const unsigned i){
283 const auto x1 = data.GetCoordComponent(i, 0);
284 const auto y = data.Value(i);
285 auto invError = data.InvError(i);
289 const double * x =
nullptr;
290 std::vector<double> xc;
291 double binVolume = 1.0;
293 unsigned int ndim = data.NDim();
294 const double * x2 = data.BinUpEdge(i);
295 xc.resize(data.NDim());
296 for (
unsigned int j = 0; j < ndim; ++j) {
297 auto xx = *data.GetCoordComponent(i, j);
298 binVolume *= std::abs(x2[j]- xx);
299 xc[j] = 0.5*(x2[j]+ xx);
303 binVolume *= wrefVolume;
304 }
else if(data.NDim() > 1) {
305 xc.resize(data.NDim());
307 for (
unsigned int j = 1; j < data.NDim(); ++j)
308 xc[j] = *data.GetCoordComponent(i, j);
315 if (!useBinIntegral) {
316 #ifdef USE_PARAMCACHE
319 fval = func ( x, p );
325 fval = igEval( x, data.BinUpEdge(i)) ;
328 if (useBinVolume) fval *= binVolume;
332 double invWeight = 1.0;
339 invWeight = data.SumOfContent()/ data.SumOfError2();
345 double invError2 = (fval > 0) ? invWeight / fval : 0.0;
346 invError = std::sqrt(invError2);
352 std::cout << x[0] <<
" " << y <<
" " << 1./invError <<
" params : ";
353 for (
unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
354 std::cout << p[ipar] <<
"\t";
355 std::cout <<
"\tfval = " << fval <<
" bin volume " << binVolume <<
" ref " << wrefVolume << std::endl;
361 double tmp = ( y -fval )* invError;
362 double resval = tmp * tmp;
366 if ( resval < maxResValue )
377 auto redFunction = [](
const std::vector<double> & objs){
378 return std::accumulate(objs.begin(), objs.end(),
double{});
384 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
385 Warning(
"FitUtil::EvaluateChi2",
"Multithread execution policy requires IMT, which is disabled. Changing "
386 "to ROOT::Fit::ExecutionPolicy::kSerial.");
387 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
392 if(executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial){
393 for (
unsigned int i=0; i<n; ++i) {
394 res += mapFunction(i);
397 }
else if(executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
398 ROOT::TThreadExecutor pool;
399 auto chunks = nChunks !=0? nChunks: setAutomaticChunking(data.Size());
400 res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction, chunks);
406 Error(
"FitUtil::EvaluateChi2",
"Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
415 double FitUtil::EvaluateChi2Effective(
const IModelFunction & func,
const BinData & data,
const double * p,
unsigned int & nPoints) {
421 unsigned int n = data.Size();
424 std::cout <<
"\n\nFit data size = " << n << std::endl;
425 std::cout <<
"evaluate effective chi2 using function " << &func <<
" " << p << std::endl;
428 assert(data.HaveCoordErrors() || data.HaveAsymErrors());
436 unsigned int ndim = func.NDim();
439 ROOT::Math::RichardsonDerivator derivator;
441 double maxResValue = std::numeric_limits<double>::max() /n;
445 for (
unsigned int i = 0; i < n; ++ i) {
449 const double * x = data.GetPoint(i,y);
451 double fval = func( x, p );
453 double delta_y_func = y - fval;
457 const double * ex = 0;
458 if (!data.HaveAsymErrors() )
459 ex = data.GetPointError(i, ey);
461 double eylow, eyhigh = 0;
462 ex = data.GetPointError(i, eylow, eyhigh);
463 if ( delta_y_func < 0)
471 while ( j < ndim && ex[j] == 0.) { j++; }
475 ROOT::Math::OneDimMultiFunctionAdapter<const IModelFunction &> f1D(func,x,0,p);
478 double kPrecision = 1.E-8;
479 for (
unsigned int icoord = 0; icoord < ndim; ++icoord) {
481 if (ex[icoord] > 0) {
483 f1D.SetCoord(icoord);
485 double x0= x[icoord];
486 double h = std::max( kEps* std::abs(ex[icoord]), 8.0*kPrecision*(std::abs(x0) + kPrecision) );
487 double deriv = derivator.Derivative1(f1D, x[icoord], h);
488 double edx = ex[icoord] * deriv;
491 std::cout <<
"error for coord " << icoord <<
" = " << ex[icoord] <<
" deriv " << deriv << std::endl;
496 double w2 = (e2 > 0) ? 1.0/e2 : 0;
497 double resval = w2 * ( y - fval ) * ( y - fval);
500 std::cout << x[0] <<
" " << y <<
" ex " << ex[0] <<
" ey " << ey <<
" params : ";
501 for (
unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
502 std::cout << p[ipar] <<
"\t";
503 std::cout <<
"\tfval = " << fval <<
"\tresval = " << resval << std::endl;
508 if ( resval < maxResValue )
521 std::cout <<
"chi2 = " << chi2 <<
" n = " << nPoints << std::endl;
536 double FitUtil::EvaluateChi2Residual(
const IModelFunction & func,
const BinData & data,
const double * p,
unsigned int i,
double * g) {
537 if (data.GetErrorType() == BinData::kCoordError && data.Opt().fCoordErrors ) {
538 MATH_ERROR_MSG(
"FitUtil::EvaluateChi2Residual",
"Error on the coordinates are not used in calculating Chi2 residual");
545 double y, invError = 0;
547 const DataOptions & fitOpt = data.Opt();
548 bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
549 bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
550 bool useExpErrors = (fitOpt.fExpErrors);
552 const double * x1 = data.GetPoint(i,y, invError);
554 IntegralEvaluator<> igEval( func, p, useBinIntegral);
556 unsigned int ndim = data.NDim();
557 double binVolume = 1.0;
558 const double * x2 = 0;
559 if (useBinVolume || useBinIntegral) x2 = data.BinUpEdge(i);
564 xc =
new double[ndim];
565 for (
unsigned int j = 0; j < ndim; ++j) {
566 binVolume *= std::abs( x2[j]-x1[j] );
567 xc[j] = 0.5*(x2[j]+ x1[j]);
570 binVolume /= data.RefVolume();
573 const double * x = (useBinVolume) ? xc : x1;
575 if (!useBinIntegral) {
576 fval = func ( x, p );
581 fval = igEval( x1, x2) ;
584 if (useBinVolume) fval *= binVolume;
595 double invError2 = (fval > 0) ? 1.0 / fval : 0.0;
596 invError = std::sqrt(invError2);
600 double resval = ( y -fval )* invError;
603 resval = CorrectValue(resval);
608 unsigned int npar = func.NPar();
609 const IGradModelFunction * gfunc =
dynamic_cast<const IGradModelFunction *
>( &func);
613 if (!useBinIntegral ) {
614 gfunc->ParameterGradient( x , p, g );
618 CalculateGradientIntegral( *gfunc, x1, x2, p, g);
622 SimpleGradientCalculator gc( npar, func);
623 if (!useBinIntegral )
624 gc.ParameterGradient(x, p, fval, g);
627 CalculateGradientIntegral( gc, x1, x2, p, g);
631 for (
unsigned int k = 0; k < npar; ++k) {
633 if (useBinVolume) g[k] *= binVolume;
637 if (useBinVolume)
delete [] xc;
643 void FitUtil::EvaluateChi2Gradient(
const IModelFunction &f,
const BinData &data,
const double *p,
double *grad,
644 unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks)
652 if (data.HaveCoordErrors()) {
653 MATH_ERROR_MSG(
"FitUtil::EvaluateChi2Gradient",
654 "Error on the coordinates are not used in calculating Chi2 gradient");
658 const IGradModelFunction *fg =
dynamic_cast<const IGradModelFunction *
>(&f);
659 assert(fg !=
nullptr);
661 const IGradModelFunction &func = *fg;
664 std::cout <<
"\n\nFit data size = " << n << std::endl;
665 std::cout <<
"evaluate chi2 using function gradient " << &func <<
" " << p << std::endl;
668 const DataOptions &fitOpt = data.Opt();
669 bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
670 bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
672 double wrefVolume = 1.0;
674 if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
677 ROOT::Math::IntegrationOneDim::Type igType = ROOT::Math::IntegrationOneDim::kDEFAULT;
678 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
680 igType = ROOT::Math::IntegrationOneDim::kGAUSS;
682 IntegralEvaluator<> igEval(func, p, useBinIntegral,igType);
684 unsigned int npar = func.NPar();
685 unsigned initialNPoints = data.Size();
687 std::vector<bool> isPointRejected(initialNPoints);
689 auto mapFunction = [&](
const unsigned int i) {
691 std::vector<double> gradFunc(npar);
692 std::vector<double> pointContribution(npar);
694 const auto x1 = data.GetCoordComponent(i, 0);
695 const auto y = data.Value(i);
696 auto invError = data.Error(i);
698 invError = (invError != 0.0) ? 1.0 / invError : 1;
702 const double *x =
nullptr;
703 std::vector<double> xc;
705 unsigned int ndim = data.NDim();
706 double binVolume = 1;
708 const double *x2 = data.BinUpEdge(i);
711 for (
unsigned int j = 0; j < ndim; ++j) {
712 auto x1_j = *data.GetCoordComponent(i, j);
713 binVolume *= std::abs(x2[j] - x1_j);
714 xc[j] = 0.5 * (x2[j] + x1_j);
720 binVolume *= wrefVolume;
721 }
else if (ndim > 1) {
724 for (
unsigned int j = 1; j < ndim; ++j)
725 xc[j] = *data.GetCoordComponent(i, j);
731 if (!useBinIntegral) {
733 func.ParameterGradient(x, p, &gradFunc[0]);
735 auto x2 = data.BinUpEdge(i);
738 fval = igEval(x, x2);
739 CalculateGradientIntegral(func, x, x2, p, &gradFunc[0]);
745 std::cout << x[0] <<
" " << y <<
" " << 1. / invError <<
" params : ";
746 for (
unsigned int ipar = 0; ipar < npar; ++ipar)
747 std::cout << p[ipar] <<
"\t";
748 std::cout <<
"\tfval = " << fval << std::endl;
750 if (!CheckInfNaNValue(fval)) {
751 isPointRejected[i] =
true;
753 return pointContribution;
757 unsigned int ipar = 0;
758 for (; ipar < npar; ++ipar) {
762 gradFunc[ipar] *= binVolume;
766 double dfval = gradFunc[ipar];
767 if (!CheckInfNaNValue(dfval)) {
772 pointContribution[ipar] = -2.0 * (y - fval) * invError * invError * gradFunc[ipar];
777 isPointRejected[i] =
true;
780 return pointContribution;
784 auto redFunction = [&](
const std::vector<std::vector<double>> &pointContributions) {
785 std::vector<double> result(npar);
787 for (
auto const &pointContribution : pointContributions) {
788 for (
unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
789 result[parameterIndex] += pointContribution[parameterIndex];
795 std::vector<double> g(npar);
799 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
800 Warning(
"FitUtil::EvaluateChi2Gradient",
"Multithread execution policy requires IMT, which is disabled. Changing "
801 "to ROOT::Fit::ExecutionPolicy::kSerial.");
802 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
806 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
807 std::vector<std::vector<double>> allGradients(initialNPoints);
808 for (
unsigned int i = 0; i < initialNPoints; ++i) {
809 allGradients[i] = mapFunction(i);
811 g = redFunction(allGradients);
814 else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
815 ROOT::TThreadExecutor pool;
816 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(initialNPoints);
817 g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, initialNPoints), redFunction, chunks);
825 Error(
"FitUtil::EvaluateChi2Gradient",
826 "Execution policy unknown. Avalaible choices:\n 0: Serial (default)\n 1: MultiThread (requires IMT)\n");
835 nPoints = initialNPoints;
837 if (std::any_of(isPointRejected.begin(), isPointRejected.end(), [](
bool point) {
return point; })) {
838 unsigned nRejected = std::accumulate(isPointRejected.begin(), isPointRejected.end(), 0);
839 assert(nRejected <= initialNPoints);
840 nPoints = initialNPoints - nRejected;
843 MATH_ERROR_MSG(
"FitUtil::EvaluateChi2Gradient",
844 "Error - too many points rejected for overflow in gradient calculation");
848 std::copy(g.begin(), g.end(), grad);
860 double FitUtil::EvaluatePdf(
const IModelFunction & func,
const UnBinData & data,
const double * p,
unsigned int i,
double * g) {
868 const double * x = data.Coords(i);
869 double fval = func ( x, p );
870 double logPdf = ROOT::Math::Util::EvalLog(fval);
872 if (g == 0)
return logPdf;
874 const IGradModelFunction * gfunc =
dynamic_cast<const IGradModelFunction *
>( &func);
879 gfunc->ParameterGradient( x , p, g );
884 SimpleGradientCalculator gc(func.NPar(), func);
885 gc.ParameterGradient(x, p, fval, g );
888 for (
unsigned int ipar = 0; ipar < func.NPar(); ++ipar) {
893 std::cout << x[i] <<
"\t";
894 std::cout <<
"\tpar = [ " << func.NPar() <<
" ] = ";
895 for (
unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
896 std::cout << p[ipar] <<
"\t";
897 std::cout <<
"\tfval = " << fval;
898 std::cout <<
"\tgrad = [ ";
899 for (
unsigned int ipar = 0; ipar < func.NPar(); ++ipar)
900 std::cout << g[ipar] <<
"\t";
901 std::cout <<
" ] " << std::endl;
908 double FitUtil::EvaluateLogL(
const IModelFunctionTempl<double> &func,
const UnBinData &data,
const double *p,
909 int iWeight,
bool extended,
unsigned int &nPoints,
910 ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks)
914 unsigned int n = data.Size();
918 bool normalizeFunc =
false;
921 #ifdef USE_PARAMCACHE
922 (
const_cast<IModelFunctionTempl<double> &
>(func)).SetParameters(p);
925 nPoints = data.Size();
930 if (!normalizeFunc) {
931 if (data.NDim() == 1) {
932 const double * x = data.GetCoordComponent(0,0);
936 std::vector<double> x(data.NDim());
937 for (
unsigned int j = 0; j < data.NDim(); ++j)
938 x[j] = *data.GetCoordComponent(0, j);
947 std::vector<double> xmin(data.NDim());
948 std::vector<double> xmax(data.NDim());
949 IntegralEvaluator<> igEval(func, p,
true);
951 if (data.Range().Size() > 0) {
953 for (
unsigned int ir = 0; ir < data.Range().Size(); ++ir) {
954 data.Range().GetRange(&xmin[0], &xmax[0], ir);
955 norm += igEval.Integral(xmin.data(), xmax.data());
959 data.Range().GetRange(&xmin[0], &xmax[0]);
961 if (func(xmin.data(), p) != 0 || func(xmax.data(), p) != 0) {
962 MATH_ERROR_MSG(
"FitUtil::EvaluateLogLikelihood",
963 "A range has not been set and the function is not zero at +/- inf");
966 norm = igEval.Integral(&xmin[0], &xmax[0]);
972 auto mapFunction = [&](
const unsigned i) {
977 if (data.NDim() > 1) {
978 std::vector<double> x(data.NDim());
979 for (
unsigned int j = 0; j < data.NDim(); ++j)
980 x[j] = *data.GetCoordComponent(i, j);
981 #ifdef USE_PARAMCACHE
982 fval = func(x.data());
984 fval = func(x.data(), p);
989 const auto x = data.GetCoordComponent(i, 0);
990 #ifdef USE_PARAMCACHE
998 fval = fval * (1 / norm);
1001 double logval = ROOT::Math::Util::EvalLog(fval);
1003 double weight = data.Weight(i);
1010 W2 = weight * weight;
1014 return LikelihoodAux<double>(logval, W, W2);
1025 auto redFunction = [](
const std::vector<LikelihoodAux<double>> & objs){
1026 auto l0 = LikelihoodAux<double>(0.0,0.0,0.0);
1027 for (
auto & l : objs ) {
1036 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1037 Warning(
"FitUtil::EvaluateLogL",
"Multithread execution policy requires IMT, which is disabled. Changing "
1038 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1039 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1046 if(executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial){
1047 for (
unsigned int i=0; i<n; ++i) {
1048 auto resArray = mapFunction(i);
1049 logl+=resArray.logvalue;
1050 sumW+=resArray.weight;
1051 sumW2+=resArray.weight2;
1054 }
else if(executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1055 ROOT::TThreadExecutor pool;
1056 auto chunks = nChunks !=0? nChunks: setAutomaticChunking(data.Size());
1057 auto resArray = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction, chunks);
1058 logl=resArray.logvalue;
1059 sumW=resArray.weight;
1060 sumW2=resArray.weight2;
1066 Error(
"FitUtil::EvaluateLogL",
"Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1071 double extendedTerm = 0;
1075 if (!normalizeFunc) {
1076 IntegralEvaluator<> igEval( func, p,
true);
1077 std::vector<double> xmin(data.NDim());
1078 std::vector<double> xmax(data.NDim());
1081 if (data.Range().Size() > 0 ) {
1083 for (
unsigned int ir = 0; ir < data.Range().Size(); ++ir) {
1084 data.Range().GetRange(&xmin[0],&xmax[0],ir);
1085 nuTot += igEval.Integral(xmin.data(),xmax.data());
1089 data.Range().GetRange(&xmin[0],&xmax[0]);
1091 if (func(xmin.data(), p) != 0 || func(xmax.data(), p) != 0) {
1092 MATH_ERROR_MSG(
"FitUtil::EvaluateLogLikelihood",
"A range has not been set and the function is not zero at +/- inf");
1095 nuTot = igEval.Integral(&xmin[0],&xmax[0]);
1101 extendedTerm = - nuTot;
1105 extendedTerm = - (sumW2 / sumW) * nuTot;
1111 extendedTerm = - nuTot + double(n) * ROOT::Math::Util::EvalLog( nuTot);
1114 logl += extendedTerm;
1119 std::cout <<
"Evaluated log L for parameters (";
1120 for (
unsigned int ip = 0; ip < func.NPar(); ++ip)
1121 std::cout <<
" " << p[ip];
1122 std::cout <<
") fval = " << -logl << std::endl;
1128 void FitUtil::EvaluateLogLGradient(
const IModelFunction &f,
const UnBinData &data,
const double *p,
double *grad,
1129 unsigned int &, ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks)
1133 const IGradModelFunction *fg =
dynamic_cast<const IGradModelFunction *
>(&f);
1134 assert(fg !=
nullptr);
1136 const IGradModelFunction &func = *fg;
1138 unsigned int npar = func.NPar();
1139 unsigned initialNPoints = data.Size();
1141 (
const_cast<IGradModelFunction &
>(func)).SetParameters(p);
1144 std::cout <<
"\n===> Evaluate Gradient for parameters ";
1145 for (
unsigned int ip = 0; ip < npar; ++ip)
1146 std::cout <<
" " << p[ip];
1150 const double kdmax1 = std::sqrt(std::numeric_limits<double>::max());
1151 const double kdmax2 = std::numeric_limits<double>::max() / (4 * initialNPoints);
1153 auto mapFunction = [&](
const unsigned int i) {
1154 std::vector<double> gradFunc(npar);
1155 std::vector<double> pointContribution(npar);
1158 const double * x =
nullptr;
1159 std::vector<double> xc;
1160 if (data.NDim() > 1) {
1161 xc.resize(data.NDim() );
1162 for (
unsigned int j = 0; j < data.NDim(); ++j)
1163 xc[j] = *data.GetCoordComponent(i, j);
1166 x = data.GetCoordComponent(i, 0);
1169 double fval = func(x, p);
1170 func.ParameterGradient(x, p, &gradFunc[0]);
1174 R__LOCKGUARD(gROOTMutex);
1175 if (i < 5 || (i > data.Size()-5) ) {
1176 if (data.NDim() > 1) std::cout << i <<
" x " << x[0] <<
" y " << x[1] <<
" func " << fval
1177 <<
" gradient " << gradFunc[0] <<
" " << gradFunc[1] <<
" " << gradFunc[3] << std::endl;
1178 else std::cout << i <<
" x " << x[0] <<
" gradient " << gradFunc[0] <<
" " << gradFunc[1] <<
" " << gradFunc[3] << std::endl;
1183 for (
unsigned int kpar = 0; kpar < npar; ++kpar) {
1185 pointContribution[kpar] = -1. / fval * gradFunc[kpar];
1186 else if (gradFunc[kpar] != 0) {
1187 double gg = kdmax1 * gradFunc[kpar];
1189 gg = std::min(gg, kdmax2);
1191 gg = std::max(gg, -kdmax2);
1192 pointContribution[kpar] = -gg;
1197 return pointContribution;
1201 auto redFunction = [&](
const std::vector<std::vector<double>> &pointContributions) {
1202 std::vector<double> result(npar);
1204 for (
auto const &pointContribution : pointContributions) {
1205 for (
unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
1206 result[parameterIndex] += pointContribution[parameterIndex];
1212 std::vector<double> g(npar);
1216 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1217 Warning(
"FitUtil::EvaluateLogLGradient",
"Multithread execution policy requires IMT, which is disabled. Changing "
1218 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1219 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1223 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1224 std::vector<std::vector<double>> allGradients(initialNPoints);
1225 for (
unsigned int i = 0; i < initialNPoints; ++i) {
1226 allGradients[i] = mapFunction(i);
1228 g = redFunction(allGradients);
1231 else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1232 ROOT::TThreadExecutor pool;
1233 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(initialNPoints);
1234 g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, initialNPoints), redFunction, chunks);
1243 Error(
"FitUtil::EvaluateLogLGradient",
"Execution policy unknown. Avalaible choices:\n "
1244 "ROOT::Fit::ExecutionPolicy::kSerial (default)\n "
1245 "ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1254 std::copy(g.begin(), g.end(), grad);
1257 std::cout <<
"FitUtil.cxx : Final gradient ";
1258 for (
unsigned int param = 0; param < npar; param++) {
1259 std::cout <<
" " << grad[param];
1270 double FitUtil::EvaluatePoissonBinPdf(
const IModelFunction & func,
const BinData & data,
const double * p,
unsigned int i,
double * g ) {
1272 const double * x1 = data.GetPoint(i,y);
1274 const DataOptions & fitOpt = data.Opt();
1275 bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1276 bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1278 IntegralEvaluator<> igEval( func, p, useBinIntegral);
1280 const double * x2 = 0;
1282 double binVolume = 1;
1283 std::vector<double> xc;
1285 unsigned int ndim = data.NDim();
1287 x2 = data.BinUpEdge(i);
1288 for (
unsigned int j = 0; j < ndim; ++j) {
1289 binVolume *= std::abs( x2[j]-x1[j] );
1290 xc[j] = 0.5*(x2[j]+ x1[j]);
1293 binVolume /= data.RefVolume();
1296 const double * x = (useBinVolume) ? &xc.front() : x1;
1298 if (!useBinIntegral ) {
1299 fval = func ( x, p );
1303 x2 = data.BinUpEdge(i);
1304 fval = igEval( x1, x2 ) ;
1306 if (useBinVolume) fval *= binVolume;
1309 fval = std::max(fval, 0.0);
1310 double logPdf = - fval;
1313 logPdf += y * ROOT::Math::Util::EvalLog( fval / y) + y;
1320 if (g == 0)
return logPdf;
1322 unsigned int npar = func.NPar();
1323 const IGradModelFunction * gfunc =
dynamic_cast<const IGradModelFunction *
>( &func);
1328 if (!useBinIntegral )
1329 gfunc->ParameterGradient( x , p, g );
1332 CalculateGradientIntegral( *gfunc, x1, x2, p, g);
1337 SimpleGradientCalculator gc(func.NPar(), func);
1338 if (!useBinIntegral )
1339 gc.ParameterGradient(x, p, fval, g);
1342 CalculateGradientIntegral( gc, x1, x2, p, g);
1346 for (
unsigned int k = 0; k < npar; ++k) {
1348 if (useBinVolume) g[k] *= binVolume;
1352 g[k] *= ( y/fval - 1.) ;
1354 const double kdmax1 = std::sqrt( std::numeric_limits<double>::max() );
1363 std::cout <<
"x = " << x[0] <<
" logPdf = " << logPdf <<
" grad";
1364 for (
unsigned int ipar = 0; ipar < npar; ++ipar)
1365 std::cout << g[ipar] <<
"\t";
1366 std::cout << std::endl;
1373 double FitUtil::EvaluatePoissonLogL(
const IModelFunction &func,
const BinData &data,
const double *p,
int iWeight,
1374 bool extended,
unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy,
1394 unsigned int n = data.Size();
1396 #ifdef USE_PARAMCACHE
1397 (
const_cast<IModelFunction &
>(func)).SetParameters(p);
1400 nPoints = data.Size();
1404 const DataOptions &fitOpt = data.Opt();
1405 bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1406 bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1407 bool useW2 = (iWeight == 2);
1410 double wrefVolume = 1.0;
1412 if (fitOpt.fNormBinVolume) wrefVolume /= data.RefVolume();
1417 std::cout <<
"Evaluate PoissonLogL for params = [ ";
1418 for (
unsigned int j = 0; j < func.NPar(); ++j) std::cout << p[j] <<
" , ";
1419 std::cout <<
"] - data size = " << n <<
" useBinIntegral " << useBinIntegral <<
" useBinVolume "
1420 << useBinVolume <<
" useW2 " << useW2 <<
" wrefVolume = " << wrefVolume << std::endl;
1424 ROOT::Math::IntegrationOneDim::Type igType = ROOT::Math::IntegrationOneDim::kDEFAULT;
1425 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1427 igType = ROOT::Math::IntegrationOneDim::kGAUSS;
1429 #ifdef USE_PARAMCACHE
1430 IntegralEvaluator<> igEval(func, 0, useBinIntegral, igType);
1432 IntegralEvaluator<> igEval(func, p, useBinIntegral, igType);
1435 auto mapFunction = [&](
const unsigned i) {
1436 auto x1 = data.GetCoordComponent(i, 0);
1437 auto y = *data.ValuePtr(i);
1439 const double *x =
nullptr;
1440 std::vector<double> xc;
1442 double binVolume = 1.0;
1445 unsigned int ndim = data.NDim();
1446 const double *x2 = data.BinUpEdge(i);
1447 xc.resize(data.NDim());
1448 for (
unsigned int j = 0; j < ndim; ++j) {
1449 auto xx = *data.GetCoordComponent(i, j);
1450 binVolume *= std::abs(x2[j] - xx);
1451 xc[j] = 0.5 * (x2[j] + xx);
1455 binVolume *= wrefVolume;
1456 }
else if (data.NDim() > 1) {
1457 xc.resize(data.NDim());
1459 for (
unsigned int j = 1; j < data.NDim(); ++j) {
1460 xc[j] = *data.GetCoordComponent(i, j);
1467 if (!useBinIntegral) {
1468 #ifdef USE_PARAMCACHE
1476 fval = igEval(x, data.BinUpEdge(i));
1478 if (useBinVolume) fval *= binVolume;
1484 if (i % NSAMPLE == 0) {
1485 std::cout <<
"evt " << i <<
" x = [ ";
1486 for (
unsigned int j = 0; j < func.NDim(); ++j) std::cout << x[j] <<
" , ";
1488 if (fitOpt.fIntegral) {
1489 std::cout <<
"x2 = [ ";
1490 for (
unsigned int j = 0; j < func.NDim(); ++j) std::cout << data.BinUpEdge(i)[j] <<
" , ";
1493 std::cout <<
" y = " << y <<
" fval = " << fval << std::endl;
1500 fval = std::max(fval, 0.0);
1502 double nloglike = 0;
1511 double weight = 1.0;
1513 double error = data.Error(i);
1514 weight = (error * error) / y;
1515 nloglike += weight * y * ( ROOT::Math::Util::EvalLog(y) - ROOT::Math::Util::EvalLog(fval) );
1519 weight = data.SumOfError2()/ data.SumOfContent();
1522 nloglike += weight * ( fval - y);
1530 if (extended) nloglike = fval - y;
1533 nloglike += y * (ROOT::Math::Util::EvalLog(y) - ROOT::Math::Util::EvalLog(fval));
1538 R__LOCKGUARD(gROOTMutex);
1539 std::cout <<
" nll = " << nloglike << std::endl;
1546 auto redFunction = [](
const std::vector<double> &objs) {
1547 return std::accumulate(objs.begin(), objs.end(),
double{});
1553 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1554 Warning(
"FitUtil::EvaluatePoissonLogL",
"Multithread execution policy requires IMT, which is disabled. Changing "
1555 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1556 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1561 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1562 for (
unsigned int i = 0; i < n; ++i) {
1563 res += mapFunction(i);
1566 }
else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1567 ROOT::TThreadExecutor pool;
1568 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(data.Size());
1569 res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, n), redFunction, chunks);
1575 Error(
"FitUtil::EvaluatePoissonLogL",
1576 "Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1580 std::cout <<
"Loglikelihood = " << res << std::endl;
1586 void FitUtil::EvaluatePoissonLogLGradient(
const IModelFunction &f,
const BinData &data,
const double *p,
double *grad,
1587 unsigned int &, ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks)
1591 const IGradModelFunction *fg =
dynamic_cast<const IGradModelFunction *
>(&f);
1592 assert(fg !=
nullptr);
1594 const IGradModelFunction &func = *fg;
1596 #ifdef USE_PARAMCACHE
1597 (
const_cast<IGradModelFunction &
>(func)).SetParameters(p);
1600 const DataOptions &fitOpt = data.Opt();
1601 bool useBinIntegral = fitOpt.fIntegral && data.HasBinEdges();
1602 bool useBinVolume = (fitOpt.fBinVolume && data.HasBinEdges());
1604 double wrefVolume = 1.0;
1605 if (useBinVolume && fitOpt.fNormBinVolume)
1606 wrefVolume /= data.RefVolume();
1608 ROOT::Math::IntegrationOneDim::Type igType = ROOT::Math::IntegrationOneDim::kDEFAULT;
1609 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1611 igType = ROOT::Math::IntegrationOneDim::kGAUSS;
1614 IntegralEvaluator<> igEval(func, p, useBinIntegral, igType);
1616 unsigned int npar = func.NPar();
1617 unsigned initialNPoints = data.Size();
1619 auto mapFunction = [&](
const unsigned int i) {
1621 std::vector<double> gradFunc(npar);
1622 std::vector<double> pointContribution(npar);
1624 const auto x1 = data.GetCoordComponent(i, 0);
1625 const auto y = data.Value(i);
1626 auto invError = data.Error(i);
1628 invError = (invError != 0.0) ? 1.0 / invError : 1;
1632 const double *x =
nullptr;
1633 std::vector<double> xc;
1635 unsigned ndim = data.NDim();
1636 double binVolume = 1.0;
1638 const double *x2 = data.BinUpEdge(i);
1641 for (
unsigned int j = 0; j < ndim; ++j) {
1642 auto x1_j = *data.GetCoordComponent(i, j);
1643 binVolume *= std::abs(x2[j] - x1_j);
1644 xc[j] = 0.5 * (x2[j] + x1_j);
1650 binVolume *= wrefVolume;
1651 }
else if (ndim > 1) {
1654 for (
unsigned int j = 1; j < ndim; ++j)
1655 xc[j] = *data.GetCoordComponent(i, j);
1661 if (!useBinIntegral) {
1663 func.ParameterGradient(x, p, &gradFunc[0]);
1667 auto x2 = data.BinUpEdge(i);
1668 fval = igEval(x, x2);
1669 CalculateGradientIntegral(func, x, x2, p, &gradFunc[0]);
1676 R__LOCKGUARD(gROOTMutex);
1677 if (i < 5 || (i > data.Size()-5) ) {
1678 if (data.NDim() > 1) std::cout << i <<
" x " << x[0] <<
" y " << x[1] <<
" func " << fval
1679 <<
" gradient " << gradFunc[0] <<
" " << gradFunc[1] <<
" " << gradFunc[3] << std::endl;
1680 else std::cout << i <<
" x " << x[0] <<
" gradient " << gradFunc[0] <<
" " << gradFunc[1] <<
" " << gradFunc[3] << std::endl;
1686 for (
unsigned int ipar = 0; ipar < npar; ++ipar) {
1690 gradFunc[ipar] *= binVolume;
1694 pointContribution[ipar] = gradFunc[ipar] * (1. - y / fval);
1695 else if (gradFunc[ipar] != 0) {
1696 const double kdmax1 = std::sqrt(std::numeric_limits<double>::max());
1697 const double kdmax2 = std::numeric_limits<double>::max() / (4 * initialNPoints);
1698 double gg = kdmax1 * gradFunc[ipar];
1700 gg = std::min(gg, kdmax2);
1702 gg = std::max(gg, -kdmax2);
1703 pointContribution[ipar] = -gg;
1708 return pointContribution;
1712 auto redFunction = [&](
const std::vector<std::vector<double>> &pointContributions) {
1713 std::vector<double> result(npar);
1715 for (
auto const &pointContribution : pointContributions) {
1716 for (
unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
1717 result[parameterIndex] += pointContribution[parameterIndex];
1723 std::vector<double> g(npar);
1727 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1728 Warning(
"FitUtil::EvaluatePoissonLogLGradient",
1729 "Multithread execution policy requires IMT, which is disabled. Changing "
1730 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1731 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1735 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1736 std::vector<std::vector<double>> allGradients(initialNPoints);
1737 for (
unsigned int i = 0; i < initialNPoints; ++i) {
1738 allGradients[i] = mapFunction(i);
1740 g = redFunction(allGradients);
1743 else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1744 ROOT::TThreadExecutor pool;
1745 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(initialNPoints);
1746 g = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, initialNPoints), redFunction, chunks);
1755 Error(
"FitUtil::EvaluatePoissonLogLGradient",
1756 "Execution policy unknown. Avalaible choices:\n 0: Serial (default)\n 1: MultiThread (requires IMT)\n");
1765 std::copy(g.begin(), g.end(), grad);
1768 std::cout <<
"***** Final gradient : ";
1769 for (
unsigned int ii = 0; ii< npar; ++ii) std::cout << grad[ii] <<
" ";
1776 unsigned FitUtil::setAutomaticChunking(
unsigned nEvents){
1777 auto ncpu = ROOT::GetImplicitMTPoolSize();
1778 if (nEvents/ncpu < 1000)
return ncpu;
1779 return nEvents/1000;