13 #ifndef ROOT_Fit_FitUtil
14 #define ROOT_Fit_FitUtil
35 #define USE_PARAMCACHE
42 vecCore::Mask<T> Int2Mask(
unsigned i)
45 for (
unsigned j = 0; j < vecCore::VectorSize<T>(); j++)
46 vecCore::Set<T>(x, j, j);
47 return vecCore::Mask<T>(x < T(i));
64 typedef ROOT::Math::IParamMultiFunction IModelFunction;
65 typedef ROOT::Math::IParamMultiGradFunction IGradModelFunction;
68 using IGradModelFunctionTempl = ROOT::Math::IParamMultiGradFunctionTempl<T>;
71 using IModelFunctionTempl = ROOT::Math::IParamMultiFunctionTempl<T>;
77 LikelihoodAux(T logv = {}, T w = {}, T w2 = {}) : logvalue(logv), weight(w), weight2(w2) {}
79 LikelihoodAux operator+(
const LikelihoodAux &l)
const
81 return LikelihoodAux<T>(logvalue + l.logvalue, weight + l.weight, weight2 + l.weight2);
84 LikelihoodAux &operator+=(
const LikelihoodAux &l)
86 logvalue += l.logvalue;
98 class LikelihoodAux<double> {
100 LikelihoodAux(
double logv = 0.0,
double w = 0.0,
double w2 = 0.0) : logvalue(logv), weight(w), weight2(w2){};
102 LikelihoodAux operator+(
const LikelihoodAux &l)
const
104 return LikelihoodAux<double>(logvalue + l.logvalue, weight + l.weight, weight2 + l.weight2);
107 LikelihoodAux &operator+=(
const LikelihoodAux &l)
109 logvalue += l.logvalue;
111 weight2 += l.weight2;
127 template <
class ParamFunc = ROOT::Math::IParamMultiFunctionTempl<
double>>
128 class IntegralEvaluator {
131 IntegralEvaluator(
const ParamFunc &func,
const double *p,
bool useIntegral =
true,
132 ROOT::Math::IntegrationOneDim::Type igType = ROOT::Math::IntegrationOneDim::kDEFAULT)
133 : fDim(0), fParams(0), fFunc(0), fIg1Dim(0), fIgNDim(0), fFunc1Dim(0), fFuncNDim(0)
136 SetFunction(func, p, igType);
140 void SetFunction(
const ParamFunc &func,
const double *p = 0,
141 ROOT::Math::IntegrationOneDim::Type igType = ROOT::Math::IntegrationOneDim::kDEFAULT)
155 new ROOT::Math::WrappedMemFunction<IntegralEvaluator, double (IntegralEvaluator::*)(double) const>(
156 *
this, &IntegralEvaluator::F1);
157 fIg1Dim =
new ROOT::Math::IntegratorOneDim(igType);
159 fIg1Dim->SetFunction(static_cast<const ROOT::Math::IGenFunction &>(*fFunc1Dim));
160 }
else if (fDim > 1) {
162 new ROOT::Math::WrappedMemMultiFunction<IntegralEvaluator, double (IntegralEvaluator::*)(
const double *)
163 const>(*
this, &IntegralEvaluator::FN, fDim);
164 fIgNDim =
new ROOT::Math::IntegratorMultiDim();
165 fIgNDim->SetFunction(*fFuncNDim);
170 void SetParameters(
const double *p)
190 double F1(
double x)
const
193 return ExecFunc(fFunc, &xx, fParams);
196 double FN(
const double *x)
const {
return ExecFunc(fFunc, x, fParams); }
198 double Integral(
const double *x1,
const double *x2)
201 return (fIg1Dim) ? fIg1Dim->Integral(*x1, *x2) : fIgNDim->Integral(x1, x2);
204 double operator()(
const double *x1,
const double *x2)
208 double dV = *x2 - *x1;
209 return fIg1Dim->Integral(*x1, *x2) / dV;
210 }
else if (fIgNDim) {
212 for (
unsigned int i = 0; i < fDim; ++i)
213 dV *= (x2[i] - x1[i]);
214 return fIgNDim->Integral(x1, x2) / dV;
224 inline double ExecFunc(T *f,
const double *x,
const double *p)
const
229 #ifdef R__HAS_VECCORE
230 inline double ExecFunc(
const IModelFunctionTempl<ROOT::Double_v> *f,
const double *x,
const double *p)
const
234 vecCore::Load<ROOT::Double_v>(xx, x);
235 const double *p0 = p;
236 auto res = (*f)(&xx, (
const double *)p0);
237 return vecCore::Get<ROOT::Double_v>(res, 0);
239 std::vector<ROOT::Double_v> xx(fDim);
240 for (
unsigned int i = 0; i < fDim; ++i) {
241 vecCore::Load<ROOT::Double_v>(xx[i], x + i);
243 auto res = (*f)(xx.data(), p);
244 return vecCore::Get<ROOT::Double_v>(res, 0);
250 IntegralEvaluator(
const IntegralEvaluator &rhs);
251 IntegralEvaluator &operator=(
const IntegralEvaluator &rhs);
254 const double *fParams;
257 const ParamFunc *fFunc;
258 ROOT::Math::IntegratorOneDim *fIg1Dim;
259 ROOT::Math::IntegratorMultiDim *fIgNDim;
260 ROOT::Math::IGenFunction *fFunc1Dim;
261 ROOT::Math::IMultiGenFunction *fFuncNDim;
270 double EvaluateChi2(
const IModelFunction &func,
const BinData &data,
const double *x,
unsigned int &nPoints,
271 ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0);
278 double EvaluateChi2Effective(
const IModelFunction &func,
const BinData &data,
const double *x,
unsigned int &nPoints);
284 void EvaluateChi2Gradient(
const IModelFunction &func,
const BinData &data,
const double *x,
double *grad,
285 unsigned int &nPoints,
286 ROOT::Fit::ExecutionPolicy executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial,
287 unsigned nChunks = 0);
293 double EvaluateLogL(
const IModelFunction &func,
const UnBinData &data,
const double *p,
int iWeight,
bool extended,
294 unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0);
300 void EvaluateLogLGradient(
const IModelFunction &func,
const UnBinData &data,
const double *x,
double *grad,
301 unsigned int &nPoints,
302 ROOT::Fit::ExecutionPolicy executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial,
303 unsigned nChunks = 0);
316 double EvaluatePoissonLogL(
const IModelFunction &func,
const BinData &data,
const double *x,
int iWeight,
317 bool extended,
unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy,
318 unsigned nChunks = 0);
324 void EvaluatePoissonLogLGradient(
const IModelFunction &func,
const BinData &data,
const double *x,
double *grad,
325 unsigned int &nPoints,
326 ROOT::Fit::ExecutionPolicy executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial,
327 unsigned nChunks = 0);
337 double EvaluateChi2Residual(
const IModelFunction &func,
const BinData &data,
const double *x,
unsigned int ipoint,
347 EvaluatePdf(
const IModelFunction &func,
const UnBinData &data,
const double *x,
unsigned int ipoint,
double *g = 0);
349 #ifdef R__HAS_VECCORE
350 template <class NotCompileIfScalarBackend = std::enable_if<!(std::is_same<double, ROOT::Double_v>::value)>>
351 double EvaluatePdf(
const IModelFunctionTempl<ROOT::Double_v> &func,
const UnBinData &data,
const double *p,
unsigned int i,
double *) {
355 const auto x = vecCore::FromPtr<ROOT::Double_v>(data.GetCoordComponent(i, 0));
356 auto fval = func(&x, p);
357 auto logPdf = ROOT::Math::Util::EvalLog(fval);
358 return vecCore::Get<ROOT::Double_v>(logPdf, 0);
368 double EvaluatePoissonBinPdf(
const IModelFunction & func,
const BinData & data,
const double * x,
unsigned int ipoint,
double * g = 0);
370 unsigned setAutomaticChunking(
unsigned nEvents);
374 #ifdef R__HAS_VECCORE
375 static double EvalChi2(
const IModelFunctionTempl<T> &func,
const BinData &data,
const double *p,
376 unsigned int &nPoints, ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0)
385 unsigned int n = data.Size();
386 nPoints = data.Size();
389 #ifdef USE_PARAMCACHE
390 (
const_cast<IModelFunctionTempl<T> &
>(func)).SetParameters(p);
397 const DataOptions &fitOpt = data.Opt();
398 if (fitOpt.fBinVolume || fitOpt.fIntegral || fitOpt.fExpErrors)
399 Error(
"FitUtil::EvaluateChi2",
"The vectorized implementation doesn't support Integrals, BinVolume or ExpErrors\n. Aborting operation.");
401 (
const_cast<IModelFunctionTempl<T> &
>(func)).SetParameters(p);
403 double maxResValue = std::numeric_limits<double>::max() / n;
404 std::vector<double> ones{1, 1, 1, 1};
405 auto vecSize = vecCore::VectorSize<T>();
407 auto mapFunction = [&](
unsigned int i) {
409 T x1, y, invErrorVec;
410 vecCore::Load<T>(x1, data.GetCoordComponent(i * vecSize, 0));
411 vecCore::Load<T>(y, data.ValuePtr(i * vecSize));
412 const auto invError = data.ErrorPtr(i * vecSize);
413 auto invErrorptr = (invError !=
nullptr) ? invError : &ones.front();
414 vecCore::Load<T>(invErrorVec, invErrorptr);
418 if(data.NDim() > 1) {
419 xc.resize(data.NDim());
421 for (
unsigned int j = 1; j < data.NDim(); ++j)
422 vecCore::Load<T>(xc[j], data.GetCoordComponent(i * vecSize, j));
430 #ifdef USE_PARAMCACHE
436 T tmp = (y - fval) * invErrorVec;
441 auto m = vecCore::Mask_v<T>(chi2 > maxResValue);
443 vecCore::MaskedAssign<T>(chi2, m, maxResValue);
448 auto redFunction = [](
const std::vector<T> &objs) {
449 return std::accumulate(objs.begin(), objs.end(), T{});
456 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
457 Warning(
"FitUtil::EvaluateChi2",
"Multithread execution policy requires IMT, which is disabled. Changing "
458 "to ROOT::Fit::ExecutionPolicy::kSerial.");
459 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
464 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
465 ROOT::TSequentialExecutor pool;
466 res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, data.Size()/vecSize), redFunction);
468 }
else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
469 ROOT::TThreadExecutor pool;
470 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(data.Size() / vecSize);
471 res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, data.Size() / vecSize), redFunction, chunks);
474 Error(
"FitUtil::EvaluateChi2",
"Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
478 if (data.Size() % vecSize != 0)
479 vecCore::MaskedAssign(res, vecCore::Int2Mask<T>(data.Size() % vecSize),
480 res + mapFunction(data.Size() / vecSize));
482 return vecCore::ReduceAdd(res);
485 static double EvalLogL(
const IModelFunctionTempl<T> &func,
const UnBinData &data,
const double *
const p,
486 int iWeight,
bool extended,
unsigned int &nPoints,
487 ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0)
490 unsigned int n = data.Size();
491 nPoints = data.Size();
494 bool normalizeFunc =
false;
497 #ifdef USE_PARAMCACHE
498 (
const_cast<IModelFunctionTempl<T> &
>(func)).SetParameters(p);
504 if (!normalizeFunc) {
505 if (data.NDim() == 1) {
507 vecCore::Load<T>(x, data.GetCoordComponent(0, 0));
511 std::vector<T> x(data.NDim());
512 for (
unsigned int j = 0; j < data.NDim(); ++j)
513 vecCore::Load<T>(x[j], data.GetCoordComponent(0, j));
523 std::vector<double> xmin(data.NDim());
524 std::vector<double> xmax(data.NDim());
525 IntegralEvaluator<IModelFunctionTempl<T>> igEval(func, p,
true);
527 if (data.Range().Size() > 0) {
529 for (
unsigned int ir = 0; ir < data.Range().Size(); ++ir) {
530 data.Range().GetRange(&xmin[0], &xmax[0], ir);
531 norm += igEval.Integral(xmin.data(), xmax.data());
535 data.Range().GetRange(&xmin[0], &xmax[0]);
538 vecCore::Load<T>(xmin_v, xmin.data());
539 vecCore::Load<T>(xmax_v, xmax.data());
540 if (vecCore::ReduceAdd(func(&xmin_v, p)) != 0 || vecCore::ReduceAdd(func(&xmax_v, p)) != 0) {
541 MATH_ERROR_MSG(
"FitUtil::EvaluateLogLikelihood",
"A range has not been set and the function is not zero at +/- inf");
544 norm = igEval.Integral(&xmin[0], &xmax[0]);
550 auto vecSize = vecCore::VectorSize<T>();
551 unsigned int numVectors = n / vecSize;
553 auto mapFunction = [ &, p](
const unsigned i) {
561 vecCore::Load<T>(x1, data.GetCoordComponent(i * vecSize, 0));
562 const T *x =
nullptr;
563 unsigned int ndim = data.NDim();
568 for (
unsigned int j = 1; j < ndim; ++j)
569 vecCore::Load<T>(xc[j], data.GetCoordComponent(i * vecSize, j));
575 #ifdef USE_PARAMCACHE
582 if (i < 5 || (i > numVectors-5) ) {
583 if (ndim == 1) std::cout << i <<
" x " << x[0] <<
" fval = " << fval;
584 else std::cout << i <<
" x " << x[0] <<
" y " << x[1] <<
" fval = " << fval;
588 if (normalizeFunc) fval = fval * (1 / norm);
591 auto logval = ROOT::Math::Util::EvalLog(fval);
594 if (data.WeightsPtr(i) ==
nullptr)
597 vecCore::Load<T>(weight, data.WeightsPtr(i*vecSize));
604 W2 = weight * weight;
609 if (i < 5 || (i > numVectors-5) ) {
610 std::cout <<
" " << fval <<
" logfval " << logval << std::endl;
614 return LikelihoodAux<T>(logval, W, W2);
617 auto redFunction = [](
const std::vector<LikelihoodAux<T>> &objs) {
618 return std::accumulate(objs.begin(), objs.end(), LikelihoodAux<T>(),
619 [](
const LikelihoodAux<T> &l1,
const LikelihoodAux<T> &l2) {
628 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
629 Warning(
"FitUtil::EvaluateLogL",
"Multithread execution policy requires IMT, which is disabled. Changing "
630 "to ROOT::Fit::ExecutionPolicy::kSerial.");
631 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
638 ROOT::Fit::FitUtil::LikelihoodAux<T> resArray;
639 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
640 ROOT::TSequentialExecutor pool;
641 resArray = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, data.Size() / vecSize), redFunction);
643 }
else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
644 ROOT::TThreadExecutor pool;
645 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking( numVectors);
646 resArray = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, data.Size() / vecSize), redFunction, chunks);
649 Error(
"FitUtil::EvaluateLogL",
"Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
652 logl_v = resArray.logvalue;
653 sumW_v = resArray.weight;
654 sumW2_v = resArray.weight2;
657 unsigned int remainingPoints = n % vecSize;
658 if (remainingPoints > 0) {
659 auto remainingPointsContribution = mapFunction(numVectors);
661 auto remainingMask = vecCore::Int2Mask<T>(remainingPoints);
662 vecCore::MaskedAssign(logl_v, remainingMask, logl_v + remainingPointsContribution.logvalue);
663 vecCore::MaskedAssign(sumW_v, remainingMask, sumW_v + remainingPointsContribution.weight);
664 vecCore::MaskedAssign(sumW2_v, remainingMask, sumW2_v + remainingPointsContribution.weight2);
669 double logl = vecCore::ReduceAdd(logl_v);
670 double sumW = vecCore::ReduceAdd(sumW_v);
671 double sumW2 = vecCore::ReduceAdd(sumW2_v);
675 double extendedTerm = 0;
679 if (!normalizeFunc) {
680 IntegralEvaluator<IModelFunctionTempl<T>> igEval(func, p,
true);
681 std::vector<double> xmin(data.NDim());
682 std::vector<double> xmax(data.NDim());
685 if (data.Range().Size() > 0) {
687 for (
unsigned int ir = 0; ir < data.Range().Size(); ++ir) {
688 data.Range().GetRange(&xmin[0], &xmax[0], ir);
689 nuTot += igEval.Integral(xmin.data(), xmax.data());
693 data.Range().GetRange(&xmin[0], &xmax[0]);
696 vecCore::Load<T>(xmin_v, xmin.data());
697 vecCore::Load<T>(xmax_v, xmax.data());
698 if (vecCore::ReduceAdd(func(&xmin_v, p)) != 0 || vecCore::ReduceAdd(func(&xmax_v, p)) != 0) {
699 MATH_ERROR_MSG(
"FitUtil::EvaluateLogLikelihood",
"A range has not been set and the function is not zero at +/- inf");
702 nuTot = igEval.Integral(&xmin[0], &xmax[0]);
708 extendedTerm = - nuTot;
712 extendedTerm = - (sumW2 / sumW) * nuTot;
717 extendedTerm = - nuTot + double(n) * ROOT::Math::Util::EvalLog(nuTot);
721 logl += extendedTerm;
725 std::cout <<
"Evaluated log L for parameters (";
726 for (
unsigned int ip = 0; ip < func.NPar(); ++ip)
727 std::cout <<
" " << p[ip];
728 std::cout <<
") nll = " << -logl << std::endl;
735 static double EvalPoissonLogL(
const IModelFunctionTempl<T> &func,
const BinData &data,
const double *p,
736 int iWeight,
bool extended,
unsigned int,
737 ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0)
754 #ifdef USE_PARAMCACHE
755 (
const_cast<IModelFunctionTempl<T> &
>(func)).SetParameters(p);
757 auto vecSize = vecCore::VectorSize<T>();
759 const DataOptions &fitOpt = data.Opt();
760 if (fitOpt.fExpErrors || fitOpt.fIntegral)
761 Error(
"FitUtil::EvaluateChi2",
762 "The vectorized implementation doesn't support Integrals or BinVolume\n. Aborting operation.");
763 bool useW2 = (iWeight == 2);
765 auto mapFunction = [&](
unsigned int i) {
767 vecCore::Load<T>(y, data.ValuePtr(i * vecSize));
770 if (data.NDim() > 1) {
771 std::vector<T> x(data.NDim());
772 for (
unsigned int j = 0; j < data.NDim(); ++j)
773 vecCore::Load<T>(x[j], data.GetCoordComponent(i * vecSize, j));
774 #ifdef USE_PARAMCACHE
775 fval = func(x.data());
777 fval = func(x.data(), p);
782 vecCore::Load<T>(x, data.GetCoordComponent(i * vecSize, 0));
783 #ifdef USE_PARAMCACHE
792 vecCore::MaskedAssign<T>(fval, fval < 0.0, 0.0);
802 assert (data.GetErrorType() != ROOT::Fit::BinData::ErrorType::kNoError);
804 vecCore::Load<T>(error, data.ErrorPtr(i * vecSize));
806 auto m = vecCore::Mask_v<T>(y != 0.0);
807 auto weight = vecCore::Blend(m,(error * error) / y, T(data.SumOfError2()/ data.SumOfContent()) );
809 nloglike = weight * ( fval - y);
811 vecCore::MaskedAssign<T>(nloglike, y != 0, nloglike + weight * y *( ROOT::Math::Util::EvalLog(y) - ROOT::Math::Util::EvalLog(fval)) );
818 if (extended) nloglike = fval - y;
820 vecCore::MaskedAssign<T>(
821 nloglike, y > 0, nloglike + y * (ROOT::Math::Util::EvalLog(y) - ROOT::Math::Util::EvalLog(fval)));
828 auto redFunction = [](
const std::vector<T> &objs) {
return std::accumulate(objs.begin(), objs.end(), T{}); };
833 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
834 Warning(
"FitUtil::Evaluate<T>::EvalPoissonLogL",
835 "Multithread execution policy requires IMT, which is disabled. Changing "
836 "to ROOT::Fit::ExecutionPolicy::kSerial.");
837 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
842 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
843 for (
unsigned int i = 0; i < (data.Size() / vecSize); i++) {
844 res += mapFunction(i);
847 }
else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
848 ROOT::TThreadExecutor pool;
849 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(data.Size() / vecSize);
850 res = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, data.Size() / vecSize), redFunction, chunks);
854 "FitUtil::Evaluate<T>::EvalPoissonLogL",
855 "Execution policy unknown. Avalaible choices:\n ROOT::Fit::ExecutionPolicy::kSerial (default)\n ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
859 if (data.Size() % vecSize != 0)
860 vecCore::MaskedAssign(res, vecCore::Int2Mask<T>(data.Size() % vecSize),
861 res + mapFunction(data.Size() / vecSize));
863 return vecCore::ReduceAdd(res);
866 static double EvalChi2Effective(
const IModelFunctionTempl<T> &,
const BinData &,
const double *,
unsigned int &)
868 Error(
"FitUtil::Evaluate<T>::EvalChi2Effective",
"The vectorized evaluation of the Chi2 with coordinate errors is still not supported");
875 static vecCore::Mask<T> CheckInfNaNValues(T &rval)
877 auto mask = rval > -vecCore::NumericLimits<T>::Max() && rval < vecCore::NumericLimits<T>::Max();
880 vecCore::MaskedAssign(rval, !mask, +vecCore::NumericLimits<T>::Max());
883 vecCore::MaskedAssign(rval, !mask && rval < 0, -vecCore::NumericLimits<T>::Max());
888 static void EvalChi2Gradient(
const IModelFunctionTempl<T> &f,
const BinData &data,
const double *p,
double *grad,
889 unsigned int &nPoints,
890 ROOT::Fit::ExecutionPolicy executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial,
891 unsigned nChunks = 0)
899 if (data.HaveCoordErrors()) {
900 MATH_ERROR_MSG(
"FitUtil::EvaluateChi2Gradient",
901 "Error on the coordinates are not used in calculating Chi2 gradient");
905 const IGradModelFunctionTempl<T> *fg =
dynamic_cast<const IGradModelFunctionTempl<T> *
>(&f);
906 assert(fg !=
nullptr);
908 const IGradModelFunctionTempl<T> &func = *fg;
910 const DataOptions &fitOpt = data.Opt();
911 if (fitOpt.fBinVolume || fitOpt.fIntegral || fitOpt.fExpErrors)
912 Error(
"FitUtil::EvaluateChi2Gradient",
"The vectorized implementation doesn't support Integrals,"
913 "BinVolume or ExpErrors\n. Aborting operation.");
915 unsigned int npar = func.NPar();
916 auto vecSize = vecCore::VectorSize<T>();
917 unsigned initialNPoints = data.Size();
918 unsigned numVectors = initialNPoints / vecSize;
921 std::vector<vecCore::Mask<T>> validPointsMasks(numVectors + 1);
923 auto mapFunction = [&](
const unsigned int i) {
925 std::vector<T> gradFunc(npar);
926 std::vector<T> pointContributionVec(npar);
930 vecCore::Load<T>(x1, data.GetCoordComponent(i * vecSize, 0));
931 vecCore::Load<T>(y, data.ValuePtr(i * vecSize));
932 const auto invErrorPtr = data.ErrorPtr(i * vecSize);
934 if (invErrorPtr ==
nullptr)
937 vecCore::Load<T>(invError, invErrorPtr);
943 const T *x =
nullptr;
945 unsigned int ndim = data.NDim();
952 for (
unsigned int j = 1; j < ndim; ++j)
953 vecCore::Load<T>(xc[j], data.GetCoordComponent(i * vecSize, j));
960 func.ParameterGradient(x, p, &gradFunc[0]);
962 validPointsMasks[i] = CheckInfNaNValues(fval);
963 if (vecCore::MaskEmpty(validPointsMasks[i])) {
965 return pointContributionVec;
969 for (
unsigned int ipar = 0; ipar < npar; ++ipar) {
972 validPointsMasks[i] = CheckInfNaNValues(gradFunc[ipar]);
974 if (vecCore::MaskEmpty(validPointsMasks[i])) {
979 vecCore::MaskedAssign(pointContributionVec[ipar], validPointsMasks[i],
980 -2.0 * (y - fval) * invError * invError * gradFunc[ipar]);
983 return pointContributionVec;
987 auto redFunction = [&](
const std::vector<std::vector<T>> &partialResults) {
988 std::vector<T> result(npar);
990 for (
auto const &pointContributionVec : partialResults) {
991 for (
unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
992 result[parameterIndex] += pointContributionVec[parameterIndex];
998 std::vector<T> gVec(npar);
999 std::vector<double> g(npar);
1006 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1007 Warning(
"FitUtil::EvaluateChi2Gradient",
1008 "Multithread execution policy requires IMT, which is disabled. Changing "
1009 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1010 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1014 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1015 ROOT::TSequentialExecutor pool;
1016 gVec = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, numVectors), redFunction);
1019 else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1020 ROOT::TThreadExecutor pool;
1021 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(numVectors);
1022 gVec = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, numVectors), redFunction, chunks);
1027 "FitUtil::EvaluateChi2Gradient",
1028 "Execution policy unknown. Avalaible choices:\n 0: Serial (default)\n 1: MultiThread (requires IMT)\n");
1032 unsigned int remainingPoints = initialNPoints % vecSize;
1033 if (remainingPoints > 0) {
1034 auto remainingPointsContribution = mapFunction(numVectors);
1036 auto remainingMask = vecCore::Int2Mask<T>(remainingPoints);
1037 for (
unsigned int param = 0; param < npar; param++) {
1038 vecCore::MaskedAssign(gVec[param], remainingMask, gVec[param] + remainingPointsContribution[param]);
1042 for (
unsigned int param = 0; param < npar; param++) {
1043 grad[param] = vecCore::ReduceAdd(gVec[param]);
1047 nPoints = initialNPoints;
1049 if (std::any_of(validPointsMasks.begin(), validPointsMasks.end(),
1050 [](vecCore::Mask<T> validPoints) {
return !vecCore::MaskFull(validPoints); })) {
1051 unsigned nRejected = 0;
1053 for (
const auto &mask : validPointsMasks) {
1054 for (
unsigned int i = 0; i < vecSize; i++) {
1055 nRejected += !vecCore::Get(mask, i);
1059 assert(nRejected <= initialNPoints);
1060 nPoints = initialNPoints - nRejected;
1062 if (nPoints < npar) {
1063 MATH_ERROR_MSG(
"FitUtil::EvaluateChi2Gradient",
1064 "Too many points rejected for overflow in gradient calculation");
1069 static double EvalChi2Residual(
const IModelFunctionTempl<T> &,
const BinData &,
const double *,
unsigned int,
double *)
1071 Error(
"FitUtil::Evaluate<T>::EvalChi2Residual",
"The vectorized evaluation of the Chi2 with the ith residual is still not supported");
1077 static double EvalPoissonBinPdf(
const IModelFunctionTempl<T> &,
const BinData &,
const double *,
unsigned int ,
double * ) {
1078 Error(
"FitUtil::Evaluate<T>::EvaluatePoissonBinPdf",
"The vectorized evaluation of the BinnedLikelihood fit evaluated point by point is still not supported");
1083 EvalPoissonLogLGradient(
const IModelFunctionTempl<T> &f,
const BinData &data,
const double *p,
double *grad,
1085 ROOT::Fit::ExecutionPolicy executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial,
1086 unsigned nChunks = 0)
1090 const IGradModelFunctionTempl<T> *fg =
dynamic_cast<const IGradModelFunctionTempl<T> *
>(&f);
1091 assert(fg !=
nullptr);
1093 const IGradModelFunctionTempl<T> &func = *fg;
1095 (
const_cast<IGradModelFunctionTempl<T> &
>(func)).SetParameters(p);
1098 const DataOptions &fitOpt = data.Opt();
1099 if (fitOpt.fBinVolume || fitOpt.fIntegral || fitOpt.fExpErrors)
1100 Error(
"FitUtil::EvaluatePoissonLogLGradient",
"The vectorized implementation doesn't support Integrals,"
1101 "BinVolume or ExpErrors\n. Aborting operation.");
1103 unsigned int npar = func.NPar();
1104 auto vecSize = vecCore::VectorSize<T>();
1105 unsigned initialNPoints = data.Size();
1106 unsigned numVectors = initialNPoints / vecSize;
1108 auto mapFunction = [&](
const unsigned int i) {
1110 std::vector<T> gradFunc(npar);
1111 std::vector<T> pointContributionVec(npar);
1115 vecCore::Load<T>(x1, data.GetCoordComponent(i * vecSize, 0));
1116 vecCore::Load<T>(y, data.ValuePtr(i * vecSize));
1120 const T *x =
nullptr;
1122 unsigned ndim = data.NDim();
1127 for (
unsigned int j = 1; j < ndim; ++j)
1128 vecCore::Load<T>(xc[j], data.GetCoordComponent(i * vecSize, j));
1135 func.ParameterGradient(x, p, &gradFunc[0]);
1138 for (
unsigned int ipar = 0; ipar < npar; ++ipar) {
1139 vecCore::Mask<T> positiveValuesMask = fval > 0;
1142 vecCore::MaskedAssign(pointContributionVec[ipar], positiveValuesMask, gradFunc[ipar] * (1. - y / fval));
1144 vecCore::Mask<T> validNegativeValuesMask = !positiveValuesMask && gradFunc[ipar] != 0;
1146 if (!vecCore::MaskEmpty(validNegativeValuesMask)) {
1147 const T kdmax1 = vecCore::math::Sqrt(vecCore::NumericLimits<T>::Max());
1148 const T kdmax2 = vecCore::NumericLimits<T>::Max() / (4 * initialNPoints);
1149 T gg = kdmax1 * gradFunc[ipar];
1150 pointContributionVec[ipar] = -vecCore::Blend(gg > 0, vecCore::math::Min(gg, kdmax2), vecCore::math::Max(gg, -kdmax2));
1154 #ifdef DEBUG_FITUTIL
1156 R__LOCKGUARD(gROOTMutex);
1157 if (i < 5 || (i > data.Size()-5) ) {
1158 if (data.NDim() > 1) std::cout << i <<
" x " << x[0] <<
" y " << x[1];
1159 else std::cout << i <<
" x " << x[0];
1160 std::cout <<
" func " << fval <<
" gradient ";
1161 for (
unsigned int ii = 0; ii < npar; ++ii) std::cout <<
" " << pointContributionVec[ii];
1167 return pointContributionVec;
1171 auto redFunction = [&](
const std::vector<std::vector<T>> &partialResults) {
1172 std::vector<T> result(npar);
1174 for (
auto const &pointContributionVec : partialResults) {
1175 for (
unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
1176 result[parameterIndex] += pointContributionVec[parameterIndex];
1182 std::vector<T> gVec(npar);
1189 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1190 Warning(
"FitUtil::EvaluatePoissonLogLGradient",
1191 "Multithread execution policy requires IMT, which is disabled. Changing "
1192 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1193 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1197 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1198 ROOT::TSequentialExecutor pool;
1199 gVec = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, numVectors), redFunction);
1202 else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1203 ROOT::TThreadExecutor pool;
1204 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(numVectors);
1205 gVec = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, numVectors), redFunction, chunks);
1209 Error(
"FitUtil::EvaluatePoissonLogLGradient",
"Execution policy unknown. Avalaible choices:\n "
1210 "ROOT::Fit::ExecutionPolicy::kSerial (default)\n "
1211 "ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1216 unsigned int remainingPoints = initialNPoints % vecSize;
1217 if (remainingPoints > 0) {
1218 auto remainingPointsContribution = mapFunction(numVectors);
1220 auto remainingMask = vecCore::Int2Mask<T>(remainingPoints);
1221 for (
unsigned int param = 0; param < npar; param++) {
1222 vecCore::MaskedAssign(gVec[param], remainingMask, gVec[param] + remainingPointsContribution[param]);
1226 for (
unsigned int param = 0; param < npar; param++) {
1227 grad[param] = vecCore::ReduceAdd(gVec[param]);
1230 #ifdef DEBUG_FITUTIL
1231 std::cout <<
"***** Final gradient : ";
1232 for (
unsigned int ii = 0; ii< npar; ++ii) std::cout << grad[ii] <<
" ";
1238 static void EvalLogLGradient(
const IModelFunctionTempl<T> &f,
const UnBinData &data,
const double *p,
1239 double *grad,
unsigned int &,
1240 ROOT::Fit::ExecutionPolicy executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial,
1241 unsigned nChunks = 0)
1245 const IGradModelFunctionTempl<T> *fg =
dynamic_cast<const IGradModelFunctionTempl<T> *
>(&f);
1246 assert(fg !=
nullptr);
1248 const IGradModelFunctionTempl<T> &func = *fg;
1251 unsigned int npar = func.NPar();
1252 auto vecSize = vecCore::VectorSize<T>();
1253 unsigned initialNPoints = data.Size();
1254 unsigned numVectors = initialNPoints / vecSize;
1256 #ifdef DEBUG_FITUTIL
1257 std::cout <<
"\n===> Evaluate Gradient for parameters ";
1258 for (
unsigned int ip = 0; ip < npar; ++ip)
1259 std::cout <<
" " << p[ip];
1263 (
const_cast<IGradModelFunctionTempl<T> &
>(func)).SetParameters(p);
1265 const T kdmax1 = vecCore::math::Sqrt(vecCore::NumericLimits<T>::Max());
1266 const T kdmax2 = vecCore::NumericLimits<T>::Max() / (4 * initialNPoints);
1268 auto mapFunction = [&](
const unsigned int i) {
1269 std::vector<T> gradFunc(npar);
1270 std::vector<T> pointContributionVec(npar);
1273 vecCore::Load<T>(x1, data.GetCoordComponent(i * vecSize, 0));
1275 const T *x =
nullptr;
1277 unsigned int ndim = data.NDim();
1278 std::vector<T> xc(ndim);
1282 for (
unsigned int j = 1; j < ndim; ++j)
1283 vecCore::Load<T>(xc[j], data.GetCoordComponent(i * vecSize, j));
1290 T fval = func(x, p);
1291 func.ParameterGradient(x, p, &gradFunc[0]);
1293 #ifdef DEBUG_FITUTIL
1294 if (i < 5 || (i > numVectors-5) ) {
1295 if (ndim > 1) std::cout << i <<
" x " << x[0] <<
" y " << x[1] <<
" gradient " << gradFunc[0] <<
" " << gradFunc[1] <<
" " << gradFunc[3] << std::endl;
1296 else std::cout << i <<
" x " << x[0] <<
" gradient " << gradFunc[0] <<
" " << gradFunc[1] <<
" " << gradFunc[3] << std::endl;
1300 vecCore::Mask<T> positiveValues = fval > 0;
1302 for (
unsigned int kpar = 0; kpar < npar; ++kpar) {
1303 if (!vecCore::MaskEmpty(positiveValues))
1304 vecCore::MaskedAssign<T>(pointContributionVec[kpar], positiveValues, -1. / fval * gradFunc[kpar]);
1306 vecCore::Mask<T> nonZeroGradientValues = !positiveValues && gradFunc[kpar] != 0;
1307 if (!vecCore::MaskEmpty(nonZeroGradientValues)) {
1308 T gg = kdmax1 * gradFunc[kpar];
1309 pointContributionVec[kpar] =
1310 vecCore::Blend(nonZeroGradientValues && gg > 0, -vecCore::math::Min(gg, kdmax2),
1311 -vecCore::math::Max(gg, -kdmax2));
1316 return pointContributionVec;
1320 auto redFunction = [&](
const std::vector<std::vector<T>> &pointContributions) {
1321 std::vector<T> result(npar);
1323 for (
auto const &pointContributionVec : pointContributions) {
1324 for (
unsigned int parameterIndex = 0; parameterIndex < npar; parameterIndex++)
1325 result[parameterIndex] += pointContributionVec[parameterIndex];
1331 std::vector<T> gVec(npar);
1332 std::vector<double> g(npar);
1339 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1340 Warning(
"FitUtil::EvaluateLogLGradient",
1341 "Multithread execution policy requires IMT, which is disabled. Changing "
1342 "to ROOT::Fit::ExecutionPolicy::kSerial.");
1343 executionPolicy = ROOT::Fit::ExecutionPolicy::kSerial;
1347 if (executionPolicy == ROOT::Fit::ExecutionPolicy::kSerial) {
1348 ROOT::TSequentialExecutor pool;
1349 gVec = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, numVectors), redFunction);
1352 else if (executionPolicy == ROOT::Fit::ExecutionPolicy::kMultithread) {
1353 ROOT::TThreadExecutor pool;
1354 auto chunks = nChunks != 0 ? nChunks : setAutomaticChunking(numVectors);
1355 gVec = pool.MapReduce(mapFunction, ROOT::TSeq<unsigned>(0, numVectors), redFunction, chunks);
1359 Error(
"FitUtil::EvaluateLogLGradient",
"Execution policy unknown. Avalaible choices:\n "
1360 "ROOT::Fit::ExecutionPolicy::kSerial (default)\n "
1361 "ROOT::Fit::ExecutionPolicy::kMultithread (requires IMT)\n");
1365 unsigned int remainingPoints = initialNPoints % vecSize;
1366 if (remainingPoints > 0) {
1367 auto remainingPointsContribution = mapFunction(numVectors);
1369 auto remainingMask = vecCore::Int2Mask<T>(initialNPoints % vecSize);
1370 for (
unsigned int param = 0; param < npar; param++) {
1371 vecCore::MaskedAssign(gVec[param], remainingMask, gVec[param] + remainingPointsContribution[param]);
1375 for (
unsigned int param = 0; param < npar; param++) {
1376 grad[param] = vecCore::ReduceAdd(gVec[param]);
1379 #ifdef DEBUG_FITUTIL
1380 std::cout <<
"Final gradient ";
1381 for (
unsigned int param = 0; param < npar; param++) {
1382 std::cout <<
" " << grad[param];
1390 struct Evaluate<double>{
1393 static double EvalChi2(
const IModelFunction &func,
const BinData &data,
const double *p,
unsigned int &nPoints,
1394 ::ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0)
1404 return FitUtil::EvaluateChi2(func, data, p, nPoints, executionPolicy, nChunks);
1407 static double EvalLogL(
const IModelFunctionTempl<double> &func,
const UnBinData &data,
const double *p,
1408 int iWeight,
bool extended,
unsigned int &nPoints,
1409 ::ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0)
1411 return FitUtil::EvaluateLogL(func, data, p, iWeight, extended, nPoints, executionPolicy, nChunks);
1414 static double EvalPoissonLogL(
const IModelFunctionTempl<double> &func,
const BinData &data,
const double *p,
1415 int iWeight,
bool extended,
unsigned int &nPoints,
1416 ::ROOT::Fit::ExecutionPolicy executionPolicy,
unsigned nChunks = 0)
1418 return FitUtil::EvaluatePoissonLogL(func, data, p, iWeight, extended, nPoints, executionPolicy, nChunks);
1421 static double EvalChi2Effective(
const IModelFunctionTempl<double> &func,
const BinData & data,
const double * p,
unsigned int &nPoints)
1423 return FitUtil::EvaluateChi2Effective(func, data, p, nPoints);
1425 static void EvalChi2Gradient(
const IModelFunctionTempl<double> &func,
const BinData &data,
const double *p,
1426 double *g,
unsigned int &nPoints,
1427 ::ROOT::Fit::ExecutionPolicy executionPolicy = ::ROOT::Fit::ExecutionPolicy::kSerial,
1428 unsigned nChunks = 0)
1430 FitUtil::EvaluateChi2Gradient(func, data, p, g, nPoints, executionPolicy, nChunks);
1432 static double EvalChi2Residual(
const IModelFunctionTempl<double> &func,
const BinData & data,
const double * p,
unsigned int i,
double *g = 0)
1434 return FitUtil::EvaluateChi2Residual(func, data, p, i, g);
1439 static double EvalPoissonBinPdf(
const IModelFunctionTempl<double> &func,
const BinData & data,
const double *p,
unsigned int i,
double *g ) {
1440 return FitUtil::EvaluatePoissonBinPdf(func, data, p, i, g);
1444 EvalPoissonLogLGradient(
const IModelFunctionTempl<double> &func,
const BinData &data,
const double *p,
double *g,
1445 unsigned int &nPoints,
1446 ::ROOT::Fit::ExecutionPolicy executionPolicy = ::ROOT::Fit::ExecutionPolicy::kSerial,
1447 unsigned nChunks = 0)
1449 FitUtil::EvaluatePoissonLogLGradient(func, data, p, g, nPoints, executionPolicy, nChunks);
1452 static void EvalLogLGradient(
const IModelFunctionTempl<double> &func,
const UnBinData &data,
const double *p,
1453 double *g,
unsigned int &nPoints,
1454 ::ROOT::Fit::ExecutionPolicy executionPolicy = ::ROOT::Fit::ExecutionPolicy::kSerial,
1455 unsigned nChunks = 0)
1457 FitUtil::EvaluateLogLGradient(func, data, p, g, nPoints, executionPolicy, nChunks);
1467 #if defined (R__HAS_VECCORE) && defined(R__HAS_VC)
1469 Vc_DECLARE_ALLOCATOR(ROOT::Fit::FitUtil::LikelihoodAux<ROOT::Double_v>);