68double round_prec(
double d,
int nSig)
72 int ndigits = std::floor(std::log10(std::abs(
d))) + 1 - nSig;
73 double sf = std::pow(10, ndigits);
74 if (std::abs(
d / sf) < 2)
76 return sf * std::round(
d / sf);
79std::string interpolationString(
const Interpolation &interpolation)
82 ss << R
"({"type":")" << toString(interpolation.type) << R"(","in":")" << toString(interpolation.in) << R"(","out":)";
83 if (interpolation.
out) {
92const char *interpolationClassName(InterpolationClass interpolationClass)
94 return interpolationClass == InterpolationClass::Piecewise ?
"PiecewiseInterpolation" :
"FlexibleInterpVar";
100 return *interpolation;
103 std::to_string(code));
107 const std::string &context)
113 interpolationClassName(interpolationClass));
117std::vector<Interpolation>
118toInterpolations(std::vector<int>
const &codes, InterpolationClass interpolationClass, std::string
const &context)
120 std::vector<Interpolation> out;
121 out.reserve(codes.size());
122 for (std::size_t i = 0; i < codes.size(); ++i) {
124 interpolationFromCode(codes[i], interpolationClass, context +
" at parameter index " + std::to_string(i)));
129std::vector<int> toCodes(std::vector<Interpolation>
const &interpolations, InterpolationClass interpolationClass,
130 std::string
const &context)
132 std::vector<int> out;
133 out.reserve(interpolations.size());
134 for (std::size_t i = 0; i < interpolations.size(); ++i) {
136 context +
" component 'interpolations' at index " + std::to_string(i)));
144 for (
auto &diagnostic : diagnostics) {
145 diagnostic.path =
name + (diagnostic.path.empty() ?
"" :
"." + diagnostic.path);
150int interpolationCode(std::optional<Interpolation>
const &interpolation,
151 std::optional<Interpolation>
const &defaultInterpolation, InterpolationClass interpolationClass,
152 std::string
const &context)
158 if (!defaultInterpolation) {
164void erasePrefix(std::string &str, std::string_view prefix)
167 str.erase(0, prefix.size());
171bool eraseSuffix(std::string &str, std::string_view suffix)
174 str.erase(str.size() - suffix.size());
182void sortByName(Coll &coll)
184 std::sort(coll.begin(), coll.end(), [](
auto &
l,
auto &
r) { return l.name < r.name; });
190 for (
const auto &client :
gamma->clients()) {
191 if (
auto casted =
dynamic_cast<T *
>(client)) {
194 T *
c = findClient<T>(client);
206 if (
auto *constraint = findClient<RooPoisson>(
g))
208 if (
auto *constraint = findClient<RooGaussian>(
g))
210 return findClient<RooLognormal>(
g);
213inline std::string defaultGammaName(std::string
const &sysname, std::size_t i)
215 return "gamma_" + sysname +
"_bin_" + std::to_string(i);
218RooRealVar &createNominal(
RooWorkspace &ws, std::string
const &parname,
double val,
double min,
double max)
220 RooRealVar &nom = getOrCreate<RooRealVar>(ws,
"nom_" + parname, val, min, max);
227std::string constraintName(std::string
const ¶mName)
229 return paramName +
"Constraint";
234 if (
auto *constraint = tool.
workspace()->
pdf(constraintName)) {
241 if (err.
child() != constraintName) {
252 double max, std::string
const &
modifier)
257 return getOrCreate<RooRealVar>(ws,
name, val, min, max);
263 "' is neither a variable nor a constant");
266void setErrorIfVariable(
RooAbsReal ¶m,
double error)
268 if (
auto *var =
dynamic_cast<RooRealVar *
>(¶m)) {
269 var->setError(error);
282 if (
type != HS3::ConstraintType::Gauss) {
285 "' is a known constraint type, but it cannot be resolved in this "
289 setErrorIfVariable(param, 1.0);
290 const std::string
name = constraintName(param.
GetName());
295 RooAbsPdf *pdf = findNamedConstraint(tool, constraintPdf.pdf.name, sample);
298 constraintPdf.pdf.name +
"' for modifier '" +
modifier +
"'");
300 if (
auto gauss =
dynamic_cast<RooGaussian *
>(pdf)) {
301 setErrorIfVariable(param, gauss->getSigma().getVal());
303 constraints.
add(*pdf);
311 std::vector<HS3::ParameterRef>
const &parnames, std::vector<double>
const &vals,
313 double gammaMax,
double minSigma, std::size_t nBins)
319 for (std::size_t i = 0; i < nBins; ++i) {
320 const std::string
name = parnames.empty() ? defaultGammaName(sysname, i) : parnames[i].
name;
322 gammas.
add(*existing);
332 for (std::size_t i = 0; same && i < gammas.
size(); ++i) {
333 same = &existingPHF->paramList()[i] == &gammas[i];
337 "of this name in the channel");
345 constraints.
add(*ws.
pdf(term->GetName()));
349 if (
auto const *pdfs = std::get_if<HS3::GammaConstraintPdfs>(&constraint)) {
350 for (
auto const &pdfName : pdfs->pdfs) {
354 auto *pdf = findNamedConstraint(tool, pdfName->name, sysname);
357 "' for modifier '" + sysname +
"'");
359 constraints.
add(*pdf);
372 gamma->setConstant(
true);
379 case HS3::ConstraintType::LogNormal:
381 "' for the gamma parameters of '" + sysname +
382 "', supported are \"Poisson\" and \"Gauss\"");
396 std::string(constraint.
GetName()));
404 if (
auto const *tau =
dynamic_cast<RooConstVar const *
>(arg)) {
405 return tau->getVal();
410 if (
auto const *real =
dynamic_cast<RooAbsReal const *
>(arg)) {
411 if (real->isConstant() ||
endsWith(std::string(real->GetName()),
"_tau")) {
412 return real->getVal();
418 std::string(constraint.
GetName()));
419 return std::numeric_limits<double>::quiet_NaN();
426 if (
auto constraintP =
dynamic_cast<RooPoisson const *
>(&constraint)) {
427 return 1. / std::sqrt(poissonTau(*constraintP, gamma));
429 if (
auto constraintG =
dynamic_cast<RooGaussian const *
>(&constraint)) {
430 return constraintG->getSigma().getVal() / constraintG->getMean().getVal();
433 return std::numeric_limits<double>::quiet_NaN();
436struct ChannelContext {
448struct SampleElements {
453 std::vector<double> overallLow;
454 std::vector<double> overallHigh;
455 std::vector<int> overallInterp;
460 std::vector<int> histoInterp;
468 std::string
const &sampleName = sample.
name;
469 std::string prefixedName = ctx.fprefix +
"_" + sampleName;
472 auto newName = [&](std::string
name) {
475 ctx.channelName +
"' has the name of an existing object");
483 SampleElements elems;
486 const bool hasStatError = std::any_of(sample.
modifiers.begin(), sample.
modifiers.end(),
487 [](
auto const &
m) { return std::holds_alternative<HS3::StatError>(m); });
489 elems.shapeElems.
add(*ctx.mcStatObject);
492 auto context = [&](
const char *
type, std::string
const &
name) {
493 return std::string(
"interpolation for ") +
type +
" modifier '" +
name +
"' in sample '" + sampleName +
494 "' of channel '" + ctx.channelName +
"'";
504 RooAbsReal ¶m = getOrCreateParameter(tool, mod.parameter.name, 1., -3, 5, mod.name);
505 setErrorIfVariable(param, 0.0);
506 elems.normElems.
add(param);
508 addConstraint(tool, mod.constraint, param, 1.0, mod.name, sampleName, ctx.constraints);
511 RooAbsReal ¶m = getOrCreateParameter(tool, mod.parameter.name, 0., -5, 5, mod.name);
512 elems.overallNps.
add(param);
514 InterpolationClass::Flexible, context(
"normsys", mod.name));
516 double high = mod.hi;
522 if (interp == 4 && low <= 0)
523 low = std::numeric_limits<double>::epsilon();
524 if (interp == 4 && high <= 0)
525 high = std::numeric_limits<double>::epsilon();
527 elems.overallLow.push_back(low);
528 elems.overallHigh.push_back(high);
529 elems.overallInterp.push_back(interp);
531 addConstraint(tool, mod.constraint, param, 0.0, mod.name, sampleName, ctx.constraints);
534 RooAbsReal ¶m = getOrCreateParameter(tool, mod.parameter.name, 0., -5, 5, mod.name);
535 elems.histNps.
add(param);
537 const std::string
name = newName(mod.name + suffix + prefixedName);
541 variation.errors ? &*variation.errors : nullptr)));
543 addVariation(elems.histoLo, mod.lo,
"Low_");
544 addVariation(elems.histoHi, mod.hi,
"High_");
546 InterpolationClass::Piecewise,
547 context(
"histosys", mod.name)));
548 addConstraint(tool, mod.constraint, param, 0.0, mod.name, sampleName, ctx.constraints);
551 const bool constrained = std::visit(
555 return std::any_of(constraints.pdfs.
begin(), constraints.pdfs.
end(),
556 [](
auto const &pdf) { return pdf.has_value(); });
561 const std::string funcName =
562 ctx.channelName +
"_" + mod.name + (constrained ?
"_ShapeSys" :
"_shapeFactor");
563 elems.shapeElems.
add(createPHF(tool, funcName, mod.name, mod.parameters, mod.vals, mod.constraint,
571 elems.shapeElems.
add(*obj);
573 elems.normElems.
add(*obj);
580 if (!elems.overallNps.
empty()) {
581 std::string interpName = newName(sampleName +
"_" + ctx.channelName +
"_epsilon");
583 interpName, elems.overallNps, 1., elems.overallLow, elems.overallHigh, elems.overallInterp));
585 if (!elems.histNps.
empty()) {
587 elems.histoHi, elems.histNps, elems.histoInterp);
589 elems.shapeElems.
add(
v);
591 elems.shapeElems.
add(hf);
595 if (!elems.normElems.
empty()) {
598 ws.
factory(
"RooConstVar::" + newName(prefixedName +
"_scaleFactors") +
"(1.)");
608 for (
auto const &sample :
dist.samples) {
609 for (
auto const &
modifier : sample.modifiers) {
610 auto const *statError = std::get_if<HS3::StatError>(&
modifier);
614 if (std::holds_alternative<HS3::GammaConstraintPdfs>(statError->constraint)) {
615 return statError->constraint;
617 if (!modifierConstraint && !std::holds_alternative<HS3::Unconstrained>(statError->constraint)) {
618 modifierConstraint = &statError->constraint;
622 return modifierConstraint ? *modifierConstraint :
dist.statError.constraint;
628 for (
auto const &sample :
dist.samples) {
629 for (
auto const &
modifier : sample.modifiers) {
630 auto const *statError = std::get_if<HS3::StatError>(&
modifier);
631 auto const *
type = statError ? std::get_if<HS3::ConstraintType>(&statError->constraint) : nullptr;
632 if (
type && *
type == HS3::ConstraintType::LogNormal) {
634 "' of the staterror modifier in channel '" +
name +
635 "', supported are \"Poisson\" and \"Gauss\"");
653 std::string
const &key()
const override
655 static const std::string keystring =
"interpolation0d";
661 const std::string context =
"FlexibleInterpVar '" + std::string{fip->GetName()} +
"'";
662 const std::size_t nParameters = fip->variables().size();
663 if (fip->low().size() != nParameters || fip->high().size() != nParameters ||
664 fip->interpolationCodes().size() != nParameters) {
672 out.
high = fip->high();
673 out.
low = fip->low();
674 out.
interpolations = toInterpolations(fip->interpolationCodes(), InterpolationClass::Flexible, context);
682 std::string
const &key()
const override
684 static const std::string keystring =
"interpolation";
690 const std::string context =
"PiecewiseInterpolation '" + std::string{pip->
GetName()} +
"'";
698 for (std::size_t i = 0; i < nParameters; ++i) {
719 auto request = [&](std::vector<HS3::NameOrNumber>
const &refs) {
721 for (
auto const &ref : refs) {
722 auto const *
value = std::get_if<double>(&ref);
732 toCodes(in.
interpolations, InterpolationClass::Piecewise,
"PiecewiseInterpolation '" +
name +
"'"));
748 for (std::string
const &var : in.parameters) {
753 toCodes(in.
interpolations, InterpolationClass::Flexible,
"FlexibleInterpVar '" +
name +
"'"));
758std::string stripOuterParens(
const std::string &s)
761 size_t end = s.size();
763 while (start < end && s[start] ==
'(' && s[end - 1] ==
')') {
765 bool balanced =
true;
766 for (
size_t i = start; i < end - 1; ++i) {
769 }
else if (s[i] ==
')') {
772 if (depth == 0 && i < end - 1) {
784 return s.substr(start, end - start);
789bool isBinaryOperator(
const std::string &expr, std::size_t i)
792 while (j > 0 && std::isspace(
static_cast<unsigned char>(expr[j - 1]))) {
798 const char prev = expr[j - 1];
799 if (prev ==
'e' || prev ==
'E') {
801 std::size_t k = j - 1;
802 while (k > 0 && (std::isdigit(
static_cast<unsigned char>(expr[k - 1])) || expr[k - 1] ==
'.')) {
805 const bool startsName = k > 0 && (std::isalpha(
static_cast<unsigned char>(expr[k - 1])) || expr[k - 1] ==
'_');
806 if (k < j - 1 && !startsName && i == j) {
810 return std::isalnum(
static_cast<unsigned char>(prev)) || prev ==
'_' || prev ==
'.' || prev ==
')' || prev ==
']';
813std::vector<std::string> splitTopLevelProduct(
const std::string &expr)
815 std::vector<std::string> parts;
818 bool foundTopLevelStar =
false;
820 for (
size_t i = 0; i < expr.size(); ++i) {
824 }
else if (
c ==
')') {
826 }
else if ((
c ==
'+' ||
c ==
'-') && depth == 0 && isBinaryOperator(expr, i)) {
828 }
else if (
c ==
'*' && depth == 0) {
829 foundTopLevelStar =
true;
830 std::string sub = expr.substr(start, i - start);
831 parts.push_back(stripOuterParens(sub));
836 if (!foundTopLevelStar) {
840 std::string sub = expr.substr(start);
841 parts.push_back(stripOuterParens(sub));
846struct FormulaNormSys {
849 double variation = 0.0;
852FormulaNormSys parseOverallModifierFormula(
const std::string &s,
RooFormulaVar *formula)
854 static const std::regex pattern(
855 R
"(^\s*1(?:\.0)?\s*([\+\-])\s*([a-zA-Z_][a-zA-Z0-9_]*|[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*\*\s*([a-zA-Z_][a-zA-Z0-9_]*|[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*$)");
859 if (std::regex_match(s, match, pattern)) {
860 const double sign = match[1].str() ==
"-" ? -1.0 : 1.0;
862 std::string token2 = match[2].str();
863 std::string token3 = match[4].str();
870 auto *constr2 = findConstraint(
v2);
871 auto *constr3 = findConstraint(
v3);
873 if (constr2 && !p3) {
875 sys.variation = sign *
toDouble(token3);
876 }
else if (!p2 && constr3) {
878 sys.variation = sign *
toDouble(token2);
879 }
else if (constr2 && p3 && !constr3) {
881 sys.variation = sign * p3->
getVal();
882 }
else if (p2 && !constr2 && constr3) {
884 sys.variation = sign * p2->
getVal();
888 sys.constraint = constr2 ? constr2 : constr3;
895 if (
auto prod =
dynamic_cast<RooProduct *
>(arg)) {
896 for (
const auto &
e : prod->components()) {
897 collectElements(elems,
e);
906 for (
auto *var : list) {
919 ": functions are not supported as parameters of this modifier");
923std::vector<double> contents(
RooDataHist const &dataHist)
929struct ExportedChannel {
934 std::map<std::string, RooAbsPdf const *> constraints;
935 std::vector<RooAbsReal *> customModifiers;
937 std::vector<std::unique_ptr<RooAbsReal>> temporaries;
939 std::map<int, double> totYield;
940 std::map<int, double> totYield2;
941 std::map<int, double> relErrors;
943 std::set<RooAbsArg const *> legacyBinWidthFactors;
952 std::vector<double> out;
954 auto *hf =
dynamic_cast<RooHistFunc const *
>(node);
955 if (!hf || hf->dataHist().numEntries() == 0) {
958 out.push_back(hf->dataHist().binVolume(0));
961 if (
auto *real =
dynamic_cast<RooRealVar const *
>(obs)) {
962 average *= (real->getMax() - real->getMin()) / real->numBins();
965 out.push_back(average);
977 std::vector<double>
const &volumes,
RooWorkspace const *ws)
982 auto const *var =
dynamic_cast<RooRealVar const *
>(&factor);
983 if (!
dynamic_cast<RooConstVar const *
>(&factor) && !(var && var->isConstant())) {
988 auto const *inSnapshot =
dynamic_cast<RooRealVar const *
>(snapshot->find(*var));
989 if (inSnapshot && !inSnapshot->isConstant()) {
994 for (
RooAbsArg const *client : factor.clients()) {
995 const bool isChannelProduct =
dynamic_cast<RooProduct const *
>(client) && channelNodes.
find(*client) == client;
996 if (client != &sumpdf && !isChannelProduct) {
1001 const double value =
static_cast<RooAbsReal const &
>(factor).getVal();
1002 return std::any_of(volumes.begin(), volumes.end(),
1003 [&](
double volume) { return volume > 0. && std::abs(value * volume - 1.) < 1e-6; });
1008 ExportedChannel channel;
1012 channel.name = pdfname;
1013 erasePrefix(channel.name,
"model_");
1014 eraseSuffix(channel.name,
"_model");
1018 const std::vector<double> volumes = binVolumes(*sumpdf);
1024 channel.constraints.emplace(pdf->
GetName(), pdf);
1027 auto gammaConstraint = [&](
RooAbsPdf const *pdf) -> std::optional<HS3::DistributionRef> {
1029 return std::nullopt;
1031 channel.constraints.emplace(pdf->
GetName(), pdf);
1035 for (
size_t sampleidx = 0; sampleidx < sumpdf->
funcList().
size(); ++sampleidx) {
1037 std::vector<ParamHistFunc *> phfs;
1039 const auto func = sumpdf->
funcList().
at(sampleidx);
1042 erasePrefix(sample.
name,
"L_x_");
1043 eraseSuffix(sample.
name,
"_shapes");
1044 eraseSuffix(sample.
name,
"_" + channel.name);
1045 erasePrefix(sample.
name, pdfname +
"_");
1048 std::vector<HS3::NormFactor> normFactors;
1049 std::vector<HS3::NormSys> normSys;
1050 std::vector<HS3::HistoSys> histoSys;
1051 std::vector<HS3::ShapeSys> shapeSys;
1052 std::vector<HS3::CustomModifier> custom;
1053 std::vector<HS3::CustomModifier> temporaries;
1054 std::optional<HS3::StatError> statError;
1058 checkModifierParameter(param, pdf);
1059 std::string sysname(param.
GetName());
1060 erasePrefix(sysname,
"alpha_");
1061 normSys.push_back({sysname, {param.
GetName()},
hi, lo, constraint(pdf), interpolation});
1064 auto updateObservables = [&](
RooDataHist const &dataHist) {
1065 if (channel.observables ==
nullptr) {
1066 channel.observables = dataHist.
get();
1069 sample.
contents = contents(dataHist);
1072 auto processElements = [&](
const auto &elements,
auto &&self) ->
void {
1076 }
else if (isLegacyBinWidthFactor(*
e, *sumpdf, channelNodes, volumes, ws)) {
1078 channel.legacyBinWidthFactors.insert(
e);
1080 oocoutW(
nullptr, ObjectHandling)
1081 <<
"RooFitHS3: the constant factor '" <<
e->GetName() <<
"' of sample '" << sample.
name
1082 <<
"' in channel '" << channel.name
1083 <<
"' is used like the inverse bin width of HistFactory before ROOT 6.26 (its value is the "
1084 "inverse bin volume), so it is not written: histfactory_dist implies it. Set "
1085 "RooJSONFactoryWSTool::config().identifyLegacyBinWidthFactors = false to write such factors "
1089 }
else if (
auto constVar =
dynamic_cast<RooConstVar *
>(
e)) {
1090 if (constVar->getVal() != 1.) {
1091 normFactors.push_back({constVar->GetName(), {constVar->GetName()},
HS3::Unconstrained{}});
1093 }
else if (
auto par =
dynamic_cast<RooRealVar *
>(
e)) {
1095 bool isConstrained =
false;
1096 for (
RooAbsArg const *pdf : ws->allPdfs()) {
1097 if (
auto gauss =
dynamic_cast<RooGaussian const *
>(pdf)) {
1098 if (std::string(par->GetName()) == gauss->getX().GetName()) {
1099 normFactors.push_back({par->GetName(), {par->GetName()}, constraint(gauss)});
1100 isConstrained =
true;
1104 if (!isConstrained) {
1107 }
else if (
auto hf =
dynamic_cast<const RooHistFunc *
>(
e)) {
1108 updateObservables(hf->dataHist());
1110 phfs.push_back(phf);
1114 for (
size_t i = 0; i < fip->variables().size(); ++i) {
1116 std::string sysname(var->
GetName());
1117 erasePrefix(sysname,
"alpha_");
1118 const std::string context =
"normsys modifier '" + sysname +
"' in sample '" + sample.
name +
1119 "' of channel '" + channel.name +
"'";
1121 addNormSys(*var, fip->high()[i], fip->low()[i],
1123 findConstraint(var));
1133 expression.ReplaceAll((
"x[" + std::to_string(i) +
"]").c_str(),
p->
GetName());
1134 expression.ReplaceAll((
"@" + std::to_string(i)).c_str(),
p->
GetName());
1136 auto components = splitTopLevelProduct(expression.Data());
1137 if (components.empty()) {
1139 custom.push_back({formula->
GetName()});
1140 channel.customModifiers.push_back(formula);
1143 std::vector<RooAbsArg *> realComponents;
1145 for (
auto &comp : components) {
1149 realComponents.push_back(part);
1153 FormulaNormSys sys = parseOverallModifierFormula(comp, formula);
1156 addNormSys(*sys.param, 1.0 + sys.variation, 1.0 - sys.variation,
1157 multiplicativePiecewiseExponential, sys.constraint);
1162 std::string
name = std::string(formula->
GetName()) +
"_part" + std::to_string(idx);
1164 temporaries.push_back({
name});
1165 channel.temporaries.push_back(std::make_unique<RooFormulaVar>(
1168 self(realComponents, self);
1170 }
else if (
auto real =
dynamic_cast<RooAbsReal *
>(
e)) {
1171 custom.push_back({real->GetName()});
1172 channel.customModifiers.push_back(real);
1178 collectElements(elems, func);
1179 collectElements(elems, sumpdf->
coefList().
at(sampleidx));
1180 processElements(elems, processElements);
1185 updateObservables(nh->dataHist());
1192 std::string sysname(var->
GetName());
1193 erasePrefix(sysname,
"alpha_");
1197 const auto *pdf = findConstraint(var);
1198 checkModifierParameter(*var, pdf);
1199 const std::string context =
"histosys modifier '" + sysname +
"' in sample '" + sample.
name +
1200 "' of channel '" + channel.name +
"'";
1204 {contents(
hi->dataHist()), std::nullopt},
1205 {contents(lo->dataHist()), std::nullopt},
1214 if (
startsWith(std::string(phf->GetName()),
"mc_stat_")) {
1215 statError.emplace();
1216 statError->name =
"staterror";
1219 for (
const auto &
g : phf->paramList()) {
1220 statError->parameters.push_back({
g->GetName()});
1223 pdfs.
pdfs.push_back(gammaConstraint(pdf));
1224 channel.totYield[idx] += sample.
contents[idx - 1];
1225 channel.totYield2[idx] += (sample.
contents[idx - 1] * sample.
contents[idx - 1]);
1227 channel.relErrors[idx] = constraintRelError(*pdf, *
g);
1230 statError->constraint = std::move(pdfs);
1233 sys.name = phf->GetName();
1234 erasePrefix(sys.name, channel.name +
"_");
1235 for (std::string_view suffix : {
"_ShapeSys",
"_shapeSys",
"_ShapeFactor",
"_shapeFactor"}) {
1236 if (eraseSuffix(sys.name, suffix))
1243 for (
const auto &
g : phf->paramList()) {
1244 sys.parameters.push_back({
g->GetName()});
1247 pdf = ws->
pdf(constraintName(
g->GetName()));
1248 pdfs.
pdfs.push_back(gammaConstraint(pdf));
1250 sys.constraint = std::move(pdfs);
1254 sortByName(normFactors);
1255 sortByName(normSys);
1256 sortByName(histoSys);
1257 sortByName(shapeSys);
1258 auto append = [&](
auto &modifiers) {
1259 sample.
modifiers.insert(sample.
modifiers.end(), std::make_move_iterator(modifiers.begin()),
1260 std::make_move_iterator(modifiers.end()));
1262 append(normFactors);
1267 append(temporaries);
1269 sample.
modifiers.push_back(std::move(*statError));
1272 channel.dist.
samples.push_back(std::move(sample));
1275 sortByName(channel.dist.
samples);
1281void configureStatError(ExportedChannel &channel)
1285 [](
auto const &
m) { return std::holds_alternative<HS3::StatError>(m); })) {
1288 std::vector<double> errors(sample.
contents.size());
1289 for (
auto const &bin : channel.relErrors) {
1290 const int i = bin.first;
1294 round_prec(bin.second * channel.totYield[i] / std::sqrt(channel.totYield2[i]) * sample.
contents[i - 1], 7);
1296 sample.
errors = std::move(errors);
1302std::set<RooAbsPdf const *>
1303nameChannelConstraints(ExportedChannel &channel, std::vector<RooAbsPdf *>
const &constraints)
1305 std::set<RooAbsPdf const *> named;
1306 auto isNamed = [&](std::string
const &
name) {
1308 if (std::find(constraints.begin(), constraints.end(), pdf) == constraints.end()) {
1315 auto const *pdf = std::get_if<HS3::ConstraintPdf>(&constraint);
1316 if (pdf && !isNamed(pdf->pdf.name)) {
1321 auto *pdfs = std::get_if<HS3::GammaConstraintPdfs>(&constraint);
1326 for (
auto &pdf : pdfs->pdfs) {
1327 if (pdf && !isNamed(pdf->name)) {
1351 std::vector<RooAbsPdf *> constraints)
1358 for (
RooAbsArg *sample : sumpdf->funcList()) {
1364 ExportedChannel channel = readChannel(tool, pdfname, sumpdf);
1368 if (
dist.samples.empty())
1370 for (
auto &sample :
dist.samples) {
1379 configureStatError(channel);
1386 std::set<RooAbsPdf const *> namedConstraints = nameChannelConstraints(channel, constraints);
1387 for (
RooAbsPdf const *pdf : namedConstraints) {
1394 for (
auto &
modifier : channel.temporaries) {
1403 if (!
startsWith(std::string{param->
GetName()},
"nom_") && !channel.legacyBinWidthFactors.count(param)) {
1409 std::vector<HS3::DistributionRef> channelConstraintNames;
1410 for (
RooAbsPdf const *pdf : constraints) {
1411 if (!namedConstraints.count(pdf)) {
1413 channelConstraintNames.push_back({pdf->
GetName()});
1423 bool autoExportDependants()
const override {
return false; }
1426 std::vector<RooAbsPdf *> constraints;
1437 constraints.push_back(pdf);
1443 return tryExportHistFactory(tool, prodpdf->
GetName(), sumpdf, constraints);
1445 std::string
const &key()
const override
1447 static const std::string keystring =
"histfactory_dist";
1452 return tryExport(tool,
static_cast<const RooProdPdf *
>(
p));
1458 bool autoExportDependants()
const override {
return false; }
1461 std::vector<RooAbsPdf *> constraints;
1462 return tryExportHistFactory(tool, sumpdf->
GetName(), sumpdf, constraints);
1464 std::string
const &key()
const override
1466 static const std::string keystring =
"histfactory_dist";
1478 registerImporter<HistFactoryImporter>(
"histfactory_dist",
true);
1479 registerImporter<PiecewiseInterpolationFactory>(
"interpolation",
true);
1480 registerImporter<FlexibleInterpVarFactory>(
"interpolation0d",
true);
1490 std::vector<DistributionRef>
const *channelConstraints)
1492 checkStatErrorConstraints(dist,
name);
1496 std::vector<std::unique_ptr<RooDataHist>>
data;
1497 data.reserve(dist.samples.size());
1498 std::vector<double> sumW;
1499 std::vector<double> sumW2;
1500 std::vector<ParameterRef> gammaParnames;
1501 for (
auto const &sample : dist.samples) {
1507 [](
auto const &
m) { return std::holds_alternative<StatError>(m); });
1511 sumW2.resize(nbins);
1514 const bool hasErrors = sample.
errors || dist.statError.impliedPoissonErrors;
1515 for (std::size_t i = 0; i < nbins; ++i) {
1516 sumW[i] += dh->
weight(i);
1519 if (gammaParnames.empty()) {
1520 gammaParnames = std::get<StatError>(*found).parameters;
1523 data.emplace_back(std::move(dh));
1529 std::size_t nBins = 1;
1530 for (
auto const &axis : dist.axes) {
1538 if (!sumW.empty()) {
1539 std::vector<double> errs(sumW.size());
1540 for (std::size_t i = 0; i < sumW.size(); ++i) {
1541 if (sumW[i] == 0.) {
1545 errs[i] = std::sqrt(sumW2[i]) / sumW[i];
1547 errs[i] = std::max(errs[i], 0.);
1552 dist.statError.relThreshold, nBins);
1555 ChannelContext ctx{tool, dist, observables,
name,
channelName, nBins, mcStatObject, constraints};
1559 for (std::size_t i = 0; i < dist.samples.size(); ++i) {
1561 buildSample(ctx, sample, *
data[i]);
1566 if (channelConstraints) {
1567 for (
auto const &ref : *channelConstraints) {
1572 constraints.
add(*pdf);
1580 sum.setAttribute(
"BinnedLikelihood");
1581 sum.setAttribute(
"GenerateBinned");
1583 if (constraints.
empty()) {
1586 std::string sumName =
name +
"_model";
1587 erasePrefix(sumName,
"model_");
1597 return dynamic_cast<HistFactoryImporter
const *
>(&importer) !=
nullptr;
1602 std::vector<DistributionRef>
const *channelConstraints)
1605 if (!
p.has_child(
"samples")) {
1608 std::stringstream ss;
bool startsWith(std::string_view str, std::string_view prefix)
bool endsWith(std::string_view str, std::string_view suffix)
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
double toDouble(const char *s)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void funcs
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t modifier
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
const RooArgList & paramList() const
The PiecewiseInterpolation is a class that can morph distributions into each other,...
const RooArgList & highList() const
const RooAbsReal * nominalHist() const
Return pointer to the nominal hist function.
bool positiveDefinite() const
const RooArgList & lowList() const
void setPositiveDefinite(bool flag=true)
const RooArgList & paramList() const
const std::vector< int > & interpolationCodes() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
bool isConstant() const
Check if the "Constant" attribute is set.
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
void treeNodeServerList(RooAbsCollection *list, const RooAbsArg *arg=nullptr, bool doBranch=true, bool doLeaf=true, bool valueOnly=false, bool recurseNonDerived=false) const
Fill supplied list with nodes of the arg tree, following all server links, starting with ourself as t...
Abstract container object that can hold multiple RooAbsArg objects.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
const_iterator end() const
Storage_t::size_type size() const
const_iterator begin() const
RooAbsArg * find(const char *name) const
Find object with given name in list.
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
Abstract interface for all probability density functions.
void setConstant(bool value=true)
Abstract base class for objects that represent a real value and implements functionality common to al...
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
RooArgList is a container object that can hold multiple RooAbsArg objects.
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Returns the bin width (or volume) given a RooHistFunc.
bool divideByBinWidth() const
Represents a constant real-valued object.
Container class to hold N-dimensional binned data.
double const * weightArray() const
double weight(std::size_t i) const
Return weight of i-th bin.
double weightSquared(std::size_t i) const
Return squared weight sum of i-th bin.
const RooArgSet * get() const override
Get bin centre of current bin.
static std::unique_ptr< JSONTree > create()
A real-valued function sampled from a multidimensional histogram.
RooAbsReal const & getMean() const
Get the mean parameter.
Efficient implementation of a product of PDFs of the form.
Represents the product of a given set of RooAbsReal objects.
Implements a PDF constructed from a sum of functions:
const RooArgList & funcList() const
const RooArgList & coefList() const
Variable that can be changed from the outside.
Persistable container for RooFit projects.
TObject * obj(RooStringView name) const
Return any type of object (RooAbsArg, RooAbsData or generic object) with given name)
RooAbsPdf * pdf(RooStringView name) const
Retrieve p.d.f (RooAbsPdf) with given name. A null pointer is returned if not found.
RooAbsArg * arg(RooStringView name) const
Return RooAbsArg with given name. A null pointer is returned if none is found.
RooFactoryWSTool & factory()
Return instance to factory tool.
bool import(const RooAbsArg &arg, const RooCmdArg &arg1={}, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}, const RooCmdArg &arg9={})
Import a RooAbsArg object, e.g.
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
const char * GetName() const override
Returns name of object.
Mother of all ROOT objects.
virtual const char * GetName() const
Returns name of object.
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
RooCmdArg RecycleConflictNodes(bool flag=true)
RooCmdArg Conditional(const RooArgSet &pdfSet, const RooArgSet &depSet, bool depsAreCond=false)
RooConstVar & RooConst(double val)
RooCmdArg Silence(bool flag=true)
CoordSystem::Scalar get(DisplacementVector2D< CoordSystem, Tag > const &p)
double dist(Rotation3D const &r1, Rotation3D const &r2)
Types that represent an HS3 document.
HistFactoryDist parseHistFactoryDist(Detail::JSONNode const &distribution, Diagnostics &diagnostics)
Reads a HistFactory distribution that doesn't come with a document.
void validateHistFactoryDist(HistFactoryDist const &dist, std::string const &name, Diagnostics &diagnostics)
Same as validate(), but for a single HistFactory distribution that doesn't come with a document.
void buildHistFactoryDist(RooJSONFactoryWSTool &tool, std::string const &name, HistFactoryDist const &dist, std::vector< DistributionRef > const *channelConstraints=nullptr)
Create the RooFit objects of a HistFactory distribution in the workspace of the tool.
void writeInterpolatedFunction(Detail::JSONNode &node, InterpolatedFunction const &function)
Writes the functions "interpolation" and "interpolation0d", with a single interpolation if they are a...
std::variant< NormFactor, NormSys, HistoSys, ShapeSys, StatError, CustomModifier > Modifier
InterpolationClass
The RooFit classes that implement the interpolations, which encode them with integer codes.
constexpr Interpolation multiplicativePolynomialExponential
constexpr Interpolation additivePolynomialLinear
void mergeDuplicateModifiers(HistFactoryDist &dist, std::string const &channel, Diagnostics &diagnostics)
Combines the modifiers of a sample that have the same type and name, which HistFactory models can hav...
ConstraintType
The constraint types that older documents named, for which RooFit creates the constraint terms.
std::optional< Interpolation > interpolationFromCode(int code, InterpolationClass interpolationClass)
The interpolation for an integer code, or nothing if the class doesn't support the code.
void factorOutDefaultInterpolation(HistFactoryDist &dist)
Makes the most common interpolation of the modifiers the "default_interpolation", which they then don...
void setExportedObject(RooJSONFactoryWSTool &tool, HistFactoryDist dist, std::vector< DistributionRef > channelConstraints)
Called by the exporter of a HistFactory distribution, which is then written by the tool.
char const * toString(InterpolationType type)
InterpolatedNumber parseInterpolatedNumber(Detail::JSONNode const &node, Diagnostics &diagnostics)
void upgradeHistFactoryDist(Detail::JSONNode &distribution, Version const &version, Diagnostics &diagnostics)
Same as upgradeDocument(), but for a single HistFactory distribution that doesn't come with a documen...
InterpolatedFunction parseInterpolatedFunction(Detail::JSONNode const &node, Diagnostics &diagnostics)
Reads the functions "interpolation" and "interpolation0d", which don't come with a document.
RooArgSet createObservables(std::vector< BinnedAxis > const &axes)
Create the variables for the axes of binned data.
std::optional< int > codeFromInterpolation(Interpolation const &interpolation, InterpolationClass interpolationClass)
The integer code of an interpolation, or nothing if the class can't represent the interpolation.
void importHistFactoryNode(RooJSONFactoryWSTool &tool, Detail::JSONNode const &node, std::vector< DistributionRef > const *channelConstraints)
What the built-in importer does with a HistFactory distribution, plus the constraint terms of the cha...
bool hasErrors(Diagnostics const &diagnostics)
Whether there is a diagnostic with error severity.
void writeInterpolatedNumber(Detail::JSONNode &node, InterpolatedNumber const &function)
constexpr Interpolation multiplicativePiecewiseExponential
std::vector< Diagnostic > Diagnostics
void report(Diagnostics const &diagnostics)
Forward the warnings to the message service, and throw if there are errors.
NameOrNumber toNameOrNumber(RooAbsArg const &arg)
The name of an object, or the value of a literal constant.
std::variant< Unconstrained, ConstraintType, ConstantGammas, GammaConstraintPdfs > GammaConstraint
bool parseVersion(std::string const &version, Version &out)
Reads the leading "MAJOR.MINOR" and the optional ".PATCH" of an HS3 version string.
BinnedAxis toBinnedAxis(RooRealVar const &var)
The axis of binned data for a variable.
std::unique_ptr< RooDataHist > createDataHist(std::string const &name, RooArgSet const &vars, std::vector< double > const &contents, std::vector< double > const *errors)
Create a RooDataHist with the contents and errors. If there are no errors, it doesn't store squared w...
std::variant< Unconstrained, ConstraintType, ConstraintPdf > Constraint
bool isBuiltinHistFactoryImporter(JSONIO::Importer const &importer)
Whether this is the importer that RooFit registers for HistFactory distributions, as opposed to the o...
constexpr double defaultShapeSysGammaMax
constexpr double minShapeUncertainty
constexpr double defaultStatErrorGammaMax
constexpr double defaultGammaMin
void configureConstrainedGammas(RooArgList const &gammas, std::span< const double > relSigmas, double minSigma)
Configure constrained gamma parameters for fitting.
CreateGammaConstraintsOutput createGammaConstraints(RooArgList const ¶mList, std::span< const double > relSigmas, double minSigma, Constraint::Type type)
void variables(TString dataset, TString fin="TMVA.root", TString dirName="InputVariables_Id", TString title="TMVA Input Variables", Bool_t isRegression=kFALSE, Bool_t useTMVAStyle=kTRUE)
#define STATIC_EXECUTE(MY_FUNC)
The gamma parameters are constant, which is a RooFit extension.
The constraint term is a distribution of the document, named in "constraints".
Multiplies the sample with a function of the document.
Every gamma parameter has its own constraint term, or none, as named in "constraints".
std::vector< std::optional< DistributionRef > > pdfs
std::optional< Interpolation > defaultInterpolation
std::vector< Sample > samples
The function "interpolation" of RooFit, implemented by PiecewiseInterpolation: a nominal function plu...
std::vector< Interpolation > interpolations
One per parameter.
std::vector< NameOrNumber > parameters
std::vector< NameOrNumber > low
std::vector< NameOrNumber > high
The function "interpolation0d" of RooFit, implemented by FlexibleInterpVar: a nominal value plus the ...
std::vector< double > high
std::vector< double > low
std::vector< std::string > parameters
std::vector< Interpolation > interpolations
One per parameter.
How a modifier of a HistFactory distribution interpolates and extrapolates its effect.
std::optional< InterpolationFunction > out
If not set, the function inside the bounds is used outside as well.
std::vector< double > contents
std::optional< std::vector< double > > errors
std::vector< Modifier > modifiers
Scales each bin by its own parameter. The modifier type "shapesys" is a deprecated alias of "shapefac...
Scales each bin by a parameter, that is constrained by the statistical uncertainty of all samples tha...
The parameter of the modifier is not constrained.
Combines lambdas into one visitor for std::visit(). The compiler complains about the alternatives wit...
static uint64_t sum(uint64_t i)