46 std::span<const double> span)
48 for (
double val : span) {
49 if (!var.
inRange(val,
nullptr)) {
50 const double lo = var.
getMin();
53 errMsg <<
"RooAbsPdf::fitTo/createNLL: cannot evaluate the likelihood because dataset \"" <<
datasetName
54 <<
"\" has an entry for observable \"" <<
obsName <<
"\" with value " << val
55 <<
", which is outside of its range [" << lo <<
", " <<
hi <<
"]. The probability density is "
56 <<
"normalized over exactly that range, so events outside of it would silently bias the fit. If "
57 <<
"you want to fit only a subset of the data, define a named range and use it in the fit, for example:\n"
58 <<
" " <<
obsName <<
".setRange(\"fitRange\", " << lo <<
", " <<
hi <<
");\n"
59 <<
" pdf.fitTo(data, RooFit::Range(\"fitRange\"));\n"
60 <<
"This way, only the events inside \"fitRange\" enter the likelihood, consistent with how the "
61 <<
"pdf is normalized.";
62 oocoutE(
nullptr, InputArguments) <<
errMsg.str() << std::endl;
63 throw std::runtime_error(
errMsg.str());
89 const std::string prefix =
"_" +
nameIdx.first +
"_";
90 std::unique_ptr<RooArgSet> vars{
channelPdf->getVariables()};
91 std::unique_ptr<RooArgSet> obs{vars->selectByAttrib(
"__obs__",
true)};
100 if (
obsName.rfind(prefix, 0) == 0) {
129 :
RooAbsReal{
"RooEvaluatorWrapper",
"RooEvaluatorWrapper"},
133 _paramSet(
"paramSet",
"Set of parameters",
this),
142 setData(*
data,
false);
144 _paramSet.add(_evaluator->getParameters());
146 _paramSet.remove(*_paramSet.find(
item.first->GetName()));
152 _evaluator{
other._evaluator},
153 _topNode(
"topNode",
this,
other._topNode),
155 _paramSet(
"paramSet",
"Set of parameters",
this),
156 _rangeName{
other._rangeName},
158 _takeGlobalObservablesFromData{
other._takeGlobalObservablesFromData},
161 _paramSet.add(
other._paramSet);
164RooEvaluatorWrapper::~RooEvaluatorWrapper() =
default;
169 outputSet.add(_evaluator->getParameters());
171 outputSet.remove(*observables,
false,
true);
175 if (_data->getGlobalObservables() && _data->getGlobalObservables()->find(
item.first->GetName())) {
187 if (_takeGlobalObservablesFromData && _data->getGlobalObservables()) {
188 outputSet.replace(*_data->getGlobalObservables());
221 void gradient(
double *out)
const
224 std::fill(out, out + _params.size(), 0.0);
227 void hessian(
double *out)
const
230 std::fill(out, out + _params.size() * _params.size(), 0.0);
239 std::vector<std::string>
const &collectedFunctions() {
return _collectedFunctions; }
244 return _func(
_varBuffer.data(), _observables.data(), _xlArr.data());
255 using Func =
double (*)(
double *,
double const *,
double const *);
256 using Grad = void (*)(
double *,
double const *,
double const *,
double *);
257 using Hessian = void (*)(
double *,
double const *,
double const *,
double *);
260 std::string _funcName;
267 std::vector<double> _observables;
268 std::unordered_map<RooFit::Detail::DataKey, std::size_t>
_obsInfos;
269 std::vector<double> _xlArr;
270 std::vector<std::string> _collectedFunctions;
275void replaceAll(std::string &str,
const std::string &from,
const std::string &to)
281 str.replace(
start_pos, from.length(), to);
291 std::unordered_set<RooFit::Detail::DataKey> dependsOnData;
293 dependsOnData.insert(arg);
297 if (arg->getAttribute(
"__obs__")) {
298 dependsOnData.insert(arg);
301 if (
server->isValueServer(*arg)) {
302 if (dependsOnData.find(
server) != dependsOnData.end() && !arg->isReducerNode()) {
303 dependsOnData.insert(arg);
310 return dependsOnData;
332 std::unordered_set<RooFit::Detail::DataKey> dependsOnData;
343 ctx.addResult(param,
"params[" + std::to_string(idx) +
"]");
354 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
356 _funcName =
ctx.buildFunction(obj, dependsOnData);
359 gInterpreter->Declare(
"#include <RooFit/CodegenImpl.h>\n");
363 std::string
debugFileName =
"_codegen_" + _funcName +
".cxx";
364 errorMsg <<
"Function " << _funcName <<
" could not be compiled. See above for details. Full code dumped to file "
372 throw std::runtime_error(
errorMsg.str().c_str());
375 _func =
reinterpret_cast<Func
>(
gInterpreter->ProcessLine((_funcName +
";").c_str()));
377 _xlArr =
ctx.xlArr();
378 _collectedFunctions =
ctx.collectedFunctions();
389 _observables.clear();
391 std::size_t
total = 0;
392 _observables.reserve(2 *
spans.size());
396 _observables.push_back(
total + 2 *
spans.size());
397 _observables.push_back(
item.second.size());
403 std::size_t
n =
item.second.size();
404 _observables.reserve(_observables.size() +
n);
405 for (std::size_t i = 0; i <
n; ++i) {
406 _observables.push_back(
item.second[i]);
412void RooFuncWrapper::createGradient()
415 std::string
gradName = _funcName +
"_grad_0";
419 gInterpreter->Declare(
"#include <Math/CladDerivator.h>\n");
425 " clad::gradient(" << _funcName <<
", \"params\");\n"
429 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
438 errorMsg <<
"Function could not be differentiated. See above for details.";
440 throw std::runtime_error(
errorMsg.str().c_str());
446 std::stringstream
ss;
448 ss <<
"static_cast<void (*)(double *, double const *, double const *, double *)>(" <<
gradName <<
");";
454 errorMsg <<
"Function could not be differentiated since ROOT was built without Clad support.";
456 throw std::runtime_error(
errorMsg.str().c_str());
460void RooFuncWrapper::createHessian()
463 std::string
hessianName = _funcName +
"_hessian_0";
464 std::string
requestName = _funcName +
"_hessian_req";
467 gInterpreter->Declare(
"#include <Math/CladDerivator.h>\n");
472 _params.size() == 1 ?
"\"params[0]\"" : (
"\"params[0:" + std::to_string(_params.size() - 1) +
"]\"");
475 " clad::hessian(" << _funcName <<
", " <<
paramsStr <<
");\n"
479 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
488 errorMsg <<
"Function could not be differentiated. See above for details.";
490 throw std::runtime_error(
errorMsg.str().c_str());
496 std::stringstream
ss;
498 ss <<
"static_cast<void (*)(double *, double const *, double const *, double *)>(" <<
hessianName <<
");";
504 errorMsg <<
"Function could not be differentiated since ROOT was built without Clad support.";
506 throw std::runtime_error(
errorMsg.str().c_str());
510void RooFuncWrapper::updateGradientVarBuffer()
const
513 return obj->isCategory() ? static_cast<RooAbsCategory *>(obj)->getCurrentIndex()
514 : static_cast<RooAbsReal *>(obj)->getVal();
519void RooFuncWrapper::writeDebugMacro(std::string
const &
filename)
const
525 for (std::string
const &
name : _collectedFunctions) {
530 std::unique_ptr<TInterpreterValue>
v =
gInterpreter->MakeInterpreterValue();
532 std::string s =
v->ToString();
533 for (
int i = 0; i < 2; ++i) {
534 s = s.erase(0, s.find(
"\n") + 1);
541 _params.size() == 1 ?
"\"params[0]\"" : (
"\"params[0:" + std::to_string(_params.size() - 1) +
"]\"");
543 outFile << R
"(//auto-generated test macro
544#include <RooFit/Detail/MathFuncs.h>
545#include <Math/CladDerivator.h>
552void gradient_request() {
554 << _funcName << R"(, "params");
557 << _funcName << ", " <<
paramsStr << R
"();
565 auto writeVector = [&](std::string
const &
name, std::span<const double> vec) {
566 std::stringstream
decl;
567 decl <<
"std::vector<double> " <<
name <<
" = {";
568 for (std::size_t i = 0; i < vec.size(); ++i) {
572 if (i < vec.size() - 1)
585 outFile <<
"// clang-format off\n" << std::endl;
592 outFile <<
"// clang-format on\n" << std::endl;
595// To run as a ROOT macro
599 const std::size_t n = parametersVec.size();
601 std::vector<double> gradientVec(n);
603 auto func = [&](std::span<double> params) {
605 << _funcName << R"((params.data(), observablesVec.data(), auxConstantsVec.data());
607 auto grad = [&](std::span<double> params, std::span<double> out) {
609 << _funcName << R"(_grad_0(parametersVec.data(), observablesVec.data(), auxConstantsVec.data(),
613 grad(parametersVec, gradientVec);
615 auto numDiff = [&](int i) {
616 const double eps = 1e-6;
617 std::vector<double> p{parametersVec};
618 p[i] = parametersVec[i] - eps;
619 double funcValDown = func(p);
620 p[i] = parametersVec[i] + eps;
621 double funcValUp = func(p);
622 return (funcValUp - funcValDown) / (2 * eps);
625 for (std::size_t i = 0; i < parametersVec.size(); ++i) {
626 std::cout << i << ":" << std::endl;
627 std::cout << " numr : " << numDiff(i) << std::endl;
628 std::cout << " clad : " << gradientVec[i] << std::endl;
634 auto hess = [&](std::span<double> params, std::span<double> out) {
636 << _funcName << R"(_hessian_0(params.data(), observablesVec.data(), auxConstantsVec.data(), out.data());
639 std::vector<double> hessianVec(n * n);
640 hess(parametersVec, hessianVec);
642 // ---------- Numerical Hessian ----------
643 // Uses central differences:
644 // diag: (f(x+ei)-2f(x)+f(x-ei))/eps^2
645 // offdiag: (f(++ ) - f(+-) - f(-+) + f(--)) / (4 eps^2)
646 auto numHess = [&](std::size_t i, std::size_t j) {
647 const double eps = 1e-5; // often needs to be a bit larger than grad eps
648 std::vector<double> p(parametersVec.begin(), parametersVec.end());
651 const double f0 = func(p);
653 p[i] = parametersVec[i] + eps;
654 const double fUp = func(p);
656 p[i] = parametersVec[i] - eps;
657 const double fDown = func(p);
659 return (fUp - 2.0 * f0 + fDown) / (eps * eps);
661 // f(x_i + eps, x_j + eps)
662 p[i] = parametersVec[i] + eps;
663 p[j] = parametersVec[j] + eps;
664 const double fPP = func(p);
666 // f(x_i + eps, x_j - eps)
667 p[i] = parametersVec[i] + eps;
668 p[j] = parametersVec[j] - eps;
669 const double fPM = func(p);
671 // f(x_i - eps, x_j + eps)
672 p[i] = parametersVec[i] - eps;
673 p[j] = parametersVec[j] + eps;
674 const double fMP = func(p);
676 // f(x_i - eps, x_j - eps)
677 p[i] = parametersVec[i] - eps;
678 p[j] = parametersVec[j] - eps;
679 const double fMM = func(p);
681 return (fPP - fPM - fMP + fMM) / (4.0 * eps * eps);
685 // Compute full numerical Hessian
686 std::vector<double> numHessianVec(n * n);
687 for (std::size_t i = 0; i < n; ++i) {
688 for (std::size_t j = 0; j < n; ++j) {
689 numHessianVec[i + n * j] = numHess(i, j); // keep same layout as your print
693 // ---------- Compare & print ----------
694 std::cout << "Hessian comparison (clad vs numeric vs diff):\n\n";
696 for (std::size_t i = 0; i < n; ++i) {
697 for (std::size_t j = 0; j < n; ++j) {
698 const std::size_t idx = i + n * j; // same indexing you used
699 const double cladH = hessianVec[idx];
700 const double numH = numHessianVec[idx];
701 const double diff = cladH - numH;
703 std::cout << "[" << i << "," << j << "] "
704 << "clad=" << cladH << " num=" << numH << " diff=" << diff << "\n";
708 std::cout << "\nRaw Clad Hessian matrix:\n";
709 for (std::size_t i = 0; i < n; ++i) {
710 for (std::size_t j = 0; j < n; ++j) {
711 std::cout << hessianVec[i + n * j] << " ";
716 std::cout << "\nRaw Numerical Hessian matrix:\n";
717 for (std::size_t i = 0; i < n; ++i) {
718 for (std::size_t j = 0; j < n; ++j) {
719 std::cout << numHessianVec[i + n * j] << " ";
728double RooEvaluatorWrapper::evaluate()
const
737 :
RooFit::EvalContext::OffsetMode::WithOffset);
739 return _evaluator->run()[0];
748 constexpr auto errMsg =
"Error in RooAbsReal::setData(): only resetting with same-structured data is supported.";
754 std::stack<std::vector<double>>{}.swap(_vectorBuffers);
755 const bool isChi2 = _topNode->getAttribute(
"Chi2EvaluationActive");
759 _takeGlobalObservablesFromData, _vectorBuffers);
760 if (_rangeName.empty()) {
765 throw std::runtime_error(
errMsg);
768 const char *
name =
item.first->GetName();
769 _evaluator->setInput(
name,
item.second,
false);
770 if (_paramSet.find(
name)) {
772 throw std::runtime_error(
errMsg);
781void RooEvaluatorWrapper::createFuncWrapper()
785 this->getParameters(_data ? _data->get() : nullptr,
paramSet,
false);
787 const bool isChi2 = _topNode->getAttribute(
"Chi2EvaluationActive");
793void RooEvaluatorWrapper::generateGradient()
801void RooEvaluatorWrapper::generateHessian()
809void RooEvaluatorWrapper::setUseGeneratedFunctionCode(
bool flag)
816void RooEvaluatorWrapper::gradient(
double *out)
const
821void RooEvaluatorWrapper::hessian(
double *out)
const
826bool RooEvaluatorWrapper::hasGradient()
const
831bool RooEvaluatorWrapper::hasHessian()
const
836void RooEvaluatorWrapper::writeDebugMacro(std::string
const &
filename)
const
844 return _evaluator->setOperModes(
opMode);
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.
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
Abstract base class for binned and unbinned datasets.
Abstract interface for all probability density functions.
virtual double getMax(const char *name=nullptr) const
Get maximum of currently defined range.
virtual double getMin(const char *name=nullptr) const
Get minimum of currently defined range.
bool inRange(const char *name) const override
Check if current value is inside range with given name.
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.
Variable that can be changed from the outside.
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.