From e9164582315a7f3ff02ccde84b2a5f6a1199a16a Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Tue, 11 Aug 2026 12:02:24 +0000 Subject: [PATCH] [RF] Add a clad pushforward for MathFuncs::binNumber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit binNumber uses std::lower_bound, which clad can't differentiate. That was already handled for reverse mode by a dummy binNumber_pullback, but Hessians also run the forward pass over binNumber, and without a pushforward clad descends into std::lower_bound and warns about differentiating __builtin_constant_p and about unsupported typedef declarations, before failing outright. Declare the forward-mode counterpart, zero-valued for the same reason as the pullback: binNumber returns an integer, so it has no derivative. Since MathFuncs.h is compiled normally as well as parsed by cling, while clad::ValueAndPushforward only exists inside the interpreter, the type is forward-declared and the return type is kept dependent so that it is only completed when clad instantiates the template. 🤖 Done with the help of AI --- .../roofitcore/inc/RooFit/Detail/MathFuncs.h | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/roofit/roofitcore/inc/RooFit/Detail/MathFuncs.h b/roofit/roofitcore/inc/RooFit/Detail/MathFuncs.h index 9dda0467cbfb8..5f81c10614992 100644 --- a/roofit/roofitcore/inc/RooFit/Detail/MathFuncs.h +++ b/roofit/roofitcore/inc/RooFit/Detail/MathFuncs.h @@ -865,19 +865,49 @@ double stepFunctionIntegral(double xmin, double xmax, std::size_t nBins, DoubleA } // namespace RooFit::Detail::MathFuncs +namespace clad { +// Only declared, never defined here: clad's own headers exist exclusively +// inside the interpreter, but this header is also compiled normally. +template +struct ValueAndPushforward; +} // namespace clad + namespace clad::custom_derivatives { namespace RooFit::Detail::MathFuncs { -// Clad can't generate the pullback for binNumber because of the +// Clad can't generate the derivatives for binNumber because of the // std::lower_bound usage. But since binNumber returns an integer, and such -// functions have mathematically no derivatives anyway, we just declare a -// custom dummy pullback that does nothing. +// functions have mathematically no derivatives anyway, we just declare custom +// dummy derivatives that do nothing. +// +// Both directions have to be covered: the pullback alone is enough for +// gradients, but Hessians also run the forward pass over binNumber, and +// without a pushforward clad descends into std::lower_bound. template void binNumber_pullback(Types...) { } +// The second parameter is unused. It is what keeps the return type of the +// pushforward below dependent, so that clad::ValueAndPushforward only has to be +// complete once that template is instantiated. That happens under clad and +// nowhere else, while parsing the declaration happens in every build that +// includes this header. A class template is needed here: an alias template +// would be expanded eagerly and defeat the purpose. +template +struct ValueAndPushforwardOf { + using type = ::clad::ValueAndPushforward; +}; + +template +typename ValueAndPushforwardOf::type +binNumber_pushforward(double x, double coef, DoubleArray boundaries, unsigned int nBoundaries, int nbins, int blo, + Types...) +{ + return {::RooFit::Detail::MathFuncs::binNumber(x, coef, boundaries, nBoundaries, nbins, blo), 0}; +} + } // namespace RooFit::Detail::MathFuncs } // namespace clad::custom_derivatives