Skip to content

[RF] Faster Hesse in RooFit by advertising which params are independent #16394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions math/mathcore/inc/Math/Functor.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class Functor : public IBaseFunctionMultiDim {
// for multi-dimensional functions
unsigned int NDim() const override { return fDim; }

bool VanishingSecondDerivative(int i, int j) const override { return fVanishSecondDerivFunc ? fVanishSecondDerivFunc(i, j) : false; }

void SetVanishingSecondDerivativeFunc(std::function<bool(int, int)> func) { fVanishSecondDerivFunc = std::move(func); }

private :

inline double DoEval (const double * x) const override {
Expand All @@ -75,6 +79,7 @@ private :

unsigned int fDim;
std::function<double(double const *)> fFunc;
std::function<bool(int, int)> fVanishSecondDerivFunc;
};

/**
Expand Down Expand Up @@ -222,6 +227,10 @@ class GradFunctor : public IGradientFunctionMultiDim {
fGradFunc(x, g);
}

bool VanishingSecondDerivative(int i, int j) const override { return fVanishSecondDerivFunc ? fVanishSecondDerivFunc(i, j) : false; }

void SetVanishingSecondDerivativeFunc(std::function<bool(int, int)> func) { fVanishSecondDerivFunc = std::move(func); }

private :

inline double DoEval (const double * x) const override {
Expand All @@ -244,6 +253,7 @@ private :
std::function<double(const double *)> fFunc;
std::function<double(double const *, unsigned int)> fDerivFunc;
std::function<void(const double *, double*)> fGradFunc;
std::function<bool(int, int)> fVanishSecondDerivFunc;
};


Expand Down
5 changes: 5 additions & 0 deletions math/mathcore/inc/Math/IFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ namespace ROOT {
// if it inherits from ROOT::Math::IGradientFunctionMultiDim.
virtual bool HasGradient() const { return false; }

/// Indicate whether a given second order derivative with respect to
/// parameters i and j is always zero. This can help to avoid
/// expensive function calls in Hessian evaluations.
virtual bool VanishingSecondDerivative(int /*i*/, int /*j*/) const { return false; }

private:

