Skip to content

Commit

Permalink
[WIP] PR#3 Vectorization: Added parallelized and vectorized Chi2 and …
Browse files Browse the repository at this point in the history
…Unbinned Likelihood (root-project#567)

* Add parallelization to Chi2 fitting.

- Adapt vectorized TF1 for a Fitter with fixed parameters.
- Made the Chi2FCN Evaluating loop independent of the size of the SIMD array.
- Parallelized both vectorized and scalar versions of the Chi2FCN evaluation.
- Added new fExecutionpolicy field to the Chi2FCN class that will determine the policy to use for the chi2 function evaluation: multiprocess, multithread or serial(none).

* Provide automatic chunking in parallel Chi2 Fit

* Change naming in template resolver

* Fix error calling EvalParVec with ftype=1

* Template Paramfunctor

Templates Paramfunctor and adds const parameters interfaces for evaluation.

* Add vectorized Chi2 Evaluation

* Add Functor type resolving to TF1

* Vectorize and parallelize unbinned likelihood fit

* Introduce the Double_v type

* Linked MathCore with Vc

* Prepare for chunking

* Adapt Fitter interfaces for vectorization

* Improve use of VecCore

* Change Mathcore linking from Thread to IMT

* Allow the evaluation of Chi2 with coord errors

* Improve identation

* Disable the multiprocessing of the fitting

* Cleaned remaining Vc directives

* Link vecCore instead of Vc

* Define R__HAS_VECCORE

* Differentiate VecCore code at compile time

* Fix warning due to unused args

* Restore original conditions for Chi2Evaluation

* Make fFunctor transient again

* Reenable Chi2Gradient and Chi2Residual evaluations

* Add ValuePtr documentation

* Fix white lines between functions

* Fix Chi2 evaluation of multidimensional data

* Fix Memory alignment in the vectorized Likelihood fit.

This reintroduces Vc dependencies

* Fix for classic builds

* Conditionate MathCore linking

* Only Fit Multithreaded if IMT is enabled

* Fix multidim integral error in the Chi2 fit

* Fix LogL evaluation of multidimensional unbinned data

* Improve VecCore usage

* Simplify fFunctor check expressions

* Move the Double_v type into the ROOT namespace

* Make executionPolicy an enum

* Explain functor operator resolving in TF1

* Improved TF1::fType documentation

* Adapt execution policy option of the histogram fit

* Completed move of executionPolicy to a enum

* Add tests for the new parallel fitting.

This tests check both Chi2 and Unbinned LogL fitting in each one of the
execution policies available.

* Correct function naming

* Rename global variables to supress shadowing warning.

* Remove legacy parallel implementations
  • Loading branch information
xvallspl authored and lmoneta committed May 19, 2017
1 parent 51fbede commit 5a4e4ba
Show file tree
Hide file tree
Showing 31 changed files with 1,648 additions and 864 deletions.
5 changes: 5 additions & 0 deletions cmake/modules/RootConfiguration.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,11 @@ if(vc)
else()
set(hasvc undef)
endif()
if(veccore)
set(hasveccore define)
else()
set(hasveccore undef)
endif()
if(cxx11)
set(cxxversion cxx11)
set(usec++11 define)
Expand Down
1 change: 1 addition & 0 deletions config/RConfigure.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#@hasxft@ R__HAS_XFT /**/
#@hascocoa@ R__HAS_COCOA /**/
#@hasvc@ R__HAS_VC /**/
#@hasveccore@ R__HAS_VECCORE /**/
#@usec++11@ R__USE_CXX11 /**/
#@usec++14@ R__USE_CXX14 /**/
#@usec++17@ R__USE_CXX17 /**/
Expand Down
7 changes: 7 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -7162,6 +7162,12 @@ else
hasvc="undef"
fi

######################################################################
#
### echo %%% vecCore Library - Contributed library
#
hasveccore="undef"

######################################################################
#
### echo %%% GDML Library - Contributed library
Expand Down Expand Up @@ -8121,6 +8127,7 @@ sed \
-e "s|@hasxft@|$hasxft|" \
-e "s|@hascocoa@|$hascocoa|" \
-e "s|@hasvc@|$hasvc|" \
-e "s|@hasveccore@|$hasveccore|" \
-e "s|@usec++11@|$usecxx11|" \
-e "s|@usec++14@|$usecxx14|" \
-e "s|@usec++17@|$usecxx17|" \
Expand Down
6 changes: 4 additions & 2 deletions hist/hist/inc/Foption.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#ifndef ROOT_Foption
#define ROOT_Foption


//////////////////////////////////////////////////////////////////////////
// //
// Foption //
Expand All @@ -20,6 +19,7 @@
// //
//////////////////////////////////////////////////////////////////////////

#include "Fit/FitExecutionPolicy.h"

struct Foption_t {
//*-* chopt may be the concatenation of the following options:
Expand Down Expand Up @@ -49,6 +49,7 @@ struct Foption_t {
int StoreResult; // "S": Stores the result in a TFitResult structure
int BinVolume; // "WIDTH": scale content by the bin width/volume
double hRobust; // value of h parameter used in robust fitting
ROOT::Fit::ExecutionPolicy ExecPolicy; // Choose the execution Policy: kSerial, kMultithread, kMultiprocess

Foption_t() :
Quiet (0),
Expand All @@ -73,7 +74,8 @@ struct Foption_t {
Robust (0),
StoreResult (0),
BinVolume (0),
hRobust (0)
hRobust (0),
ExecPolicy (ROOT::Fit::kSerial)
{}
};

Expand Down
132 changes: 113 additions & 19 deletions hist/hist/inc/TF1.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,63 @@ namespace ROOT {
struct TF1Builder {
static void Build(TF1 *f, Func func);
};

template<class Func>
struct TF1Builder<Func *> {
static void Build(TF1 *f, Func *func);
};

// Internal class used by TF1 for obtaining the type from a functor
// out of the set of valid operator() signatures.
template<typename T>
struct GetFunctorType {
};

template<typename F, typename T>
struct GetFunctorType<T(F::*)(const T *, const double *)> {
using type = T;
};

template<typename F, typename T>
struct GetFunctorType<T(F::*)(const T *, const double *) const> {
using type = T;
};

template<typename F, typename T>
struct GetFunctorType<T(F::*)(T *, double *)> {
using type = T;
};

template<typename F, typename T>
struct GetFunctorType<T(F::*)(T *, double *) const> {
using type = T;
};

// Internal class used by TF1 to get the right operator() signature
// from a Functor with several ones.
template<typename T, typename F>
auto GetTheRightOp(T(F::*opPtr)(const T *, const double *)) -> decltype(opPtr)
{
return opPtr;
}

template<typename T, typename F>
auto GetTheRightOp(T(F::*opPtr)(const T *, const double *) const) -> decltype(opPtr)
{
return opPtr;
}

template<typename T, typename F>
auto GetTheRightOp(T(F::*opPtr)(T *, double *)) -> decltype(opPtr)
{
return opPtr;
}

template<typename T, typename F>
auto GetTheRightOp(T(F::*opPtr)(T *, double *) const) -> decltype(opPtr)
{
return opPtr;
}
}
}

Expand All @@ -161,12 +218,17 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
};

protected:
struct TF1FunctionPointer {};
struct TF1FunctorPointer {};

Double_t fXmin; //Lower bounds for the range
Double_t fXmax; //Upper bounds for the range
Int_t fNpar; //Number of parameters
Int_t fNdim; //Function dimension
Int_t fNpx; //Number of points used for the graphical representation
Int_t fType; //(=0 for standard functions, 1 if pointer to function, 3 vectorized interface)
Int_t fType; //(=0 for formula functions which can be stored, 1 if pointer to scalar free function,
// 2, interpreted functions constructed by name,
// 3 templated functors or vectorized free functions)
Int_t fNpfits; //Number of points used in the fit
Int_t fNDF; //Number of degrees of freedom in the fit
Double_t fChisquare; //Function fit chisquare
Expand All @@ -185,20 +247,29 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
TMethodCall *fMethodCall; //!Pointer to MethodCall in case of interpreted function
Bool_t fNormalized; //Normalization option (false by default)
Double_t fNormIntegral;//Integral of the function before being normalized
ROOT::Math::ParamFunctor fFunctor; //! Functor object to wrap any C++ callable object
TF1FunctorPointer *fFunctor = nullptr; //! Functor object to wrap any C++ callable object
TFormula *fFormula; //Pointer to TFormula in case when user define formula
TF1Parameters *fParams; //Pointer to Function parameters object (exusts only for not-formula functions)

public:

struct TF1FunctionPointer {};

template<class T>
struct TF1FunctionPointerImpl: TF1FunctionPointer {
TF1FunctionPointerImpl(const std::function<T(const T *f, const Double_t *param)> &&func): fimpl(func) {};
std::function<T(const T *f, const Double_t *param)> fimpl;
};



template<class T>
struct TF1FunctorPointerImpl: TF1FunctorPointer {
TF1FunctorPointerImpl(const ROOT::Math::ParamFunctorTempl<T> &&func): fImpl(func) {};

ROOT::Math::ParamFunctorTempl<T> fImpl;
};



TF1FunctionPointer *fFunctp = nullptr; //!Pointer to vectorized function

static std::atomic<Bool_t> fgAbsValue; //use absolute value of function when computing integral
Expand Down Expand Up @@ -290,12 +361,13 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
TF1(const char *name, Func f, Double_t xmin, Double_t xmax, Int_t npar, Int_t ndim = 1, EAddToList addToGlobList = EAddToList::kDefault);

// backward compatible interface

template <typename Func>
TF1(const char *name, Func f, Double_t xmin, Double_t xmax, Int_t npar, const char *, EAddToList addToGlobList = EAddToList::kDefault) :
TNamed(name, name), TAttLine(), TAttFill(), TAttMarker(),
fXmin(xmin), fXmax(xmax),
fNpar(npar), fNdim(1),
fNpx(100), fType(1),
fNpx(100), fType(3),
fNpfits(0), fNDF(0), fChisquare(0),
fMinimum(-1111), fMaximum(-1111),
fParErrors(std::vector<Double_t>(npar)),
Expand All @@ -304,10 +376,12 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
fParent(0), fHistogram(0),
fMethodCall(0),
fNormalized(false), fNormIntegral(0),
fFunctor(ROOT::Math::ParamFunctor(f)),
fFormula(0),
fParams(new TF1Parameters(npar))
{
using Fnc_t = typename ROOT::Internal::GetFunctorType<decltype(ROOT::Internal::GetTheRightOp(&Func::operator()))>::type;

fFunctor = new TF1FunctorPointerImpl<Fnc_t>(ROOT::Math::ParamFunctorTempl<Fnc_t>(f)),
DoInitialize(addToGlobList);
}

Expand All @@ -325,7 +399,7 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
TNamed(name, name), TAttLine(), TAttFill(), TAttMarker(),
fXmin(xmin), fXmax(xmax),
fNpar(npar), fNdim(ndim),
fNpx(100), fType(1),
fNpx(100), fType(3),
fNpfits(0), fNDF(0), fChisquare(0),
fMinimum(-1111), fMaximum(-1111),
fParErrors(std::vector<Double_t>(npar)),
Expand All @@ -334,7 +408,7 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
fParent(0), fHistogram(0),
fMethodCall(0),
fNormalized(false), fNormIntegral(0),
fFunctor(ROOT::Math::ParamFunctor(p, memFn)),
fFunctor(new TF1FunctorPointerImpl<double>(ROOT::Math::ParamFunctor(p, memFn))),
fFormula(0),
fParams(new TF1Parameters(npar))
{
Expand All @@ -346,7 +420,7 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
TNamed(name, name), TAttLine(), TAttFill(), TAttMarker(),
fXmin(xmin), fXmax(xmax),
fNpar(npar), fNdim(1),
fNpx(100), fType(1),
fNpx(100), fType(3),
fNpfits(0), fNDF(0), fChisquare(0),
fMinimum(-1111), fMaximum(-1111),
fParErrors(std::vector<Double_t>(npar)),
Expand All @@ -355,7 +429,7 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
fParent(0), fHistogram(0),
fMethodCall(0),
fNormalized(false), fNormIntegral(0),
fFunctor(ROOT::Math::ParamFunctor(p, memFn)),
fFunctor(new TF1FunctorPointerImpl<double>(ROOT::Math::ParamFunctor(p, memFn))),
fFormula(0),
fParams(new TF1Parameters(npar))
{
Expand Down Expand Up @@ -395,6 +469,10 @@ class TF1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
template<class T> T operator()(const T *data, const Double_t *params);
virtual void ExecuteEvent(Int_t event, Int_t px, Int_t py);
virtual void FixParameter(Int_t ipar, Double_t value);
bool IsTemplated()
{
return fType == 3;
}
Double_t GetChisquare() const
{
return fChisquare;
Expand Down Expand Up @@ -649,7 +727,7 @@ TF1::TF1(const char *name, Func f, Double_t xmin, Double_t xmax, Int_t npar, Int
TNamed(name, name), TAttLine(), TAttFill(), TAttMarker(),
fXmin(xmin), fXmax(xmax),
fNpar(npar), fNdim(ndim),
fNpx(100), fType(1),
fNpx(100), fType(3),
fNpfits(0), fNDF(0), fChisquare(0),
fMinimum(-1111), fMaximum(-1111),
fParErrors(std::vector<Double_t>(npar)),
Expand All @@ -672,10 +750,21 @@ namespace ROOT {
template<class Func>
void TF1Builder<Func>::Build(TF1 *f, Func func)
{
f->fType = 1;
f->fFunctor = ROOT::Math::ParamFunctor(func);
using Fnc_t = typename ROOT::Internal::GetFunctorType<decltype(ROOT::Internal::GetTheRightOp(&Func::operator()))>::type;
f->fType = 3;
f->fFunctor = new TF1::TF1FunctorPointerImpl<Fnc_t>(ROOT::Math::ParamFunctorTempl<Fnc_t>(func));
f->fParams = new TF1Parameters(f->fNpar);
}

template<class Func>
void TF1Builder<Func *>::Build(TF1 *f, Func *func)
{
using Fnc_t = typename ROOT::Internal::GetFunctorType<decltype(ROOT::Internal::GetTheRightOp(&Func::operator()))>::type;
f->fType = 3;
f->fFunctor = new TF1::TF1FunctorPointerImpl<Fnc_t>(ROOT::Math::ParamFunctorTempl<Fnc_t>(func));
f->fParams = new TF1Parameters(f->fNpar);
}

/// TF1 building from a string
/// used to build a TFormula based on a lambda function
template<>
Expand All @@ -694,9 +783,6 @@ namespace ROOT {
}
}




inline Double_t TF1::operator()(Double_t x, Double_t y, Double_t z, Double_t t) const
{
return Eval(x, y, z, t);
Expand All @@ -719,12 +805,20 @@ T TF1::EvalPar(const T *x, const Double_t *params)
{
if (fType == 3) {
return EvalParVec(x, params);
}
} else
return TF1::EvalPar((double *) x, params);
}

template<class T>
inline T TF1::EvalParVec(const T *data, const Double_t *params)
{
if (fType != 3) {
//This should throw an error
return TF1::EvalPar((double *) data, params);
}
if (fFunctor != nullptr)
return ((TF1FunctorPointerImpl<T> *)fFunctor)->fImpl(data, params);

return ((TF1FunctionPointerImpl<T> *)(fFunctp))->fimpl(data, params);
}

Expand All @@ -742,14 +836,14 @@ void TF1::SetFunction(Func f)
{
// set function from a generic C++ callable object
fType = 1;
fFunctor = ROOT::Math::ParamFunctor(f);
fFunctor = new TF1::TF1FunctorPointerImpl<double>(ROOT::Math::ParamFunctor(f));
}
template <class PtrObj, typename MemFn>
void TF1::SetFunction(PtrObj &p, MemFn memFn)
{
// set from a pointer to a member function
fType = 1;
fFunctor = ROOT::Math::ParamFunctor(p, memFn);
fFunctor = new TF1::TF1FunctorPointerImpl<double>(ROOT::Math::ParamFunctor(p, memFn));
}

#endif
Loading

0 comments on commit 5a4e4ba

Please sign in to comment.