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),
139 setData(*
data,
false);
141 _paramSet.add(_evaluator->getParameters());
143 _paramSet.remove(*_paramSet.find(
item.first->GetName()));
149 _evaluator{
other._evaluator},
150 _topNode(
"topNode",
this,
other._topNode),
152 _paramSet(
"paramSet",
"Set of parameters",
this),
153 _rangeName{
other._rangeName},
155 _takeGlobalObservablesFromData{
other._takeGlobalObservablesFromData},
158 _paramSet.add(
other._paramSet);
161RooEvaluatorWrapper::~RooEvaluatorWrapper() =
default;
166 outputSet.add(_evaluator->getParameters());
168 outputSet.remove(*observables,
false,
true);
172 if (_data->getGlobalObservables() && _data->getGlobalObservables()->find(
item.first->GetName())) {
184 if (_takeGlobalObservablesFromData && _data->getGlobalObservables()) {
185 outputSet.replace(*_data->getGlobalObservables());
218 void gradient(
double *out)
const
221 std::fill(out, out + _params.size(), 0.0);
224 void hessian(
double *out)
const
227 std::fill(out, out + _params.size() * _params.size(), 0.0);
236 std::vector<std::string>
const &collectedFunctions() {
return _collectedFunctions; }
241 return _func(
_varBuffer.data(), _observables.data(), _xlArr.data());
252 using Func =
double (*)(
double *,
double const *,
double const *);
253 using Grad = void (*)(
double *,
double const *,
double const *,
double *);
254 using Hessian = void (*)(
double *,
double const *,
double const *,
double *);
257 std::string _funcName;
264 std::vector<double> _observables;
265 std::unordered_map<RooFit::Detail::DataKey, std::size_t>
_obsInfos;
266 std::vector<double> _xlArr;
267 std::vector<std::string> _collectedFunctions;
272void replaceAll(std::string &str,
const std::string &from,
const std::string &to)
278 str.replace(
start_pos, from.length(), to);
288 std::unordered_set<RooFit::Detail::DataKey> dependsOnData;
290 dependsOnData.insert(arg);
294 if (arg->getAttribute(
"__obs__")) {
295 dependsOnData.insert(arg);
298 if (
server->isValueServer(*arg)) {
299 if (dependsOnData.find(
server) != dependsOnData.end() && !arg->isReducerNode()) {
300 dependsOnData.insert(arg);
307 return dependsOnData;
329 std::unordered_set<RooFit::Detail::DataKey> dependsOnData;
340 ctx.
addResult(param,
"params[" + std::to_string(idx) +
"]");
351 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
356 gInterpreter->Declare(
"#include <RooFit/CodegenImpl.h>\n");
360 std::string
debugFileName =
"_codegen_" + _funcName +
".cxx";
361 errorMsg <<
"Function " << _funcName <<
" could not be compiled. See above for details. Full code dumped to file "
369 throw std::runtime_error(
errorMsg.str().c_str());
372 _func =
reinterpret_cast<Func
>(
gInterpreter->ProcessLine((_funcName +
";").c_str()));
374 _xlArr = ctx.
xlArr();
386 _observables.clear();
388 std::size_t
total = 0;
389 _observables.reserve(2 *
spans.size());
393 _observables.push_back(
total + 2 *
spans.size());
394 _observables.push_back(
item.second.size());
400 std::size_t
n =
item.second.size();
401 _observables.reserve(_observables.size() +
n);
402 for (std::size_t i = 0; i <
n; ++i) {
403 _observables.push_back(
item.second[i]);
409void RooFuncWrapper::createGradient()
412 std::string
gradName = _funcName +
"_grad_0";
416 gInterpreter->Declare(
"#include <Math/CladDerivator.h>\n");
422 " clad::gradient(" << _funcName <<
", \"params\");\n"
426 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
435 errorMsg <<
"Function could not be differentiated. See above for details.";
437 throw std::runtime_error(
errorMsg.str().c_str());
443 std::stringstream
ss;
445 ss <<
"static_cast<void (*)(double *, double const *, double const *, double *)>(" <<
gradName <<
");";
451 errorMsg <<
"Function could not be differentiated since ROOT was built without Clad support.";
453 throw std::runtime_error(
errorMsg.str().c_str());
457void RooFuncWrapper::createHessian()
460 std::string
hessianName = _funcName +
"_hessian_0";
461 std::string
requestName = _funcName +
"_hessian_req";
464 gInterpreter->Declare(
"#include <Math/CladDerivator.h>\n");
469 _params.size() == 1 ?
"\"params[0]\"" : (
"\"params[0:" + std::to_string(_params.size() - 1) +
"]\"");
472 " clad::hessian(" << _funcName <<
", " <<
paramsStr <<
");\n"
476 auto print = [](std::string
const &
msg) {
oocoutI(
nullptr, Fitting) <<
msg << std::endl; };
485 errorMsg <<
"Function could not be differentiated. See above for details.";
487 throw std::runtime_error(
errorMsg.str().c_str());
493 std::stringstream
ss;
495 ss <<
"static_cast<void (*)(double *, double const *, double const *, double *)>(" <<
hessianName <<
");";
501 errorMsg <<
"Function could not be differentiated since ROOT was built without Clad support.";
503 throw std::runtime_error(
errorMsg.str().c_str());
507void RooFuncWrapper::updateGradientVarBuffer()
const
510 return obj->isCategory() ? static_cast<RooAbsCategory *>(obj)->getCurrentIndex()
511 : static_cast<RooAbsReal *>(obj)->getVal();
516void RooFuncWrapper::writeDebugMacro(std::string
const &
filename)
const
522 for (std::string
const &
name : _collectedFunctions) {
527 std::unique_ptr<TInterpreterValue>
v =
gInterpreter->MakeInterpreterValue();
529 std::string s =
v->ToString();
530 for (
int i = 0; i < 2; ++i) {
531 s = s.erase(0, s.find(
"\n") + 1);
538 _params.size() == 1 ?
"\"params[0]\"" : (
"\"params[0:" + std::to_string(_params.size() - 1) +
"]\"");
540 outFile << R
"(//auto-generated test macro
541#include <RooFit/Detail/MathFuncs.h>
542#include <Math/CladDerivator.h>
549void gradient_request() {
551 << _funcName << R"(, "params");
554 << _funcName << ", " <<
paramsStr << R
"();
562 auto writeVector = [&](std::string
const &
name, std::span<const double> vec) {
563 std::stringstream
decl;
564 decl <<
"std::vector<double> " <<
name <<
" = {";
565 for (std::size_t i = 0; i < vec.size(); ++i) {
569 if (i < vec.size() - 1)
582 outFile <<
"// clang-format off\n" << std::endl;
589 outFile <<
"// clang-format on\n" << std::endl;
592// To run as a ROOT macro
596 const std::size_t n = parametersVec.size();
598 std::vector<double> gradientVec(n);
600 auto func = [&](std::span<double> params) {
602 << _funcName << R"((params.data(), observablesVec.data(), auxConstantsVec.data());
604 auto grad = [&](std::span<double> params, std::span<double> out) {
606 << _funcName << R"(_grad_0(parametersVec.data(), observablesVec.data(), auxConstantsVec.data(),
610 grad(parametersVec, gradientVec);
612 auto numDiff = [&](int i) {
613 const double eps = 1e-6;
614 std::vector<double> p{parametersVec};
615 p[i] = parametersVec[i] - eps;
616 double funcValDown = func(p);
617 p[i] = parametersVec[i] + eps;
618 double funcValUp = func(p);
619 return (funcValUp - funcValDown) / (2 * eps);
622 for (std::size_t i = 0; i < parametersVec.size(); ++i) {
623 std::cout << i << ":" << std::endl;
624 std::cout << " numr : " << numDiff(i) << std::endl;
625 std::cout << " clad : " << gradientVec[i] << std::endl;
631 auto hess = [&](std::span<double> params, std::span<double> out) {
633 << _funcName << R"(_hessian_0(params.data(), observablesVec.data(), auxConstantsVec.data(), out.data());
636 std::vector<double> hessianVec(n * n);
637 hess(parametersVec, hessianVec);
639 // ---------- Numerical Hessian ----------
640 // Uses central differences:
641 // diag: (f(x+ei)-2f(x)+f(x-ei))/eps^2
642 // offdiag: (f(++ ) - f(+-) - f(-+) + f(--)) / (4 eps^2)
643 auto numHess = [&](std::size_t i, std::size_t j) {
644 const double eps = 1e-5; // often needs to be a bit larger than grad eps
645 std::vector<double> p(parametersVec.begin(), parametersVec.end());
648 const double f0 = func(p);
650 p[i] = parametersVec[i] + eps;
651 const double fUp = func(p);
653 p[i] = parametersVec[i] - eps;
654 const double fDown = func(p);
656 return (fUp - 2.0 * f0 + fDown) / (eps * eps);
658 // f(x_i + eps, x_j + eps)
659 p[i] = parametersVec[i] + eps;
660 p[j] = parametersVec[j] + eps;
661 const double fPP = func(p);
663 // f(x_i + eps, x_j - eps)
664 p[i] = parametersVec[i] + eps;
665 p[j] = parametersVec[j] - eps;
666 const double fPM = func(p);
668 // f(x_i - eps, x_j + eps)
669 p[i] = parametersVec[i] - eps;
670 p[j] = parametersVec[j] + eps;
671 const double fMP = func(p);
673 // f(x_i - eps, x_j - eps)
674 p[i] = parametersVec[i] - eps;
675 p[j] = parametersVec[j] - eps;
676 const double fMM = func(p);
678 return (fPP - fPM - fMP + fMM) / (4.0 * eps * eps);
682 // Compute full numerical Hessian
683 std::vector<double> numHessianVec(n * n);
684 for (std::size_t i = 0; i < n; ++i) {
685 for (std::size_t j = 0; j < n; ++j) {
686 numHessianVec[i + n * j] = numHess(i, j); // keep same layout as your print
690 // ---------- Compare & print ----------
691 std::cout << "Hessian comparison (clad vs numeric vs diff):\n\n";
693 for (std::size_t i = 0; i < n; ++i) {
694 for (std::size_t j = 0; j < n; ++j) {
695 const std::size_t idx = i + n * j; // same indexing you used
696 const double cladH = hessianVec[idx];
697 const double numH = numHessianVec[idx];
698 const double diff = cladH - numH;
700 std::cout << "[" << i << "," << j << "] "
701 << "clad=" << cladH << " num=" << numH << " diff=" << diff << "\n";
705 std::cout << "\nRaw Clad Hessian matrix:\n";
706 for (std::size_t i = 0; i < n; ++i) {
707 for (std::size_t j = 0; j < n; ++j) {
708 std::cout << hessianVec[i + n * j] << " ";
713 std::cout << "\nRaw Numerical Hessian matrix:\n";
714 for (std::size_t i = 0; i < n; ++i) {
715 for (std::size_t j = 0; j < n; ++j) {
716 std::cout << numHessianVec[i + n * j] << " ";
725double RooEvaluatorWrapper::evaluate()
const
734 :
RooFit::EvalContext::OffsetMode::WithOffset);
736 return _evaluator->run()[0];
745 constexpr auto errMsg =
"Error in RooAbsReal::setData(): only resetting with same-structured data is supported.";
751 std::stack<std::vector<double>>{}.swap(_vectorBuffers);
752 const bool isChi2 = _topNode->getAttribute(
"Chi2EvaluationActive");
756 _takeGlobalObservablesFromData, _vectorBuffers);
757 if (_rangeName.empty()) {
762 throw std::runtime_error(
errMsg);
765 const char *
name =
item.first->GetName();
766 _evaluator->setInput(
name,
item.second,
false);
767 if (_paramSet.find(
name)) {
769 throw std::runtime_error(
errMsg);
778void RooEvaluatorWrapper::createFuncWrapper()
782 this->getParameters(_data ? _data->get() : nullptr,
paramSet,
false);
784 const bool isChi2 = _topNode->getAttribute(
"Chi2EvaluationActive");
790void RooEvaluatorWrapper::generateGradient()
798void RooEvaluatorWrapper::generateHessian()
806void RooEvaluatorWrapper::setUseGeneratedFunctionCode(
bool flag)
813void RooEvaluatorWrapper::gradient(
double *out)
const
818void RooEvaluatorWrapper::hessian(
double *out)
const
823bool RooEvaluatorWrapper::hasGradient()
const
828bool RooEvaluatorWrapper::hasHessian()
const
833void RooEvaluatorWrapper::writeDebugMacro(std::string
const &
filename)
const
841 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.
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()
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.