diff --git a/roofit/CMakeLists.txt b/roofit/CMakeLists.txt index 791a77b842206..76acf8f1c4684 100644 --- a/roofit/CMakeLists.txt +++ b/roofit/CMakeLists.txt @@ -7,6 +7,7 @@ set(roofit_legacy_eval_backend ON CACHE BOOL "" FORCE) add_subdirectory(batchcompute) +add_subdirectory(codegen) if (roofit_multiprocess) add_subdirectory(roofitZMQ) add_subdirectory(multiprocess) diff --git a/roofit/codegen/CMakeLists.txt b/roofit/codegen/CMakeLists.txt new file mode 100644 index 0000000000000..e1e5164c9e25d --- /dev/null +++ b/roofit/codegen/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. +# All rights reserved. +# +# For the licensing terms see $ROOTSYS/LICENSE. +# For the list of contributors see $ROOTSYS/README/CREDITS. + +############################################################################ +# CMakeLists.txt file for building ROOT rootfit/histfactory package +# @author Pere Mato, CERN +############################################################################ + +ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCodegen + HEADERS + RooFit/CodegenImpl.h + SOURCES + src/CodegenImpl.cxx + DICTIONARY_OPTIONS + "-writeEmptyRootPCM" + DEPENDENCIES + RooFit + RooFitCore + HistFactory +) diff --git a/roofit/codegen/LinkDef.h b/roofit/codegen/LinkDef.h new file mode 100644 index 0000000000000..a18c9b6715f9a --- /dev/null +++ b/roofit/codegen/LinkDef.h @@ -0,0 +1,7 @@ +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#endif diff --git a/roofit/codegen/inc/RooFit/CodegenImpl.h b/roofit/codegen/inc/RooFit/CodegenImpl.h new file mode 100644 index 0000000000000..48a3a6f9906d5 --- /dev/null +++ b/roofit/codegen/inc/RooFit/CodegenImpl.h @@ -0,0 +1,155 @@ +/* + * Project: RooFit + * Authors: + * Jonas Rembser, CERN 2024 + * + * Copyright (c) 2024, CERN + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted according to the terms + * listed in LICENSE (http://roofit.sourceforge.net/license.txt) + */ + +#ifndef RooFit_Detail_CodegenImpl_h +#define RooFit_Detail_CodegenImpl_h + +#include + +#include + +class ParamHistFunc; +class PiecewiseInterpolation; +class RooAbsArg; +class RooAbsReal; +class RooAddPdf; +class RooAddition; +class RooBernstein; +class RooBifurGauss; +class RooCBShape; +class RooChebychev; +class RooConstVar; +class RooConstraintSum; +class RooEffProd; +class RooEfficiency; +class RooExponential; +class RooExtendPdf; +class RooFormulaVar; +class RooGamma; +class RooGaussian; +class RooGenericPdf; +class RooHistFunc; +class RooHistPdf; +class RooLandau; +class RooLognormal; +class RooMultiVarGaussian; +class RooParamHistFunc; +class RooPoisson; +class RooPolyVar; +class RooPolynomial; +class RooProduct; +class RooRatio; +class RooRealIntegral; +class RooRealSumFunc; +class RooRealSumPdf; +class RooRealVar; +class RooRecursiveFraction; +class RooUniform; + +namespace RooStats { +namespace HistFactory { +class FlexibleInterpVar; +} +} // namespace RooStats + +namespace RooFit { + +namespace Detail { +class RooFixedProdPdf; +class RooNLLVarNew; +class RooNormalizedPdf; +} // namespace Detail + +class CodegenContext; + +void codegenImpl(Detail::RooFixedProdPdf &arg, CodegenContext &ctx); +void codegenImpl(Detail::RooNLLVarNew &arg, CodegenContext &ctx); +void codegenImpl(Detail::RooNormalizedPdf &arg, CodegenContext &ctx); +void codegenImpl(ParamHistFunc &arg, CodegenContext &ctx); +void codegenImpl(PiecewiseInterpolation &arg, CodegenContext &ctx); +void codegenImpl(RooAbsArg &arg, RooFit::CodegenContext &ctx); +void codegenImpl(RooAddPdf &arg, CodegenContext &ctx); +void codegenImpl(RooAddition &arg, CodegenContext &ctx); +void codegenImpl(RooBernstein &arg, CodegenContext &ctx); +void codegenImpl(RooBifurGauss &arg, CodegenContext &ctx); +void codegenImpl(RooCBShape &arg, CodegenContext &ctx); +void codegenImpl(RooChebychev &arg, CodegenContext &ctx); +void codegenImpl(RooConstVar &arg, CodegenContext &ctx); +void codegenImpl(RooConstraintSum &arg, CodegenContext &ctx); +void codegenImpl(RooEffProd &arg, CodegenContext &ctx); +void codegenImpl(RooEfficiency &arg, CodegenContext &ctx); +void codegenImpl(RooExponential &arg, CodegenContext &ctx); +void codegenImpl(RooExtendPdf &arg, CodegenContext &ctx); +void codegenImpl(RooFormulaVar &arg, CodegenContext &ctx); +void codegenImpl(RooGamma &arg, CodegenContext &ctx); +void codegenImpl(RooGaussian &arg, CodegenContext &ctx); +void codegenImpl(RooGenericPdf &arg, CodegenContext &ctx); +void codegenImpl(RooHistFunc &arg, CodegenContext &ctx); +void codegenImpl(RooHistPdf &arg, CodegenContext &ctx); +void codegenImpl(RooLandau &arg, CodegenContext &ctx); +void codegenImpl(RooLognormal &arg, CodegenContext &ctx); +void codegenImpl(RooMultiVarGaussian &arg, CodegenContext &ctx); +void codegenImpl(RooParamHistFunc &arg, CodegenContext &ctx); +void codegenImpl(RooPoisson &arg, CodegenContext &ctx); +void codegenImpl(RooPolyVar &arg, CodegenContext &ctx); +void codegenImpl(RooPolynomial &arg, CodegenContext &ctx); +void codegenImpl(RooProduct &arg, CodegenContext &ctx); +void codegenImpl(RooRatio &arg, CodegenContext &ctx); +void codegenImpl(RooRealIntegral &arg, CodegenContext &ctx); +void codegenImpl(RooRealSumFunc &arg, CodegenContext &ctx); +void codegenImpl(RooRealSumPdf &arg, CodegenContext &ctx); +void codegenImpl(RooRealVar &arg, CodegenContext &ctx); +void codegenImpl(RooRecursiveFraction &arg, CodegenContext &ctx); +void codegenImpl(RooStats::HistFactory::FlexibleInterpVar &arg, CodegenContext &ctx); +void codegenImpl(RooUniform &arg, CodegenContext &ctx); + +std::string codegenIntegralImpl(RooAbsReal &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooBernstein &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooBifurGauss &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooCBShape &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooChebychev &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooEfficiency &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooExponential &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooGamma &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooGaussian &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooHistFunc &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooHistPdf &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooLandau &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooLognormal &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooMultiVarGaussian &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooPoisson &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooPolyVar &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooPolynomial &arg, int code, const char *rangeName, CodegenContext &ctx); +std::string codegenIntegralImpl(RooUniform &arg, int code, const char *rangeName, CodegenContext &ctx); + +template +std::string codegenIntegralImpl(Arg_t &arg, int code, const char *rangeName, CodegenContext &ctx, Prio