/// Implementation of the evaluation function. Must be implemented by derived classes.
Expand Down
2 changes: 2 additions & 0 deletions math/minuit2/inc/Minuit2/FCNAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class FCNAdapter : public FCNBase {

void SetErrorDef(double up) override { fUp = up; }

bool VanishingSecondDerivative(int i, int j) const override { return fFunc.VanishingSecondDerivative(i, j); }

private:
const Function &fFunc;
double fUp;
Expand Down
5 changes: 5 additions & 0 deletions math/minuit2/inc/Minuit2/FCNBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ class FCNBase : public GenericFunction {
virtual bool HasHessian() const { return false; }

virtual bool HasG2() const { return false; }

/// Indicate whether a given second order derivative with respect to
/// parameters i and j is always zero. This can help to avoid
/// expensive function calls in Hessian evaluations.
virtual bool VanishingSecondDerivative(int /*i*/, int /*j*/) const { return false; }
};

} // namespace Minuit2
Expand Down
2 changes: 2 additions & 0 deletions math/minuit2/inc/Minuit2/FCNGradAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class FCNGradAdapter : public FCNBase {

void SetErrorDef(double up) override { fUp = up; }

bool VanishingSecondDerivative(int i, int j) const override { return fFunc.VanishingSecondDerivative(i, j); }

private:
const Function &fFunc;
double fUp;
Expand Down
11 changes: 7 additions & 4 deletions math/minuit2/src/MnHesse.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,17 @@ MinimumState ComputeNumerical(const MnFcn &mfcn, const MinimumState &st, const M
if ((i + 1) == j || in == startParIndexOffDiagonal)
x(i) += dirin(i);

x(j) += dirin(j);

double fs1 = mfcnCaller(x);
if(!doCentralFD) {
if(mfcn.Fcn().VanishingSecondDerivative(i, j)) {
vhmat(i, j) = 0.;
} else if(!doCentralFD) {
x(j) += dirin(j);
double fs1 = mfcnCaller(x);
double elem = (fs1 + amin - yy(i) - yy(j)) / (dirin(i) * dirin(j));
vhmat(i, j) = elem;
x(j) -= dirin(j);
} else {
x(j) += dirin(j);
double fs1 = mfcnCaller(x);
// three more function evaluations required for central fd
x(i) -= dirin(i);
x(i) -= dirin(i);
Expand Down
1 change: 1 addition & 0 deletions roofit/roofitcore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore
RooFit/TestStatistics/RooUnbinnedL.h
RooFit/TestStatistics/SharedOffset.h
RooFit/TestStatistics/buildLikelihood.h
RooFit/VariableGroups.h
RooFitLegacy/RooCatTypeLegacy.h
RooFitLegacy/RooCategorySharedProperties.h
RooFitLegacy/RooTreeData.h
Expand Down
5 changes: 3 additions & 2 deletions roofit/roofitcore/inc/RooAbsArg.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ namespace RooFit {
namespace Experimental {
class CodegenContext;
}
}
struct VariableGroups;
} // namespace RooFit

class RooRefArray : public TObjArray {
public:
Expand Down Expand Up @@ -248,7 +249,7 @@ class RooAbsArg : public TNamed, public RooPrintable {
bool recursiveCheckObservables(const RooArgSet* nset) const ;
RooFit::OwningPtr<RooArgSet> getComponents() const ;


virtual void fillVariableGroups(RooFit::VariableGroups &out) const;

void attachArgs(const RooAbsCollection &set);
void attachDataSet(const RooAbsData &set);
Expand Down
2 changes: 2 additions & 0 deletions roofit/roofitcore/inc/RooAddition.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class RooAddition : public RooAbsReal {

void doEval(RooFit::EvalContext &) const override;

void fillVariableGroups(RooFit::VariableGroups &out) const override;

protected:

RooArgList _ownedList ; ///< List of owned components
Expand Down
2 changes: 2 additions & 0 deletions roofit/roofitcore/inc/RooConstraintSum.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class RooConstraintSum : public RooAbsReal {

std::unique_ptr<RooAbsArg> compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext & ctx) const override;

void fillVariableGroups(RooFit::VariableGroups &out) const override;

protected:

RooListProxy _set1 ; ///< Set of constraint terms
Expand Down
33 changes: 33 additions & 0 deletions roofit/roofitcore/inc/RooFit/VariableGroups.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef RooFit_VariableGroups_h
#define RooFit_VariableGroups_h

#include <TNamed.h>

#include <iostream>
#include <unordered_map>
#include <vector>

class TNamed;

namespace RooFit {

struct VariableGroups {

std::unordered_map<TNamed const*, std::vector<int>> groups;

inline void print() {
for (auto const& item : groups) {
std::cout << item.first->GetName() << " :";
for (int n : item.second) {
std::cout << " " << n;
}
std::cout << std::endl;
}
}

int currentIndex = 0;
};

}

#endif
20 changes: 19 additions & 1 deletion roofit/roofitcore/src/RooAbsArg.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ for single nodes.
#include <RooArgSet.h>
#include <RooConstVar.h>
#include <RooExpensiveObjectCache.h>
#include <RooFit/VariableGroups.h>
#include <RooHelpers.h>
#include "RooFitImplHelpers.h"
#include <RooListProxy.h>
#include <RooMsgService.h>
#include <RooRealIntegral.h>
Expand All @@ -87,6 +87,8 @@ for single nodes.
#include <RooVectorDataStore.h>
#include <RooWorkspace.h>

#include "RooFitImplHelpers.h"

#include <TBuffer.h>
#include <TClass.h>
#include <TVirtualStreamerInfo.h>
Expand Down Expand Up @@ -2529,3 +2531,19 @@ void RooAbsArg::setDataToken(std::size_t index)
}
_dataToken = index;
}

void RooAbsArg::fillVariableGroups(RooFit::VariableGroups &out) const
{
// Get the set of nodes in the computation graph. Do the detour via
// RooArgList to avoid deduplication done after adding each element.
RooArgSet serverSet;
RooArgList serverList;
treeNodeServerList(&serverList, nullptr, /*branches*/ false, /*leaves*/ true, /*valueOnly*/ false,
/*recurseFundamental*/ true);
serverSet.add(serverList.begin(), serverList.end());

for (RooAbsArg const *arg : serverSet) {
out.groups[arg->namePtr()].push_back(out.currentIndex);
}
out.currentIndex++;
}
7 changes: 7 additions & 0 deletions roofit/roofitcore/src/RooAddition.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,10 @@ std::list<double>* RooAddition::plotSamplingHint(RooAbsRealLValue& obs, double x
{
return RooRealSumPdf::plotSamplingHint(_set, obs, xlo, xhi);
}

void RooAddition::fillVariableGroups(RooFit::VariableGroups &out) const
{
for (RooAbsArg *arg : _set) {
arg->fillVariableGroups(out);
}
}
7 changes: 7 additions & 0 deletions roofit/roofitcore/src/RooConstraintSum.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,10 @@ bool RooConstraintSum::setData(RooAbsData const& data, bool /*cloneData=true*/)
}
return true;
}

void RooConstraintSum::fillVariableGroups(RooFit::VariableGroups &out) const
{
for (RooAbsArg *arg : _set1) {
arg->fillVariableGroups(out);
}
}
5 changes: 5 additions & 0 deletions roofit/roofitcore/src/RooEvaluatorWrapper.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,9 @@ bool RooEvaluatorWrapper::setData(RooAbsData &data, bool /*cloneData*/)
return true;
}

void RooEvaluatorWrapper::fillVariableGroups(RooFit::VariableGroups &out) const
{
_topNode->fillVariableGroups(out);
}

/// \endcond
2 changes: 2 additions & 0 deletions roofit/roofitcore/src/RooEvaluatorWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class RooEvaluatorWrapper final : public RooAbsReal {
/// The RooFit::Evaluator is dealing with constant terms itself.
void constOptimizeTestStatistic(ConstOpCode /*opcode*/, bool /*doAlsoTrackingOpt*/) override {}

void fillVariableGroups(RooFit::VariableGroups &out) const override;

protected:
double evaluate() const override;

Expand Down
2 changes: 1 addition & 1 deletion roofit/roofitcore/src/RooMinimizer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ RooMinimizer::RooMinimizer(RooAbsReal &function, Config const &cfg) : _cfg(cfg)
_fcn = std::make_unique<RooMinimizerFcn>(&function, this);
}
initMinimizerFcnDependentPart(function.defaultErrorLevel());
};
}

