From cc2ec308484152fcfabb59c8d49352d4579ccd45 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 17 Sep 2025 15:05:25 +0200 Subject: [PATCH 1/5] [RF] Make testRooMulti independent of clad. When clad is off, the tests were failing. With the standard backend, they run fine, though. All backends are tested in parametrised tests, so testRooMulti can be made independent of any backend. --- roofit/roofitcore/test/testRooMulti.cxx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/roofit/roofitcore/test/testRooMulti.cxx b/roofit/roofitcore/test/testRooMulti.cxx index 06d40574b3e27..271b0785a64fd 100644 --- a/roofit/roofitcore/test/testRooMulti.cxx +++ b/roofit/roofitcore/test/testRooMulti.cxx @@ -58,6 +58,7 @@ TEST(RooMultiPdfTest, FitConvergesAndReturnsReasonableResult) { using namespace RooFit; + const EvalBackend backend{EvalBackend::defaultValue()}; RooRealVar x("x", "x", -10, 10); @@ -78,7 +79,7 @@ TEST(RooMultiPdfTest, FitConvergesAndReturnsReasonableResult) // Fit 1 - RooMultiPdf fit - std::unique_ptr nll{multipdf.createNLL(*data, EvalBackend("codegen"))}; + std::unique_ptr nll{multipdf.createNLL(*data, backend)}; RooMinimizer minim{*nll}; minim.setStrategy(0); @@ -92,7 +93,7 @@ TEST(RooMultiPdfTest, FitConvergesAndReturnsReasonableResult) m1.setError(0.0); s1.setError(0.0); // Fit 2 - Reference fit - std::unique_ptr nll1{gaus1.createNLL(*data, EvalBackend("codegen"))}; + std::unique_ptr nll1{gaus1.createNLL(*data, backend)}; RooMinimizer minim1{*nll1}; minim1.setStrategy(0); @@ -122,10 +123,10 @@ TEST(RooMultiPdfTest, FitConvergesAndReturnsReasonableResult) indx.setIndex(i); - std::unique_ptr nll_multi{multipdf.createNLL(*data, EvalBackend("codegen"))}; + std::unique_ptr nll_multi{multipdf.createNLL(*data, backend)}; RooAbsPdf *selectedPdf = multipdf.getPdf(i); - std::unique_ptr nll_direct{selectedPdf->createNLL(*data, EvalBackend("codegen"))}; + std::unique_ptr nll_direct{selectedPdf->createNLL(*data, backend)}; int n_param = countFloatingParametersIncludingObservable(*selectedPdf); @@ -142,6 +143,7 @@ TEST(RooMultiPdfTest, FitConvergesAndReturnsReasonableResult) TEST(RooMultiPdfTest, PenaltyTermIsAppliedCorrectly) { using namespace RooFit; + const EvalBackend backend = EvalBackend::defaultValue(); RooRealVar x("x", "x", -10, 10); @@ -162,9 +164,9 @@ TEST(RooMultiPdfTest, PenaltyTermIsAppliedCorrectly) std::unique_ptr data{gauss1.generate(x, 100)}; - std::unique_ptr nll_gauss1{gauss1.createNLL(*data, EvalBackend("codegen"))}; + std::unique_ptr nll_gauss1{gauss1.createNLL(*data, backend)}; - std::unique_ptr nll_multi{multiPdf.createNLL(*data, EvalBackend("codegen"))}; + std::unique_ptr nll_multi{multiPdf.createNLL(*data, backend)}; double val_gauss1 = nll_gauss1->getVal(); double val_multi = nll_multi->getVal(); From 4f76fe98cc4045d392708f0e9c21421c0f8fcab1 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 17 Sep 2025 14:31:02 +0200 Subject: [PATCH 2/5] [RF] Make tutorial r619 runnable without clad. Also fix a warning that protects from a Gaussian with sigma=0. Co-authored-by: Jonas Rembser --- tutorials/roofit/roofit/rf619_discrete_profiling.C | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorials/roofit/roofit/rf619_discrete_profiling.C b/tutorials/roofit/roofit/rf619_discrete_profiling.C index d54e3562b1b9a..4f698e1239e5e 100644 --- a/tutorials/roofit/roofit/rf619_discrete_profiling.C +++ b/tutorials/roofit/roofit/rf619_discrete_profiling.C @@ -35,7 +35,7 @@ void rf619_discrete_profiling() // Category 1 Pdf-s: Gaussian + Landau. RooRealVar mean("mean", "shared mean", 25, 0, 50); - RooRealVar sigmaG("sigmaG", "Gaussian width", 2.0, 0.0, 5.0); + RooRealVar sigmaG("sigmaG", "Gaussian width", 2.0, 0.01, 5.0); RooRealVar sigmaL("sigmaL", "Landau width", 3.0, 1.0, 8.0); RooGaussian gauss1("gauss1", "Gaussian", x, mean, sigmaG); @@ -118,8 +118,8 @@ void rf619_discrete_profiling() data->add(vars); } - // Create NLL with codegen and minimize it via the discrete profiling method. - std::unique_ptr nll1(simPdf.createNLL(*data, EvalBackend("codegen"))); + // Create an NLL and minimize it via the discrete profiling method. + std::unique_ptr nll1(simPdf.createNLL(*data)); RooMinimizer minim(*nll1); minim.setStrategy(1); From 97213fba25ca4beb5384c755cf92ec81e8ae2099 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Tue, 9 Sep 2025 10:06:29 +0200 Subject: [PATCH 3/5] [math] Throw exception when RootFinder is constructed with invalid algo. When MathMore cannot be loaded, the tutorial exampleFunction.py used to crash. Here, a throw is added to the constructor, which is caught in the Python tutorial. --- math/mathcore/src/RootFinder.cxx | 16 ++++++++++------ tutorials/math/exampleFunction.py | 18 ++++++------------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/math/mathcore/src/RootFinder.cxx b/math/mathcore/src/RootFinder.cxx index 4d94c95cd271f..93d4f7d4dc204 100644 --- a/math/mathcore/src/RootFinder.cxx +++ b/math/mathcore/src/RootFinder.cxx @@ -41,11 +41,15 @@ RootFinder::RootFinder(RootFinder::EType type) : fSolver(nullptr) { // constructor passing type (default is kBRENT) - SetMethod(type); + const bool success = SetMethod(type); + if (!success) + throw std::runtime_error("ROOT::Math::RootFinder: ROOT was built without the requested algorithm"); } bool RootFinder::SetMethod(RootFinder::EType type) { + fSolver = nullptr; + // set method - Use the plug-in manager if method is implemented in MathMore if ( type == RootFinder::kBRENT ) { @@ -125,16 +129,16 @@ bool RootFinder::SetMethod(RootFinder::EType type) return false; } - fSolver = reinterpret_cast( h->ExecPlugin(0) ); - assert(fSolver != nullptr); + fSolver = reinterpret_cast(h->ExecPlugin(0)); } - else { + +#endif + + if (!fSolver) { MATH_ERROR_MSG("RootFinder::SetMethod","Error loading RootFinderMethod"); return false; } -#endif - return true; } diff --git a/tutorials/math/exampleFunction.py b/tutorials/math/exampleFunction.py index 09c0bf9ef11a6..065f06a457a8c 100644 --- a/tutorials/math/exampleFunction.py +++ b/tutorials/math/exampleFunction.py @@ -69,18 +69,10 @@ def RosenbrockFunction(xx): def g(x): return 2 * x -gradFunc = ROOT.Math.GradFunctor1D(f, g) - -#check if ROOT has mathmore -prevLevel = ROOT.gErrorIgnoreLevel -ROOT.gErrorIgnoreLevel=ROOT.kFatal -ret = ROOT.gSystem.Load("libMathMore") -ROOT.gErrorIgnoreLevel=prevLevel -if (ret < 0) : - print("ROOT has not Mathmore") - print("derivative value at x = 1", gradFunc.Derivative(1) ) - -else : +# GSL_Newton is part of ROOT's MathMore library, which is not always active. +# Let's therefore run this in a try block: +try: + gradFunc = ROOT.Math.GradFunctor1D(f, g) rf = ROOT.Math.RootFinder(ROOT.Math.RootFinder.kGSL_NEWTON) rf.SetFunction(gradFunc, 3) rf.Solve() @@ -88,6 +80,8 @@ def g(x): return 2 * x print("Found root value x0 : f(x0) = 0 : ", value) if (value != 1): print("Error finding a ROOT of function f(x)=x^2-1") +except Exception as e: + print(e) print("\n\nUse GradFunctor for making a function object implementing f(x,y) and df(x,y)/dx and df(x,y)/dy") From 99cce7a038b01e163c4d9185a392d07e8176ac48 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Thu, 9 Oct 2025 09:00:00 +0200 Subject: [PATCH 4/5] [tutorials] Apply ruff suggestions to exampleFunction.py --- tutorials/math/exampleFunction.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tutorials/math/exampleFunction.py b/tutorials/math/exampleFunction.py index 065f06a457a8c..16056fb53da04 100644 --- a/tutorials/math/exampleFunction.py +++ b/tutorials/math/exampleFunction.py @@ -10,12 +10,14 @@ ## ## \author Lorenzo Moneta -import ROOT import array + +import ROOT + try: import numpy as np -except: - print("Failed to import numpy.") +except Exception as e: + print("Failed to import numpy:", e) exit() ## example 1D function @@ -72,16 +74,16 @@ def g(x): return 2 * x # GSL_Newton is part of ROOT's MathMore library, which is not always active. # Let's therefore run this in a try block: try: - gradFunc = ROOT.Math.GradFunctor1D(f, g) - rf = ROOT.Math.RootFinder(ROOT.Math.RootFinder.kGSL_NEWTON) - rf.SetFunction(gradFunc, 3) - rf.Solve() - value = rf.Root() - print("Found root value x0 : f(x0) = 0 : ", value) - if (value != 1): - print("Error finding a ROOT of function f(x)=x^2-1") + gradFunc = ROOT.Math.GradFunctor1D(f, g) + rf = ROOT.Math.RootFinder(ROOT.Math.RootFinder.kGSL_NEWTON) + rf.SetFunction(gradFunc, 3) + rf.Solve() + value = rf.Root() + print("Found root value x0 : f(x0) = 0 : ", value) + if value != 1: + print("Error finding a ROOT of function f(x)=x^2-1") except Exception as e: - print(e) + print(e) print("\n\nUse GradFunctor for making a function object implementing f(x,y) and df(x,y)/dx and df(x,y)/dy") From 9aa6423213880d3f6e1b5c63f48eb2c1c9876c60 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 8 Oct 2025 18:09:52 +0200 Subject: [PATCH 5/5] [RF] Add fallback to RooFit's EvalBackend when built without clad. --- roofit/roofitcore/src/RooGlobalFunc.cxx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/roofit/roofitcore/src/RooGlobalFunc.cxx b/roofit/roofitcore/src/RooGlobalFunc.cxx index b2a3a7274ed38..a9bd2713f6002 100644 --- a/roofit/roofitcore/src/RooGlobalFunc.cxx +++ b/roofit/roofitcore/src/RooGlobalFunc.cxx @@ -560,7 +560,16 @@ RooCmdArg EventRange(Int_t nStart, Int_t nStop) // RooAbsPdf::fitTo arguments -EvalBackend::EvalBackend(EvalBackend::Value value) : RooCmdArg{"EvalBackend", static_cast(value)} {} +EvalBackend::EvalBackend(EvalBackend::Value value) : RooCmdArg{"EvalBackend", static_cast(value)} +{ +#ifndef ROOFIT_CLAD + if (value == Value::Codegen || value == Value::CodegenNoGrad) { + oocoutE(nullptr, InputArguments) + << "RooFit was built without clad. Codegen backends are unavailable. Falling back to default.\n"; + setInt(0, static_cast(defaultValue())); + } +#endif +} EvalBackend::EvalBackend(std::string const &name) : EvalBackend{toValue(name)} {} EvalBackend::Value EvalBackend::toValue(std::string const &name) {