p) +{ + if constexpr (std::is_same, PrioLowest>::value) { + return codegenIntegralImpl(arg, code, rangeName, ctx); + } else { + return codegenIntegralImpl(arg, code, rangeName, ctx, p.next()); + } +} + +template +struct CodegenIntegralImplCaller { + + static auto call(RooAbsReal &arg, int code, const char *rangeName, RooFit::CodegenContext &ctx) + { + return codegenIntegralImpl(static_cast(arg), code, rangeName, ctx, PrioHighest{}); + } +}; + +} // namespace RooFit + +#endif diff --git a/roofit/codegen/src/CodegenImpl.cxx b/roofit/codegen/src/CodegenImpl.cxx new file mode 100644 index 0000000000000..eec3f065c49d8 --- /dev/null +++ b/roofit/codegen/src/CodegenImpl.cxx @@ -0,0 +1,847 @@ +/* + * Project: RooFit + * Authors: + * Garima Singh, CERN 2023 + * Jonas Rembser, CERN 2024 + * + * Copyright (c) 2024, CERN + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted according to the terms + * listed in LICENSE (http://roofit.sourceforge.net/license.txt) + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "RooFitImplHelpers.h" + +#include + +namespace RooFit { + +namespace { + +std::string mathFunc(std::string const &name) +{ + return "RooFit::Detail::MathFuncs::" + name; +} + +void rooHistTranslateImpl(RooAbsArg const &arg, CodegenContext &ctx, int intOrder, RooDataHist const &dataHist, + const RooArgSet &obs, bool correctForBinSize, bool cdfBoundaries) +{ + if (intOrder != 0 && !(!cdfBoundaries && !correctForBinSize && intOrder == 1 && obs.size() == 1)) { + ooccoutE(&arg, InputArguments) << "RooHistPdf::weight(" << arg.GetName() + << ") ERROR: codegen currently only supports non-interpolation cases." + << std::endl; + return; + } + + if (intOrder == 1) { + RooAbsBinning const &binning = *dataHist.getBinnings()[0]; + std::string weightArr = dataHist.declWeightArrayForCodeSquash(ctx, correctForBinSize); + ctx.addResult(&arg, ctx.buildCall(mathFunc("interpolate1d"), binning.lowBound(), binning.highBound(), *obs[0], + binning.numBins(), weightArr)); + return; + } + std::string const &offset = dataHist.calculateTreeIndexForCodeSquash(&arg, ctx, obs); + std::string weightArr = dataHist.declWeightArrayForCodeSquash(ctx, correctForBinSize); + ctx.addResult(&arg, "*(" + weightArr + " + " + offset + ")"); +} + +std::string realSumPdfTranslateImpl(CodegenContext &ctx, RooAbsArg const &arg, RooArgList const &funcList, + RooArgList const &coefList, bool normalize) +{ + bool noLastCoeff = funcList.size() != coefList.size(); + + std::string const &funcName = ctx.buildArg(funcList); + std::string const &coeffName = ctx.buildArg(coefList); + std::string const &coeffSize = std::to_string(coefList.size()); + + std::string sum = ctx.getTmpVarName(); + std::string coeffSum = ctx.getTmpVarName(); + ctx.addToCodeBody(&arg, "double " + sum + " = 0;\ndouble " + coeffSum + "= 0;\n"); + + std::string iterator = "i_" + ctx.getTmpVarName(); + std::string subscriptExpr = "[" + iterator + "]"; + + std::string code = "for(int " + iterator + " = 0; " + iterator + " < " + coeffSize + "; " + iterator + "++) {\n" + + sum + " += " + funcName + subscriptExpr + " * " + coeffName + subscriptExpr + ";\n"; + code += coeffSum + " += " + coeffName + subscriptExpr + ";\n"; + code += "}\n"; + + if (noLastCoeff) { + code += sum + " += " + funcName + "[" + coeffSize + "]" + " * (1 - " + coeffSum + ");\n"; + } else if (normalize) { + code += sum + " /= " + coeffSum + ";\n"; + } + ctx.addToCodeBody(&arg, code); + + return sum; +} + +} // namespace + +void codegenImpl(Detail::RooFixedProdPdf &arg, CodegenContext &ctx) +{ + if (arg.cache()._isRearranged) { + ctx.addResult(&arg, ctx.buildCall(mathFunc("ratio"), *arg.cache()._rearrangedNum, *arg.cache()._rearrangedDen)); + } else { + ctx.addResult(&arg, ctx.buildCall(mathFunc("product"), arg.cache()._partList, arg.cache()._partList.size())); + } +} + +void codegenImpl(ParamHistFunc &arg, CodegenContext &ctx) +{ + std::string const &idx = arg.dataHist().calculateTreeIndexForCodeSquash(&arg, ctx, arg.dataVars(), true); + std::string const ¶mNames = ctx.buildArg(arg.paramList()); + + ctx.addResult(&arg, paramNames + "[" + idx + "]"); +} + +void codegenImpl(PiecewiseInterpolation &arg, CodegenContext &ctx) +{ + auto const &interpCodes = arg.interpolationCodes(); + + std::size_t n = interpCodes.size(); + + std::string resName = "total_" + ctx.getTmpVarName(); + for (std::size_t i = 0; i < n; ++i) { + if (interpCodes[i] != interpCodes[0]) { + oocoutE(&arg, InputArguments) + << "FlexibleInterpVar::evaluate ERROR: Code Squashing AD does not yet support having " + "different interpolation codes for the same class object " + << std::endl; + } + } + + // The PiecewiseInterpolation class is used in the context of HistFactory + // models, where is is always used the same way: all RooAbsReals in _lowSet, + // _histSet, and also nominal are 1D RooHistFuncs with with same structure. + // + // Therefore, we can make a big optimization: we get the bin index only once + // here in the generated code for PiecewiseInterpolation. Then, we also + // rearrange the histogram data in such a way that we can always pass the + // same arrays to the free function that implements the interpolation, just + // with a dynamic offset calculated from the bin index. + RooDataHist const &nomHist = dynamic_cast(*arg.nominalHist()).dataHist(); + int nBins = nomHist.numEntries(); + std::vector valsNominal; + std::vector valsLow; + std::vector valsHigh; + for (int i = 0; i < nBins; ++i) { + valsNominal.push_back(nomHist.weight(i)); + } + for (int i = 0; i < nBins; ++i) { + for (std::size_t iParam = 0; iParam < n; ++iParam) { + valsLow.push_back(dynamic_cast(arg.lowList()[iParam]).dataHist().weight(i)); + valsHigh.push_back(dynamic_cast(arg.highList()[iParam]).dataHist().weight(i)); + } + } + std::string idxName = ctx.getTmpVarName(); + std::string valsNominalStr = ctx.buildArg(valsNominal); + std::string valsLowStr = ctx.buildArg(valsLow); + std::string valsHighStr = ctx.buildArg(valsHigh); + std::string nStr = std::to_string(n); + std::string code; + + std::string lowName = ctx.getTmpVarName(); + std::string highName = ctx.getTmpVarName(); + std::string nominalName = ctx.getTmpVarName(); + code += "unsigned int " + idxName + " = " + + nomHist.calculateTreeIndexForCodeSquash(&arg, ctx, + dynamic_cast(*arg.nominalHist()).variables()) + + ";\n"; + code += "double const* " + lowName + " = " + valsLowStr + " + " + nStr + " * " + idxName + ";\n"; + code += "double const* " + highName + " = " + valsHighStr + " + " + nStr + " * " + idxName + ";\n"; + code += "double " + nominalName + " = *(" + valsNominalStr + " + " + idxName + ");\n"; + + std::string funcCall = ctx.buildCall(mathFunc("flexibleInterp"), interpCodes[0], arg.paramList(), n, lowName, + highName, 1.0, nominalName, 0.0); + code += "double " + resName + " = " + funcCall + ";\n"; + + if (arg.positiveDefinite()) { + code += resName + " = " + resName + " < 0 ? 0 : " + resName + ";\n"; + } + + ctx.addToCodeBody(&arg, code); + ctx.addResult(&arg, resName); +} + +//////////////////////////////////////////////////////////////////////////////// +/// This function defines a translation for each RooAbsReal based object that can be used +/// to express the class as simple C++ code. The function adds the code represented by +/// each class as an std::string (that is later concatenated with code strings from translate calls) +/// to form the C++ code that AD tools can understand. Any class that wants to support AD, has to +/// implement this function. +/// +/// \param[in] ctx An object to manage auxiliary information for code-squashing. Also takes the +/// code string that this class outputs into the squashed code through the 'addToCodeBody' function. +void codegenImpl(RooAbsArg &arg, RooFit::CodegenContext &ctx) +{ + std::stringstream errorMsg; + errorMsg << "Translate function for class \"" << arg.ClassName() << "\" has not yet been implemented."; + oocoutE(&arg, Minimization) << errorMsg.str() << std::endl; + return ctx.addResult(&arg, "1.0"); +} + +void codegenImpl(RooAddPdf &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, realSumPdfTranslateImpl(ctx, arg, arg.pdfList(), arg.coefList(), true)); +} + +void codegenImpl(RooMultiVarGaussian &arg, CodegenContext &ctx) +{ + auto const &covI = arg.covarianceMatrixInverse(); + std::span covISpan{covI.GetMatrixArray(), static_cast(covI.GetNoElements())}; + ctx.addResult(&arg, + ctx.buildCall(mathFunc("multiVarGaussian"), arg.xVec().size(), arg.xVec(), arg.muVec(), covISpan)); +} + +void codegenImpl(RooAddition &arg, CodegenContext &ctx) +{ + if (arg.list().empty()) { + ctx.addResult(&arg, "0.0"); + } + std::string result; + if (arg.list().size() > 1) + result += "("; + + std::size_t i = 0; + for (auto *component : static_range_cast(arg.list())) { + + if (!dynamic_cast(component) || arg.list().size() == 1) { + result += ctx.getResult(*component); + ++i; + if (i < arg.list().size()) + result += '+'; + continue; + } + result += ctx.buildFunction(*component, ctx.outputSizes()) + "(params, obs, xlArr)"; + ++i; + if (i < arg.list().size()) + result += '+'; + } + if (arg.list().size() > 1) + result += ')'; + ctx.addResult(&arg, result); +} + +void codegenImpl(RooBernstein &arg, CodegenContext &ctx) +{ + arg.fillBuffer(); + ctx.addResult(&arg, ctx.buildCall(mathFunc("bernstein"), arg.x(), arg.xmin(), arg.xmax(), arg.coefList(), + arg.coefList().size())); +} + +void codegenImpl(RooBifurGauss &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, + ctx.buildCall(mathFunc("bifurGauss"), arg.getX(), arg.getMean(), arg.getSigmaL(), arg.getSigmaR())); +} + +void codegenImpl(RooCBShape &arg, CodegenContext &ctx) +{ + ctx.addResult( + &arg, ctx.buildCall(mathFunc("cbShape"), arg.getM(), arg.getM0(), arg.getSigma(), arg.getAlpha(), arg.getN())); +} + +void codegenImpl(RooChebychev &arg, CodegenContext &ctx) +{ + // first bring the range of the variable _x to the normalised range [-1, 1] + // calculate sum_k c_k T_k(x) where x is given in the normalised range, + // c_0 = 1, and the higher coefficients are given in _coefList + double xmax = static_cast(arg.x()).getMax(arg.refRangeName()); + double xmin = static_cast(arg.x()).getMin(arg.refRangeName()); + + ctx.addResult(&arg, + ctx.buildCall(mathFunc("chebychev"), arg.coefList(), arg.coefList().size(), arg.x(), xmin, xmax)); +} + +void codegenImpl(RooConstVar &arg, CodegenContext &ctx) +{ + // Just return a stringy-field version of the const value. + // Formats to the maximum precision. + constexpr auto max_precision{std::numeric_limits::digits10 + 1}; + std::stringstream ss; + ss.precision(max_precision); + // Just use toString to make sure we do not output 'inf'. + // This is really ugly for large numbers... + ss << std::fixed << RooNumber::toString(arg.getVal()); + ctx.addResult(&arg, ss.str()); +} + +void codegenImpl(RooConstraintSum &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall(mathFunc("constraintSum"), arg.list(), arg.list().size())); +} + +void codegenImpl(RooGamma &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall("TMath::GammaDist", arg.getX(), arg.getGamma(), arg.getMu(), arg.getBeta())); +} + +void codegenImpl(RooFormulaVar &arg, CodegenContext &ctx) +{ + arg.getVal(); // to trigger the creation of the TFormula + std::string funcName = arg.getUniqueFuncName(); + ctx.collectFunction(funcName); + ctx.addResult(&arg, ctx.buildCall(funcName, arg.dependents())); +} + +void codegenImpl(RooEffProd &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall(mathFunc("effProd"), arg.eff(), arg.pdf())); +} + +void codegenImpl(RooEfficiency &arg, CodegenContext &ctx) +{ + RooAbsCategory const &cat = arg.cat(); + int sigCatIndex = cat.lookupIndex(arg.sigCatName()); + ctx.addResult(&arg, ctx.buildCall(mathFunc("efficiency"), arg.effFunc(), cat, sigCatIndex)); +} + +void codegenImpl(RooExponential &arg, CodegenContext &ctx) +{ + // Build a call to the stateless exponential defined later. + std::string coef; + if (arg.negateCoefficient()) { + coef += "-"; + } + coef += ctx.getResult(arg.coefficient()); + ctx.addResult(&arg, "std::exp(" + coef + " * " + ctx.getResult(arg.variable()) + ")"); +} + +void codegenImpl(RooExtendPdf &arg, CodegenContext &ctx) +{ + // Use the result of the underlying pdf. + ctx.addResult(&arg, ctx.getResult(arg.pdf())); +} + +void codegenImpl(RooGaussian &arg, CodegenContext &ctx) +{ + // Build a call to the stateless gaussian defined later. + ctx.addResult(&arg, ctx.buildCall(mathFunc("gaussian"), arg.getX(), arg.getMean(), arg.getSigma())); +} + +void codegenImpl(RooGenericPdf &arg, CodegenContext &ctx) +{ + arg.getVal(); // to trigger the creation of the TFormula + std::string funcName = arg.getUniqueFuncName(); + ctx.collectFunction(funcName); + ctx.addResult(&arg, ctx.buildCall(funcName, arg.dependents())); +} + +void codegenImpl(RooHistFunc &arg, CodegenContext &ctx) +{ + rooHistTranslateImpl(arg, ctx, arg.getInterpolationOrder(), arg.dataHist(), arg.variables(), false, + arg.getCdfBoundaries()); +} + +void codegenImpl(RooHistPdf &arg, CodegenContext &ctx) +{ + rooHistTranslateImpl(arg, ctx, arg.getInterpolationOrder(), arg.dataHist(), arg.variables(), !arg.haveUnitNorm(), + arg.getCdfBoundaries()); +} + +void codegenImpl(RooLandau &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall(mathFunc("landau"), arg.getX(), arg.getMean(), arg.getSigma())); +} + +void codegenImpl(RooLognormal &arg, CodegenContext &ctx) +{ + std::string funcName = arg.useStandardParametrization() ? "logNormalEvaluateStandard" : "logNormal"; + ctx.addResult(&arg, ctx.buildCall(mathFunc(funcName), arg.getX(), arg.getShapeK(), arg.getMedian())); +} + +void codegenImpl(Detail::RooNLLVarNew &arg, CodegenContext &ctx) +{ + if (arg.binnedL() && !arg.pdf().getAttribute("BinnedLikelihoodActiveYields")) { + std::stringstream errorMsg; + errorMsg << "codegen: binned likelihood optimization is only supported when raw pdf " + "values can be interpreted as yields." + << " This is not the case for HistFactory models written with ROOT versions before 6.26.00"; + oocoutE(&arg, InputArguments) << errorMsg.str() << std::endl; + throw std::runtime_error(errorMsg.str()); + } + + std::string weightSumName = RooFit::Detail::makeValidVarName(arg.GetName()) + "WeightSum"; + std::string resName = RooFit::Detail::makeValidVarName(arg.GetName()) + "Result"; + ctx.addResult(&arg, resName); + ctx.addToGlobalScope("double " + weightSumName + " = 0.0;\n"); + ctx.addToGlobalScope("double " + resName + " = 0.0;\n"); + + const bool needWeightSum = arg.expectedEvents() || arg.simCount() > 1; + + if (needWeightSum) { + auto scope = ctx.beginLoop(&arg); + ctx.addToCodeBody(weightSumName + " += " + ctx.getResult(arg.weightVar()) + ";\n"); + } + if (arg.simCount() > 1) { + std::string simCountStr = std::to_string(static_cast(arg.simCount())); + ctx.addToCodeBody(resName + " += " + weightSumName + " * std::log(" + simCountStr + ");\n"); + } + + // Begin loop scope for the observables and weight variable. If the weight + // is a scalar, the context will ignore it for the loop scope. The closing + // brackets of the loop is written at the end of the scopes lifetime. + { + auto scope = ctx.beginLoop(&arg); + std::string term = ctx.buildCall(mathFunc("nll"), arg.pdf(), arg.weightVar(), arg.binnedL(), 0); + ctx.addToCodeBody(&arg, resName + " += " + term + ";"); + } + if (arg.expectedEvents()) { + std::string expected = ctx.getResult(*arg.expectedEvents()); + ctx.addToCodeBody(resName + " += " + expected + " - " + weightSumName + " * std::log(" + expected + ");\n"); + } +} + +void codegenImpl(Detail::RooNormalizedPdf &arg, CodegenContext &ctx) +{ + // For now just return function/normalization integral. + ctx.addResult(&arg, ctx.getResult(arg.pdf()) + "/" + ctx.getResult(arg.normIntegral())); +} + +void codegenImpl(RooParamHistFunc &arg, CodegenContext &ctx) +{ + std::string const &idx = arg.dataHist().calculateTreeIndexForCodeSquash(&arg, ctx, arg.xList()); + std::string arrName = ctx.buildArg(arg.paramList()); + std::string result = arrName + "[" + idx + "]"; + if (arg.relParam()) { + // get weight[idx] * binv[idx]. Here we get the bin volume for the first element as we assume the distribution to + // be binned uniformly. + double binV = arg.dataHist().binVolume(0); + std::string weightArr = arg.dataHist().declWeightArrayForCodeSquash(ctx, false); + result += " * *(" + weightArr + " + " + idx + ") * " + std::to_string(binV); + } + ctx.addResult(&arg, result); +} + +void codegenImpl(RooPoisson &arg, CodegenContext &ctx) +{ + std::string xName = ctx.getResult(arg.getX()); + if (!arg.getNoRounding()) + xName = "std::floor(" + xName + ")"; + + ctx.addResult(&arg, ctx.buildCall(mathFunc("poisson"), xName, arg.getMean())); +} + +void codegenImpl(RooPolyVar &arg, CodegenContext &ctx) +{ + const unsigned sz = arg.coefList().size(); + if (!sz) { + ctx.addResult(&arg, std::to_string(arg.lowestOrder() ? 1. : 0.)); + return; + } + + ctx.addResult(&arg, ctx.buildCall(mathFunc("polynomial"), arg.coefList(), sz, arg.lowestOrder(), arg.x())); +} + +void codegenImpl(RooPolynomial &arg, CodegenContext &ctx) +{ + const unsigned sz = arg.coefList().size(); + if (!sz) { + ctx.addResult(&arg, std::to_string(arg.lowestOrder() ? 1. : 0.)); + return; + } + + ctx.addResult(&arg, ctx.buildCall(mathFunc("polynomial"), arg.coefList(), sz, arg.lowestOrder(), arg.x())); +} + +void codegenImpl(RooProduct &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall(mathFunc("product"), arg.realComponents(), arg.realComponents().size())); +} + +void codegenImpl(RooRatio &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall(mathFunc("ratio"), arg.numerator(), arg.denominator())); +} + +namespace { + +std::string codegenIntegral(RooAbsReal &arg, int code, const char *rangeName, CodegenContext &ctx) +{ + using Func = std::string (*)(RooAbsReal &, int, const char *, CodegenContext &); + + Func func; + + TClass *tclass = arg.IsA(); + + // Cache the overload resolutions + static std::unordered_map dispatchMap; + + auto found = dispatchMap.find(tclass); + + if (found != dispatchMap.end()) { + func = found->second; + } else { + // Can probably done with CppInterop in the future to avoid string manipulation. + std::stringstream cmd; + cmd << "&RooFit::CodegenIntegralImplCaller<" << tclass->GetName() << ">::call;"; + func = reinterpret_cast(gInterpreter->ProcessLine(cmd.str().c_str())); + dispatchMap[tclass] = func; + } + + return func(arg, code, rangeName, ctx); +} + +} // namespace + +void codegenImpl(RooRealIntegral &arg, CodegenContext &ctx) +{ + if (arg.numIntCatVars().empty() && arg.numIntRealVars().empty()) { + ctx.addResult(&arg, codegenIntegral(const_cast(arg.integrand()), arg.mode(), arg.intRange(), ctx)); + return; + } + + if (arg.intVars().size() != 1 || arg.numIntRealVars().size() != 1) { + std::stringstream errorMsg; + errorMsg << "Only analytical integrals and 1D numeric integrals are supported for AD for class" + << arg.integrand().GetName(); + oocoutE(&arg, Minimization) << errorMsg.str() << std::endl; + throw std::runtime_error(errorMsg.str().c_str()); + } + + auto &intVar = static_cast(*arg.numIntRealVars()[0]); + + std::string obsName = ctx.getTmpVarName(); + std::string oldIntVarResult = ctx.getResult(intVar); + ctx.addResult(&intVar, "obs[0]"); + + std::string funcName = ctx.buildFunction(arg.integrand(), {}); + + std::stringstream ss; + + ss << "double " << obsName << "[1];\n"; + + std::string resName = RooFit::Detail::makeValidVarName(arg.GetName()) + "Result"; + ctx.addResult(&arg, resName); + ctx.addToGlobalScope("double " + resName + " = 0.0;\n"); + + // TODO: once Clad has support for higher-order functions (follow also the + // Clad issue #637), we could refactor this code into an actual function + // instead of hardcoding it here as a string. + ss << "{\n" + << " const int n = 1000; // number of sampling points\n" + << " double d = " << intVar.getMax(arg.intRange()) << " - " << intVar.getMin(arg.intRange()) << ";\n" + << " double eps = d / n;\n" + << " for (int i = 0; i < n; ++i) {\n" + << " " << obsName << "[0] = " << intVar.getMin(arg.intRange()) << " + eps * i;\n" + << " double tmpA = " << funcName << "(params, " << obsName << ", xlArr);\n" + << " " << obsName << "[0] = " << intVar.getMin(arg.intRange()) << " + eps * (i + 1);\n" + << " double tmpB = " << funcName << "(params, " << obsName << ", xlArr);\n" + << " " << resName << " += (tmpA + tmpB) * 0.5 * eps;\n" + << " }\n" + << "}\n"; + + ctx.addToGlobalScope(ss.str()); + + ctx.addResult(&intVar, oldIntVarResult); +} + +void codegenImpl(RooRealSumFunc &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, realSumPdfTranslateImpl(ctx, arg, arg.funcList(), arg.coefList(), false)); +} + +void codegenImpl(RooRealSumPdf &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, realSumPdfTranslateImpl(ctx, arg, arg.funcList(), arg.coefList(), false)); +} + +void codegenImpl(RooRealVar &arg, CodegenContext &ctx) +{ + if (!arg.isConstant()) { + ctx.addResult(&arg, arg.GetName()); + } + // Just return a formatted version of the const value. + // Formats to the maximum precision. + constexpr auto max_precision{std::numeric_limits::digits10 + 1}; + std::stringstream ss; + ss.precision(max_precision); + // Just use toString to make sure we do not output 'inf'. + // This is really ugly for large numbers... + ss << std::fixed << RooNumber::toString(arg.getVal()); + ctx.addResult(&arg, ss.str()); +} + +void codegenImpl(RooRecursiveFraction &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, ctx.buildCall(mathFunc("recursiveFraction"), arg.variables(), arg.variables().size())); +} + +void codegenImpl(RooStats::HistFactory::FlexibleInterpVar &arg, CodegenContext &ctx) +{ + auto const &interpCodes = arg.interpolationCodes(); + + unsigned int n = interpCodes.size(); + + int interpCode = interpCodes[0]; + // To get consistent codes with the PiecewiseInterpolation + if (interpCode == 4) { + interpCode = 5; + } + + for (unsigned int i = 1; i < n; i++) { + if (interpCodes[i] != interpCodes[0]) { + oocoutE(&arg, InputArguments) + << "FlexibleInterpVar::evaluate ERROR: Code Squashing AD does not yet support having " + "different interpolation codes for the same class object " + << std::endl; + } + } + + std::string const &resName = ctx.buildCall(mathFunc("flexibleInterp"), interpCode, arg.variables(), n, arg.low(), + arg.high(), arg.globalBoundary(), arg.nominal(), 1.0); + ctx.addResult(&arg, resName); +} + +void codegenImpl(RooUniform &arg, CodegenContext &ctx) +{ + ctx.addResult(&arg, "1.0"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// This function defines the analytical integral translation for the class. +/// +/// \param[in] code The code that decides the integrands. +/// \param[in] rangeName Name of the normalization range. +/// \param[in] ctx An object to manage auxiliary information for code-squashing. +/// +/// \returns The representative code string of the integral for the given object. +std::string codegenIntegralImpl(RooAbsReal &arg, int, const char *, CodegenContext &) +{ + std::stringstream errorMsg; + errorMsg << "An analytical integral function for class \"" << arg.ClassName() << "\" has not yet been implemented."; + oocoutE(&arg, Minimization) << errorMsg.str() << std::endl; + throw std::runtime_error(errorMsg.str().c_str()); +} + +std::string codegenIntegralImpl(RooBernstein &arg, int, const char *rangeName, CodegenContext &ctx) +{ + arg.fillBuffer(); // to get the right xmin() and xmax() + auto &x = dynamic_cast(arg.x()); + return ctx.buildCall(mathFunc("bernsteinIntegral"), x.getMin(rangeName), x.getMax(rangeName), arg.xmin(), arg.xmax(), + arg.coefList(), arg.coefList().size()); +} + +std::string codegenIntegralImpl(RooBifurGauss &arg, int code, const char *rangeName, CodegenContext &ctx) +{ + auto &constant = code == 1 ? arg.getMean() : arg.getX(); + auto &integrand = dynamic_cast(code == 1 ? arg.getX() : arg.getMean()); + + return ctx.buildCall(mathFunc("bifurGaussIntegral"), integrand.getMin(rangeName), integrand.getMax(rangeName), + constant, arg.getSigmaL(), arg.getSigmaR()); +} + +std::string codegenIntegralImpl(RooCBShape &arg, int /*code*/, const char *rangeName, CodegenContext &ctx) +{ + auto &m = dynamic_cast(arg.getM()); + return ctx.buildCall(mathFunc("cbShapeIntegral"), m.getMin(rangeName), m.getMax(rangeName), arg.getM0(), + arg.getSigma(), arg.getAlpha(), arg.getN()); +} + +std::string codegenIntegralImpl(RooChebychev &arg, int, const char *rangeName, CodegenContext &ctx) +{ + auto &x = dynamic_cast(arg.x()); + double xmax = x.getMax(arg.refRangeName()); + double xmin = x.getMin(arg.refRangeName()); + unsigned int sz = arg.coefList().size(); + + return ctx.buildCall(mathFunc("chebychevIntegral"), arg.coefList(), sz, xmin, xmax, x.getMin(rangeName), + x.getMax(rangeName)); +} + +std::string codegenIntegralImpl(RooEfficiency &, int, const char *, CodegenContext &) +{ + return "1.0"; +} + +std::string codegenIntegralImpl(RooExponential &arg, int code, const char *rangeName, CodegenContext &ctx) +{ + bool isOverX = code == 1; + + std::string constant; + if (arg.negateCoefficient() && isOverX) { + constant += "-"; + } + constant += ctx.getResult(isOverX ? arg.coefficient() : arg.variable()); + + auto &integrand = dynamic_cast(isOverX ? arg.variable() : arg.coefficient()); + + double min = integrand.getMin(rangeName); + double max = integrand.getMax(rangeName); + + if (!isOverX && arg.negateCoefficient()) { + std::swap(min, max); + min = -min; + max = -max; + } + + return ctx.buildCall(mathFunc("exponentialIntegral"), min, max, constant); +} + +std::string codegenIntegralImpl(RooGamma &arg, int, const char *rangeName, CodegenContext &ctx) +{ + auto &x = dynamic_cast(arg.getX()); + const std::string a = + ctx.buildCall("ROOT::Math::gamma_cdf", x.getMax(rangeName), arg.getGamma(), arg.getBeta(), arg.getMu()); + const std::string b = + ctx.buildCall("ROOT::Math::gamma_cdf", x.getMin(rangeName), arg.getGamma(), arg.getBeta(), arg.getMu()); + return a + " - " + b; +} + +std::string codegenIntegralImpl(RooGaussian &arg, int code, const char *rangeName, CodegenContext &ctx) +{ + auto &constant = code == 1 ? arg.getMean() : arg.getX(); + auto &integrand = dynamic_cast(code == 1 ? arg.getX() : arg.getMean()); + + return ctx.buildCall(mathFunc("gaussianIntegral"), integrand.getMin(rangeName), integrand.getMax(rangeName), + constant, arg.getSigma()); +} + +namespace { + +std::string rooHistIntegralTranslateImpl(int code, RooAbsArg const &arg, RooDataHist const &dataHist, + const RooArgSet &obs, bool histFuncMode) +{ + if (((2 << obs.size()) - 1) != code) { + oocoutE(&arg, InputArguments) << "RooHistPdf::integral(" << arg.GetName() + << ") ERROR: AD currently only supports integrating over all histogram observables." + << std::endl; + return ""; + } + return std::to_string(dataHist.sum(histFuncMode)); +} + +} // namespace + +std::string codegenIntegralImpl(RooHistFunc &arg, int code, const char *, CodegenContext &) +{ + return rooHistIntegralTranslateImpl(code, arg, arg.dataHist(), arg.variables(), true); +} + +std::string codegenIntegralImpl(RooHistPdf &arg, int code, const char *, CodegenContext &) +{ + return rooHistIntegralTranslateImpl(code, arg, arg.dataHist(), arg.variables(), false); +} + +std::string codegenIntegralImpl(RooLandau &arg, int, const char *rangeName, CodegenContext &ctx) +{ + // Don't do anything with "code". It can only be "1" anyway (see + // implementation of getAnalyticalIntegral). + auto &x = dynamic_cast(arg.getX()); + const std::string a = ctx.buildCall("ROOT::Math::landau_cdf", x.getMax(rangeName), arg.getSigma(), arg.getMean()); + const std::string b = ctx.buildCall("ROOT::Math::landau_cdf", x.getMin(rangeName), arg.getSigma(), arg.getMean()); + return ctx.getResult(arg.getSigma()) + " * " + "(" + a + " - " + b + ")"; +} + +std::string codegenIntegralImpl(RooLognormal &arg, int, const char *rangeName, CodegenContext &ctx) +{ + std::string funcName = arg.useStandardParametrization() ? "logNormalIntegralStandard" : "logNormalIntegral"; + auto &x = dynamic_cast(arg.getX()); + return ctx.buildCall(mathFunc(funcName), x.getMin(rangeName), x.getMax(rangeName), arg.getMedian(), arg.getShapeK()); +} + +std::string codegenIntegralImpl(RooMultiVarGaussian &arg, int code, const char *rangeName, CodegenContext &) +{ + if (code != -1) { + std::stringstream errorMsg; + errorMsg << "Partial integrals over RooMultiVarGaussian are not supported."; + oocoutE(&arg, Minimization) << errorMsg.str() << std::endl; + throw std::runtime_error(errorMsg.str().c_str()); + } + + return std::to_string(arg.analyticalIntegral(code, rangeName)); +} + +std::string codegenIntegralImpl(RooPoisson &arg, int code, const char *rangeName, CodegenContext &ctx) +{ + assert(code == 1 || code == 2); + std::string xName = ctx.getResult(arg.getX()); + if (!arg.getNoRounding()) + xName = "std::floor(" + xName + ")"; + + auto &integrand = dynamic_cast(code == 1 ? arg.getX() : arg.getMean()); + // Since the integral function is the same for both codes, we need to make sure the indexed observables do not appear + // in the function if they are not required. + xName = code == 1 ? "0" : xName; + return ctx.buildCall(mathFunc("poissonIntegral"), code, arg.getMean(), xName, integrand.getMin(rangeName), + integrand.getMax(rangeName), arg.getProtectNegativeMean()); +} + +std::string codegenIntegralImpl(RooPolyVar &arg, int, const char *rangeName, CodegenContext &ctx) +{ + auto &x = dynamic_cast(arg.x()); + const double xmin = x.getMin(rangeName); + const double xmax = x.getMax(rangeName); + const unsigned sz = arg.coefList().size(); + if (!sz) + return std::to_string(arg.lowestOrder() ? xmax - xmin : 0.0); + + return ctx.buildCall(mathFunc("polynomialIntegral"), arg.coefList(), sz, arg.lowestOrder(), xmin, xmax); +} + +std::string codegenIntegralImpl(RooPolynomial &arg, int, const char *rangeName, CodegenContext &ctx) +{ + auto &x = dynamic_cast(arg.x()); + const double xmin = x.getMin(rangeName); + const double xmax = x.getMax(rangeName); + const unsigned sz = arg.coefList().size(); + if (!sz) + return std::to_string(arg.lowestOrder() ? xmax - xmin : 0.0); + + return ctx.buildCall(mathFunc("polynomialIntegral"), arg.coefList(), sz, arg.lowestOrder(), xmin, xmax); +} + +std::string codegenIntegralImpl(RooUniform &arg, int code, const char *rangeName, CodegenContext &) +{ + // The integral of a uniform distribution is static, so we can just hardcode + // the result in a string. + return std::to_string(arg.analyticalIntegral(code, rangeName)); +} + +} // namespace RooFit diff --git a/roofit/doc/developers/roofit_ad.md b/roofit/doc/developers/roofit_ad.md index 991ae8824e7ce..7b7c6ae957fb5 100644 --- a/roofit/doc/developers/roofit_ad.md +++ b/roofit/doc/developers/roofit_ad.md @@ -94,8 +94,8 @@ computation, but without any overhead that is hard to digest by the AD tool. On a high level, this *code generation* is implemented as follows: 1. The computation graph is visited recursively by a - RooFit::Detail::CodeSquashContext object, via the virtual - RooAbsArg::translate() function that implements the translation of a + RooFit::CodegenContext object, via the + RooFit::codegenImpl(RooAbsArg &, RooFit::CodegenContext &) function that implements the translation of a given RooFit class to minimal C++ code. This is an example of the visitor pattern. @@ -124,10 +124,10 @@ separate header file. Since Clad prefers the code for models to be within a single translation unit, in many classes, this has been implemented by moving the computational aspects of the RooFit class -to free functions in a single header file named [MathFuncs] (and/or -[MathFuncs], where relevant). This approach enables easier debugging - (e.g., you can standalone-compile the generated code with just a few header -files copied outside ROOT) +to free functions in a single header file named [MathFuncs]. +This approach enables easier debugging +(e.g., you can standalone-compile the generated code with just a few header +files copied outside ROOT). *Refactoring* It is important to refactor the code such that: @@ -153,14 +153,14 @@ installation. Please note the following recommendations: header file (e.g., as part of the class definition), and - if/when your class is upstreamed to RooFit, expect to move into the -`RooFit::detail` namespace and their implementations into `MathFuncs.h`. +`RooFit::Detail` namespace and their implementations into `MathFuncs.h`. \htmlonly \endhtmlonly -*Overriding the Translate Function*: The `RooAbsArg::translate()` function -needs to be overridden to specify how the class is translating to C++ code +*Overloading the code generation function*: The `RooFit::codegenImpl()` function +needs to be overloaded to specify how the class is translating to C++ code that is using the aforementioned free function. **Sample Steps**: To add Code Generation support to an existing RooFit class, @@ -176,25 +176,27 @@ This implementation must be compatible with the syntax supported by Clad. can reduce code duplication and potential for bugs. This may require some effort if an extensive caching infrastructure is used in your model. -**3. Add translate():** RooFit classes are extended using a (typically) simple - `translate()` function that extracts the mathematically differentiable +**3. Add RooFit::codegenImpl():** Define a (typically) simple + `RooFit::codegenImpl()` function that extracts the mathematically differentiable properties out of the RooFit classes that make up the statistical model. +This function needs to be declared in a public header file, such that it is known to the interpreter. -The `translate()` function helps implement the Code Squashing logic that is +The `RooFit::codegenImpl()` function helps implement the code generation logic that is used to optimize numerical evaluations. It accomplishes this by using a small subset of helper functions that are available in the -`RooFit::Detail::CodeSquashContext` and `RooFuncWrapper` classes -(see Appendix B). It converts a RooFit expression into a form that can be +`RooFit::CodegenContext` class (see Appendix B). +It converts a RooFit expression into a form that can be efficiently evaluated by Clad. -The `translate()` function returns an `std::string` representing the +The `RooFit::codegenImpl()` function places a `std::string` inside the `RooFit::CodegenContext`. +This string is representing the underlying mathematical notation of the class as code, that can later be concatenated into a single string representing the entire model. This string of code is then just-in-time compiled by Cling (a C++ interpreter for Root). -**4. analyticalIntegral() Use Case:** If your class includes (or should +**4. RooFit::codegenIntegralImpl() Use Case:** If your class includes (or should include) the `analyticalIntegral()` function, then a simple -`buildCallToAnalyticIntegral()` function needs to be created to help call the +`RooFit::codegenIntegralImpl()` function needs to be defined to help call the `analyticalIntegral()` function. @@ -277,20 +279,21 @@ together. > Directory path: [roofit/roofitcore/inc/RooFit/Detail/MathFuncs.h](https://github.com/root-project/root/blob/master/roofit/roofitcore/inc/RooFit/Detail/MathFuncs.h) -### Step 2. Override RooAbsArg::translate() +### Step 2. Overload RooFit::codegenImpl() -**translate() Example 1:** Continuing our RooPoisson example: +**RooFit::codegenImpl() Example 1:** Continuing our RooPoisson example: -To translate the `RooPoisson` class, create a translate function and in it -include a call to the updated function. +To translate the `RooPoisson` class, create a code generation function and in it +include a call to the free function. ``` {.cpp} -void RooPoisson::translate(RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const +void RooFit::codegenImpl(RooPoisson &arg, RooFit::CodegenContext &ctx) { - std::string xName = ctx.getResult(x); - if (!_noRounding) + std::string xName = ctx.getResult(arg.getX()); + if (!arg.getNoRounding()) xName = "std::floor(" + xName + ")"; - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::poissonEvaluate", xName, mean)); + + ctx.addResult(&arg, ctx.buildCall("RooFit::Detail::MathFuncs::poisson", xName, arg.getMean())); } ``` @@ -299,58 +302,59 @@ member of RooPoisson) is retrieved and stored in the `xName` variable. Next, there's an `if` condition that does an operation on `x` (may or may not round it to the nearest integer, depending on the condition). -The important part is where the `RooPoisson::addResult()` function helps add +The important part is where the `RooFit::CodegenContext::addResult()` function adds the result of evaluating the Poisson function to the context (`ctx`). It uses -the `RooPoisson::buildCall()` method to construct a function call to the fully +the `RooFit::CodegenContext::buildCall()` method to construct a function call to the fully qualified name of `MathFuncs::poissonEvaluate` (which now resides in the -`MathFuncs` file), with arguments `xName` and `mean`. +`MathFuncs` file), with arguments `xName` and the mean of the Poisson. -Essentially, the `RooPoisson::translate()` function constructs a function call +Essentially, the `RooFit::codegenImpl()` function constructs a function call to evaluate the Poisson function using 'x' and 'mean' variables, and adds the result to the context. Helper Functions: -- `getResult()` helps lookup the result of a child node (the string that the -child node previously saved in a variable using the `addResult()` function). +- `RooFit::CodegenContext::getResult()` helps lookup the result of a child node (the string that the +child node previously saved in a variable using the `RooFit::CodegenContext::addResult()` function). -- `addResult()` It may include a function call, an expression, or something +- `RooFit::CodegenContext::addResult()` It may include a function call, an expression, or something more complicated. For a specific class, it will add whatever is represented on the right-hand side to the result of that class, which can then be propagated in the rest of the compute graph. -\note For each `translate()` function, it is important to call `addResult()` since this is what enables the squashing to happen. +\note For each `RooFit::codegenImpl()` function, it is important to call `RooFit::CodegenContext::addResult()` since this is what enables the squashing to happen. **translate() Example 2:** Following is a code snippet from `RooGaussian.cxx` *after* it has AD support. ``` {.cpp} -void RooGaussian::translate(RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const +void RooFit::codegenImpl(RooGaussian &arg, RooFit::CodegenContext &ctx) { - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::gaussianEvaluate", x, mean, sigma)); + // Build a call to the stateless gaussian defined later. + ctx.addResult(&arg, ctx.buildCall("RooFit::Detail::MathFuncs::gaussian", arg.getX(), arg.getMean(), arg.getSigma())); } ``` -Here we can see that the `RooGaussian::translate()` function constructs a -function call using the `buildCall()` method. It specifies the fully qualified -name of the `gaussianEvaluate` function (which is now part of the +Here we can see that the `RooFit::codegenImpl(RooGaussian &, RooFit::CodegenContext &)` function constructs a +function call using the `RooFit::CodegenContext::buildCall()` method. It specifies the fully qualified +name of the `gaussian` function (which is now part of the `MathFuncs` file), and includes the x, mean, and sigma variables as arguments to this function call. Helper Function: -- `buildCall()` helps build a function call. Requires the fully qualified name - (`RooFit::Detail::MathFuncs::gaussianEvaluate`) of the function. When -this external `buildCall()` function is called, internally, the `getResult()` +- `RooFit::CodegenContext::buildCall()` helps build a function call. Requires the fully qualified name + (`RooFit::Detail::MathFuncs::gaussian`) of the function. When +this external `RooFit::CodegenContext::buildCall()` function is called, internally, the `RooFit::CodegenContext::getResult()` function is called on the input RooFit objects (e.g., x, mean, sigma). That's the only way to propagate these upwards into the compute graph. -**translate() Example 3:** A more complicated example of a `translate()` +**translate() Example 3:** A more complicated example of a `RooFit::codegenImpl()` function can be seen here: ``` {.cpp} -void RooNLLVarNew::translate(RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const +void RooFit::codegenImpl(RooFit::Detail::RooNLLVarNew &arg, RooFit::CodegenContext &ctx) { std::string weightSumName = ctx.makeValidVarName(GetName()) + "WeightSum"; std::string resName = ctx.makeValidVarName(GetName()) + "Result"; @@ -376,14 +380,14 @@ void RooNLLVarNew::translate(RooFit::Detail::RooFit::Detail::CodeSquashContext & > Source: - [RooNLLVarNew](https://github.com/root-project/root/blob/master/roofit/roofitcore/src/RooNLLVarNew.cxx) -The complexity of the `RooNLLVarNew::translate()` function in this example can +The complexity of the `RooFit::codegenImpl()` function in this example can be attributed to the more complex scenarios/operations specific to the computation of negative log-likelihood (NLL) values for probability density functions (PDFs) in RooFit, especially for simultaneous fits (multiple simultaneous PDFs being considered) and binned likelihoods (adding further complexity). -In this example, the `RooNLLVarNew::translate()` function generates code to +In this example, the `RooFit::codegenImpl()` function generates code to compute the Negative Log likelihood (NLL). We can see that the intermediate result variable `resName` is added to the context so that it can be accessed and used in the generated code. This variable is made available globally @@ -516,7 +520,7 @@ the `res` variable in the following example) of this class, which can then be propagated in the rest of the compute graph. ``` {.cpp} - void translate(RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const override { + void translate(RooFit::CodegenContext &ctx) const override { std::string res = ctx.buildCall("MathFuncs::doFoo", a, b); ctx.addResult(this, res); } @@ -561,7 +565,7 @@ return the output using the `buildCall()` function. ``` {.cpp} std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const override { + buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::CodegenContext &ctx) const override { return ctx.buildCall("EvaluateFunc::integralFoo", a, b); } ``` @@ -594,13 +598,13 @@ class RooFoo : public RooAbsReal { } //// ************************** functions for AD Support *********************** - void translate(RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const override { + void translate(RooFit::CodegenContext &ctx) const override { std::string res = ctx.buildCall("EvaluateFunc::doFoo", a, b); ctx.addResult(this, res); } std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::RooFit::Detail::CodeSquashContext &ctx) const override { + buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::CodegenContext &ctx) const override { return ctx.buildCall("EvaluateFunc::integralFoo", a, b); } //// ************************** functions for AD Support *********************** @@ -717,19 +721,19 @@ compile times are reasonable, but with an increase in the level of complexity, Following classes provide several Helper Functions to translate existing logic into AD-supported logic. -a - RooFit::Detail::CodeSquashContext +a - RooFit::CodegenContext b - RooFuncWrapper -### a. RooFit::Detail::CodeSquashContext +### a. RooFit::CodegenContext -> [roofit/roofitcore/inc/RooFit/Detail/CodeSquashContext.h](https://github.com/root-project/root/blob/master/roofit/roofitcore/inc/RooFit/Detail/CodeSquashContext.h) +> [roofit/roofitcore/inc/RooFit/CodegenContext.h](https://github.com/root-project/root/blob/master/roofit/roofitcore/inc/RooFit/CodegenContext.h) It handles how to create a C++ function out of the compute graph (which is created with different RooFit classes). This C++ function will be independent of these RooFit classes. -RooFit::Detail::CodeSquashContext helps traverse the compute graph received from RooFit and +RooFit::CodegenContext helps traverse the compute graph received from RooFit and then it translates that into a single piece of code (a C++ function), that can then be differentiated using Clad. It also helps evaluate the model. @@ -747,7 +751,7 @@ the squashing to happen. #### Helper Functions -- **RooFit::Detail::CodeSquashContext**: this class maintains the context for squashing of +- **RooFit::CodegenContext**: this class maintains the context for squashing of RooFit models into code. It keeps track of the results of various expressions to avoid redundant calculations. @@ -787,7 +791,7 @@ code body of the squashed function. These functions will appear again in this document with more contextual examples. For detailed in-line documentation (code comments), please see: -> [roofit/roofitcore/src/RooFit/Detail/CodeSquashContext.cxx](https://github.com/root-project/root/blob/master/roofit/roofitcore/src/RooFit/Detail/CodeSquashContext.cxx) +> [roofit/roofitcore/src/RooFit/CodegenContext.cxx](https://github.com/root-project/root/blob/master/roofit/roofitcore/src/RooFit/Detail/CodegenContext.cxx) ### b. RooFuncWrapper @@ -828,7 +832,7 @@ examples. For detailed in-line documentation (code comments), please see: ## Appendix C - Helper functions discussed in this document -- **RooFit::Detail::CodeSquashContext::addResult()**: For a specific class, it +- **RooFit::CodegenContext::addResult()**: For a specific class, it will add whatever is represented on the right-hand side (a function call, an expression, etc.) to the result of this class, which can then be propagated in the rest of the compute graph. A to call `addResult()`must be included in @@ -838,14 +842,14 @@ expression, etc.) to the result of this class, which can then be propagated in new name to assign/overwrite). - Output: Adds (or overwrites) the string representing the result of a node. -- **RooFit::Detail::CodeSquashContext::getResult()**: It helps lookup the +- **RooFit::CodegenContext::getResult()**: It helps lookup the result of a child node (the string that the child node previously saved in a variable using the `addResult()` function). - Input: `key` (the node to get the result string for). - Output: String representing the result of this node. -- **RooFit::Detail::CodeSquashContext::addToCodeBody()**: Takes whatever string +- **RooFit::CodegenContext::addToCodeBody()**: Takes whatever string is computed in its arguments and adds it to the overall function string (which will later be just-in-time compiled). @@ -853,7 +857,7 @@ variable using the `addResult()` function). (string to add to the squashed code). - Output: Adds the input string to the squashed code body. -- **RooFit::Detail::CodeSquashContext::addToGlobalScope()**: Helps declare and +- **RooFit::CodegenContext::addToGlobalScope()**: Helps declare and initialize the results variable, so that it can be available globally (throughout the function body). @@ -861,14 +865,14 @@ initialize the results variable, so that it can be available globally - Output: Adds the given string to the string block that will be emitted at the top of the squashed function. -- **RooFit::Detail::CodeSquashContext::assembleCode()**: combines the generated +- **RooFit::CodegenContext::assembleCode()**: combines the generated code statements into the final code body of the squashed function. - Input: `returnExpr` (he string representation of what the squashed function should return, usually the head node). - Output: The final body of the function. -- **RooFit::Detail::CodeSquashContext::beginLoop()**: The code squashing task +- **RooFit::CodegenContext::beginLoop()**: The code squashing task will automatically build a For loop around the indented statements that follow this function. @@ -876,13 +880,13 @@ will automatically build a For loop around the indented statements that follow dependent variables). - Output: A scope for iterating over vector observables. -- **RooFit::Detail::CodeSquashContext::buildArg()**: helps convert RooFit +- **RooFit::CodegenContext::buildArg()**: helps convert RooFit objects into arrays or other C++ representations for efficient computation. - Input: `in` (the list to convert to array). - Output: Name of the array that stores the input list in the squashed code. -- **RooFit::Detail::CodeSquashContext::buildCall()**: Creates a string +- **RooFit::CodegenContext::buildCall()**: Creates a string representation of the function to be called and its arguments. - Input: A function with name `funcname`, passing some arguments. diff --git a/roofit/histfactory/inc/RooStats/HistFactory/FlexibleInterpVar.h b/roofit/histfactory/inc/RooStats/HistFactory/FlexibleInterpVar.h index 46285e2f60b68..d1a7f2243e257 100644 --- a/roofit/histfactory/inc/RooStats/HistFactory/FlexibleInterpVar.h +++ b/roofit/histfactory/inc/RooStats/HistFactory/FlexibleInterpVar.h @@ -55,11 +55,10 @@ namespace HistFactory{ double nominal() const { return _nominal; } const std::vector& low() const { return _low; } const std::vector& high() const { return _high; } + double globalBoundary() const { return _interpBoundary;} void doEval(RooFit::EvalContext &) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooListProxy _paramList ; diff --git a/roofit/histfactory/inc/RooStats/HistFactory/ParamHistFunc.h b/roofit/histfactory/inc/RooStats/HistFactory/ParamHistFunc.h index f94b506e3350a..c6006d65365b5 100644 --- a/roofit/histfactory/inc/RooStats/HistFactory/ParamHistFunc.h +++ b/roofit/histfactory/inc/RooStats/HistFactory/ParamHistFunc.h @@ -46,6 +46,8 @@ class ParamHistFunc : public RooAbsReal { const RooArgSet* get(Int_t masterIdx) const { return _dataSet.get( masterIdx ) ; } const RooArgSet* get(const RooArgSet& coord) const { return _dataSet.get( coord ) ; } + RooDataHist const& dataHist() const { return _dataSet; } + double binVolume() const { return _dataSet.binVolume(); } bool forceAnalyticalInt(const RooAbsArg&) const override { return true ; } @@ -61,6 +63,7 @@ class ParamHistFunc : public RooAbsReal { std::list* plotSamplingHint(RooAbsRealLValue& obs, double xlo, double xhi) const override; bool isBinnedDistribution(const RooArgSet& obs) const override { return _dataVars.overlaps(obs); } + const RooArgList& dataVars() const { return _dataVars; } protected: @@ -103,8 +106,6 @@ class ParamHistFunc : public RooAbsReal { double evaluate() const override; void doEval(RooFit::EvalContext &) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - private: static NumBins getNumBinsPerDim(RooArgSet const& vars); diff --git a/roofit/histfactory/inc/RooStats/HistFactory/PiecewiseInterpolation.h b/roofit/histfactory/inc/RooStats/HistFactory/PiecewiseInterpolation.h index 5676b7f4d1c1d..fcf30fcbd3f03 100644 --- a/roofit/histfactory/inc/RooStats/HistFactory/PiecewiseInterpolation.h +++ b/roofit/histfactory/inc/RooStats/HistFactory/PiecewiseInterpolation.h @@ -69,8 +69,6 @@ class PiecewiseInterpolation : public RooAbsReal { std::list* plotSamplingHint(RooAbsRealLValue& obs, double xlo, double xhi) const override ; bool isBinnedDistribution(const RooArgSet& obs) const override ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: class CacheElem : public RooAbsCacheElement { diff --git a/roofit/histfactory/src/FlexibleInterpVar.cxx b/roofit/histfactory/src/FlexibleInterpVar.cxx index d65dfd2e64d01..ecd0eb018d904 100644 --- a/roofit/histfactory/src/FlexibleInterpVar.cxx +++ b/roofit/histfactory/src/FlexibleInterpVar.cxx @@ -231,29 +231,6 @@ double FlexibleInterpVar::evaluate() const return total; } -void FlexibleInterpVar::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - unsigned int n = _interpCode.size(); - - int interpCode = _interpCode[0]; - // To get consistent codes with the PiecewiseInterpolation - if (interpCode == 4) { - interpCode = 5; - } - - for (unsigned int i = 1; i < n; i++) { - if (_interpCode[i] != _interpCode[0]) { - coutE(InputArguments) << "FlexibleInterpVar::evaluate ERROR: Code Squashing AD does not yet support having " - "different interpolation codes for the same class object " - << std::endl; - } - } - - std::string const &resName = ctx.buildCall("RooFit::Detail::MathFuncs::flexibleInterp", interpCode, - _paramList, n, _low, _high, _interpBoundary, _nominal, 1.0); - ctx.addResult(this, resName); -} - void FlexibleInterpVar::doEval(RooFit::EvalContext &ctx) const { double total(_nominal); diff --git a/roofit/histfactory/src/ParamHistFunc.cxx b/roofit/histfactory/src/ParamHistFunc.cxx index 94321ca0f28f9..e05af08615322 100644 --- a/roofit/histfactory/src/ParamHistFunc.cxx +++ b/roofit/histfactory/src/ParamHistFunc.cxx @@ -560,21 +560,6 @@ double ParamHistFunc::evaluate() const return getParameter().getVal(); } -void ParamHistFunc::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - auto const &n = _numBinsPerDim; - - // check if _numBins needs to be filled - if (n.x == 0) { - _numBinsPerDim = getNumBinsPerDim(_dataVars); - } - - std::string const &idx = _dataSet.calculateTreeIndexForCodeSquash(this, ctx, _dataVars, true); - std::string const ¶mNames = ctx.buildArg(_paramSet); - - ctx.addResult(this, paramNames + "[" + idx + "]"); -} - //////////////////////////////////////////////////////////////////////////////// /// Find all bins corresponding to the values of the observables in `evalData`, and evaluate /// the associated parameters. diff --git a/roofit/histfactory/src/PiecewiseInterpolation.cxx b/roofit/histfactory/src/PiecewiseInterpolation.cxx index 3dca4123f8e4f..83f550716d951 100644 --- a/roofit/histfactory/src/PiecewiseInterpolation.cxx +++ b/roofit/histfactory/src/PiecewiseInterpolation.cxx @@ -178,68 +178,6 @@ double PiecewiseInterpolation::evaluate() const } -void PiecewiseInterpolation::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - std::size_t n = _interpCode.size(); - - std::string resName = "total_" + ctx.getTmpVarName(); - for (std::size_t i = 0; i < n; ++i) { - if (_interpCode[i] != _interpCode[0]) { - coutE(InputArguments) << "FlexibleInterpVar::evaluate ERROR: Code Squashing AD does not yet support having " - "different interpolation codes for the same class object " - << endl; - } - } - - // The PiecewiseInterpolation class is used in the context of HistFactory - // models, where is is always used the same way: all RooAbsReals in _lowSet, - // _histSet, and also nominal are 1D RooHistFuncs with with same structure. - // - // Therefore, we can make a big optimization: we get the bin index only once - // here in the generated code for PiecewiseInterpolation. Then, we also - // rearrange the histogram data in such a way that we can always pass the - // same arrays to the free function that implements the interpolation, just - // with a dynamic offset calculated from the bin index. - RooDataHist const &nomHist = dynamic_cast(*_nominal).dataHist(); - int nBins = nomHist.numEntries(); - std::vector valsNominal; - std::vector valsLow; - std::vector valsHigh; - for (int i = 0; i < nBins; ++i) { - valsNominal.push_back(nomHist.weight(i)); - } - for (int i = 0; i < nBins; ++i) { - for (std::size_t iParam = 0; iParam < n; ++iParam) { - valsLow.push_back(dynamic_cast(_lowSet[iParam]).dataHist().weight(i)); - valsHigh.push_back(dynamic_cast(_highSet[iParam]).dataHist().weight(i)); - } - } - std::string idxName = ctx.getTmpVarName(); - std::string valsNominalStr = ctx.buildArg(valsNominal); - std::string valsLowStr = ctx.buildArg(valsLow); - std::string valsHighStr = ctx.buildArg(valsHigh); - std::string nStr = std::to_string(n); - std::string code; - - std::string lowName = ctx.getTmpVarName(); - std::string highName = ctx.getTmpVarName(); - std::string nominalName = ctx.getTmpVarName(); - code += "unsigned int " + idxName + " = " + nomHist.calculateTreeIndexForCodeSquash(this, ctx, dynamic_cast(*_nominal).variables()) + ";\n"; - code += "double const* " + lowName + " = " + valsLowStr + " + " + nStr + " * " + idxName + ";\n"; - code += "double const* " + highName + " = " + valsHighStr + " + " + nStr + " * " + idxName + ";\n"; - code += "double " + nominalName + " = *(" + valsNominalStr + " + " + idxName + ");\n"; - - std::string funcCall = ctx.buildCall("RooFit::Detail::MathFuncs::flexibleInterp", _interpCode[0], _paramSet, n, - lowName, highName, 1.0, nominalName, 0.0); - code += "double " + resName + " = " + funcCall + ";\n"; - - if (_positiveDefinite) - code += resName + " = " + resName + " < 0 ? 0 : " + resName + ";\n"; - - ctx.addToCodeBody(this, code); - ctx.addResult(this, resName); -} - namespace { inline double broadcast(std::span const &s, std::size_t i) diff --git a/roofit/roofit/inc/RooBernstein.h b/roofit/roofit/inc/RooBernstein.h index 3241fedc761db..ca91009d1e9d0 100644 --- a/roofit/roofit/inc/RooBernstein.h +++ b/roofit/roofit/inc/RooBernstein.h @@ -32,15 +32,18 @@ class RooBernstein : public RooAbsPdf { double analyticalIntegral(Int_t code, const char *rangeName = nullptr) const override; void selectNormalizationRange(const char *rangeName = nullptr, bool force = false) override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsRealLValue const &x() const { return *_x; } + RooArgList const &coefList() const { return _coefList; } -private: + // Implementation detail. Do not use. void fillBuffer() const; + // Implementation detail. Do not use. inline double xmin() const { return _buffer[_coefList.size()]; } + // Implementation detail. Do not use. inline double xmax() const { return _buffer[_coefList.size() + 1]; } +private: + RooTemplateProxy _x; RooListProxy _coefList; std::string _refRangeName; diff --git a/roofit/roofit/inc/RooBifurGauss.h b/roofit/roofit/inc/RooBifurGauss.h index 29553ff1a991a..9f0902f34bc5e 100644 --- a/roofit/roofit/inc/RooBifurGauss.h +++ b/roofit/roofit/inc/RooBifurGauss.h @@ -31,9 +31,17 @@ class RooBifurGauss : public RooAbsPdf { Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char *rangeName = nullptr) const override; double analyticalIntegral(Int_t code, const char *rangeName = nullptr) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const override; + /// Get the x variable. + RooAbsReal const& getX() const { return x.arg(); } + + /// Get the mean parameter. + RooAbsReal const& getMean() const { return mean.arg(); } + + /// Get the left sigma parameter. + RooAbsReal const& getSigmaL() const { return sigmaL.arg(); } + + /// Get the right sigma parameter. + RooAbsReal const& getSigmaR() const { return sigmaR.arg(); } protected: RooRealProxy x; diff --git a/roofit/roofit/inc/RooCBShape.h b/roofit/roofit/inc/RooCBShape.h index 348f6c2bc7588..2886915215765 100644 --- a/roofit/roofit/inc/RooCBShape.h +++ b/roofit/roofit/inc/RooCBShape.h @@ -23,7 +23,7 @@ class RooRealVar; class RooCBShape : public RooAbsPdf { public: - RooCBShape() {} ; + RooCBShape() {} RooCBShape(const char *name, const char *title, RooAbsReal& _m, RooAbsReal& _m0, RooAbsReal& _sigma, RooAbsReal& _alpha, RooAbsReal& _n); @@ -38,9 +38,11 @@ class RooCBShape : public RooAbsPdf { Int_t getMaxVal(const RooArgSet& vars) const override ; double maxVal(Int_t code) const override ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsReal const& getM() const { return m.arg(); } + RooAbsReal const& getM0() const { return m0.arg(); } + RooAbsReal const& getSigma() const { return sigma.arg(); } + RooAbsReal const& getAlpha() const { return alpha.arg(); } + RooAbsReal const& getN() const { return n.arg(); } protected: diff --git a/roofit/roofit/inc/RooChebychev.h b/roofit/roofit/inc/RooChebychev.h index 1b2354b2a8ad0..a4b67e6f8e6fd 100644 --- a/roofit/roofit/inc/RooChebychev.h +++ b/roofit/roofit/inc/RooChebychev.h @@ -37,11 +37,12 @@ class RooChebychev : public RooAbsPdf { void selectNormalizationRange(const char* rangeName=nullptr, bool force=false) override ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsReal const &x() const { return *_x; } + RooArgList const &coefList() const { return _coefList; } - private: + const char *refRangeName() const { return RooNameReg::str(_refRangeName); } + +private: RooRealProxy _x; RooListProxy _coefList ; mutable TNamed* _refRangeName = nullptr; diff --git a/roofit/roofit/inc/RooExponential.h b/roofit/roofit/inc/RooExponential.h index 6604f6da290d0..4e8e004d65139 100644 --- a/roofit/roofit/inc/RooExponential.h +++ b/roofit/roofit/inc/RooExponential.h @@ -38,10 +38,6 @@ class RooExponential : public RooAbsPdf { bool negateCoefficient() const { return _negateCoefficient; } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooRealProxy x; RooRealProxy c; diff --git a/roofit/roofit/inc/RooGamma.h b/roofit/roofit/inc/RooGamma.h index 4000787dd0cb5..99ba0771ee7a8 100644 --- a/roofit/roofit/inc/RooGamma.h +++ b/roofit/roofit/inc/RooGamma.h @@ -31,9 +31,10 @@ class RooGamma : public RooAbsPdf { Int_t getGenerator(const RooArgSet& directVars, RooArgSet &generateVars, bool staticInitOK=true) const override; void generateEvent(Int_t code) override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsReal const &getX() const { return *x; } + RooAbsReal const &getGamma() const { return *gamma; } + RooAbsReal const &getBeta() const { return *beta; } + RooAbsReal const &getMu() const { return *mu; } protected: diff --git a/roofit/roofit/inc/RooGaussian.h b/roofit/roofit/inc/RooGaussian.h index 571cdd3942814..3b487f447683d 100644 --- a/roofit/roofit/inc/RooGaussian.h +++ b/roofit/roofit/inc/RooGaussian.h @@ -50,10 +50,6 @@ class RooGaussian : public RooAbsPdf { /// Get the sigma parameter. RooAbsReal const& getSigma() const { return sigma.arg(); } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooRealProxy x ; diff --git a/roofit/roofit/inc/RooLandau.h b/roofit/roofit/inc/RooLandau.h index 4e7f91c1cf314..e303ab5182291 100644 --- a/roofit/roofit/inc/RooLandau.h +++ b/roofit/roofit/inc/RooLandau.h @@ -37,9 +37,9 @@ class RooLandau : public RooAbsPdf { Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char *rangeName = nullptr) const override; double analyticalIntegral(Int_t code, const char *rangeName) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsReal const& getX() const { return *x; } + RooAbsReal const& getMean() const { return *mean; } + RooAbsReal const& getSigma() const { return *sigma; } protected: diff --git a/roofit/roofit/inc/RooLognormal.h b/roofit/roofit/inc/RooLognormal.h index f824bfae046e8..bed014bcc57df 100644 --- a/roofit/roofit/inc/RooLognormal.h +++ b/roofit/roofit/inc/RooLognormal.h @@ -28,10 +28,6 @@ class RooLognormal : public RooAbsPdf { Int_t getGenerator(const RooArgSet &directVars, RooArgSet &generateVars, bool staticInitOK = true) const override; void generateEvent(Int_t code) override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(int code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; - /// Get the x variable. RooAbsReal const &getX() const { return x.arg(); } diff --git a/roofit/roofit/inc/RooParamHistFunc.h b/roofit/roofit/inc/RooParamHistFunc.h index b42392bd59e33..31b9c25f38ae5 100644 --- a/roofit/roofit/inc/RooParamHistFunc.h +++ b/roofit/roofit/inc/RooParamHistFunc.h @@ -43,9 +43,10 @@ class RooParamHistFunc : public RooAbsReal { double getNominal(Int_t ibin) const ; double getNominalError(Int_t ibin) const ; + const RooArgList& xList() const { return _x ; } const RooArgList& paramList() const { return _p ; } - - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; + const RooDataHist& dataHist() const { return _dh ; } + bool relParam() const { return _relParam; } protected: diff --git a/roofit/roofit/inc/RooPoisson.h b/roofit/roofit/inc/RooPoisson.h index ab1ec42591a6c..9b4bb7ef3d893 100644 --- a/roofit/roofit/inc/RooPoisson.h +++ b/roofit/roofit/inc/RooPoisson.h @@ -39,16 +39,14 @@ class RooPoisson : public RooAbsPdf { /// Switch on or off protection against negative means. void protectNegativeMean(bool flag = true) {_protectNegative = flag;} + bool getProtectNegativeMean() const { return _protectNegative; } + /// Get the x variable. RooAbsReal const& getX() const { return x.arg(); } /// Get the mean parameter. RooAbsReal const& getMean() const { return mean.arg(); } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(int code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooRealProxy x ; diff --git a/roofit/roofit/inc/RooPolynomial.h b/roofit/roofit/inc/RooPolynomial.h index 8a9282ca5d5d9..1dcc58d32a040 100644 --- a/roofit/roofit/inc/RooPolynomial.h +++ b/roofit/roofit/inc/RooPolynomial.h @@ -48,10 +48,6 @@ class RooPolynomial : public RooAbsPdf { // pdf is a reducer node because it doesn't depend on the observables. bool isReducerNode() const override { return _coefList.empty(); } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooRealProxy _x; RooListProxy _coefList; diff --git a/roofit/roofit/inc/RooUniform.h b/roofit/roofit/inc/RooUniform.h index 2f13a735b398d..fc19c09ee99b1 100644 --- a/roofit/roofit/inc/RooUniform.h +++ b/roofit/roofit/inc/RooUniform.h @@ -34,10 +34,6 @@ class RooUniform : public RooAbsPdf { Int_t getGenerator(const RooArgSet& directVars, RooArgSet &generateVars, bool staticInitOK=true) const override; void generateEvent(Int_t code) override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooListProxy x ; diff --git a/roofit/roofit/src/RooBernstein.cxx b/roofit/roofit/src/RooBernstein.cxx index ff7a440f91d51..e4fd2125cf6a6 100644 --- a/roofit/roofit/src/RooBernstein.cxx +++ b/roofit/roofit/src/RooBernstein.cxx @@ -81,13 +81,6 @@ double RooBernstein::evaluate() const return RooFit::Detail::MathFuncs::bernstein(_x, xmin(), xmax(), _buffer.data(), _coefList.size()); } -void RooBernstein::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - fillBuffer(); - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::bernstein", _x, xmin(), xmax(), _coefList, - _coefList.size())); -} - /// Compute multiple values of Bernstein distribution. void RooBernstein::doEval(RooFit::EvalContext &ctx) const { @@ -106,11 +99,3 @@ double RooBernstein::analyticalIntegral(Int_t /*code*/, const char *rangeName) c return RooFit::Detail::MathFuncs::bernsteinIntegral(_x.min(rangeName), _x.max(rangeName), xmin(), xmax(), _buffer.data(), _coefList.size()); } - -std::string RooBernstein::buildCallToAnalyticIntegral(Int_t /*code*/, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - fillBuffer(); // to get the right xmin() and xmax() - return ctx.buildCall("RooFit::Detail::MathFuncs::bernsteinIntegral", _x.min(rangeName), _x.max(rangeName), - xmin(), xmax(), _coefList, _coefList.size()); -} diff --git a/roofit/roofit/src/RooBifurGauss.cxx b/roofit/roofit/src/RooBifurGauss.cxx index 80b736ebda9b2..90e1e9d5788aa 100644 --- a/roofit/roofit/src/RooBifurGauss.cxx +++ b/roofit/roofit/src/RooBifurGauss.cxx @@ -60,13 +60,6 @@ double RooBifurGauss::evaluate() const return RooFit::Detail::MathFuncs::bifurGauss(x, mean, sigmaL, sigmaR); } -//////////////////////////////////////////////////////////////////////////////// - -void RooBifurGauss::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::bifurGauss", x, mean, sigmaL, sigmaR)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of BifurGauss distribution. void RooBifurGauss::doEval(RooFit::EvalContext & ctx) const @@ -96,15 +89,3 @@ double RooBifurGauss::analyticalIntegral(Int_t code, const char *rangeName) cons return RooFit::Detail::MathFuncs::bifurGaussIntegral(integrand.min(rangeName), integrand.max(rangeName), constant, sigmaL, sigmaR); } - -//////////////////////////////////////////////////////////////////////////////// - -std::string RooBifurGauss::buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - auto &constant = code == 1 ? mean : x; - auto &integrand = code == 1 ? x : mean; - - return ctx.buildCall("RooFit::Detail::MathFuncs::bifurGaussIntegral", integrand.min(rangeName), - integrand.max(rangeName), constant, sigmaL, sigmaR); -} diff --git a/roofit/roofit/src/RooCBShape.cxx b/roofit/roofit/src/RooCBShape.cxx index 69962dcd69d29..54e876c72db0b 100644 --- a/roofit/roofit/src/RooCBShape.cxx +++ b/roofit/roofit/src/RooCBShape.cxx @@ -64,11 +64,6 @@ double RooCBShape::evaluate() const return RooFit::Detail::MathFuncs::cbShape(m, m0, sigma, alpha, n); } -void RooCBShape::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::cbShape", m, m0, sigma, alpha, n)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of Crystal ball Shape distribution. void RooCBShape::doEval(RooFit::EvalContext &ctx) const @@ -95,13 +90,6 @@ double RooCBShape::analyticalIntegral(Int_t /*code*/, const char *rangeName) con return cbShapeIntegral(m.min(rangeName), m.max(rangeName), m0, sigma, alpha, n); } -std::string -RooCBShape::buildCallToAnalyticIntegral(Int_t /*code*/, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const -{ - return ctx.buildCall("RooFit::Detail::MathFuncs::cbShapeIntegral", - m.min(rangeName), m.max(rangeName), m0, sigma, alpha, n); -} - //////////////////////////////////////////////////////////////////////////////// /// Advertise that we know the maximum of self for given (m0,alpha,n,sigma) diff --git a/roofit/roofit/src/RooChebychev.cxx b/roofit/roofit/src/RooChebychev.cxx index 8aa19c71becc0..c98caa702c9c4 100644 --- a/roofit/roofit/src/RooChebychev.cxx +++ b/roofit/roofit/src/RooChebychev.cxx @@ -90,18 +90,6 @@ double RooChebychev::evaluate() const return RooFit::Detail::MathFuncs::chebychev(coeffs.data(), _coefList.size(), _x, xmin, xmax); } -void RooChebychev::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - // first bring the range of the variable _x to the normalised range [-1, 1] - // calculate sum_k c_k T_k(x) where x is given in the normalised range, - // c_0 = 1, and the higher coefficients are given in _coefList - double xmax = _x.max(_refRangeName ? _refRangeName->GetName() : nullptr); - double xmin = _x.min(_refRangeName ? _refRangeName->GetName() : nullptr); - - ctx.addResult(this, - ctx.buildCall("RooFit::Detail::MathFuncs::chebychev", _coefList, _coefList.size(), _x, xmin, xmax)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of Chebychev. void RooChebychev::doEval(RooFit::EvalContext &ctx) const @@ -121,37 +109,24 @@ void RooChebychev::doEval(RooFit::EvalContext &ctx) const Int_t RooChebychev::getAnalyticalIntegral(RooArgSet& allVars, RooArgSet& analVars, const char* /* rangeName */) const { - if (matchArgs(allVars, analVars, _x)) return 1; - return 0; + return matchArgs(allVars, analVars, _x) ? 1 : 0; } //////////////////////////////////////////////////////////////////////////////// -double RooChebychev::analyticalIntegral(Int_t code, const char* rangeName) const +double RooChebychev::analyticalIntegral(Int_t code, const char *rangeName) const { - assert(1 == code); (void)code; + assert(1 == code); + (void)code; - double xmax = _x.max(_refRangeName ? _refRangeName->GetName() : nullptr); - double xmaxFull = _x.max(rangeName); - double xmin = _x.min(_refRangeName ? _refRangeName->GetName() : nullptr); - double xminFull = _x.min(rangeName); - unsigned int sz = _coefList.size(); - - std::vector coeffs; - for (auto it : _coefList) - coeffs.push_back(static_cast(*it).getVal()); - - return RooFit::Detail::MathFuncs::chebychevIntegral(coeffs.data(), sz, xmin, xmax, xminFull, xmaxFull); -} - -std::string RooChebychev::buildCallToAnalyticIntegral(Int_t /* code */, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ double xmax = _x.max(_refRangeName ? _refRangeName->GetName() : nullptr); - double xmaxFull = _x.max(rangeName); double xmin = _x.min(_refRangeName ? _refRangeName->GetName() : nullptr); - double xminFull = _x.min(rangeName); unsigned int sz = _coefList.size(); - return ctx.buildCall("RooFit::Detail::MathFuncs::chebychevIntegral", _coefList, sz, xmin, xmax, xminFull, xmaxFull); + std::vector coeffs; + for (auto it : _coefList) + coeffs.push_back(static_cast(*it).getVal()); + + return RooFit::Detail::MathFuncs::chebychevIntegral(coeffs.data(), sz, xmin, xmax, _x.min(rangeName), + _x.max(rangeName)); } diff --git a/roofit/roofit/src/RooExponential.cxx b/roofit/roofit/src/RooExponential.cxx index be95366154a1e..2c7e7c27164bb 100644 --- a/roofit/roofit/src/RooExponential.cxx +++ b/roofit/roofit/src/RooExponential.cxx @@ -110,43 +110,3 @@ double RooExponential::analyticalIntegral(Int_t code, const char *rangeName) con return RooFit::Detail::MathFuncs::exponentialIntegral(min, max, constant); } - -//////////////////////////////////////////////////////////////////////////////// - -void RooExponential::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - // Build a call to the stateless exponential defined later. - std::string coef; - if (_negateCoefficient) { - coef += "-"; - } - coef += ctx.getResult(c); - ctx.addResult(this, "std::exp(" + coef + " * " + ctx.getResult(x) + ")"); -} - -//////////////////////////////////////////////////////////////////////////////// - -std::string RooExponential::buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - bool isOverX = code == 1; - - std::string constant; - if (_negateCoefficient && isOverX) { - constant += "-"; - } - constant += ctx.getResult(isOverX ? c : x); - - auto &integrand = isOverX ? x : c; - - double min = integrand.min(rangeName); - double max = integrand.max(rangeName); - - if (!isOverX && _negateCoefficient) { - std::swap(min, max); - min = -min; - max = -max; - } - - return ctx.buildCall("RooFit::Detail::MathFuncs::exponentialIntegral", min, max, constant); -} diff --git a/roofit/roofit/src/RooGamma.cxx b/roofit/roofit/src/RooGamma.cxx index 91ada3f568c22..6ef6941647446 100644 --- a/roofit/roofit/src/RooGamma.cxx +++ b/roofit/roofit/src/RooGamma.cxx @@ -85,13 +85,6 @@ double RooGamma::evaluate() const return TMath::GammaDist(x, gamma, mu, beta) ; } -//////////////////////////////////////////////////////////////////////////////// - -void RooGamma::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("TMath::GammaDist", x, gamma, mu, beta)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of Gamma PDF. void RooGamma::doEval(RooFit::EvalContext &ctx) const @@ -117,16 +110,6 @@ double RooGamma::analyticalIntegral(Int_t /*code*/, const char *rangeName) const ROOT::Math::gamma_cdf(x.min(rangeName), gamma, beta, mu); } -//////////////////////////////////////////////////////////////////////////////// - -std::string RooGamma::buildCallToAnalyticIntegral(Int_t /*code*/, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - const std::string a = ctx.buildCall("ROOT::Math::gamma_cdf", x.max(rangeName), gamma, beta, mu); - const std::string b = ctx.buildCall("ROOT::Math::gamma_cdf", x.min(rangeName), gamma, beta, mu); - return a + " - " + b; -} - namespace { inline double randomGamma(double gamma, double beta, double mu, double xmin, double xmax) diff --git a/roofit/roofit/src/RooGaussian.cxx b/roofit/roofit/src/RooGaussian.cxx index 79b2e4adaec65..ca31e50bcabab 100644 --- a/roofit/roofit/src/RooGaussian.cxx +++ b/roofit/roofit/src/RooGaussian.cxx @@ -126,23 +126,3 @@ void RooGaussian::generateEvent(Int_t code) return; } - -//////////////////////////////////////////////////////////////////////////////// - -void RooGaussian::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - // Build a call to the stateless gaussian defined later. - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::gaussian", x, mean, sigma)); -} - -//////////////////////////////////////////////////////////////////////////////// - -std::string RooGaussian::buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - auto& constant = code == 1 ? mean : x; - auto& integrand = code == 1 ? x : mean; - - return ctx.buildCall("RooFit::Detail::MathFuncs::gaussianIntegral", - integrand.min(rangeName), integrand.max(rangeName), constant, sigma); -} diff --git a/roofit/roofit/src/RooLandau.cxx b/roofit/roofit/src/RooLandau.cxx index 9262ae714a3f7..cc29e989d4bc4 100644 --- a/roofit/roofit/src/RooLandau.cxx +++ b/roofit/roofit/src/RooLandau.cxx @@ -61,13 +61,6 @@ double RooLandau::evaluate() const return RooFit::Detail::MathFuncs::landau(x, mean, sigma); } -//////////////////////////////////////////////////////////////////////////////// - -void RooLandau::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::landau", x, mean, sigma)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of Landau distribution. void RooLandau::doEval(RooFit::EvalContext &ctx) const @@ -103,18 +96,6 @@ Double_t RooLandau::analyticalIntegral(Int_t /*code*/, const char *rangeName) co //////////////////////////////////////////////////////////////////////////////// -std::string RooLandau::buildCallToAnalyticIntegral(Int_t /*code*/, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - // Don't do anything with "code". It can only be "1" anyway (see - // implementation of getAnalyticalIntegral). - const std::string a = ctx.buildCall("ROOT::Math::landau_cdf", x.max(rangeName), sigma, mean); - const std::string b = ctx.buildCall("ROOT::Math::landau_cdf", x.min(rangeName), sigma, mean); - return ctx.getResult(sigma) + " * " + "(" + a + " - " + b + ")"; -} - -//////////////////////////////////////////////////////////////////////////////// - void RooLandau::generateEvent(Int_t code) { assert(1 == code); (void)code; diff --git a/roofit/roofit/src/RooLognormal.cxx b/roofit/roofit/src/RooLognormal.cxx index f771d83e75b29..a251b62127561 100644 --- a/roofit/roofit/src/RooLognormal.cxx +++ b/roofit/roofit/src/RooLognormal.cxx @@ -82,12 +82,6 @@ double RooLognormal::evaluate() const return ROOT::Math::lognormal_pdf(x, ln_m0, ln_k); } -void RooLognormal::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - std::string funcName = _useStandardParametrization ? "logNormalEvaluateStandard" : "logNormal"; - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::" + funcName, x, k, m0)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of Lognormal distribution. void RooLognormal::doEval(RooFit::EvalContext &ctx) const @@ -116,13 +110,6 @@ double RooLognormal::analyticalIntegral(Int_t /*code*/, const char *rangeName) c return 0.5 * (RooMath::erf(scaledMax / (root2 * ln_k)) - RooMath::erf(scaledMin / (root2 * ln_k))); } -std::string RooLognormal::buildCallToAnalyticIntegral(int /*code*/, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - std::string funcName = _useStandardParametrization ? "logNormalIntegralStandard" : "logNormalIntegral"; - return ctx.buildCall("RooFit::Detail::MathFuncs::" + funcName, x.min(rangeName), x.max(rangeName), m0, k); -} - //////////////////////////////////////////////////////////////////////////////// Int_t RooLognormal::getGenerator(const RooArgSet &directVars, RooArgSet &generateVars, bool /*staticInitOK*/) const diff --git a/roofit/roofit/src/RooParamHistFunc.cxx b/roofit/roofit/src/RooParamHistFunc.cxx index 0ca4527d5448e..42e954dfb8313 100644 --- a/roofit/roofit/src/RooParamHistFunc.cxx +++ b/roofit/roofit/src/RooParamHistFunc.cxx @@ -77,21 +77,6 @@ double RooParamHistFunc::evaluate() const return _relParam ? ret * getNominal(idx) : ret; } -void RooParamHistFunc::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - std::string const &idx = _dh.calculateTreeIndexForCodeSquash(this, ctx, _x); - std::string arrName = ctx.buildArg(_p); - std::string result = arrName + "[" + idx + "]"; - if (_relParam) { - // get weight[idx] * binv[idx]. Here we get the bin volume for the first element as we assume the distribution to - // be binned uniformly. - double binV = _dh.binVolume(0); - std::string weightArr = _dh.declWeightArrayForCodeSquash(ctx, false); - result += " * *(" + weightArr + " + " + idx + ") * " + std::to_string(binV); - } - ctx.addResult(this, result); -} - //////////////////////////////////////////////////////////////////////////////// double RooParamHistFunc::getActual(Int_t ibin) diff --git a/roofit/roofit/src/RooPoisson.cxx b/roofit/roofit/src/RooPoisson.cxx index 247605f287ccb..efffd71712c03 100644 --- a/roofit/roofit/src/RooPoisson.cxx +++ b/roofit/roofit/src/RooPoisson.cxx @@ -64,15 +64,6 @@ double RooPoisson::evaluate() const return RooFit::Detail::MathFuncs::poisson(k, mean); } -void RooPoisson::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - std::string xName = ctx.getResult(x); - if (!_noRounding) - xName = "std::floor(" + xName + ")"; - - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::poisson", xName, mean)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute multiple values of the Poisson distribution. void RooPoisson::doEval(RooFit::EvalContext &ctx) const @@ -102,22 +93,6 @@ double RooPoisson::analyticalIntegral(Int_t code, const char* rangeName) const code, mean, _noRounding ? x : std::floor(x), integrand.min(rangeName), integrand.max(rangeName), _protectNegative); } -std::string -RooPoisson::buildCallToAnalyticIntegral(int code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const -{ - R__ASSERT(code == 1 || code == 2); - std::string xName = ctx.getResult(x); - if (!_noRounding) - xName = "std::floor(" + xName + ")"; - - RooRealProxy const &integrand = code == 1 ? x : mean; - // Since the integral function is the same for both codes, we need to make sure the indexed observables do not appear - // in the function if they are not required. - xName = code == 1 ? "0" : xName; - return ctx.buildCall("RooFit::Detail::MathFuncs::poissonIntegral", code, mean, xName, - integrand.min(rangeName), integrand.max(rangeName), _protectNegative); -} - //////////////////////////////////////////////////////////////////////////////// /// Advertise internal generator in x diff --git a/roofit/roofit/src/RooPolynomial.cxx b/roofit/roofit/src/RooPolynomial.cxx index eac663acf88e1..39384cf8ee990 100644 --- a/roofit/roofit/src/RooPolynomial.cxx +++ b/roofit/roofit/src/RooPolynomial.cxx @@ -111,17 +111,6 @@ double RooPolynomial::evaluate() const return RooFit::Detail::MathFuncs::polynomial(_wksp.data(), sz, _lowestOrder, _x); } -void RooPolynomial::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - const unsigned sz = _coefList.size(); - if (!sz) { - ctx.addResult(this, std::to_string((_lowestOrder ? 1. : 0.))); - return; - } - - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::polynomial", _coefList, sz, _lowestOrder, _x)); -} - /// Compute multiple values of Polynomial. void RooPolynomial::doEval(RooFit::EvalContext &ctx) const { @@ -151,16 +140,3 @@ double RooPolynomial::analyticalIntegral(Int_t code, const char *rangeName) cons return RooFit::Detail::MathFuncs::polynomialIntegral(_wksp.data(), sz, _lowestOrder, xmin, xmax); } - -std::string RooPolynomial::buildCallToAnalyticIntegral(Int_t /* code */, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - const double xmin = _x.min(rangeName); - const double xmax = _x.max(rangeName); - const unsigned sz = _coefList.size(); - if (!sz) - return std::to_string(_lowestOrder ? xmax - xmin : 0.0); - - return ctx.buildCall("RooFit::Detail::MathFuncs::polynomialIntegral", _coefList, sz, _lowestOrder, - xmin, xmax); -} diff --git a/roofit/roofit/src/RooUniform.cxx b/roofit/roofit/src/RooUniform.cxx index 14a3391fd6fb1..71793251f55f4 100644 --- a/roofit/roofit/src/RooUniform.cxx +++ b/roofit/roofit/src/RooUniform.cxx @@ -50,11 +50,6 @@ double RooUniform::evaluate() const return 1 ; } -void RooUniform::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, "1.0"); -} - //////////////////////////////////////////////////////////////////////////////// /// Advertise analytical integral @@ -93,14 +88,6 @@ double RooUniform::analyticalIntegral(Int_t code, const char* rangeName) const return ret ; } -std::string RooUniform::buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext & /*ctx*/) const -{ - // The integral of a uniform distribution is static, so we can just hardcode - // the result in a string. - return std::to_string(analyticalIntegral(code, rangeName)); -} - //////////////////////////////////////////////////////////////////////////////// /// Advertise internal generator diff --git a/roofit/roofitcore/CMakeLists.txt b/roofit/roofitcore/CMakeLists.txt index 29ffc54ab1239..d82756854233f 100644 --- a/roofit/roofitcore/CMakeLists.txt +++ b/roofit/roofitcore/CMakeLists.txt @@ -116,10 +116,12 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore RooFactoryWSTool.h RooFirstMoment.h RooFit.h + RooFit/CodegenContext.h RooFit/Config.h - RooFit/Detail/CodeSquashContext.h RooFit/Detail/MathFuncs.h RooFit/Detail/NormalizationHelpers.h + RooFit/Detail/RooNLLVarNew.h + RooFit/Detail/RooNormalizedPdf.h RooFit/EvalContext.h RooFit/Evaluator.h RooFit/Floats.h @@ -132,8 +134,8 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore RooFit/TestStatistics/RooSubsidiaryL.h RooFit/TestStatistics/RooSumL.h RooFit/TestStatistics/RooUnbinnedL.h - RooFit/TestStatistics/buildLikelihood.h RooFit/TestStatistics/SharedOffset.h + RooFit/TestStatistics/buildLikelihood.h RooFitLegacy/RooCatTypeLegacy.h RooFitLegacy/RooCategorySharedProperties.h RooFitLegacy/RooTreeData.h @@ -324,7 +326,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore src/RooFFTConvPdf.cxx src/RooFactoryWSTool.cxx src/RooFirstMoment.cxx - src/RooFit/Detail/CodeSquashContext.cxx + src/RooFit/CodegenContext.cxx src/RooFit/EvalContext.cxx src/RooFit/Evaluator.cxx src/RooFitImplHelpers.cxx diff --git a/roofit/roofitcore/inc/LinkDef.h b/roofit/roofitcore/inc/LinkDef.h index 4e643d16459fb..83fa0c52e8ac5 100644 --- a/roofit/roofitcore/inc/LinkDef.h +++ b/roofit/roofitcore/inc/LinkDef.h @@ -233,6 +233,7 @@ #pragma link C++ class RooRealSumFunc + ; #pragma link C++ class RooResolutionModel+ ; #pragma link C++ class RooTruthModel+ ; +#pragma link C++ class RooFit::Detail::RooFixedProdPdf+ ; #pragma link C++ class RooProdPdf+ ; #pragma read sourceClass="RooProdPdf" targetClass="RooProdPdf" version="[-5]" \ source="RooLinkedList _pdfNSetList" target="_pdfNSetList" \ @@ -334,5 +335,7 @@ #pragma link off class RooErrorHandler+ ; #pragma link C++ class RooBinSamplingPdf+; #pragma link C++ class RooBinWidthFunction+; +#pragma link C++ class RooFit::Detail::RooNLLVarNew+; +#pragma link C++ class RooFit::Detail::RooNormalizedPdf+ ; #endif diff --git a/roofit/roofitcore/inc/RooAbsArg.h b/roofit/roofitcore/inc/RooAbsArg.h index 902fa00447b88..655ed128dfbcf 100644 --- a/roofit/roofitcore/inc/RooAbsArg.h +++ b/roofit/roofitcore/inc/RooAbsArg.h @@ -54,9 +54,7 @@ using RooListProxy = RooCollectionProxy; class RooExpensiveObjectCache ; class RooWorkspace ; namespace RooFit { -namespace Detail { -class CodeSquashContext; -} +class CodegenContext; } class RooRefArray : public TObjArray { @@ -523,8 +521,6 @@ class RooAbsArg : public TNamed, public RooPrintable { virtual bool isCategory() const { return false; } - virtual void translate(RooFit::Detail::CodeSquashContext &ctx) const; - protected: void graphVizAddConnections(std::set >&) ; diff --git a/roofit/roofitcore/inc/RooAbsReal.h b/roofit/roofitcore/inc/RooAbsReal.h index 8537b0192e7e1..5300c32c3b84a 100644 --- a/roofit/roofitcore/inc/RooAbsReal.h +++ b/roofit/roofitcore/inc/RooAbsReal.h @@ -21,7 +21,7 @@ #include "RooArgSet.h" #include "RooCmdArg.h" #include "RooCurve.h" -#include "RooFit/Detail/CodeSquashContext.h" +#include "RooFit/CodegenContext.h" #include "RooFit/EvalContext.h" #include "RooGlobalFunc.h" @@ -389,9 +389,6 @@ class RooAbsReal : public RooAbsArg { if(!hasGradient()) throw std::runtime_error("RooAbsReal::gradient(double *) not implemented by this class!"); } - virtual std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const; - // PlotOn with command list virtual RooPlot* plotOn(RooPlot* frame, RooLinkedList& cmdList) const ; diff --git a/roofit/roofitcore/inc/RooAddPdf.h b/roofit/roofitcore/inc/RooAddPdf.h index e2b5036cc9554..26dfa58ec646e 100644 --- a/roofit/roofitcore/inc/RooAddPdf.h +++ b/roofit/roofitcore/inc/RooAddPdf.h @@ -93,8 +93,6 @@ class RooAddPdf : public RooAbsPdf { CacheMode canNodeBeCached() const override { return RooAbsArg::NotAdvised ; }; void setCacheAndTrackHints(RooArgSet&) override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::unique_ptr compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext & ctx) const override; protected: diff --git a/roofit/roofitcore/inc/RooAddition.h b/roofit/roofitcore/inc/RooAddition.h index d1f1f74367316..d07f5c3e49454 100644 --- a/roofit/roofitcore/inc/RooAddition.h +++ b/roofit/roofitcore/inc/RooAddition.h @@ -56,8 +56,6 @@ class RooAddition : public RooAbsReal { void doEval(RooFit::EvalContext &) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: RooArgList _ownedList ; ///< List of owned components diff --git a/roofit/roofitcore/inc/RooConstVar.h b/roofit/roofitcore/inc/RooConstVar.h index c89d0b9d1edd4..1772124d77326 100644 --- a/roofit/roofitcore/inc/RooConstVar.h +++ b/roofit/roofitcore/inc/RooConstVar.h @@ -48,8 +48,6 @@ class RooConstVar final : public RooAbsReal { _value = value; } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: double evaluate() const override { diff --git a/roofit/roofitcore/inc/RooConstraintSum.h b/roofit/roofitcore/inc/RooConstraintSum.h index 73360e3965307..cf38ded44c96f 100644 --- a/roofit/roofitcore/inc/RooConstraintSum.h +++ b/roofit/roofitcore/inc/RooConstraintSum.h @@ -45,7 +45,6 @@ class RooConstraintSum : public RooAbsReal { std::unique_ptr compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext & ctx) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; protected: RooListProxy _set1 ; ///< Set of constraint terms diff --git a/roofit/roofitcore/inc/RooDataHist.h b/roofit/roofitcore/inc/RooDataHist.h index 2da88fc9da5a9..54794b9ae36c5 100644 --- a/roofit/roofitcore/inc/RooDataHist.h +++ b/roofit/roofitcore/inc/RooDataHist.h @@ -218,9 +218,9 @@ class RooDataHist : public RooAbsData, public RooDirItem { double const* wgtErrHiArray() const { return _errHi; } double const* sumW2Array() const { return _sumw2; } - std::string calculateTreeIndexForCodeSquash(RooAbsArg const *klass, RooFit::Detail::CodeSquashContext &ctx, + std::string calculateTreeIndexForCodeSquash(RooAbsArg const *klass, RooFit::CodegenContext &ctx, const RooAbsCollection &coords, bool reverse = false) const; - std::string declWeightArrayForCodeSquash(RooFit::Detail::CodeSquashContext &ctx, + std::string declWeightArrayForCodeSquash(RooFit::CodegenContext &ctx, bool correctForBinSize) const; protected: diff --git a/roofit/roofitcore/inc/RooEffProd.h b/roofit/roofitcore/inc/RooEffProd.h index dd14c5f6e6cad..3b9e65e8c4bfd 100644 --- a/roofit/roofitcore/inc/RooEffProd.h +++ b/roofit/roofitcore/inc/RooEffProd.h @@ -28,12 +28,13 @@ class RooEffProd: public RooAbsPdf { RooAbsGenContext* genContext(const RooArgSet &vars, const RooDataSet *prototype, const RooArgSet* auxProto, bool verbose) const override; + RooAbsReal const& pdf() const { return *_pdf; } + RooAbsReal const& eff() const { return *_eff; } + protected: // Function evaluation double evaluate() const override ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - RooRealProxy _pdf ; ///< Probability Density function RooRealProxy _eff; ///< Efficiency function diff --git a/roofit/roofitcore/inc/RooEfficiency.h b/roofit/roofitcore/inc/RooEfficiency.h index c5232ba2da12e..829706aae9330 100644 --- a/roofit/roofitcore/inc/RooEfficiency.h +++ b/roofit/roofitcore/inc/RooEfficiency.h @@ -33,14 +33,16 @@ class RooEfficiency : public RooAbsPdf { Int_t getAnalyticalIntegral(RooArgSet& allVars, RooArgSet& analVars, const char* rangeName=nullptr) const override ; double analyticalIntegral(Int_t code, const char* rangeName=nullptr) const override ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; + + RooAbsCategory const& cat() const { return *_cat; } + RooAbsReal const& effFunc() const { return *_effFunc; } + std::string sigCatName() const { return _sigCatName.Data(); } protected: // Function evaluation double evaluate() const override ; + RooCategoryProxy _cat ; ///< Accept/reject categort RooRealProxy _effFunc ; ///< Efficiency modeling function TString _sigCatName ; ///< Name of accept state of accept/reject category diff --git a/roofit/roofitcore/inc/RooExtendPdf.h b/roofit/roofitcore/inc/RooExtendPdf.h index dfb287e3554ea..c0ffad65c5b10 100644 --- a/roofit/roofitcore/inc/RooExtendPdf.h +++ b/roofit/roofitcore/inc/RooExtendPdf.h @@ -48,7 +48,7 @@ class RooExtendPdf : public RooAbsPdf { double expectedEvents(const RooArgSet* nset) const override ; std::unique_ptr createExpectedEventsFunc(const RooArgSet* nset) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsPdf const& pdf() const { return *_pdf; } protected: diff --git a/roofit/roofitcore/inc/RooFit/Detail/CodeSquashContext.h b/roofit/roofitcore/inc/RooFit/CodegenContext.h similarity index 79% rename from roofit/roofitcore/inc/RooFit/Detail/CodeSquashContext.h rename to roofit/roofitcore/inc/RooFit/CodegenContext.h index a074669f8f221..c65bc0294a449 100644 --- a/roofit/roofitcore/inc/RooFit/Detail/CodeSquashContext.h +++ b/roofit/roofitcore/inc/RooFit/CodegenContext.h @@ -11,8 +11,8 @@ * listed in LICENSE (http://roofit.sourceforge.net/license.txt) */ -#ifndef RooFit_Detail_CodeSquashContext_h -#define RooFit_Detail_CodeSquashContext_h +#ifndef RooFit_Detail_CodegenContext_h +#define RooFit_Detail_CodegenContext_h #include #include @@ -32,18 +32,18 @@ class RooTemplateProxy; namespace RooFit { -namespace Experimental { -class RooFuncWrapper; -} +template +struct Prio { + static_assert(P >= 1 && P <= 10, "P must be 1 <= P <= 10!"); + static auto next() { return Prio