/// Initialize the part of the minimizer that is independent of the function to be minimized
void RooMinimizer::initMinimizerFirstPart()
Expand Down
59 changes: 53 additions & 6 deletions roofit/roofitcore/src/RooMinimizerFcn.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@
#include "RooAbsArg.h"
#include "RooAbsPdf.h"
#include "RooArgSet.h"
#include "RooRealVar.h"
#include "RooMsgService.h"
#include "RooFit/VariableGroups.h"
#include "RooMinimizer.h"
#include "RooMsgService.h"
#include "RooNaNPacker.h"
#include "RooRealVar.h"

#include "Math/Functor.h"
#include "TMatrixDSym.h"
Expand All @@ -40,6 +41,23 @@ using std::setprecision;

namespace {

template <class InputIt1, class InputIt2>
bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
{
while (first1 != last1 && first2 != last2) {
if (*first1 < *first2) {
++first1;
continue;
}
if (*first2 < *first1) {
++first2;
continue;
}
return true;
}
return false;
}

// Helper function that wraps RooAbsArg::getParameters and directly returns the
// output RooArgSet. To be used in the initializer list of the RooMinimizerFcn
// constructor.
Expand All @@ -56,14 +74,38 @@ RooArgSet getParameters(RooAbsReal const &funct)
RooMinimizerFcn::RooMinimizerFcn(RooAbsReal *funct, RooMinimizer *context)
: RooAbsMinimizerFcn(getParameters(*funct), context), _funct(funct)
{
unsigned int nDim = getNDim();
RooFit::VariableGroups groups;
funct->fillVariableGroups(groups);

RooArgList parameters;
for (std::size_t i = 0; i < getNDim(); ++i) {
parameters.add(floatableParam(i));
}

std::size_t nParams = parameters.size();

_secondDerivMask.resize(nParams * nParams);
for (std::size_t i = 0; i < nParams; ++i) {
_secondDerivMask[nParams * i + i] = 1;
for (std::size_t j = 0; j < i; ++j) {
// std::cout << parameters[i].GetName() << " " << parameters[j].GetName() << std::endl;
auto const &gr1 = groups.groups.at(parameters[i].namePtr());
auto const &gr2 = groups.groups.at(parameters[j].namePtr());
_secondDerivMask[nParams * i + j] = intersect(gr1.begin(), gr1.end(), gr2.begin(), gr2.end());
_secondDerivMask[nParams * j + i] = _secondDerivMask[nParams * i + j];
}
}

if (context->_cfg.useGradient && funct->hasGradient()) {
_gradientOutput.resize(_allParams.size());
_multiGenFcn = std::make_unique<ROOT::Math::GradFunctor>(this, &RooMinimizerFcn::operator(),
&RooMinimizerFcn::evaluateGradient, nDim);
auto functor = std::make_unique<ROOT::Math::GradFunctor>(this, &RooMinimizerFcn::operator(),
&RooMinimizerFcn::evaluateGradient, getNDim());
functor->SetVanishingSecondDerivativeFunc([this](int i, int j) { return this->vanishingSecondDerivative(i, j); });
_multiGenFcn = std::move(functor);
} else {
_multiGenFcn = std::make_unique<ROOT::Math::Functor>(std::cref(*this), getNDim());
auto functor = std::make_unique<ROOT::Math::Functor>(std::cref(*this), getNDim());
functor->SetVanishingSecondDerivativeFunc([this](int i, int j) { return this->vanishingSecondDerivative(i, j); });
_multiGenFcn = std::move(functor);
}
}

Expand Down Expand Up @@ -148,4 +190,9 @@ void RooMinimizerFcn::setOffsetting(bool flag)
_funct->enableOffsetting(flag);
}

