44 :
RooAbsReal{
"RooEvaluatorWrapper",
"RooEvaluatorWrapper"},
48 _paramSet(
"paramSet",
"Set of parameters",
this),
54 setData(*
data,
false);
56 _paramSet.add(_evaluator->getParameters());
58 _paramSet.remove(*_paramSet.find(
item.first->GetName()));
64 _evaluator{
other._evaluator},
65 _topNode(
"topNode",
this,
other._topNode),
67 _paramSet(
"paramSet",
"Set of parameters",
this),
68 _rangeName{
other._rangeName},
70 _takeGlobalObservablesFromData{
other._takeGlobalObservablesFromData},
73 _paramSet.add(
other._paramSet);
76RooEvaluatorWrapper::~RooEvaluatorWrapper() =
default;
81 outputSet.add(_evaluator->getParameters());
83 outputSet.remove(*observables,
false,
true);
87 if (_data->getGlobalObservables() && _data->getGlobalObservables()->find(
item.first->GetName())) {
99 if (_takeGlobalObservablesFromData && _data->getGlobalObservables()) {
100 outputSet.replace(*_data->getGlobalObservables());
132 void gradient(
double *out)
const
135 std::fill(out, out + _params.size(), 0.0);
138 void hessian(
double *out)
const
141 std::fill(out, out + _params.size() * _params.size(), 0.0);
150 std::vector<std::string>
const &collectedFunctions() {
return _collectedFunctions; }
155 return _func(
_varBuffer.data(), _observables.data(), _xlArr.data());
165 using Func =
double (*)(
double *,
double const *,
double const *);
166 using Grad = void (*)(
double *,
double const *,
double const *,
double *);
167 using Hessian = void (*)(
double *,
double const *,
double const *,
double *);
177 std::vector<double> _observables;
178 std::unordered_map<RooFit::Detail::DataKey, std::size_t>
_obsInfos;
179 std::vector<double> _xlArr;
180 std::vector<std::string> _collectedFunctions;
185void replaceAll(std::string &str,
const std::string &from,
const std::string &to)
191 str.replace(
start_pos, from.length(), to);
201 std::unordered_set<RooFit::Detail::DataKey> dependsOnData;
203 dependsOnData.insert(arg);
207 if (arg->getAttribute(
"__obs__")) {
208 dependsOnData.insert(arg);
211 if (
server->isValueServer(*arg)) {
212 if (dependsOnData.find(
server) != dependsOnData.end() && !arg->isReducerNode()) {
213 dependsOnData.insert(arg);
220 return dependsOnData;
242 std::unordered_set<RooFit::Detail::DataKey> dependsOnData;
253 ctx.
addResult(param,
"params[" + std::to_string(idx) +
"]");
264 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
269 gInterpreter->Declare(
"#include <RooFit/CodegenImpl.h>\n");
274 errorMsg <<
"Function " <<
_funcName <<
" could not be compiled. See above for details. Full code dumped to file "
282 throw std::runtime_error(
errorMsg.str().c_str());
287 _xlArr = ctx.
xlArr();
297 _observables.clear();
299 std::size_t
total = 0;
300 _observables.reserve(2 *
spans.size());
304 _observables.push_back(
total + 2 *
spans.size());
305 _observables.push_back(
item.second.size());
311 std::size_t
n =
item.second.size();
312 _observables.reserve(_observables.size() +
n);
313 for (std::size_t i = 0; i <
n; ++i) {
314 _observables.push_back(
item.second[i]);
320void RooFuncWrapper::createGradient()
327 gInterpreter->Declare(
"#include <Math/CladDerivator.h>\n");
333 " clad::gradient(" <<
_funcName <<
", \"params\");\n"
337 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
346 errorMsg <<
"Function could not be differentiated. See above for details.";
348 throw std::runtime_error(
errorMsg.str().c_str());
354 std::stringstream
ss;
356 ss <<
"static_cast<void (*)(double *, double const *, double const *, double *)>(" <<
gradName <<
");";
362 errorMsg <<
"Function could not be differentiated since ROOT was built without Clad support.";
364 throw std::runtime_error(
errorMsg.str().c_str());
368void RooFuncWrapper::createHessian()
375 gInterpreter->Declare(
"#include <Math/CladDerivator.h>\n");
380 _params.size() == 1 ?
"\"params[0]\"" : (
"\"params[0:" + std::to_string(_params.size() - 1) +
"]\"");
387 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
396 errorMsg <<
"Function could not be differentiated. See above for details.";
398 throw std::runtime_error(
errorMsg.str().c_str());
404 std::stringstream
ss;
406 ss <<
"static_cast<void (*)(double *, double const *, double const *, double *)>(" <<
hessianName <<
");";
412 errorMsg <<
"Function could not be differentiated since ROOT was built without Clad support.";
414 throw std::runtime_error(
errorMsg.str().c_str());
418void RooFuncWrapper::updateGradientVarBuffer()
const
421 return obj->isCategory() ? static_cast<RooAbsCategory *>(obj)->getCurrentIndex()
422 : static_cast<RooAbsReal *>(obj)->getVal();
427void RooFuncWrapper::writeDebugMacro(std::string
const &
filename)
const
433 for (std::string
const &
name : _collectedFunctions) {
438 std::unique_ptr<TInterpreterValue>
v =
gInterpreter->MakeInterpreterValue();
440 std::string s =
v->ToString();
441 for (
int i = 0; i < 2; ++i) {
442 s = s.erase(0, s.find(
"\n") + 1);
449 _params.size() == 1 ?
"\"params[0]\"" : (
"\"params[0:" + std::to_string(_params.size() - 1) +
"]\"");
451 outFile << R
"(//auto-generated test macro
452#include <RooFit/Detail/MathFuncs.h>
453#include <Math/CladDerivator.h>
460void gradient_request() {
474 std::stringstream
decl;
475 decl <<
"std::vector<double> " <<
name <<
" = {";
476 for (std::size_t i = 0; i <
vec.size(); ++i) {
480 if (i <
vec.size() - 1)
493 outFile <<
"// clang-format off\n" << std::endl;
500 outFile <<
"// clang-format on\n" << std::endl;
503// To run as a ROOT macro
507 const std::size_t n = parametersVec.size();
509 std::vector<double> gradientVec(n);
511 auto func = [&](std::span<double> params) {
513 << _funcName << R"((params.data(), observablesVec.data(), auxConstantsVec.data());
515 auto grad = [&](std::span<double> params, std::span<double> out) {
517 << _funcName << R"(_grad_0(parametersVec.data(), observablesVec.data(), auxConstantsVec.data(),
521 grad(parametersVec, gradientVec);
523 auto numDiff = [&](int i) {
524 const double eps = 1e-6;
525 std::vector<double> p{parametersVec};
526 p[i] = parametersVec[i] - eps;
527 double funcValDown = func(p);
528 p[i] = parametersVec[i] + eps;
529 double funcValUp = func(p);
530 return (funcValUp - funcValDown) / (2 * eps);
533 for (std::size_t i = 0; i < parametersVec.size(); ++i) {
534 std::cout << i << ":" << std::endl;
535 std::cout << " numr : " << numDiff(i) << std::endl;
536 std::cout << " clad : " << gradientVec[i] << std::endl;
542 auto hess = [&](std::span<double> params, std::span<double> out) {
544 << _funcName << R"(_hessian_0(params.data(), observablesVec.data(), auxConstantsVec.data(), out.data());
547 std::vector<double> hessianVec(n * n);
548 hess(parametersVec, hessianVec);
550 // ---------- Numerical Hessian ----------
551 // Uses central differences:
552 // diag: (f(x+ei)-2f(x)+f(x-ei))/eps^2
553 // offdiag: (f(++ ) - f(+-) - f(-+) + f(--)) / (4 eps^2)
554 auto numHess = [&](std::size_t i, std::size_t j) {
555 const double eps = 1e-5; // often needs to be a bit larger than grad eps
556 std::vector<double> p(parametersVec.begin(), parametersVec.end());
559 const double f0 = func(p);
561 p[i] = parametersVec[i] + eps;
562 const double fUp = func(p);
564 p[i] = parametersVec[i] - eps;
565 const double fDown = func(p);
567 return (fUp - 2.0 * f0 + fDown) / (eps * eps);
569 // f(x_i + eps, x_j + eps)
570 p[i] = parametersVec[i] + eps;
571 p[j] = parametersVec[j] + eps;
572 const double fPP = func(p);
574 // f(x_i + eps, x_j - eps)
575 p[i] = parametersVec[i] + eps;
576 p[j] = parametersVec[j] - eps;
577 const double fPM = func(p);
579 // f(x_i - eps, x_j + eps)
580 p[i] = parametersVec[i] - eps;
581 p[j] = parametersVec[j] + eps;
582 const double fMP = func(p);
584 // f(x_i - eps, x_j - eps)
585 p[i] = parametersVec[i] - eps;
586 p[j] = parametersVec[j] - eps;
587 const double fMM = func(p);
589 return (fPP - fPM - fMP + fMM) / (4.0 * eps * eps);
593 // Compute full numerical Hessian
594 std::vector<double> numHessianVec(n * n);
595 for (std::size_t i = 0; i < n; ++i) {
596 for (std::size_t j = 0; j < n; ++j) {
597 numHessianVec[i + n * j] = numHess(i, j); // keep same layout as your print
601 // ---------- Compare & print ----------
602 std::cout << "Hessian comparison (clad vs numeric vs diff):\n\n";
604 for (std::size_t i = 0; i < n; ++i) {
605 for (std::size_t j = 0; j < n; ++j) {
606 const std::size_t idx = i + n * j; // same indexing you used
607 const double cladH = hessianVec[idx];
608 const double numH = numHessianVec[idx];
609 const double diff = cladH - numH;
611 std::cout << "[" << i << "," << j << "] "
612 << "clad=" << cladH << " num=" << numH << " diff=" << diff << "\n";
616 std::cout << "\nRaw Clad Hessian matrix:\n";
617 for (std::size_t i = 0; i < n; ++i) {
618 for (std::size_t j = 0; j < n; ++j) {
619 std::cout << hessianVec[i + n * j] << " ";
624 std::cout << "\nRaw Numerical Hessian matrix:\n";
625 for (std::size_t i = 0; i < n; ++i) {
626 for (std::size_t j = 0; j < n; ++j) {
627 std::cout << numHessianVec[i + n * j] << " ";
636double RooEvaluatorWrapper::evaluate()
const
645 :
RooFit::EvalContext::OffsetMode::WithOffset);
647 return _evaluator->run()[0];
656 constexpr auto errMsg =
"Error in RooAbsReal::setData(): only resetting with same-structured data is supported.";
662 std::stack<std::vector<double>>{}.swap(_vectorBuffers);
663 bool skipZeroWeights = !_pdf || !_pdf->getAttribute(
"BinnedLikelihoodActive");
666 _takeGlobalObservablesFromData, _vectorBuffers);
669 throw std::runtime_error(
errMsg);
672 const char *
name =
item.first->GetName();
673 _evaluator->setInput(
name,
item.second,
false);
674 if (_paramSet.find(
name)) {
676 throw std::runtime_error(
errMsg);
685void RooEvaluatorWrapper::createFuncWrapper()
689 this->getParameters(_data ? _data->get() : nullptr,
paramSet,
false);
695void RooEvaluatorWrapper::generateGradient()
703void RooEvaluatorWrapper::generateHessian()
711void RooEvaluatorWrapper::setUseGeneratedFunctionCode(
bool flag)
718void RooEvaluatorWrapper::gradient(
double *out)
const
723void RooEvaluatorWrapper::hessian(
double *out)
const
728bool RooEvaluatorWrapper::hasGradient()
const
733bool RooEvaluatorWrapper::hasHessian()
const
738void RooEvaluatorWrapper::writeDebugMacro(std::string
const &
filename)
const
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
static unsigned int total
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Abstract base class for binned and unbinned datasets.
Abstract interface for all probability density functions.
Abstract base class for objects that represent a real value and implements functionality common to al...
RooArgList is a container object that can hold multiple RooAbsArg objects.
RooArgSet is a container object that can hold multiple RooAbsArg objects.
A class to maintain the context for squashing of RooFit models into code.
void addResult(RooAbsArg const *key, std::string const &value)
A function to save an expression that includes/depends on the result of the input node.
void addVecObs(const char *key, int idx)
Since the squashed code represents all observables as a single flattened array, it is important to ke...
std::string buildFunction(RooAbsArg const &arg, std::unordered_set< RooFit::Detail::DataKey > const &dependsOnData={})
Assemble and return the final code with the return expression and global statements.
std::vector< std::string > const & collectedFunctions()
std::string const & collectedCode()
std::vector< double > const & xlArr()
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
void replaceAll(std::string &inOut, std::string_view what, std::string_view with)
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
void getSortedComputationGraph(RooAbsArg const &func, RooArgSet &out)
void evaluate(typename Architecture_t::Tensor_t &A, EActivationFunction f)
Apply the given activation function to each value in the given tensor A.