{}; } +}; -namespace Detail { +using PrioHighest = Prio<1>; +using PrioLowest = Prio<10>; /// @brief A class to maintain the context for squashing of RooFit models into code. -class CodeSquashContext { +class CodegenContext { public: - CodeSquashContext(std::map const &outputSizes, std::vector &xlarr, - Experimental::RooFuncWrapper &wrapper); - void addResult(RooAbsArg const *key, std::string const &value); void addResult(const char *key, std::string const &value); @@ -68,7 +68,6 @@ class CodeSquashContext { } void addToGlobalScope(std::string const &str); - std::string assembleCode(std::string const &returnExpr); void addVecObs(const char *key, int idx); void addToCodeBody(RooAbsArg const *klass, std::string const &in); @@ -94,13 +93,13 @@ class CodeSquashContext { /// } class LoopScope { public: - LoopScope(CodeSquashContext &ctx, std::vector &&vars) : _ctx{ctx}, _vars{vars} {} + LoopScope(CodegenContext &ctx, std::vector &&vars) : _ctx{ctx}, _vars{vars} {} ~LoopScope() { _ctx.endLoop(*this); } std::vector const &vars() const { return _vars; } private: - CodeSquashContext &_ctx; + CodegenContext &_ctx; const std::vector _vars; }; @@ -113,9 +112,15 @@ class CodeSquashContext { std::string buildArg(std::span arr); std::string buildArg(std::span arr) { return buildArgSpanImpl(arr); } + std::vector const &xlArr() { return _xlArr; } + void collectFunction(std::string const &name); + std::vector const &collectedFunctions() { return _collectedFunctions; } - Experimental::RooFuncWrapper *_wrapper = nullptr; + std::string + buildFunction(RooAbsArg const &arg, std::map const &outputSizes = {}); + + auto const &outputSizes() const { return _nodeOutputSizes; } private: template @@ -189,23 +194,24 @@ class CodeSquashContext { /// Mainly used for placing decls outside of loops. std::string _tempScope; /// @brief A map to keep track of list names as assigned by addResult. - std::unordered_map::Value_t, std::string> listNames; - std::vector &_xlArr; + std::unordered_map::Value_t, std::string> _listNames; + std::vector _xlArr; + std::vector _collectedFunctions; }; template <> -inline std::string CodeSquashContext::typeName() const +inline std::string CodegenContext::typeName() const { return "double"; } template <> -inline std::string CodeSquashContext::typeName() const +inline std::string CodegenContext::typeName() const { return "int"; } template -std::string CodeSquashContext::buildArgSpanImpl(std::span arr) +std::string CodegenContext::buildArgSpanImpl(std::span arr) { unsigned int n = arr.size(); std::string arrName = getTmpVarName(); @@ -220,7 +226,26 @@ std::string CodeSquashContext::buildArgSpanImpl(std::span arr) return arrName; } -} // namespace Detail +template +void codegenImpl(Arg_t &arg, RooFit::CodegenContext &ctx, Prio

p) +{ + if constexpr (std::is_same, PrioLowest>::value) { + return codegenImpl(arg, ctx); + } else { + return codegenImpl(arg, ctx, p.next()); + } +} + +template +struct CodegenImplCaller { + + static auto call(RooAbsArg &arg, RooFit::CodegenContext &ctx) { + return codegenImpl(static_cast(arg), ctx, PrioHighest{}); + } + +}; + +void codegen(RooAbsArg &arg, RooFit::CodegenContext &ctx); } // namespace RooFit diff --git a/roofit/roofitcore/src/RooNLLVarNew.h b/roofit/roofitcore/inc/RooFit/Detail/RooNLLVarNew.h similarity index 86% rename from roofit/roofitcore/src/RooNLLVarNew.h rename to roofit/roofitcore/inc/RooFit/Detail/RooNLLVarNew.h index ce0dbfcc6cbe5..d7ef8e8fda613 100644 --- a/roofit/roofitcore/src/RooNLLVarNew.h +++ b/roofit/roofitcore/inc/RooFit/Detail/RooNLLVarNew.h @@ -23,6 +23,9 @@ #include +namespace RooFit { +namespace Detail { + class RooNLLVarNew : public RooAbsReal { public: @@ -52,7 +55,11 @@ class RooNLLVarNew : public RooAbsReal { void setSimCount(int simCount) { _simCount = simCount; } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; + RooAbsPdf const &pdf() const { return *_pdf; } + RooAbsReal const &weightVar() const { return *_weightVar; } + bool binnedL() const { return _binnedL; } + int simCount() const { return _simCount; } + RooAbsReal const *expectedEvents() const { return _expectedEvents ? &**_expectedEvents : nullptr; } private: double evaluate() const override { return _value; } @@ -77,7 +84,11 @@ class RooNLLVarNew : public RooAbsReal { std::vector _binw; mutable ROOT::Math::KahanSum _offset{0.}; /// #include +namespace RooFit { +namespace Detail { + class RooNormalizedPdf : public RooAbsPdf { public: RooNormalizedPdf(RooAbsPdf &pdf, RooArgSet const &normSet) : _pdf("numerator", "numerator", this, pdf), _normIntegral( "denominator", "denominator", this, - std::unique_ptr{pdf.createIntegral(normSet, *pdf.getIntegratorConfig(), pdf.normRange())}, - true, false), + std::unique_ptr{pdf.createIntegral(normSet, *pdf.getIntegratorConfig(), pdf.normRange())}, true, + false), _normSet{normSet} { auto name = std::string(pdf.GetName()) + "_over_" + _normIntegral->GetName(); @@ -51,7 +54,8 @@ class RooNormalizedPdf : public RooAbsPdf { return _pdf->getAnalyticalIntegralWN(allVars, analVars, &_normSet, rangeName); } /// Forward calculation of analytical integrals to input p.d.f - double analyticalIntegralWN(Int_t code, const RooArgSet * /*normSet*/, const char *rangeName = nullptr) const override + double + analyticalIntegralWN(Int_t code, const RooArgSet * /*normSet*/, const char *rangeName = nullptr) const override { return _pdf->analyticalIntegralWN(code, &_normSet, rangeName); } @@ -64,10 +68,11 @@ class RooNormalizedPdf : public RooAbsPdf { return _pdf->createExpectedEventsFunc(&_normSet); } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - bool canComputeBatchWithCuda() const override { return true; } + RooAbsPdf const &pdf() const { return *_pdf; } + RooAbsReal const &normIntegral() const { return *_normIntegral; } + protected: void doEval(RooFit::EvalContext &) const override; double evaluate() const override @@ -85,6 +90,11 @@ class RooNormalizedPdf : public RooAbsPdf { RooTemplateProxy _pdf; RooRealProxy _normIntegral; RooArgSet _normSet; + + ClassDefOverride(RooFit::Detail::RooNormalizedPdf, 0); }; +} // namespace Detail +} // namespace RooFit + #endif diff --git a/roofit/roofitcore/inc/RooFormulaVar.h b/roofit/roofitcore/inc/RooFormulaVar.h index 1a44a4348667c..3fd0b228a12ac 100644 --- a/roofit/roofitcore/inc/RooFormulaVar.h +++ b/roofit/roofitcore/inc/RooFormulaVar.h @@ -74,7 +74,8 @@ class RooFormulaVar : public RooAbsReal { // Function evaluation double evaluate() const override ; void doEval(RooFit::EvalContext &ctx) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; + + std::string getUniqueFuncName() const; protected: // Post-processing of server redirection diff --git a/roofit/roofitcore/inc/RooFuncWrapper.h b/roofit/roofitcore/inc/RooFuncWrapper.h index 66b98950b85a6..12383a442624c 100644 --- a/roofit/roofitcore/inc/RooFuncWrapper.h +++ b/roofit/roofitcore/inc/RooFuncWrapper.h @@ -60,20 +60,16 @@ class RooFuncWrapper final : public RooAbsReal { void writeDebugMacro(std::string const &) const; - std::string declareFunction(std::string const &funcBody); - void collectFunction(std::string const &funcName) { _collectedFunctions.emplace_back(funcName); } std::vector const &collectedFunctions() { return _collectedFunctions; } - std::string buildCode(RooAbsReal const &head); - protected: double evaluate() const override; private: void updateGradientVarBuffer() const; - void loadParamsAndData(RooAbsArg const *head, RooArgSet const ¶mSet, const RooAbsData *data, - RooSimultaneous const *simPdf); + std::map> + loadParamsAndData(RooArgSet const ¶mSet, const RooAbsData *data, RooSimultaneous const *simPdf); void buildFuncAndGradFunctors(); @@ -96,7 +92,6 @@ class RooFuncWrapper final : public RooAbsReal { mutable std::vector _gradientVarBuffer; std::vector _observables; std::map _obsInfos; - std::map _nodeOutputSizes; std::vector _xlArr; std::vector _collectedFunctions; }; diff --git a/roofit/roofitcore/inc/RooGenericPdf.h b/roofit/roofitcore/inc/RooGenericPdf.h index fede017979fb6..4fc0bfad5e463 100644 --- a/roofit/roofitcore/inc/RooGenericPdf.h +++ b/roofit/roofitcore/inc/RooGenericPdf.h @@ -59,6 +59,8 @@ class RooGenericPdf : public RooAbsPdf { const char* expression() const { return _formExpr.Data(); } const RooArgList& dependents() const { return _actualVars; } + std::string getUniqueFuncName() const; + protected: RooFormula& formula() const ; @@ -67,7 +69,6 @@ class RooGenericPdf : public RooAbsPdf { RooListProxy _actualVars ; double evaluate() const override ; void doEval(RooFit::EvalContext &) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; // Post-processing of server redirection bool redirectServersHook(const RooAbsCollection& newServerList, bool mustReplaceAll, bool nameChange, bool isRecursive) override ; diff --git a/roofit/roofitcore/inc/RooHistFunc.h b/roofit/roofitcore/inc/RooHistFunc.h index 0304e38bbefe0..4a7be1f6f19d5 100644 --- a/roofit/roofitcore/inc/RooHistFunc.h +++ b/roofit/roofitcore/inc/RooHistFunc.h @@ -99,10 +99,6 @@ class RooHistFunc : public RooAbsReal { Int_t getBin() const; std::vector getBins(RooFit::EvalContext & ctx) const; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(int code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; - RooArgSet const &variables() const { return _depList; } protected: diff --git a/roofit/roofitcore/inc/RooHistPdf.h b/roofit/roofitcore/inc/RooHistPdf.h index f520fced18a32..e55d35f561ebf 100644 --- a/roofit/roofitcore/inc/RooHistPdf.h +++ b/roofit/roofitcore/inc/RooHistPdf.h @@ -95,11 +95,9 @@ class RooHistPdf : public RooAbsPdf { void doEval(RooFit::EvalContext &) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(int code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; + RooArgSet const &variables() const { return _pdfObsList; } - protected: +protected: bool areIdentical(const RooDataHist& dh1, const RooDataHist& dh2) ; bool importWorkspaceHook(RooWorkspace& ws) override ; @@ -147,13 +145,6 @@ class RooHistPdf : public RooAbsPdf { double xlo, double xhi); - static void rooHistTranslateImpl(RooAbsArg const *klass, RooFit::Detail::CodeSquashContext &ctx, int intOrder, - RooDataHist const *dataHist, const RooArgSet &obs, bool correctForBinSize, bool cdfBoundaries); - - static std::string rooHistIntegralTranslateImpl(int code, RooAbsArg const *klass, RooDataHist const *dataHist, - const RooArgSet &obs, bool histFuncMode); - -private: inline void initializeOwnedDataHist(std::unique_ptr &&dataHist) { _ownedDataHist = std::move(dataHist); diff --git a/roofit/roofitcore/inc/RooMultiVarGaussian.h b/roofit/roofitcore/inc/RooMultiVarGaussian.h index e30c185c9f6f9..4712359c764ee 100644 --- a/roofit/roofitcore/inc/RooMultiVarGaussian.h +++ b/roofit/roofitcore/inc/RooMultiVarGaussian.h @@ -49,6 +49,7 @@ class RooMultiVarGaussian : public RooAbsPdf { void generateEvent(Int_t code) override; const TMatrixDSym& covarianceMatrix() const { return _cov ; } + const TMatrixDSym& covarianceMatrixInverse() const { return _covI ; } const RooArgList& xVec() const { return _x;} const RooArgList& muVec() const { return _mu; } @@ -84,10 +85,6 @@ class RooMultiVarGaussian : public RooAbsPdf { static void blockDecompose(const TMatrixD& input, const std::vector& map1, const std::vector& map2, TMatrixDSym& S11, TMatrixD& S12, TMatrixD& S21, TMatrixDSym& S22) ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string - buildCallToAnalyticIntegral(Int_t code, const char *rangeName, RooFit::Detail::CodeSquashContext &ctx) const override; - protected: void decodeCode(Int_t code, std::vector& map1, std::vector& map2) const; diff --git a/roofit/roofitcore/inc/RooPolyVar.h b/roofit/roofitcore/inc/RooPolyVar.h index d793c4c493124..ff3bd3d88c7d0 100644 --- a/roofit/roofitcore/inc/RooPolyVar.h +++ b/roofit/roofitcore/inc/RooPolyVar.h @@ -34,9 +34,9 @@ class RooPolyVar : public RooAbsReal { Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char *rangeName = nullptr) const override; double analyticalIntegral(Int_t code, const char *rangeName = nullptr) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - std::string buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const override; + RooRealProxy const &x() const { return _x; } + RooArgList const &coefList() const { return _coefList; } + int lowestOrder() const { return _lowestOrder; } protected: RooRealProxy _x; diff --git a/roofit/roofitcore/inc/RooProdPdf.h b/roofit/roofitcore/inc/RooProdPdf.h index abe8b14f2aa9d..272b9a5cb9410 100644 --- a/roofit/roofitcore/inc/RooProdPdf.h +++ b/roofit/roofitcore/inc/RooProdPdf.h @@ -30,6 +30,12 @@ typedef RooArgList* pRooArgList ; typedef RooLinkedList* pRooLinkedList ; +namespace RooFit { +namespace Detail { +class RooFixedProdPdf; +} +} + class RooProdPdf : public RooAbsPdf { public: @@ -98,6 +104,25 @@ class RooProdPdf : public RooAbsPdf { std::unique_ptr compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext & ctx) const override; + // The cache object. Internal, do not use. + class CacheElem final : public RooAbsCacheElement { + public: + CacheElem() : _isRearranged(false) { } + // Payload + RooArgList _partList ; + RooArgList _numList ; + RooArgList _denList ; + RooArgList _ownedList ; + std::vector> _normList; + bool _isRearranged ; + std::unique_ptr _rearrangedNum{}; + std::unique_ptr _rearrangedDen{}; + // Cache management functions + RooArgList containedArgs(Action) override ; + void printCompactTreeHook(std::ostream&, const char *, Int_t, Int_t) override ; + void writeToStream(std::ostream& os) const ; + } ; + private: std::unique_ptr fillNormSetForServer(RooArgSet const& normSet, RooAbsArg const& server) const; @@ -131,25 +156,6 @@ class RooProdPdf : public RooAbsPdf { CacheMode canNodeBeCached() const override { return RooAbsArg::NotAdvised ; } ; void setCacheAndTrackHints(RooArgSet&) override ; - // The cache object - class CacheElem final : public RooAbsCacheElement { - public: - CacheElem() : _isRearranged(false) { } - // Payload - RooArgList _partList ; - RooArgList _numList ; - RooArgList _denList ; - RooArgList _ownedList ; - std::vector> _normList; - bool _isRearranged ; - std::unique_ptr _rearrangedNum{}; - std::unique_ptr _rearrangedDen{}; - // Cache management functions - RooArgList containedArgs(Action) override ; - void printCompactTreeHook(std::ostream&, const char *, Int_t, Int_t) override ; - void writeToStream(std::ostream& os) const ; - } ; - std::unique_ptr createCacheElem(const RooArgSet* nset, const RooArgSet* iset, const char* isetRangeName=nullptr) const; mutable RooObjCacheManager _cacheMgr ; //! The cache manager @@ -163,7 +169,7 @@ class RooProdPdf : public RooAbsPdf { friend class RooProdGenContext ; - friend class RooFixedProdPdf ; + friend class RooFit::Detail::RooFixedProdPdf ; RooAbsGenContext* genContext(const RooArgSet &vars, const RooDataSet *prototype=nullptr, const RooArgSet *auxProto=nullptr, bool verbose= false) const override ; @@ -190,5 +196,73 @@ class RooProdPdf : public RooAbsPdf { ClassDefOverride(RooProdPdf,6) // PDF representing a product of PDFs }; +namespace RooFit { +namespace Detail { + +/// A RooProdPdf with a fixed normalization set can be replaced by this class. +/// Its purpose is to provide the right client-server interface for the +/// evaluation of RooProdPdf cache elements that were created for a given +/// normalization set. +class RooFixedProdPdf : public RooAbsPdf { +public: + RooFixedProdPdf(std::unique_ptr &&prodPdf, RooArgSet const &normSet); + RooFixedProdPdf(const RooFixedProdPdf &other, const char *name = nullptr); + + inline TObject *clone(const char *newname) const override { return new RooFixedProdPdf(*this, newname); } + + inline bool selfNormalized() const override { return true; } + + inline bool canComputeBatchWithCuda() const override { return true; } + + inline void doEval(RooFit::EvalContext &ctx) const override { _prodPdf->doEvalImpl(this, *_cache, ctx); } + + inline ExtendMode extendMode() const override { return _prodPdf->extendMode(); } + inline double expectedEvents(const RooArgSet * /*nset*/) const override + { + return _prodPdf->expectedEvents(&_normSet); + } + inline std::unique_ptr createExpectedEventsFunc(const RooArgSet * /*nset*/) const override + { + return _prodPdf->createExpectedEventsFunc(&_normSet); + } + + // Analytical Integration handling + inline bool forceAnalyticalInt(const RooAbsArg &dep) const override { return _prodPdf->forceAnalyticalInt(dep); } + inline Int_t getAnalyticalIntegralWN(RooArgSet &allVars, RooArgSet &analVars, const RooArgSet *normSet, + const char *rangeName = nullptr) const override + { + return _prodPdf->getAnalyticalIntegralWN(allVars, analVars, normSet, rangeName); + } + inline Int_t + getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &numVars, const char *rangeName = nullptr) const override + { + return _prodPdf->getAnalyticalIntegral(allVars, numVars, rangeName); + } + inline double analyticalIntegralWN(Int_t code, const RooArgSet *normSet, const char *rangeName) const override + { + return _prodPdf->analyticalIntegralWN(code, normSet, rangeName); + } + inline double analyticalIntegral(Int_t code, const char *rangeName = nullptr) const override + { + return _prodPdf->analyticalIntegral(code, rangeName); + } + + RooProdPdf::CacheElem const &cache() const { return *_cache; } + +private: + void initialize(); + + inline double evaluate() const override { return _prodPdf->calculate(*_cache); } + + RooArgSet _normSet; + std::unique_ptr _cache; + RooSetProxy _servers; + std::unique_ptr _prodPdf; + + ClassDefOverride(RooFit::Detail::RooFixedProdPdf, 0); +}; + +} // namespace Detail +} // namespace RooFit #endif diff --git a/roofit/roofitcore/inc/RooProduct.h b/roofit/roofitcore/inc/RooProduct.h index 6b51c33d6f425..8f5e9700d90ec 100644 --- a/roofit/roofitcore/inc/RooProduct.h +++ b/roofit/roofitcore/inc/RooProduct.h @@ -63,7 +63,6 @@ class RooProduct : public RooAbsReal { CacheMode canNodeBeCached() const override { return RooAbsArg::NotAdvised ; } ; void setCacheAndTrackHints(RooArgSet&) override ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; protected: void ioStreamerPass2() override ; diff --git a/roofit/roofitcore/inc/RooRatio.h b/roofit/roofitcore/inc/RooRatio.h index ffd1123fbb0eb..fd8bf7bdc124d 100644 --- a/roofit/roofitcore/inc/RooRatio.h +++ b/roofit/roofitcore/inc/RooRatio.h @@ -31,13 +31,14 @@ class RooRatio : public RooAbsReal { TObject *clone(const char *newname) const override { return new RooRatio(*this, newname); } ~RooRatio() override; + RooAbsReal const &numerator() const { return *_numerator; } + RooAbsReal const &denominator() const { return *_denominator; } + protected: double evaluate() const override; void doEval(RooFit::EvalContext &) const override; inline bool canComputeBatchWithCuda() const override { return true; } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - RooRealProxy _numerator; RooRealProxy _denominator; diff --git a/roofit/roofitcore/inc/RooRealIntegral.h b/roofit/roofitcore/inc/RooRealIntegral.h index 8660f1d38a512..d28d2047cf64a 100644 --- a/roofit/roofitcore/inc/RooRealIntegral.h +++ b/roofit/roofitcore/inc/RooRealIntegral.h @@ -78,10 +78,10 @@ class RooRealIntegral : public RooAbsReal { std::unique_ptr compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext & ctx) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - inline RooArgSet const* funcNormSet() const { return _funcNormSet.get(); } + int mode() const { return _mode; } + protected: mutable bool _valid = false; diff --git a/roofit/roofitcore/inc/RooRealSumFunc.h b/roofit/roofitcore/inc/RooRealSumFunc.h index 29a765b45191f..9f8cef5afd50e 100644 --- a/roofit/roofitcore/inc/RooRealSumFunc.h +++ b/roofit/roofitcore/inc/RooRealSumFunc.h @@ -60,8 +60,6 @@ class RooRealSumFunc : public RooAbsReal { std::unique_ptr compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext & ctx) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: mutable RooObjCacheManager _normIntMgr; //! The integration cache manager diff --git a/roofit/roofitcore/inc/RooRealSumPdf.h b/roofit/roofitcore/inc/RooRealSumPdf.h index 0f231848a2d1a..4c913c47e4588 100644 --- a/roofit/roofitcore/inc/RooRealSumPdf.h +++ b/roofit/roofitcore/inc/RooRealSumPdf.h @@ -71,8 +71,6 @@ class RooRealSumPdf : public RooAbsPdf { std::unique_ptr createExpectedEventsFunc(const RooArgSet* nset) const override; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: class CacheElem : public RooAbsCacheElement { @@ -109,9 +107,6 @@ class RooRealSumPdf : public RooAbsPdf { bool doFloor, bool & hasWarnedBefore); - static std::string translateImpl(RooFit::Detail::CodeSquashContext &ctx, RooAbsArg const *klass, - RooArgList const &funcList, RooArgList const &coefList, bool normalize=false); - static bool checkObservables(RooAbsReal const &caller, RooArgSet const *nset, RooArgList const &funcList, RooArgList const &coefList); diff --git a/roofit/roofitcore/inc/RooRealVar.h b/roofit/roofitcore/inc/RooRealVar.h index 0cd3199d630d7..73cea994065fc 100644 --- a/roofit/roofitcore/inc/RooRealVar.h +++ b/roofit/roofitcore/inc/RooRealVar.h @@ -132,8 +132,6 @@ class RooRealVar : public RooAbsRealLValue { static void cleanup() ; - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; - protected: static bool _printScientific ; diff --git a/roofit/roofitcore/inc/RooRecursiveFraction.h b/roofit/roofitcore/inc/RooRecursiveFraction.h index 3b2849c479543..c4f4592e2b88a 100644 --- a/roofit/roofitcore/inc/RooRecursiveFraction.h +++ b/roofit/roofitcore/inc/RooRecursiveFraction.h @@ -19,9 +19,6 @@ #include "RooAbsReal.h" #include "RooListProxy.h" -class RooRealVar; -class RooArgList ; - class RooRecursiveFraction : public RooAbsReal { public: @@ -31,7 +28,7 @@ class RooRecursiveFraction : public RooAbsReal { RooRecursiveFraction(const RooRecursiveFraction& other, const char *name = nullptr); TObject* clone(const char* newname) const override { return new RooRecursiveFraction(*this, newname); } - void translate(RooFit::Detail::CodeSquashContext &ctx) const override; + RooArgList const &variables() const { return _list; } protected: diff --git a/roofit/roofitcore/src/BatchModeDataHelpers.cxx b/roofit/roofitcore/src/BatchModeDataHelpers.cxx index 274d084b049f4..180ca3cc68f81 100644 --- a/roofit/roofitcore/src/BatchModeDataHelpers.cxx +++ b/roofit/roofitcore/src/BatchModeDataHelpers.cxx @@ -19,7 +19,7 @@ #include #include "RooFitImplHelpers.h" -#include "RooNLLVarNew.h" +#include "RooFit/Detail/RooNLLVarNew.h" #include @@ -93,8 +93,8 @@ getSingleDataSpans(RooAbsData const &data, std::string_view rangeName, std::stri assignSpan(weight, {buffer.data(), nNonZeroWeight}); assignSpan(weightSumW2, {bufferSumW2.data(), nNonZeroWeight}); } - insert(RooNLLVarNew::weightVarName, weight); - insert(RooNLLVarNew::weightVarNameSumW2, weightSumW2); + insert(RooFit::Detail::RooNLLVarNew::weightVarName, weight); + insert(RooFit::Detail::RooNLLVarNew::weightVarNameSumW2, weightSumW2); } // Get the real-valued batches and cast the also to double branches to put in diff --git a/roofit/roofitcore/src/FitHelpers.cxx b/roofit/roofitcore/src/FitHelpers.cxx index 97bfeefe975bf..8e5b45ac0c86f 100644 --- a/roofit/roofitcore/src/FitHelpers.cxx +++ b/roofit/roofitcore/src/FitHelpers.cxx @@ -42,7 +42,7 @@ #include "ConstraintHelpers.h" #include "RooEvaluatorWrapper.h" #include "RooFitImplHelpers.h" -#include "RooNLLVarNew.h" +#include "RooFit/Detail/RooNLLVarNew.h" #ifdef ROOFIT_LEGACY_EVAL_BACKEND #include "RooChi2Var.h" @@ -50,6 +50,8 @@ #include "RooXYChi2Var.h" #endif +using RooFit::Detail::RooNLLVarNew; + namespace { constexpr int extendedFitDefault = 2; diff --git a/roofit/roofitcore/src/RooAbsAnaConvPdf.cxx b/roofit/roofitcore/src/RooAbsAnaConvPdf.cxx index 3d1b3fd16d209..fce6647ff30e1 100644 --- a/roofit/roofitcore/src/RooAbsAnaConvPdf.cxx +++ b/roofit/roofitcore/src/RooAbsAnaConvPdf.cxx @@ -62,7 +62,7 @@ #include "RooAbsAnaConvPdf.h" -#include "RooNormalizedPdf.h" +#include "RooFit/Detail/RooNormalizedPdf.h" #include "RooMsgService.h" #include "Riostream.h" #include "RooResolutionModel.h" @@ -677,7 +677,7 @@ RooAbsAnaConvPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::Co std::unique_ptr pdfClone(static_cast(_convSet[0].Clone())); ctx.compileServers(*pdfClone, normSet); - auto newArg = std::make_unique(*pdfClone, normSet); + auto newArg = std::make_unique(*pdfClone, normSet); // The direct servers are this pdf and the normalization integral, which // don't need to be compiled further. @@ -717,7 +717,7 @@ RooAbsAnaConvPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::Co ctx.compileServers(*pdfClone, normSet); // Finally, this RooAbsAnaConvPdf needs to be normalized - auto newArg = std::make_unique(*pdfClone, normSet); + auto newArg = std::make_unique(*pdfClone, normSet); // The direct servers are this pdf and the normalization integral, which // don't need to be compiled further. diff --git a/roofit/roofitcore/src/RooAbsArg.cxx b/roofit/roofitcore/src/RooAbsArg.cxx index 54f91f5dffa3c..e3ba07d9b8c1b 100644 --- a/roofit/roofitcore/src/RooAbsArg.cxx +++ b/roofit/roofitcore/src/RooAbsArg.cxx @@ -2515,23 +2515,6 @@ std::unique_ptr RooAbsArg::compileForNormSet(RooArgSet const & normSe } -//////////////////////////////////////////////////////////////////////////////// -/// This function defines a translation for each RooAbsReal based object that can be used -/// to express the class as simple C++ code. The function adds the code represented by -/// each class as an std::string (that is later concatenated with code strings from translate calls) -/// to form the C++ code that AD tools can understand. Any class that wants to support AD, has to -/// implement this function. -/// -/// \param[in] ctx An object to manage auxiliary information for code-squashing. Also takes the -/// code string that this class outputs into the squashed code through the 'addToCodeBody' function. -void RooAbsArg::translate(RooFit::Detail::CodeSquashContext & /*ctx*/) const -{ - std::stringstream errorMsg; - errorMsg << "Translate function for class \"" << ClassName() << "\" has not yet been implemented."; - coutE(Minimization) << errorMsg.str() << std::endl; - throw std::runtime_error(errorMsg.str().c_str()); -} - /// Sets the token for retrieving results in the BatchMode. For internal use only. void RooAbsArg::setDataToken(std::size_t index) { diff --git a/roofit/roofitcore/src/RooAbsCachedPdf.cxx b/roofit/roofitcore/src/RooAbsCachedPdf.cxx index 66e40bd792f4a..f462298fc72f8 100644 --- a/roofit/roofitcore/src/RooAbsCachedPdf.cxx +++ b/roofit/roofitcore/src/RooAbsCachedPdf.cxx @@ -33,7 +33,7 @@ for changes to trigger a refilling of the cache histogram. #include "RooDataHist.h" #include "RooHistPdf.h" #include "RooExpensiveObjectCache.h" -#include "RooNormalizedPdf.h" +#include "RooFit/Detail/RooNormalizedPdf.h" ClassImp(RooAbsCachedPdf); @@ -415,7 +415,7 @@ RooAbsCachedPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::Com std::unique_ptr pdfClone(static_cast(this->Clone())); ctx.compileServers(*pdfClone, {}); - auto newArg = std::make_unique(*pdfClone, normSet); + auto newArg = std::make_unique(*pdfClone, normSet); // The direct servers are this pdf and the normalization integral, which // don't need to be compiled further. diff --git a/roofit/roofitcore/src/RooAbsPdf.cxx b/roofit/roofitcore/src/RooAbsPdf.cxx index bd5b77c57fc30..46e4eadf1c423 100644 --- a/roofit/roofitcore/src/RooAbsPdf.cxx +++ b/roofit/roofitcore/src/RooAbsPdf.cxx @@ -138,7 +138,7 @@ called for each data event. #include "RooAbsPdf.h" #include "FitHelpers.h" -#include "RooNormalizedPdf.h" +#include "RooFit/Detail/RooNormalizedPdf.h" #include "RooMsgService.h" #include "RooArgSet.h" #include "RooArgProxy.h" @@ -2793,7 +2793,7 @@ RooAbsPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileCo std::unique_ptr pdfClone(static_cast(this->Clone())); ctx.compileServers(*pdfClone, normSet); - auto newArg = std::make_unique(*pdfClone, normSet); + auto newArg = std::make_unique(*pdfClone, normSet); // The direct servers are this pdf and the normalization integral, which // don't need to be compiled further. diff --git a/roofit/roofitcore/src/RooAbsReal.cxx b/roofit/roofitcore/src/RooAbsReal.cxx index 8d69ef584d31b..f1bb7c1db4a7d 100644 --- a/roofit/roofitcore/src/RooAbsReal.cxx +++ b/roofit/roofitcore/src/RooAbsReal.cxx @@ -4414,23 +4414,6 @@ void RooAbsReal::doEval(RooFit::EvalContext & ctx) const } } -//////////////////////////////////////////////////////////////////////////////// -/// This function defines the analytical integral translation for the class. -/// -/// \param[in] code The code that decides the integrands. -/// \param[in] rangeName Name of the normalization range. -/// \param[in] ctx An object to manage auxiliary information for code-squashing. -/// -/// \returns The representative code string of the integral for the given object. -std::string RooAbsReal::buildCallToAnalyticIntegral(Int_t /* code */, const char * /* rangeName */, - RooFit::Detail::CodeSquashContext & /*ctx*/) const -{ - std::stringstream errorMsg; - errorMsg << "An analytical integral function for class \"" << ClassName() << "\" has not yet been implemented."; - coutE(Minimization) << errorMsg.str() << std::endl; - throw std::runtime_error(errorMsg.str().c_str()); -} - double RooAbsReal::_DEBUG_getVal(const RooArgSet* normalisationSet) const { const bool tmpFast = _fast; diff --git a/roofit/roofitcore/src/RooAddPdf.cxx b/roofit/roofitcore/src/RooAddPdf.cxx index e10e8525f413d..9773b8185e3c8 100644 --- a/roofit/roofitcore/src/RooAddPdf.cxx +++ b/roofit/roofitcore/src/RooAddPdf.cxx @@ -545,11 +545,6 @@ double RooAddPdf::getValV(const RooArgSet* normSet) const return _value; } -void RooAddPdf::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, RooRealSumPdf::translateImpl(ctx, this, _pdfList, _coefList, true)); -} - //////////////////////////////////////////////////////////////////////////////// /// Compute addition of PDFs in batches. void RooAddPdf::doEval(RooFit::EvalContext & ctx) const diff --git a/roofit/roofitcore/src/RooAddition.cxx b/roofit/roofitcore/src/RooAddition.cxx index 263140d3373f0..0f6e61d2de176 100644 --- a/roofit/roofitcore/src/RooAddition.cxx +++ b/roofit/roofitcore/src/RooAddition.cxx @@ -33,10 +33,9 @@ in the two sets. #include "RooErrorHandler.h" #include "RooArgSet.h" #include "RooNameReg.h" -#include "RooNLLVarNew.h" +#include "RooFit/Detail/RooNLLVarNew.h" #include "RooMsgService.h" #include "RooBatchCompute.h" -#include "RooFuncWrapper.h" #ifdef ROOFIT_LEGACY_EVAL_BACKEND #include "RooNLLVar.h" @@ -155,37 +154,6 @@ void RooAddition::doEval(RooFit::EvalContext &ctx) const RooBatchCompute::compute(ctx.config(this), RooBatchCompute::AddPdf, ctx.output(), pdfs, coefs); } -//////////////////////////////////////////////////////////////////////////////// - -void RooAddition::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - if (_set.empty()) { - ctx.addResult(this, "0.0"); - } - std::string result; - if (_set.size() > 1) - result += "("; - - std::size_t i = 0; - for (auto *component : static_range_cast(_set)) { - - if (!dynamic_cast(component) || _set.size() == 1) { - result += ctx.getResult(*component); - ++i; - if (i < _set.size()) result += '+'; - continue; - } - auto &wrp = *ctx._wrapper; - auto funcName = wrp.declareFunction(wrp.buildCode(*component)); - result += funcName + "(params, obs, xlArr)"; - ++i; - if (i < _set.size()) result += '+'; - } - if (_set.size() > 1) - result += ')'; - ctx.addResult(this, result); -} - //////////////////////////////////////////////////////////////////////////////// /// Return the default error level for MINUIT error analysis /// If the addition contains one or more RooNLLVars and @@ -202,7 +170,7 @@ double RooAddition::defaultErrorLevel() const std::unique_ptr comps{getComponents()}; for(RooAbsArg * arg : *comps) { - if (dynamic_cast(arg)) { + if (dynamic_cast(arg)) { nllArg = static_cast(arg) ; } #ifdef ROOFIT_LEGACY_EVAL_BACKEND diff --git a/roofit/roofitcore/src/RooClassFactory.cxx b/roofit/roofitcore/src/RooClassFactory.cxx index 6dbd104305da1..a6fd99cea777e 100644 --- a/roofit/roofitcore/src/RooClassFactory.cxx +++ b/roofit/roofitcore/src/RooClassFactory.cxx @@ -506,8 +506,7 @@ class CLASS_NAME : public BASE_NAME { CLASS_NAME(const char *name, const char *title,)"; // Insert list of input arguments - unsigned int i ; - for (i=0 ; i::digits10 + 1}; - std::stringstream ss; - ss.precision(max_precision); - // Just use toString to make sure we do not output 'inf'. - // This is really ugly for large numbers... - ss << std::fixed << RooNumber::toString(_value); - ctx.addResult(this, ss.str()); -} diff --git a/roofit/roofitcore/src/RooConstraintSum.cxx b/roofit/roofitcore/src/RooConstraintSum.cxx index 18348e69385d0..3401c694d3115 100644 --- a/roofit/roofitcore/src/RooConstraintSum.cxx +++ b/roofit/roofitcore/src/RooConstraintSum.cxx @@ -79,11 +79,6 @@ double RooConstraintSum::evaluate() const return sum; } -void RooConstraintSum::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::constraintSum", _set1, _set1.size())); -} - void RooConstraintSum::doEval(RooFit::EvalContext &ctx) const { double sum(0); diff --git a/roofit/roofitcore/src/RooDataHist.cxx b/roofit/roofitcore/src/RooDataHist.cxx index cb31a6f73ca6a..b216c2d0c270c 100644 --- a/roofit/roofitcore/src/RooDataHist.cxx +++ b/roofit/roofitcore/src/RooDataHist.cxx @@ -992,7 +992,7 @@ Int_t RooDataHist::getIndex(const RooAbsCollection& coord, bool fast) const { return calcTreeIndex(coord, fast); } -std::string RooDataHist::declWeightArrayForCodeSquash(RooFit::Detail::CodeSquashContext &ctx, +std::string RooDataHist::declWeightArrayForCodeSquash(RooFit::CodegenContext &ctx, bool correctForBinSize) const { std::vector vals(_arrSize); @@ -1008,7 +1008,7 @@ std::string RooDataHist::declWeightArrayForCodeSquash(RooFit::Detail::CodeSquash return ctx.buildArg(vals); } -std::string RooDataHist::calculateTreeIndexForCodeSquash(RooAbsArg const * /*klass*/, RooFit::Detail::CodeSquashContext &ctx, +std::string RooDataHist::calculateTreeIndexForCodeSquash(RooAbsArg const * /*klass*/, RooFit::CodegenContext &ctx, const RooAbsCollection &coords, bool reverse) const { assert(coords.size() == _vars.size()); diff --git a/roofit/roofitcore/src/RooEffProd.cxx b/roofit/roofitcore/src/RooEffProd.cxx index e4e1f27a39f75..44b6acd916289 100644 --- a/roofit/roofitcore/src/RooEffProd.cxx +++ b/roofit/roofitcore/src/RooEffProd.cxx @@ -71,8 +71,3 @@ RooAbsGenContext* RooEffProd::genContext(const RooArgSet &vars, const RooDataSet static_cast(_eff.arg()), vars,prototype,auxProto,verbose) ; } - -void RooEffProd::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::effProd", _eff, _pdf)); -} diff --git a/roofit/roofitcore/src/RooEfficiency.cxx b/roofit/roofitcore/src/RooEfficiency.cxx index a5078ec7bef8f..05f2e9f5e598c 100644 --- a/roofit/roofitcore/src/RooEfficiency.cxx +++ b/roofit/roofitcore/src/RooEfficiency.cxx @@ -86,15 +86,3 @@ double RooEfficiency::analyticalIntegral(int /*code*/, const char * /*rangeName* { return 1.0; } - -void RooEfficiency::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - int sigCatIndex = _cat->lookupIndex(_sigCatName.Data()); - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::efficiency", _effFunc, _cat, sigCatIndex)); -} - -std::string RooEfficiency::buildCallToAnalyticIntegral(int /*code*/, const char * /*rangeName*/, - RooFit::Detail::CodeSquashContext &) const -{ - return "1.0"; -} diff --git a/roofit/roofitcore/src/RooExtendPdf.cxx b/roofit/roofitcore/src/RooExtendPdf.cxx index 556d5c53910aa..55f347675592e 100644 --- a/roofit/roofitcore/src/RooExtendPdf.cxx +++ b/roofit/roofitcore/src/RooExtendPdf.cxx @@ -172,10 +172,3 @@ std::unique_ptr RooExtendPdf::createExpectedEventsFunc(const RooArgS } return out; } - - -void RooExtendPdf::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - // Use the result of the underlying pdf. - ctx.addResult(this, ctx.getResult(_pdf)); -} diff --git a/roofit/roofitcore/src/RooFit/Detail/CodeSquashContext.cxx b/roofit/roofitcore/src/RooFit/CodegenContext.cxx similarity index 69% rename from roofit/roofitcore/src/RooFit/Detail/CodeSquashContext.cxx rename to roofit/roofitcore/src/RooFit/CodegenContext.cxx index 8350c5c639d9e..44238a5b0ff2e 100644 --- a/roofit/roofitcore/src/RooFit/Detail/CodeSquashContext.cxx +++ b/roofit/roofitcore/src/RooFit/CodegenContext.cxx @@ -11,36 +11,39 @@ * listed in LICENSE (http://roofit.sourceforge.net/license.txt) */ -#include - -#include "RooFuncWrapper.h" +#include #include "RooFitImplHelpers.h" +#include + #include #include +#include +#include -namespace RooFit { +namespace { -namespace Detail { - -CodeSquashContext::CodeSquashContext(std::map const &outputSizes, - std::vector &xlarr, Experimental::RooFuncWrapper &wrapper) - : _wrapper{&wrapper}, _nodeOutputSizes(outputSizes), _xlArr(xlarr) +bool startsWith(std::string_view str, std::string_view prefix) { + return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix); } +} // namespace + +namespace RooFit { + /// @brief Adds (or overwrites) the string representing the result of a node. /// @param key The name of the node to add the result for. /// @param value The new name to assign/overwrite. -void CodeSquashContext::addResult(const char *key, std::string const &value) +void CodegenContext::addResult(const char *key, std::string const &value) { const TNamed *namePtr = RooNameReg::known(key); if (namePtr) addResult(namePtr, value); } -void CodeSquashContext::addResult(TNamed const *key, std::string const &value) +void CodegenContext::addResult(TNamed const *key, std::string const &value) { _nodeNames[key] = value; } @@ -50,7 +53,7 @@ void CodeSquashContext::addResult(TNamed const *key, std::string const &value) /// existing code body. /// @param key The node to get the result string for. /// @return String representing the result of this node. -std::string const &CodeSquashContext::getResult(RooAbsArg const &arg) +std::string const &CodegenContext::getResult(RooAbsArg const &arg) { // If the result has already been recorded, just return the result. // It is usually the responsibility of each translate function to assign @@ -70,7 +73,7 @@ std::string const &CodeSquashContext::getResult(RooAbsArg const &arg) } // Now, recursively call translate into the current argument to load the correct result. - arg.translate(*this); + RooFit::codegen(const_cast(arg), *this); return _nodeNames.at(arg.namePtr()); } @@ -78,25 +81,17 @@ std::string const &CodeSquashContext::getResult(RooAbsArg const &arg) /// @brief Adds the given string to the string block that will be emitted at the top of the squashed function. Useful /// for variable declarations. /// @param str The string to add to the global scope. -void CodeSquashContext::addToGlobalScope(std::string const &str) +void CodegenContext::addToGlobalScope(std::string const &str) { _globalScope += str; } -/// @brief Assemble and return the final code with the return expression and global statements. -/// @param returnExpr The string representation of what the squashed function should return, usually the head node. -/// @return The final body of the function. -std::string CodeSquashContext::assembleCode(std::string const &returnExpr) -{ - return _globalScope + _code + "\n return " + returnExpr + ";\n"; -} - /// @brief Since the squashed code represents all observables as a single flattened array, it is important /// to keep track of the start index for a vector valued observable which can later be expanded to access the correct /// element. For example, a vector valued variable x with 10 entries will be squashed to obs[start_idx + i]. /// @param key The name of the node representing the vector valued observable. /// @param idx The start index (or relative position of the observable in the set of all observables). -void CodeSquashContext::addVecObs(const char *key, int idx) +void CodegenContext::addVecObs(const char *key, int idx) { const TNamed *namePtr = RooNameReg::known(key); if (namePtr) @@ -108,7 +103,7 @@ void CodeSquashContext::addVecObs(const char *key, int idx) /// loops, automatically determines if code needs to be stored inside or outside loop scope. /// @param klass The class requesting this addition, usually 'this'. /// @param in String to add to the squashed code. -void CodeSquashContext::addToCodeBody(RooAbsArg const *klass, std::string const &in) +void CodegenContext::addToCodeBody(RooAbsArg const *klass, std::string const &in) { // If we are in a loop and the value is scope independent, save it at the top of the loop. // else, just save it in the current scope. @@ -120,7 +115,7 @@ void CodeSquashContext::addToCodeBody(RooAbsArg const *klass, std::string const /// a value/collection of values is scope independent. /// @param in String to add to the squashed code. /// @param isScopeIndep The value determining if the input is scope dependent. -void CodeSquashContext::addToCodeBody(std::string const &in, bool isScopeIndep /* = false */) +void CodegenContext::addToCodeBody(std::string const &in, bool isScopeIndep /* = false */) { // If we are in a loop and the value is scope independent, save it at the top of the loop. // else, just save it in the current scope. @@ -134,7 +129,7 @@ void CodeSquashContext::addToCodeBody(std::string const &in, bool isScopeIndep / /// @brief Create a RAII scope for iterating over vector observables. You can't use the result of vector observables /// outside these loop scopes. /// @param in A pointer to the calling class, used to determine the loop dependent variables. -std::unique_ptr CodeSquashContext::beginLoop(RooAbsArg const *in) +std::unique_ptr CodegenContext::beginLoop(RooAbsArg const *in) { std::string idx = "loopIdx" + std::to_string(_loopLevel); @@ -170,7 +165,7 @@ std::unique_ptr CodeSquashContext::beginLoop(RooAb return std::make_unique(*this, std::move(vars)); } -void CodeSquashContext::endLoop(LoopScope const &scope) +void CodegenContext::endLoop(LoopScope const &scope) { _code += "}\n"; @@ -188,7 +183,7 @@ void CodeSquashContext::endLoop(LoopScope const &scope) } /// @brief Get a unique variable name to be used in the generated code. -std::string CodeSquashContext::getTmpVarName() const +std::string CodegenContext::getTmpVarName() const { return "t" + std::to_string(_tmpVarIdx++); } @@ -196,7 +191,7 @@ std::string CodeSquashContext::getTmpVarName() const /// @brief A function to save an expression that includes/depends on the result of the input node. /// @param in The node on which the valueToSave depends on/belongs to. /// @param valueToSave The actual string value to save as a temporary. -void CodeSquashContext::addResult(RooAbsArg const *in, std::string const &valueToSave) +void CodegenContext::addResult(RooAbsArg const *in, std::string const &valueToSave) { // std::string savedName = RooFit::Detail::makeValidVarName(in->GetName()); std::string savedName = getTmpVarName(); @@ -221,14 +216,14 @@ void CodeSquashContext::addResult(RooAbsArg const *in, std::string const &valueT /// @brief Function to save a RooListProxy as an array in the squashed code. /// @param in The list to convert to array. /// @return Name of the array that stores the input list in the squashed code. -std::string CodeSquashContext::buildArg(RooAbsCollection const &in) +std::string CodegenContext::buildArg(RooAbsCollection const &in) { if (in.empty()) { return "nullptr"; } - auto it = listNames.find(in.uniqueId().value()); - if (it != listNames.end()) + auto it = _listNames.find(in.uniqueId().value()); + if (it != _listNames.end()) return it->second; std::string savedName = getTmpVarName(); @@ -245,11 +240,11 @@ std::string CodeSquashContext::buildArg(RooAbsCollection const &in) addToCodeBody(declStrm.str(), canSaveOutside); - listNames.insert({in.uniqueId().value(), savedName}); + _listNames.insert({in.uniqueId().value(), savedName}); return savedName; } -std::string CodeSquashContext::buildArg(std::span arr) +std::string CodegenContext::buildArg(std::span arr) { unsigned int n = arr.size(); std::string offset = std::to_string(_xlArr.size()); @@ -260,17 +255,84 @@ std::string CodeSquashContext::buildArg(std::span arr) return "xlArr + " + offset; } -bool CodeSquashContext::isScopeIndependent(RooAbsArg const *in) const +bool CodegenContext::isScopeIndependent(RooAbsArg const *in) const { return !in->isReducerNode() && outputSize(in->namePtr()) == 1; } /// @brief Register a function that is only know to the interpreter to the context. /// This is useful to dump the standalone C++ code for the computation graph. -void CodeSquashContext::collectFunction(std::string const &name) +void CodegenContext::collectFunction(std::string const &name) +{ + _collectedFunctions.emplace_back(name); +} + +/// @brief Assemble and return the final code with the return expression and global statements. +/// @param returnExpr The string representation of what the squashed function should return, usually the head node. +/// @return The name of the declared function. +std::string +CodegenContext::buildFunction(RooAbsArg const &arg, std::map const &outputSizes) { - _wrapper->collectFunction(name); + CodegenContext ctx; + ctx._nodeOutputSizes = outputSizes; + ctx._vecObsIndices = _vecObsIndices; + // We only want to take over parameters and observables + for (auto const &item : _nodeNames) { + if (startsWith(item.second, "params[") || startsWith(item.second, "obs[")) { + ctx._nodeNames.insert(item); + } + } + ctx._xlArr = _xlArr; + ctx._collectedFunctions = _collectedFunctions; + + static int iCodegen = 0; + auto funcName = "roo_codegen_" + std::to_string(iCodegen++); + + std::string funcBody = ctx.getResult(arg); + funcBody = ctx._globalScope + ctx._code + "\n return " + funcBody + ";\n"; + + // Declare the function + std::stringstream bodyWithSigStrm; + bodyWithSigStrm << "double " << funcName << "(double* params, double const* obs, double const* xlArr) {\n" + << funcBody << "\n}"; + ctx._collectedFunctions.emplace_back(funcName); + if (!gInterpreter->Declare(bodyWithSigStrm.str().c_str())) { + std::stringstream errorMsg; + errorMsg << "Function " << funcName << " could not be compiled. See above for details."; + oocoutE(nullptr, InputArguments) << errorMsg.str() << std::endl; + throw std::runtime_error(errorMsg.str().c_str()); + } + + _xlArr = ctx._xlArr; + _collectedFunctions = ctx._collectedFunctions; + + return funcName; +} + +void codegen(RooAbsArg &arg, RooFit::CodegenContext &ctx) +{ + using Func = void (*)(RooAbsArg &, RooFit::CodegenContext &); + + Func func; + + TClass *tclass = arg.IsA(); + + // Cache the overload resolutions + static std::unordered_map dispatchMap; + + auto found = dispatchMap.find(tclass); + + if (found != dispatchMap.end()) { + func = found->second; + } else { + // Can probably done with CppInterop in the future to avoid string manipulation. + std::stringstream cmd; + cmd << "&RooFit::CodegenImplCaller<" << tclass->GetName() << ">::call;"; + func = reinterpret_cast(gInterpreter->ProcessLine(cmd.str().c_str())); + dispatchMap[tclass] = func; + } + + return func(arg, ctx); } -} // namespace Detail } // namespace RooFit diff --git a/roofit/roofitcore/src/RooFormulaVar.cxx b/roofit/roofitcore/src/RooFormulaVar.cxx index 5d374d396613d..0a4d98d372d09 100644 --- a/roofit/roofitcore/src/RooFormulaVar.cxx +++ b/roofit/roofitcore/src/RooFormulaVar.cxx @@ -313,10 +313,7 @@ double RooFormulaVar::defaultErrorLevel() const return 1.0 ; } -void RooFormulaVar::translate(RooFit::Detail::CodeSquashContext &ctx) const +std::string RooFormulaVar::getUniqueFuncName() const { - getVal(); // to trigger the creation of the TFormula - std::string funcName = _formula->getTFormula()->GetUniqueFuncName().Data(); - ctx.collectFunction(funcName); - ctx.addResult(this, ctx.buildCall(funcName, _actualVars)); + return getFormula().getTFormula()->GetUniqueFuncName().Data(); } diff --git a/roofit/roofitcore/src/RooFuncWrapper.cxx b/roofit/roofitcore/src/RooFuncWrapper.cxx index c524801d0b2de..12f9163435739 100644 --- a/roofit/roofitcore/src/RooFuncWrapper.cxx +++ b/roofit/roofitcore/src/RooFuncWrapper.cxx @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -57,22 +57,50 @@ RooFuncWrapper::RooFuncWrapper(const char *name, const char *title, RooAbsReal & _absReal = std::make_unique(obj, const_cast(data), false, "", simPdf, false); } - std::string func; - // Get the parameters. RooArgSet paramSet; obj.getParameters(data ? data->get() : nullptr, paramSet); // Load the parameters and observables. - loadParamsAndData(&obj, paramSet, data, simPdf); + auto spans = loadParamsAndData(paramSet, data, simPdf); + + // Set up the code generation context + std::map nodeOutputSizes = + RooFit::BatchModeDataHelpers::determineOutputSizes(obj, [&spans](RooFit::Detail::DataKey key) -> int { + auto found = spans.find(key); + return found != spans.end() ? found->second.size() : -1; + }); + + RooFit::CodegenContext ctx; + + // First update the result variable of params in the compute graph to in[]. + int idx = 0; + for (RooAbsArg *param : _params) { + ctx.addResult(param, "params[" + std::to_string(idx) + "]"); + idx++; + } - func = buildCode(obj); + for (auto const &item : _obsInfos) { + const char *obsName = item.first->GetName(); + // If the observable is scalar, set name to the start idx. else, store + // the start idx and later set the the name to obs[start_idx + curr_idx], + // here curr_idx is defined by a loop producing parent node. + if (item.second.size == 1) { + ctx.addResult(obsName, "obs[" + std::to_string(item.second.idx) + "]"); + } else { + ctx.addResult(obsName, "obs"); + ctx.addVecObs(obsName, item.second.idx); + } + } gInterpreter->Declare("#pragma cling optimize(2)"); // Declare the function and create its derivative. - _funcName = declareFunction(func); + _funcName = ctx.buildFunction(obj, nodeOutputSizes); _func = reinterpret_cast(gInterpreter->ProcessLine((_funcName + ";").c_str())); + + _xlArr = ctx.xlArr(); + _collectedFunctions = ctx.collectedFunctions(); } RooFuncWrapper::RooFuncWrapper(const RooFuncWrapper &other, const char *name) @@ -87,8 +115,8 @@ RooFuncWrapper::RooFuncWrapper(const RooFuncWrapper &other, const char *name) { } -void RooFuncWrapper::loadParamsAndData(RooAbsArg const *head, RooArgSet const ¶mSet, const RooAbsData *data, - RooSimultaneous const *simPdf) +std::map> +RooFuncWrapper::loadParamsAndData(RooArgSet const ¶mSet, const RooAbsData *data, RooSimultaneous const *simPdf) { // Extract observables std::stack> vectorBuffers; // for data loading @@ -124,32 +152,7 @@ void RooFuncWrapper::loadParamsAndData(RooAbsArg const *head, RooArgSet const &p } _gradientVarBuffer.resize(_params.size()); - if (head) { - _nodeOutputSizes = RooFit::BatchModeDataHelpers::determineOutputSizes( - *head, [&spans](RooFit::Detail::DataKey key) -> int { - auto found = spans.find(key); - return found != spans.end() ? found->second.size() : -1; - }); - } -} - -std::string RooFuncWrapper::declareFunction(std::string const &funcBody) -{ - static int iFuncWrapper = 0; - auto funcName = "roo_func_wrapper_" + std::to_string(iFuncWrapper++); - - // Declare the function - std::stringstream bodyWithSigStrm; - bodyWithSigStrm << "double " << funcName << "(double* params, double const* obs, double const* xlArr) {\n" - << funcBody << "\n}"; - _collectedFunctions.emplace_back(funcName); - if (!gInterpreter->Declare(bodyWithSigStrm.str().c_str())) { - std::stringstream errorMsg; - errorMsg << "Function " << funcName << " could not be compiled. See above for details."; - oocoutE(nullptr, InputArguments) << errorMsg.str() << std::endl; - throw std::runtime_error(errorMsg.str().c_str()); - } - return funcName; + return spans; } void RooFuncWrapper::createGradient() @@ -209,33 +212,6 @@ void RooFuncWrapper::gradient(const double *x, double *g) const _grad(const_cast(x), _observables.data(), _xlArr.data(), g); } -std::string RooFuncWrapper::buildCode(RooAbsReal const &head) -{ - RooFit::Detail::CodeSquashContext ctx(_nodeOutputSizes, _xlArr, *this); - - // First update the result variable of params in the compute graph to in[]. - int idx = 0; - for (RooAbsArg *param : _params) { - ctx.addResult(param, "params[" + std::to_string(idx) + "]"); - idx++; - } - - for (auto const &item : _obsInfos) { - const char *name = item.first->GetName(); - // If the observable is scalar, set name to the start idx. else, store - // the start idx and later set the the name to obs[start_idx + curr_idx], - // here curr_idx is defined by a loop producing parent node. - if (item.second.size == 1) { - ctx.addResult(name, "obs[" + std::to_string(item.second.idx) + "]"); - } else { - ctx.addResult(name, "obs"); - ctx.addVecObs(name, item.second.idx); - } - } - - return ctx.assembleCode(ctx.getResult(head)); -} - /// @brief Dumps a macro "filename.C" that can be used to test and debug the generated code and gradient. void RooFuncWrapper::writeDebugMacro(std::string const &filename) const { diff --git a/roofit/roofitcore/src/RooGenericPdf.cxx b/roofit/roofitcore/src/RooGenericPdf.cxx index 83fc3da3b1a65..33679d015b945 100644 --- a/roofit/roofitcore/src/RooGenericPdf.cxx +++ b/roofit/roofitcore/src/RooGenericPdf.cxx @@ -202,10 +202,7 @@ void RooGenericPdf::writeToStream(ostream& os, bool compact) const } } -void RooGenericPdf::translate(RooFit::Detail::CodeSquashContext &ctx) const +std::string RooGenericPdf::getUniqueFuncName() const { - getVal(); // to trigger the creation of the TFormula - std::string funcName = _formula->getTFormula()->GetUniqueFuncName().Data(); - ctx.collectFunction(funcName); - ctx.addResult(this, ctx.buildCall(funcName, _actualVars)); + return formula().getTFormula()->GetUniqueFuncName().Data(); } diff --git a/roofit/roofitcore/src/RooHistFunc.cxx b/roofit/roofitcore/src/RooHistFunc.cxx index 67eaf780c9198..108d0f53dab96 100644 --- a/roofit/roofitcore/src/RooHistFunc.cxx +++ b/roofit/roofitcore/src/RooHistFunc.cxx @@ -190,11 +190,6 @@ double RooHistFunc::evaluate() const return ret ; } -void RooHistFunc::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - RooHistPdf::rooHistTranslateImpl(this, ctx, _intOrder, _dataHist, _depList, false, _cdfBoundaries); -} - void RooHistFunc::doEval(RooFit::EvalContext & ctx) const { std::span output = ctx.output(); @@ -322,12 +317,6 @@ bool RooHistFunc::forceAnalyticalInt(const RooAbsArg& dep) const return RooHistPdf::forceAnalyticalInt(_depList, dep); } -std::string RooHistFunc::buildCallToAnalyticIntegral(int code, const char * /* rangeName */, - RooFit::Detail::CodeSquashContext & /* ctx */) const -{ - return RooHistPdf::rooHistIntegralTranslateImpl(code, this, _dataHist, _depList, true); -} - //////////////////////////////////////////////////////////////////////////////// /// Return sampling hint for making curves of (projections) of this function /// as the recursive division strategy of RooCurve cannot deal efficiently diff --git a/roofit/roofitcore/src/RooHistPdf.cxx b/roofit/roofitcore/src/RooHistPdf.cxx index fc11d305a34d0..ee0c2a81b3d15 100644 --- a/roofit/roofitcore/src/RooHistPdf.cxx +++ b/roofit/roofitcore/src/RooHistPdf.cxx @@ -222,34 +222,6 @@ double RooHistPdf::evaluate() const return std::max(ret, 0.0); } -void RooHistPdf::rooHistTranslateImpl(RooAbsArg const *klass, RooFit::Detail::CodeSquashContext &ctx, int intOrder, - RooDataHist const *dataHist, const RooArgSet &obs, bool correctForBinSize, - bool cdfBoundaries) -{ - if (intOrder != 0 && !(!cdfBoundaries && !correctForBinSize && intOrder == 1 && obs.size() == 1)) { - ooccoutE(klass, InputArguments) << "RooHistPdf::weight(" << klass->GetName() - << ") ERROR: Code Squashing currently only supports non-interpolation cases." - << std::endl; - return; - } - - if (intOrder == 1) { - RooAbsBinning const &binning = *dataHist->getBinnings()[0]; - std::string weightArr = dataHist->declWeightArrayForCodeSquash(ctx, correctForBinSize); - ctx.addResult(klass, ctx.buildCall("RooFit::Detail::MathFuncs::interpolate1d", binning.lowBound(), - binning.highBound(), *obs[0], binning.numBins(), weightArr)); - return; - } - std::string const &offset = dataHist->calculateTreeIndexForCodeSquash(klass, ctx, obs); - std::string weightArr = dataHist->declWeightArrayForCodeSquash(ctx, correctForBinSize); - ctx.addResult(klass, "*(" + weightArr + " + " + offset + ")"); -} - -void RooHistPdf::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - rooHistTranslateImpl(this, ctx, _intOrder, _dataHist, _pdfObsList, !_unitNorm, _cdfBoundaries); -} - //////////////////////////////////////////////////////////////////////////////// /// Return the total volume spanned by the observables of the RooHistPdf @@ -395,24 +367,6 @@ double RooHistPdf::analyticalIntegral(Int_t code, return ret ; } -std::string RooHistPdf::rooHistIntegralTranslateImpl(int code, RooAbsArg const *klass, RooDataHist const *dataHist, - const RooArgSet &obs, bool histFuncMode) -{ - if (((2 << obs.size()) - 1) != code) { - oocoutE(klass, InputArguments) - << "RooHistPdf::integral(" << klass->GetName() - << ") ERROR: AD currently only supports integrating over all histogram observables." << std::endl; - return ""; - } - return std::to_string(dataHist->sum(histFuncMode)); -} - -std::string RooHistPdf::buildCallToAnalyticIntegral(int code, const char * /*rangeName */, - RooFit::Detail::CodeSquashContext & /* ctx */) const -{ - return rooHistIntegralTranslateImpl(code, this, _dataHist, _pdfObsList, false); -} - //////////////////////////////////////////////////////////////////////////////// /// Determine integration scenario. If no interpolation is used, /// RooHistPdf can perform all integrals over its dependents diff --git a/roofit/roofitcore/src/RooMultiVarGaussian.cxx b/roofit/roofitcore/src/RooMultiVarGaussian.cxx index 8c66cada01437..f760968ddfa03 100644 --- a/roofit/roofitcore/src/RooMultiVarGaussian.cxx +++ b/roofit/roofitcore/src/RooMultiVarGaussian.cxx @@ -187,12 +187,6 @@ double RooMultiVarGaussian::evaluate() const return exp(-0.5*alpha) ; } -void RooMultiVarGaussian::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - std::span covISpan{_covI.GetMatrixArray(), static_cast(_covI.GetNoElements())}; - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::multiVarGaussian", _x.size(), _x, _mu, covISpan)); -} - //////////////////////////////////////////////////////////////////////////////// Int_t RooMultiVarGaussian::getAnalyticalIntegral(RooArgSet& allVarsIn, RooArgSet& analVars, const char* rangeName) const @@ -314,20 +308,6 @@ double RooMultiVarGaussian::analyticalIntegral(Int_t code, const char* /*rangeNa } -std::string RooMultiVarGaussian::buildCallToAnalyticIntegral(Int_t code, const char *rangeName, - RooFit::Detail::CodeSquashContext & /*ctx*/) const -{ - if (code != -1) { - std::stringstream errorMsg; - errorMsg << "Partial integrals over RooMultiVarGaussian are not supported."; - coutE(Minimization) << errorMsg.str() << std::endl; - throw std::runtime_error(errorMsg.str().c_str()); - } - - return std::to_string(analyticalIntegral(code, rangeName)); -} - - //////////////////////////////////////////////////////////////////////////////// /// Check if cache entry was previously created diff --git a/roofit/roofitcore/src/RooNLLVarNew.cxx b/roofit/roofitcore/src/RooNLLVarNew.cxx index 5af0c0b9092e8..f5edc2205fb17 100644 --- a/roofit/roofitcore/src/RooNLLVarNew.cxx +++ b/roofit/roofitcore/src/RooNLLVarNew.cxx @@ -23,7 +23,7 @@ This class calls functions from `RooBatchCompute` library to provide faster computation times. **/ -#include "RooNLLVarNew.h" +#include "RooFit/Detail/RooNLLVarNew.h" #include #include @@ -46,6 +46,11 @@ computation times. #include #include +ClassImp(RooFit::Detail::RooNLLVarNew); + +namespace RooFit { +namespace Detail { + // Declare constexpr static members to make them available if odr-used in C++14. constexpr const char *RooNLLVarNew::weightVarName; constexpr const char *RooNLLVarNew::weightVarNameSumW2; @@ -331,46 +336,7 @@ void RooNLLVarNew::finalizeResult(RooFit::EvalContext &ctx, ROOT::Math::KahanSum ctx.setOutputWithOffset(this, result, _offset); } -void RooNLLVarNew::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - if (_binnedL && !_pdf->getAttribute("BinnedLikelihoodActiveYields")) { - std::stringstream errorMsg; - errorMsg << "RooNLLVarNew::translate(): binned likelihood optimization is only supported when raw pdf " - "values can be interpreted as yields." - << " This is not the case for HistFactory models written with ROOT versions before 6.26.00"; - coutE(InputArguments) << errorMsg.str() << std::endl; - throw std::runtime_error(errorMsg.str()); - } - - std::string weightSumName = RooFit::Detail::makeValidVarName(GetName()) + "WeightSum"; - std::string resName = RooFit::Detail::makeValidVarName(GetName()) + "Result"; - ctx.addResult(this, resName); - ctx.addToGlobalScope("double " + weightSumName + " = 0.0;\n"); - ctx.addToGlobalScope("double " + resName + " = 0.0;\n"); - - const bool needWeightSum = _expectedEvents || _simCount > 1; - - if (needWeightSum) { - auto scope = ctx.beginLoop(this); - ctx.addToCodeBody(weightSumName + " += " + ctx.getResult(*_weightVar) + ";\n"); - } - if (_simCount > 1) { - std::string simCountStr = std::to_string(static_cast(_simCount)); - ctx.addToCodeBody(resName + " += " + weightSumName + " * std::log(" + simCountStr + ");\n"); - } - - // Begin loop scope for the observables and weight variable. If the weight - // is a scalar, the context will ignore it for the loop scope. The closing - // brackets of the loop is written at the end of the scopes lifetime. - { - auto scope = ctx.beginLoop(this); - std::string term = ctx.buildCall("RooFit::Detail::MathFuncs::nll", _pdf, _weightVar, _binnedL, 0); - ctx.addToCodeBody(this, resName + " += " + term + ";"); - } - if (_expectedEvents) { - std::string expected = ctx.getResult(**_expectedEvents); - ctx.addToCodeBody(resName + " += " + expected + " - " + weightSumName + " * std::log(" + expected + ");\n"); - } -} +} // namespace Detail +} // namespace RooFit /// \endcond diff --git a/roofit/roofitcore/src/RooNormalizedPdf.cxx b/roofit/roofitcore/src/RooNormalizedPdf.cxx index 52512365212b5..a2db2b8477fd2 100644 --- a/roofit/roofitcore/src/RooNormalizedPdf.cxx +++ b/roofit/roofitcore/src/RooNormalizedPdf.cxx @@ -10,11 +10,14 @@ * listed in LICENSE (http://roofit.sourceforge.net/license.txt) */ -#include "RooNormalizedPdf.h" +#include "RooFit/Detail/RooNormalizedPdf.h" + #include "RooBatchCompute.h" #include +ClassImp(RooFit::Detail::RooNormalizedPdf); + /** * \class RooNormalizedPdf * @@ -22,6 +25,9 @@ * normalization set into a new self-normalized pdf. */ +namespace RooFit { +namespace Detail { + void RooNormalizedPdf::doEval(RooFit::EvalContext &ctx) const { auto nums = ctx.at(_pdf); @@ -48,8 +54,5 @@ void RooNormalizedPdf::doEval(RooFit::EvalContext &ctx) const } } -void RooNormalizedPdf::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - // For now just return function/normalization integral. - ctx.addResult(this, ctx.getResult(_pdf) + "/" + ctx.getResult(_normIntegral)); -} +} // namespace Detail +} // namespace RooFit diff --git a/roofit/roofitcore/src/RooPolyVar.cxx b/roofit/roofitcore/src/RooPolyVar.cxx index db6b14d577047..e2dc18fcd5bc7 100644 --- a/roofit/roofitcore/src/RooPolyVar.cxx +++ b/roofit/roofitcore/src/RooPolyVar.cxx @@ -112,17 +112,6 @@ double RooPolyVar::evaluate() const return RooFit::Detail::MathFuncs::polynomial(_wksp.data(), sz, _lowestOrder, _x); } -void RooPolyVar::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - const unsigned sz = _coefList.size(); - if (!sz) { - ctx.addResult(this, std::to_string((_lowestOrder ? 1. : 0.))); - return; - } - - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::polynomial", _coefList, sz, _lowestOrder, _x)); -} - void RooPolyVar::doEvalImpl(RooAbsArg const *caller, RooFit::EvalContext &ctx, RooAbsReal const &x, RooArgList const &coefs, int lowestOrder) { @@ -187,15 +176,3 @@ double RooPolyVar::analyticalIntegral(Int_t code, const char *rangeName) const return RooFit::Detail::MathFuncs::polynomialIntegral(_wksp.data(), sz, _lowestOrder, xmin, xmax); } - -std::string RooPolyVar::buildCallToAnalyticIntegral(Int_t /* code */, const char *rangeName, - RooFit::Detail::CodeSquashContext &ctx) const -{ - const double xmin = _x.min(rangeName); - const double xmax = _x.max(rangeName); - const unsigned sz = _coefList.size(); - if (!sz) - return std::to_string(_lowestOrder ? xmax - xmin : 0.0); - - return ctx.buildCall("RooFit::Detail::MathFuncs::polynomialIntegral", _coefList, sz, _lowestOrder, xmin, xmax); -} diff --git a/roofit/roofitcore/src/RooProdPdf.cxx b/roofit/roofitcore/src/RooProdPdf.cxx index 5a178f77ec2bc..e9ddd0e1e6654 100644 --- a/roofit/roofitcore/src/RooProdPdf.cxx +++ b/roofit/roofitcore/src/RooProdPdf.cxx @@ -73,6 +73,7 @@ have to appear in any specific place in the list. using std::endl, std::string, std::vector, std::list, std::ostream, std::map, std::ostringstream; +ClassImp(RooFit::Detail::RooFixedProdPdf); ClassImp(RooProdPdf); @@ -2311,98 +2312,6 @@ std::unique_ptr RooProdPdf::fillNormSetForServer(RooArgSet const &nor } } -/// A RooProdPdf with a fixed normalization set can be replaced by this class. -/// Its purpose is to provide the right client-server interface for the -/// evaluation of RooProdPdf cache elements that were created for a given -/// normalization set. -class RooFixedProdPdf : public RooAbsPdf { -public: - RooFixedProdPdf(std::unique_ptr &&prodPdf, RooArgSet const &normSet) - : RooAbsPdf(prodPdf->GetName(), prodPdf->GetTitle()), _normSet{normSet}, - _servers("!servers", "List of servers", this), _prodPdf{std::move(prodPdf)} - { - initialize(); - } - RooFixedProdPdf(const RooFixedProdPdf &other, const char *name = nullptr) - : RooAbsPdf(other, name), _normSet{other._normSet}, - _servers("!servers", "List of servers", this), _prodPdf{static_cast(other._prodPdf->Clone())} - { - initialize(); - } - TObject *clone(const char *newname) const override { return new RooFixedProdPdf(*this, newname); } - - bool selfNormalized() const override { return true; } - - inline bool canComputeBatchWithCuda() const override { return true; } - - void doEval(RooFit::EvalContext &ctx) const override - { - _prodPdf->doEvalImpl(this, *_cache, ctx); - } - - void translate(RooFit::Detail::CodeSquashContext &ctx) const override - { - if (_cache->_isRearranged) { - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::ratio", *_cache->_rearrangedNum, *_cache->_rearrangedDen)); - } else { - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::product", _cache->_partList, _cache->_partList.size())); - } - } - - ExtendMode extendMode() const override { return _prodPdf->extendMode(); } - double expectedEvents(const RooArgSet * /*nset*/) const override { return _prodPdf->expectedEvents(&_normSet); } - std::unique_ptr createExpectedEventsFunc(const RooArgSet * /*nset*/) const override - { - return _prodPdf->createExpectedEventsFunc(&_normSet); - } - - // Analytical Integration handling - bool forceAnalyticalInt(const RooAbsArg &dep) const override { return _prodPdf->forceAnalyticalInt(dep); } - Int_t getAnalyticalIntegralWN(RooArgSet &allVars, RooArgSet &analVars, const RooArgSet *normSet, - const char *rangeName = nullptr) const override - { - return _prodPdf->getAnalyticalIntegralWN(allVars, analVars, normSet, rangeName); - } - Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &numVars, const char *rangeName = nullptr) const override - { - return _prodPdf->getAnalyticalIntegral(allVars, numVars, rangeName); - } - double analyticalIntegralWN(Int_t code, const RooArgSet *normSet, const char *rangeName) const override - { - return _prodPdf->analyticalIntegralWN(code, normSet, rangeName); - } - double analyticalIntegral(Int_t code, const char *rangeName = nullptr) const override - { - return _prodPdf->analyticalIntegral(code, rangeName); - } - -private: - void initialize() - { - _cache = _prodPdf->createCacheElem(&_normSet, nullptr); - auto &cache = *_cache; - - // The actual servers for a given normalization set depend on whether the - // cache is rearranged or not. See RooProdPdf::calculateBatch to see - // which args in the cache are used directly. - if (cache._isRearranged) { - _servers.add(*cache._rearrangedNum); - _servers.add(*cache._rearrangedDen); - } else { - for (std::size_t i = 0; i < cache._partList.size(); ++i) { - _servers.add(cache._partList[i]); - } - } - } - - double evaluate() const override { return _prodPdf->calculate(*_cache); } - - RooArgSet _normSet; - std::unique_ptr _cache; - RooSetProxy _servers; - std::unique_ptr _prodPdf; -}; - std::unique_ptr RooProdPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext &ctx) const { @@ -2426,8 +2335,50 @@ RooProdPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileC ctx.compileServer(*server, *prodPdfClone, depList); } - auto fixedProdPdf = std::make_unique(std::move(prodPdfClone), normSet); + auto fixedProdPdf = std::make_unique(std::move(prodPdfClone), normSet); ctx.markAsCompiled(*fixedProdPdf); return fixedProdPdf; } + +namespace RooFit { +namespace Detail { + +RooFixedProdPdf::RooFixedProdPdf(std::unique_ptr &&prodPdf, RooArgSet const &normSet) + : RooAbsPdf(prodPdf->GetName(), prodPdf->GetTitle()), + _normSet{normSet}, + _servers("!servers", "List of servers", this), + _prodPdf{std::move(prodPdf)} +{ + initialize(); +} + +RooFixedProdPdf::RooFixedProdPdf(const RooFixedProdPdf &other, const char *name) + : RooAbsPdf(other, name), + _normSet{other._normSet}, + _servers("!servers", "List of servers", this), + _prodPdf{static_cast(other._prodPdf->Clone())} +{ + initialize(); +} + +void RooFixedProdPdf::initialize() +{ + _cache = _prodPdf->createCacheElem(&_normSet, nullptr); + auto &cache = *_cache; + + // The actual servers for a given normalization set depend on whether the + // cache is rearranged or not. See RooProdPdf::calculateBatch to see + // which args in the cache are used directly. + if (cache._isRearranged) { + _servers.add(*cache._rearrangedNum); + _servers.add(*cache._rearrangedDen); + } else { + for (std::size_t i = 0; i < cache._partList.size(); ++i) { + _servers.add(cache._partList[i]); + } + } +} + +} // namespace Detail +} // namespace RooFit diff --git a/roofit/roofitcore/src/RooProduct.cxx b/roofit/roofitcore/src/RooProduct.cxx index 911e8e2cf5cb2..e08dffbf0f223 100644 --- a/roofit/roofitcore/src/RooProduct.cxx +++ b/roofit/roofitcore/src/RooProduct.cxx @@ -488,13 +488,6 @@ void RooProduct::setCacheAndTrackHints(RooArgSet& trackNodes) } } -//////////////////////////////////////////////////////////////////////////////// - -void RooProduct::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::product", _compRSet, _compRSet.size())); -} - //////////////////////////////////////////////////////////////////////////////// /// Customized printing of arguments of a RooProduct to more intuitively reflect the contents of the /// product operator construction diff --git a/roofit/roofitcore/src/RooRatio.cxx b/roofit/roofitcore/src/RooRatio.cxx index e58acce3dc3ec..3d05f4f250cb4 100644 --- a/roofit/roofitcore/src/RooRatio.cxx +++ b/roofit/roofitcore/src/RooRatio.cxx @@ -123,8 +123,3 @@ void RooRatio::doEval(RooFit::EvalContext &ctx) const RooBatchCompute::compute(ctx.config(this), RooBatchCompute::Ratio, ctx.output(), {ctx.at(_numerator), ctx.at(_denominator)}); } - -void RooRatio::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::ratio", _numerator, _denominator)); -} diff --git a/roofit/roofitcore/src/RooRealIntegral.cxx b/roofit/roofitcore/src/RooRealIntegral.cxx index 0dfd2fcbec3be..5d2b61f3c2289 100644 --- a/roofit/roofitcore/src/RooRealIntegral.cxx +++ b/roofit/roofitcore/src/RooRealIntegral.cxx @@ -35,8 +35,6 @@ integration is performed in the various implementations of the RooAbsIntegrator #include #include #include -#include -#include #include #include #include @@ -46,8 +44,6 @@ integration is performed in the various implementations of the RooAbsIntegrator #include #include -#include "RooFitImplHelpers.h" - #include #include @@ -1032,76 +1028,6 @@ void RooRealIntegral::setAllowComponentSelection(bool allow){ _respectCompSelect = allow; } -void RooRealIntegral::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - if (_sumList.empty() && _intList.empty()) { - ctx.addResult(this, _function.arg().buildCallToAnalyticIntegral(_mode, RooNameReg::str(_rangeName), ctx)); - return; - } - - if (intVars().size() != 1 || _intList.size() != 1) { - std::stringstream errorMsg; - errorMsg << "Only analytical integrals and 1D numeric integrals are supported for AD for class" - << _function.GetName(); - coutE(Minimization) << errorMsg.str() << std::endl; - throw std::runtime_error(errorMsg.str().c_str()); - } - - auto &intVar = static_cast(*_intList[0]); - - RooFit::Experimental::RooFuncWrapper wrapper{GetName(), GetTitle(), *_function}; - for (std::string const& name : wrapper.collectedFunctions()) { - ctx._wrapper->collectFunction(name); - } - - RooArgSet params; - _function->getParameters(nullptr, params); - - std::string paramsName = ctx.getTmpVarName(); - - std::stringstream ss; - - std::string resName = RooFit::Detail::makeValidVarName(GetName()) + "Result"; - ctx.addResult(this, resName); - ctx.addToGlobalScope("double " + resName + " = 0.0;\n"); - - ss << "double " << paramsName << "[] = {"; - std::string args; - int intVarIdx = 0; - for (RooAbsArg *param : params) { - // Fill the integration variable with dummy value for now. This will then - // be reset in the sampling loop. - if (param->namePtr() == intVar.namePtr()) { - args += "0.0,"; - } else if (!param->isConstant()) { - args += ctx.getResult(*param) + ","; - intVarIdx++; - } - } - if (!args.empty()) { - args.pop_back(); - } - ss << args << "};\n"; - - // TODO: once Clad has support for higher-order functions (follow also the - // Clad issue #637), we could refactor this code into an actual function - // instead of hardcoding it here as a string. - ss << "{\n" - << " const int n = 1000; // number of sampling points\n" - << " double d = " << intVar.getMax(intRange()) << " - " << intVar.getMin(intRange()) << ";\n" - << " double eps = d / n;\n" - << " for (int i = 0; i < n; ++i) {\n" - << " " << paramsName << "[" << intVarIdx << "] = " << intVar.getMin(intRange()) << " + eps * i;\n" - << " double tmpA = " << ctx.buildCall(wrapper.funcName(), paramsName, nullptr, nullptr) << ";\n" - << " " << paramsName << "[" << intVarIdx << "] = " << intVar.getMin(intRange()) << " + eps * (i + 1);\n" - << " double tmpB = " << ctx.buildCall(wrapper.funcName(), paramsName, nullptr, nullptr) << ";\n" - << " " << resName << " += (tmpA + tmpB) * 0.5 * eps;\n" - << " }\n" - << "}\n"; - - ctx.addToGlobalScope(ss.str()); -} - //////////////////////////////////////////////////////////////////////////////// /// Customized printing of arguments of a RooRealIntegral to more intuitively reflect the contents of the /// integration operation diff --git a/roofit/roofitcore/src/RooRealSumFunc.cxx b/roofit/roofitcore/src/RooRealSumFunc.cxx index 191dc14333a65..d509b7a623923 100644 --- a/roofit/roofitcore/src/RooRealSumFunc.cxx +++ b/roofit/roofitcore/src/RooRealSumFunc.cxx @@ -114,11 +114,6 @@ double RooRealSumFunc::evaluate() const return RooRealSumPdf::evaluate(*this, _funcList, _coefList, _doFloor || _doFloorGlobal, _haveWarned); } -void RooRealSumFunc::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, RooRealSumPdf::translateImpl(ctx, this, _funcList, _coefList)); -} - //_____________________________________________________________________________ bool RooRealSumFunc::checkObservables(const RooArgSet *nset) const { diff --git a/roofit/roofitcore/src/RooRealSumPdf.cxx b/roofit/roofitcore/src/RooRealSumPdf.cxx index 30c792be50669..2b4b648cf721c 100644 --- a/roofit/roofitcore/src/RooRealSumPdf.cxx +++ b/roofit/roofitcore/src/RooRealSumPdf.cxx @@ -46,7 +46,7 @@ to the fractions of the various functions. **This requires setting the last argu #include "RooRealSumPdf.h" -#include "RooNormalizedPdf.h" +#include "RooFit/Detail/RooNormalizedPdf.h" #include "RooRealIntegral.h" #include "RooRealProxy.h" #include "RooRealVar.h" @@ -297,42 +297,6 @@ void RooRealSumPdf::doEval(RooFit::EvalContext & ctx) const } } -std::string RooRealSumPdf::translateImpl(RooFit::Detail::CodeSquashContext &ctx, RooAbsArg const *klass, - RooArgList const &funcList, RooArgList const &coefList, bool normalize) -{ - bool noLastCoeff = funcList.size() != coefList.size(); - - std::string const &funcName = ctx.buildArg(funcList); - std::string const &coeffName = ctx.buildArg(coefList); - std::string const &coeffSize = std::to_string(coefList.size()); - - std::string sum = ctx.getTmpVarName(); - std::string coeffSum = ctx.getTmpVarName(); - ctx.addToCodeBody(klass, "double " + sum + " = 0;\ndouble " + coeffSum + "= 0;\n"); - - std::string iterator = "i_" + ctx.getTmpVarName(); - std::string subscriptExpr = "[" + iterator + "]"; - - std::string code = "for(int " + iterator + " = 0; " + iterator + " < " + coeffSize + "; " + iterator + "++) {\n" + - sum + " += " + funcName + subscriptExpr + " * " + coeffName + subscriptExpr + ";\n"; - code += coeffSum + " += " + coeffName + subscriptExpr + ";\n"; - code += "}\n"; - - if (noLastCoeff) { - code += sum + " += " + funcName + "[" + coeffSize + "]" + " * (1 - " + coeffSum + ");\n"; - } else if (normalize) { - code += sum + " /= " + coeffSum + ";\n"; - } - ctx.addToCodeBody(klass, code); - - return sum; -} - -void RooRealSumPdf::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, translateImpl(ctx, this, _funcList, _coefList)); -} - bool RooRealSumPdf::checkObservables(RooAbsReal const& caller, RooArgSet const* nset, RooArgList const& funcList, RooArgList const& coefList) { @@ -777,7 +741,7 @@ RooRealSumPdf::compileForNormSet(RooArgSet const &normSet, RooFit::Detail::Compi RooArgSet depList; pdfClone->getObservables(&normSet, depList); - auto newArg = std::make_unique(*pdfClone, depList); + auto newArg = std::make_unique(*pdfClone, depList); // The direct servers are this pdf and the normalization integral, which // don't need to be compiled further. diff --git a/roofit/roofitcore/src/RooRealVar.cxx b/roofit/roofitcore/src/RooRealVar.cxx index 33b7adb8f9a40..a1c2c3790b6f4 100644 --- a/roofit/roofitcore/src/RooRealVar.cxx +++ b/roofit/roofitcore/src/RooRealVar.cxx @@ -82,22 +82,6 @@ void RooRealVar::cleanup() //////////////////////////////////////////////////////////////////////////////// -void RooRealVar::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - if(!isConstant()) { - ctx.addResult(this, GetName()); - } - // Just return a formatted version of the const value. - // Formats to the maximum precision. - constexpr auto max_precision{std::numeric_limits::digits10 + 1}; - std::stringstream ss; - ss.precision(max_precision); - // Just use toString to make sure we do not output 'inf'. - // This is really ugly for large numbers... - ss << std::fixed << RooNumber::toString(_value); - ctx.addResult(this, ss.str()); -} - /// Return a dummy object to use when properties are not initialised. RooRealVarSharedProperties& RooRealVar::_nullProp() { diff --git a/roofit/roofitcore/src/RooRecursiveFraction.cxx b/roofit/roofitcore/src/RooRecursiveFraction.cxx index 5f0460ef9319a..8a9d7f10cad4d 100644 --- a/roofit/roofitcore/src/RooRecursiveFraction.cxx +++ b/roofit/roofitcore/src/RooRecursiveFraction.cxx @@ -87,8 +87,3 @@ double RooRecursiveFraction::evaluate() const return prod ; } - -void RooRecursiveFraction::translate(RooFit::Detail::CodeSquashContext &ctx) const -{ - ctx.addResult(this, ctx.buildCall("RooFit::Detail::MathFuncs::recursiveFraction", _list, _list.size())); -}