bool RooMinimizerFcn::vanishingSecondDerivative(int i, int j) const
{
return _secondDerivMask[getNDim() * i + j] == 0;
}

/// \endcond
3 changes: 3 additions & 0 deletions roofit/roofitcore/src/RooMinimizerFcn.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ class RooMinimizerFcn : public RooAbsMinimizerFcn {
double operator()(const double *x) const;
void evaluateGradient(const double *x, double *out) const;

bool vanishingSecondDerivative(int i, int j) const;

private:
RooAbsReal *_funct = nullptr;
std::unique_ptr<ROOT::Math::IBaseFunctionMultiDim> _multiGenFcn;
mutable std::vector<double> _gradientOutput;
std::vector<int> _secondDerivMask;
};

#endif
Expand Down
3 changes: 3 additions & 0 deletions roofit/roofitcore/src/TestStatistics/MinuitFcnGrad.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class MinuitGradFunctor : public ROOT::Math::IMultiGradFunction {

bool returnsInMinuit2ParameterSpace() const override { return _fcn.returnsInMinuit2ParameterSpace(); }

// TODO: Implement this
bool VanishingSecondDerivative(int /*i*/, int /*j*/) const override { return false; }

private:
double DoEval(const double *x) const override { return _fcn(x); }

Expand Down
Loading