Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooJSONFactoryWSTool.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * Carsten D. Burgard, DESY/ATLAS, Dec 2021
5 *
6 * Copyright (c) 2022, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13#include <RooFitHS3/JSONIO.h>
15
16#include <RooConstVar.h>
17#include <RooRealVar.h>
18#include <RooBinning.h>
19#include <RooAbsCategory.h>
20#include <RooArgProxy.h>
21#include <RooAbsProxy.h>
22#include <RooCategory.h>
23#include <RooDataSet.h>
24#include <RooDataHist.h>
25#include <RooSimultaneous.h>
26#include <RooFit/ModelConfig.h>
27#include <RooFitImplHelpers.h>
28#include <RooAbsCollection.h>
29
30#include "JSONIOUtils.h"
31#include "Domains.h"
32
33#include <TROOT.h>
34
35#include <algorithm>
36#include <fstream>
37#include <iostream>
38#include <sstream>
39#include <stdexcept>
40
41/** \class RooJSONFactoryWSTool
42\ingroup roofit_dev_docs_hs3
43
44When using \ref Roofitmain, statistical models can be conveniently handled and
45stored as a RooWorkspace. However, for the sake of interoperability
46with other statistical frameworks, and also ease of manipulation, it
47may be useful to store statistical models in text form.
48
49The RooJSONFactoryWSTool is a helper class to achieve exactly this,
50exporting to and importing from JSON.
51
52In order to import a workspace from a JSON file, you can do
53
54~~~ {.py}
55ws = ROOT.RooWorkspace("ws")
56tool = ROOT.RooJSONFactoryWSTool(ws)
57tool.importJSON("myjson.json")
58~~~
59
60Similarly, in order to export a workspace to a JSON file, you can do
61
62~~~ {.py}
63tool = ROOT.RooJSONFactoryWSTool(ws)
64tool.exportJSON("myjson.json")
65~~~
66
67Analogously, in C++, you can do
68
69~~~ {.cxx}
70#include "RooFitHS3/RooJSONFactoryWSTool.h"
71// ...
72RooWorkspace ws("ws");
73RooJSONFactoryWSTool tool(ws);
74tool.importJSON("myjson.json");
75~~~
76
77and
78
79~~~ {.cxx}
80#include "RooFitHS3/RooJSONFactoryWSTool.h"
81// ...
82RooJSONFactoryWSTool tool(ws);
83tool.exportJSON("myjson.json");
84~~~
85
86For more details, consult the tutorial <a href="rf515__hfJSON_8py.html">rf515_hfJSON</a>.
87
88The RooJSONFactoryWSTool only knows about a limited set of classes for
89import and export. If import or export of a class you're interested in
90fails, you might need to add your own importer or exporter. Please
91consult the relevant section in the \ref roofit_dev_docs to learn how to do that (\ref roofit_dev_docs_hs3).
92
93You can always get a list of all the available importers and exporters by calling the following functions:
94~~~ {.py}
95ROOT.RooFit.JSONIO.printImporters()
96ROOT.RooFit.JSONIO.printExporters()
97ROOT.RooFit.JSONIO.printFactoryExpressions()
98ROOT.RooFit.JSONIO.printExportKeys()
99~~~
100
101Alternatively, you can generate a LaTeX version of the available importers and exporters by calling
102~~~ {.py}
103tool = ROOT.RooJSONFactoryWSTool(ws)
104tool.writedoc("hs3.tex")
105~~~
106*/
107
108constexpr auto hs3VersionTag = "0.2";
109
112
113namespace {
114
115std::vector<std::string> valsToStringVec(JSONNode const &node)
116{
117 std::vector<std::string> out;
118 out.reserve(node.num_children());
119 for (JSONNode const &elem : node.children()) {
120 out.push_back(elem.val());
121 }
122 return out;
123}
124
125// True if the number of components in `data` matches the number of categories in `pdf`.
126bool matches(const RooJSONFactoryWSTool::CombinedData &data, const RooSimultaneous *pdf)
127{
128 return data.components.size() == pdf->indexCat().size();
129}
130
131// True if the entire string parses as a number (integer or floating-point).
132bool isNumber(const std::string &str)
133{
134 // Parse with the same mechanism as toDouble() and require that the whole string is consumed, so that isNumber(s) is
135 // true exactly when toDouble(s) can turn the entire string into a value. (std::from_chars for floating-point types
136 // is not portably available on all platforms ROOT supports, so we rely on the stream extraction instead.)
137 std::istringstream stream(str);
138 double value = 0.0;
139 return (stream >> value) && stream.eof();
140}
141
142// Configure `v` (value, error, binning, constness) from the JSON node `p`.
144{
145 if (!p.has_child("name")) {
146 RooJSONFactoryWSTool::error("cannot instantiate variable without \"name\"!");
147 }
148 if (auto n = p.find("value"))
149 v.setVal(n->val_double());
150 domains.writeVariable(v);
151 if (auto n = p.find("nbins"))
152 v.setBins(n->val_int());
153 if (auto n = p.find("relErr"))
154 v.setError(v.getVal() * n->val_double());
155 if (auto n = p.find("err"))
156 v.setError(n->val_double());
157 if (auto n = p.find("const")) {
158 v.setConstant(n->val_bool());
159 } else {
160 v.setConstant(false);
161 }
162}
163
165{
166 auto paramPointsNode = rootNode.find("parameter_points");
167 if (!paramPointsNode)
168 return nullptr;
169 auto out = RooJSONFactoryWSTool::findNamedChild(*paramPointsNode, "default_values");
170 if (out == nullptr)
171 return nullptr;
172 return &((*out)["parameters"]);
173}
174
175std::string genPrefix(const JSONNode &p, bool trailing_underscore)
176{
177 std::string prefix;
178 if (!p.is_map())
179 return prefix;
180 if (auto node = p.find("namespaces")) {
181 for (const auto &ns : node->children()) {
182 if (!prefix.empty())
183 prefix += "_";
184 prefix += ns.val();
185 }
186 }
187 if (trailing_underscore && !prefix.empty())
188 prefix += "_";
189 return prefix;
190}
191
192// helpers for serializing / deserializing binned datasets
193void genIndicesHelper(std::vector<std::vector<int>> &combinations, std::vector<int> &curr_comb,
194 const std::vector<int> &vars_numbins, size_t curridx)
195{
196 if (curridx == vars_numbins.size()) {
197 // we have filled a combination. Copy it.
198 combinations.emplace_back(curr_comb);
199 } else {
200 for (int i = 0; i < vars_numbins[curridx]; ++i) {
201 curr_comb[curridx] = i;
203 }
204 }
205}
206
207// Import string attributes ("dict") and boolean tags ("tags") from `node` onto `arg`.
208void importAttributes(RooAbsArg *arg, JSONNode const &node)
209{
210 if (auto seq = node.find("dict")) {
211 for (const auto &attr : seq->children()) {
212 arg->setStringAttribute(attr.key().c_str(), attr.val().c_str());
213 }
214 }
215 if (auto seq = node.find("tags")) {
216 for (const auto &attr : seq->children()) {
217 arg->setAttribute(attr.val().c_str());
218 }
219 }
220}
221
222void addIfPresent(RooArgSet &out, RooArgSet const *args)
223{
224 if (args) {
225 out.add(*args, true);
226 }
227}
228
231{
232 for (TObject *obj : workspace.allGenericObjects()) {
233 auto const *mc = dynamic_cast<RooFit::ModelConfig const *>(obj);
234 if (!mc) {
235 continue;
236 }
237
238 addIfPresent(candidates, mc->GetParametersOfInterest());
239 addIfPresent(candidates, mc->GetNuisanceParameters());
240
241 addIfPresent(excluded, mc->GetObservables());
242 addIfPresent(excluded, mc->GetGlobalObservables());
243 addIfPresent(excluded, mc->GetConditionalObservables());
244 }
245}
246
247void collectParameterStepWidthCandidatesFromPdfs(std::vector<RooAbsPdf *> const &pdfs,
248 std::vector<RooAbsData *> const &data, RooArgSet &candidates,
250{
251 for (RooAbsPdf const *pdf : pdfs) {
252 RooArgSet observables;
253 for (RooAbsData const *dataset : data) {
254 std::unique_ptr<RooArgSet> pdfObs{pdf->getObservables(*dataset->get())};
255 observables.add(*pdfObs, true);
256 }
257
258 if (observables.empty()) {
259 continue;
260 }
261
262 RooArgSet params;
263 pdf->getParameters(&observables, params);
264 candidates.add(params, true);
265 excluded.add(observables, true);
266 }
267}
268
269void exportParameterStepWidths(RooWorkspace const &workspace, std::vector<RooAbsPdf *> const &pdfs,
270 std::vector<RooAbsData *> const &data, JSONNode &rootnode)
271{
274
277
278 candidates.sort();
279
281 for (RooAbsArg *arg : candidates) {
282 if (excluded.find(*arg)) {
283 continue;
284 }
285
286 auto *var = dynamic_cast<RooRealVar *>(arg);
287 if (!var || !var->hasError()) {
288 continue;
289 }
290
292 parameterStepWidthsNode = &rootnode["misc"]["minimization"]["parameter_stepwidths"].set_seq();
293 }
294
296 stepWidthNode["step_width"] << var->getError();
297 }
298}
299
300void importParameterStepWidths(RooWorkspace &workspace, JSONNode const &rootnode)
301{
302 auto const *parameterStepWidthsNode = rootnode.find("misc", "minimization", "parameter_stepwidths");
304 return;
305 }
306 if (!parameterStepWidthsNode->is_seq()) {
307 RooJSONFactoryWSTool::warning("RooFitHS3: misc.minimization.parameter_stepwidths is not a sequence, skipping.");
308 return;
309 }
310
311 for (JSONNode const &stepWidthNode : parameterStepWidthsNode->children()) {
312 if (!stepWidthNode.is_map() || !stepWidthNode.has_child("name") || !stepWidthNode.has_child("step_width")) {
313 RooJSONFactoryWSTool::warning("RooFitHS3: skipping malformed parameter_stepwidths entry.");
314 continue;
315 }
316
317 const std::string name = RooJSONFactoryWSTool::name(stepWidthNode);
318 RooAbsArg *arg = workspace.arg(name);
319 auto *var = dynamic_cast<RooRealVar *>(arg);
320 if (!var) {
322 "RooFitHS3: skipping parameter_stepwidths entry for unknown or non-real variable '" + name + "'.");
323 continue;
324 }
325
326 var->setError(stepWidthNode.find("step_width")->val_double());
327 }
328}
329
330// RooWSFactoryTool expression handling
331std::string generate(const RooFit::JSONIO::ImportExpression &ex, const JSONNode &p, RooJSONFactoryWSTool *tool)
332{
333 std::stringstream expression;
334 std::string classname(ex.tclass->GetName());
335 size_t colon = classname.find_last_of(':');
336 expression << (colon < classname.size() ? classname.substr(colon + 1) : classname);
337 bool first = true;
338 const auto &name = RooJSONFactoryWSTool::name(p);
339 for (auto k : ex.arguments) {
340 expression << (first ? "::" + name + "(" : ",");
341 first = false;
342 if (k == "true" || k == "false") {
343 expression << (k == "true" ? "1" : "0");
344 } else if (!p.has_child(k)) {
345 RooJSONFactoryWSTool::error("node '" + name + "' is missing key '" + k + "'");
346 } else if (p[k].is_seq()) {
347 bool firstInner = true;
348 expression << "{";
349 for (RooAbsArg *arg : tool->requestArgList<RooAbsReal>(p, k)) {
350 expression << (firstInner ? "" : ",") << arg->GetName();
351 firstInner = false;
352 }
353 expression << "}";
354 } else {
355 tool->requestArg<RooAbsReal>(p, p[k].key());
356 expression << p[k].val();
357 }
358 }
359 expression << ")";
360 return expression.str();
361}
362
363// Generate all combinations of bin indices for the RooRealVars in `vars`.
364std::vector<std::vector<int>> generateBinIndices(const RooArgSet &vars)
365{
366 std::vector<std::vector<int>> combinations;
367 std::vector<int> vars_numbins;
368 vars_numbins.reserve(vars.size());
369 for (const auto *absv : static_range_cast<RooRealVar *>(vars)) {
370 vars_numbins.push_back(absv->getBins());
371 }
372 std::vector<int> curr_comb(vars.size());
374 return combinations;
375}
376
377template <typename... Keys_t>
378JSONNode const *findRooFitInternal(JSONNode const &node, Keys_t const &...keys)
379{
380 return node.find("misc", "ROOT_internal", keys...);
381}
382
383// True if `arg` is a RooConstVar whose name is a plain number (i.e. a literal constant).
384bool isLiteralConstVar(RooAbsArg const &arg)
385{
386 bool isRooConstVar = dynamic_cast<RooConstVar const *>(&arg);
387 return isRooConstVar && isNumber(arg.GetName());
388}
389
390// Export the string attributes and tags of `arg` into the ROOT-internal attributes node.
391void exportAttributes(const RooAbsArg *arg, JSONNode &rootnode)
392{
393 // If this RooConst is a literal number, we don't need to export the attributes.
394 if (isLiteralConstVar(*arg)) {
395 return;
396 }
397
398 JSONNode *node = nullptr;
399
400 auto initializeNode = [&]() {
401 if (node)
402 return;
403
404 node = &RooJSONFactoryWSTool::getRooFitInternal(rootnode, "attributes").set_map()[arg->GetName()].set_map();
405 };
406
407 if (dynamic_cast<RooConstVar const *>(arg)) {
408 return;
409 }
410
411 // export all string attributes of an object
412 if (!arg->stringAttributes().empty()) {
413 for (const auto &it : arg->stringAttributes()) {
414 // Skip some RooFit internals
415 if (it.first == "factory_tag" || it.first == "PROD_TERM_TYPE")
416 continue;
418 (*node)["dict"].set_map()[it.first] << it.second;
419 }
420 }
421 if (!arg->attributes().empty()) {
422 for (auto const &attr : arg->attributes()) {
423 // Skip some RooFit internals
424 if (attr == "SnapShot_ExtRefClone" || attr == "RooRealConstant_Factory_Object")
425 continue;
427 (*node)["tags"].set_seq().append_child() << attr;
428 }
429 }
430}
431
432// Collect the observables named in the "axes" field of `node` from the workspace into `out`.
433void getObservables(RooWorkspace const &ws, const JSONNode &node, RooAbsCollection &out)
434{
435 for (const auto &p : node["axes"].children()) {
436 std::string name(RooJSONFactoryWSTool::name(p));
437 if (ws.var(name)) {
438 out.add(*ws.var(name));
439 } else {
440 RooJSONFactoryWSTool::error("The observable \"" + name + "\" could not be found in the workspace!");
441 }
442 }
443}
444
445// Create a RooAbsData (binned or unbinned) from the JSON node `p`.
446std::unique_ptr<RooAbsData> loadData(const JSONNode &p, RooWorkspace &workspace)
447{
448 std::string name(RooJSONFactoryWSTool::name(p));
449
451
452 std::string const &type = p["type"].val();
453 if (type == "binned") {
454 // binned
456 } else if (type == "unbinned") {
457 // unbinned
458 RooArgList varlist;
459 getObservables(workspace, p, varlist);
460 RooArgSet vars(varlist);
461 auto data = std::make_unique<RooDataSet>(name, name, vars, RooFit::WeightVar());
462 auto &coords = p["entries"];
463 if (!coords.is_seq()) {
464 RooJSONFactoryWSTool::error("key 'entries' is not a list!");
465 }
466 std::vector<double> weightVals;
467 if (p.has_child("weights")) {
468 auto &weights = p["weights"];
469 if (coords.num_children() != weights.num_children()) {
470 RooJSONFactoryWSTool::error("inconsistent number of entries and weights!");
471 }
472 for (auto const &weight : weights.children()) {
473 weightVals.push_back(weight.val_double());
474 }
475 }
476 std::size_t i = 0;
477 for (auto const &point : coords.children()) {
478 if (!point.is_seq()) {
479 RooJSONFactoryWSTool::error("coordinate point '" + std::to_string(i) + "' is not a list!");
480 }
481 if (point.num_children() != varlist.size()) {
482 RooJSONFactoryWSTool::error("inconsistent number of entries and observables!");
483 }
484 std::size_t j = 0;
485 for (auto const &pointj : point.children()) {
486 auto *v = static_cast<RooRealVar *>(varlist.at(j));
487 v->setVal(pointj.val_double());
488 ++j;
489 }
490 if (weightVals.size() > 0) {
491 data->add(vars, weightVals[i]);
492 } else {
493 data->add(vars, 1.);
494 }
495 ++i;
496 }
497 return data;
498 }
499
500 RooJSONFactoryWSTool::error("RooJSONFactoryWSTool() failed to create dataset " + name);
501}
502
503// Import an analysis (likelihood + domains) as one or more ModelConfig objects into the workspace.
504void importAnalysis(const JSONNode &rootnode, const JSONNode &analysisNode, const JSONNode &likelihoodsNode,
505 const JSONNode &domainsNode, RooWorkspace &workspace,
506 const std::vector<std::unique_ptr<RooAbsData>> &datasets)
507{
508 // if this is a toplevel pdf, also create a modelConfig for it
510 JSONNode const *mcAuxNode = findRooFitInternal(rootnode, "ModelConfigs", analysisName);
511
512 JSONNode const *mcNameNode = mcAuxNode ? mcAuxNode->find("mcName") : nullptr;
513 std::string mcname = mcNameNode ? mcNameNode->val() : analysisName;
514 if (workspace.obj(mcname))
515 return;
516
517 workspace.import(RooFit::ModelConfig{mcname.c_str(), mcname.c_str()});
518 auto *mc = static_cast<RooFit::ModelConfig *>(workspace.obj(mcname));
519 mc->SetWS(workspace);
520
522 if (!nllNode) {
523 throw std::runtime_error("likelihood node not found!");
524 }
525 if (!nllNode->has_child("distributions")) {
526 throw std::runtime_error("likelihood node has no distributions attached!");
527 }
528 if (!nllNode->has_child("data")) {
529 throw std::runtime_error("likelihood node has no data attached!");
530 }
531 std::vector<std::string> nllDistNames = valsToStringVec((*nllNode)["distributions"]);
533 for (auto &nameNode : (*nllNode)["aux_distributions"].children()) {
534 if (RooAbsArg *extConstraint = workspace.arg(nameNode.val())) {
536 }
537 }
538 RooArgSet observables;
539 for (auto &nameNode : (*nllNode)["data"].children()) {
540 bool found = false;
541 for (const auto &d : datasets) {
542 if (d->GetName() == nameNode.val()) {
543 found = true;
544 observables.add(*d->get(), true);
545 }
546 }
547 if (nameNode.val() != "0" && !found)
548 throw std::runtime_error("dataset '" + nameNode.val() + "' cannot be found!");
549 }
550
551 JSONNode const *pdfNameNode = mcAuxNode ? mcAuxNode->find("pdfName") : nullptr;
552 std::string const pdfName = pdfNameNode ? pdfNameNode->val() : "simPdf";
553
554 RooAbsPdf *pdf = workspace.pdf(pdfName);
555
556 if (!pdf) {
557 // if there is no simultaneous pdf, we can check whether there is only one pdf in the list
558 if (nllDistNames.size() == 1) {
559 // if so, we can use that one to populate the ModelConfig
560 pdf = workspace.pdf(nllDistNames[0]);
561 } else {
562 // otherwise, we have no choice but to build a simPdf by hand
563 std::string simPdfName = analysisName + "_simPdf";
564 std::string indexCatName = analysisName + "_categoryIndex";
565 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
566 std::map<std::string, RooAbsPdf *> pdfMap;
567 for (std::size_t i = 0; i < nllDistNames.size(); ++i) {
568 indexCat.defineType(nllDistNames[i], i);
569 pdfMap[nllDistNames[i]] = workspace.pdf(nllDistNames[i]);
570 }
571 RooSimultaneous simPdf{simPdfName.c_str(), simPdfName.c_str(), pdfMap, indexCat};
573 pdf = workspace.pdf(simPdfName);
574 }
575 }
576
577 mc->SetPdf(*pdf);
578
579 if (!extConstraints.empty())
580 mc->SetExternalConstraints(extConstraints);
581
582 auto readArgSet = [&](std::string const &name) {
583 RooArgSet out;
584 for (auto const &child : analysisNode[name].children()) {
585 out.add(*workspace.arg(child.val()));
586 }
587 return out;
588 };
589
590 mc->SetParametersOfInterest(readArgSet("parameters_of_interest"));
591 mc->SetObservables(observables);
592 RooArgSet pars;
593 pdf->getParameters(&observables, pars);
594
595 // Figure out the set parameters that appear in the main measurement:
596 // getAllConstraints() has the side effect to remove all parameters from
597 // "mainPars" that are not part of any pdf over observables.
598 RooArgSet mainPars{pars};
599 pdf->getAllConstraints(observables, mainPars, /*stripDisconnected*/ true);
600
602 for (auto &domain : analysisNode["domains"].children()) {
604 if (!thisDomain || !thisDomain->has_child("axes"))
605 continue;
606 for (auto &var : (*thisDomain)["axes"].children()) {
607 auto *wsvar = workspace.var(RooJSONFactoryWSTool::name(var));
608 if (wsvar)
609 domainPars.add(*wsvar);
610 }
611 }
612
614 RooArgSet globs;
615 for (const auto &p : pars) {
616 if (mc->GetParametersOfInterest()->find(*p))
617 continue;
618 if (p->isConstant() && !mainPars.find(*p) && domainPars.find(*p)) {
619 globs.add(*p);
620 } else if (domainPars.find(*p)) {
621 nps.add(*p);
622 }
623 }
624
625 mc->SetGlobalObservables(globs);
626 mc->SetNuisanceParameters(nps);
627
628 if (mcAuxNode) {
629 if (auto found = mcAuxNode->find("combined_data_name")) {
630 pdf->setStringAttribute("combined_data_name", found->val().c_str());
631 }
632 }
633
634 if (analysisNode.has_child("init") && workspace.getSnapshot(analysisNode["init"].val().c_str())) {
635 mc->SetSnapshot(*workspace.getSnapshot(analysisNode["init"].val().c_str()));
636 }
637}
638
639void combinePdfs(const JSONNode &rootnode, RooWorkspace &ws)
640{
641 auto *combinedPdfInfoNode = findRooFitInternal(rootnode, "combined_distributions");
642
643 // If there is no info on combining pdfs
644 if (combinedPdfInfoNode == nullptr) {
645 return;
646 }
647
648 for (auto &info : combinedPdfInfoNode->children()) {
649
650 // parse the information
651 std::string combinedName = info.key();
652 std::string indexCatName = info["index_cat"].val();
653 std::vector<std::string> labels = valsToStringVec(info["labels"]);
654 std::vector<int> indices;
655 std::vector<std::string> pdfNames = valsToStringVec(info["distributions"]);
656 for (auto &n : info["indices"].children()) {
657 indices.push_back(n.val_int());
658 }
659
660 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
661 std::map<std::string, RooAbsPdf *> pdfMap;
662
663 for (std::size_t iChannel = 0; iChannel < labels.size(); ++iChannel) {
664 indexCat.defineType(labels[iChannel], indices[iChannel]);
665 pdfMap[labels[iChannel]] = ws.pdf(pdfNames[iChannel]);
666 }
667
668 RooSimultaneous simPdf{combinedName.c_str(), combinedName.c_str(), pdfMap, indexCat};
670 }
671}
672
673void combineDatasets(const JSONNode &rootnode, std::vector<std::unique_ptr<RooAbsData>> &datasets)
674{
675 auto *combinedDataInfoNode = findRooFitInternal(rootnode, "combined_datasets");
676
677 // If there is no info on combining datasets
678 if (combinedDataInfoNode == nullptr) {
679 return;
680 }
681
682 for (auto &info : combinedDataInfoNode->children()) {
683
684 // parse the information
685 std::string combinedName = info.key();
686 std::string indexCatName = info["index_cat"].val();
687 std::vector<std::string> labels = valsToStringVec(info["labels"]);
688 std::vector<int> indices;
689 for (auto &n : info["indices"].children()) {
690 indices.push_back(n.val_int());
691 }
692 if (indices.size() != labels.size()) {
693 RooJSONFactoryWSTool::error("mismatch in number of indices and labels!");
694 }
695
696 // Create the combined dataset for RooFit
697 std::map<std::string, std::unique_ptr<RooAbsData>> dsMap;
698 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
699 RooArgSet allVars{indexCat};
700 for (std::size_t iChannel = 0; iChannel < labels.size(); ++iChannel) {
701 auto componentName = combinedName + "_" + labels[iChannel];
702 // We move the found channel data out of the "datasets" vector, such that
703 // the data components don't get imported anymore.
704 std::unique_ptr<RooAbsData> &component = *std::find_if(
705 datasets.begin(), datasets.end(), [&](auto &d) { return d && d->GetName() == componentName; });
706 if (!component)
707 RooJSONFactoryWSTool::error("unable to obtain component matching component name '" + componentName + "'");
708 allVars.add(*component->get(), true);
709 dsMap.insert({labels[iChannel], std::move(component)});
710 indexCat.defineType(labels[iChannel], indices[iChannel]);
711 }
712
713 auto combined = std::make_unique<RooDataSet>(combinedName, combinedName, allVars, RooFit::Import(dsMap),
714 RooFit::Index(indexCat));
715 datasets.emplace_back(std::move(combined));
716 }
717}
718
719template <class T>
720void sortByName(T &coll)
721{
722 std::sort(coll.begin(), coll.end(), [](auto &l, auto &r) { return strcmp(l->GetName(), r->GetName()) < 0; });
723}
724
725/// Check whether an object is a top-level object of the workspace, i.e. not a
726/// component of some other workspace object. Only clients that are part of the
727/// workspace count: RooFit also registers evaluation artifacts as clients,
728/// like the normalization integral that RooAbsPdf caches after
729/// getVal(normSet), or the integral returned by createIntegral(). These live
730/// outside the workspace and are not evidence that the object is a sub-node of
731/// a bigger model (see https://github.com/root-project/root/issues/23221).
732bool isTopLevel(RooAbsArg const &arg, RooWorkspace const &ws)
733{
734 for (RooAbsArg const *client : arg.clients()) {
735 if (ws.components().containsInstance(*client)) {
736 return false;
737 }
738 }
739 return true;
740}
741
742/// Find the single category observable of a dataset, if any.
744{
745 RooAbsCategory *cat = nullptr;
746 for (RooAbsArg *obs : *data.get()) {
747 if (auto *c = dynamic_cast<RooAbsCategory *>(obs)) {
748 if (cat) {
749 RooJSONFactoryWSTool::error("dataset '" + std::string(data.GetName()) +
750 " has several category observables!");
751 }
752 cat = c;
753 }
754 }
755 return cat;
756}
757
758} // namespace
759
761
763
765{
766 const size_t old_children = node.num_children();
767 node.set_seq();
768 size_t n = 0;
769 for (RooAbsArg const *arg : coll) {
770 if (n >= nMax)
771 break;
772 if (isLiteralConstVar(*arg)) {
773 node.append_child() << static_cast<RooConstVar const *>(arg)->getVal();
774 } else {
775 node.append_child() << arg->GetName();
776 }
777 ++n;
778 }
779 if (node.num_children() != old_children + coll.size()) {
780 error("unable to stream collection " + std::string(coll.GetName()) + " to " + node.key());
781 }
782}
783
785{
787 return node.set_map()[name].set_map();
788 }
789 JSONNode &child = node.set_seq().append_child().set_map();
790 child["name"] << name;
791 return child;
792}
793
794JSONNode const *RooJSONFactoryWSTool::findNamedChild(JSONNode const &node, std::string const &name)
795{
797 if (!node.is_map())
798 return nullptr;
799 return node.find(name);
800 }
801 if (!node.is_seq())
802 return nullptr;
803 for (JSONNode const &child : node.children()) {
804 if (child["name"].val() == name)
805 return &child;
806 }
807
808 return nullptr;
809}
810
811/**
812 * @brief Check if a string is a valid name.
813 *
814 * A valid name should start with a letter or an underscore, followed by letters, digits, or underscores.
815 * Only characters from the ASCII character set are allowed.
816 *
817 * @param str The string to be checked.
818 * @return bool Returns true if the string is a valid name; otherwise, returns false.
819 */
820bool RooJSONFactoryWSTool::isValidName(const std::string &str)
821{
822 // Check if the string is empty or starts with a non-letter/non-underscore character
823 if (str.empty() || !(std::isalpha(str[0]) || str[0] == '_')) {
824 return false;
825 }
826
827 // Check the remaining characters in the string
828 for (char c : str) {
829 // Allow letters, digits, and underscore
830 if (!(std::isalnum(c) || c == '_')) {
831 return false;
832 }
833 }
834
835 // If all characters are valid, the string is a valid name
836 return true;
837}
838
840{
842 std::stringstream ss;
843 ss << "RooJSONFactoryWSTool() name '" << name << "' is not valid!" << std::endl
844 << "Sanitize names by setting RooJSONFactoryWSTool::config().allowSanitizeNames = true." << std::endl;
845 if (RooJSONFactoryWSTool::config().allowExportInvalidNames && !forceError) {
847 return false;
848 } else {
850 }
851 }
852 return true;
853}
854
856{
857 return useListsInsteadOfDicts ? n["name"].val() : n.key();
858}
859
861{
862 return appendNamedChild(rootNode["parameter_points"], "default_values")["parameters"];
863}
864
865template <>
866RooRealVar *RooJSONFactoryWSTool::requestImpl<RooRealVar>(const std::string &objname)
867{
869 return retval;
870 if (const auto *vars = getVariablesNode(*_rootnodeInput)) {
871 if (const auto &node = vars->find(objname)) {
872 this->importVariable(*node);
874 return retval;
875 }
876 }
877 return nullptr;
878}
879
880template <>
881RooAbsPdf *RooJSONFactoryWSTool::requestImpl<RooAbsPdf>(const std::string &objname)
882{
884 return retval;
885 auto it = _distributionsByName.find(objname);
886 if (it != _distributionsByName.end()) {
887 this->importFunction(*it->second, true);
889 return retval;
890 }
891 return nullptr;
892}
893
894template <>
895RooAbsReal *RooJSONFactoryWSTool::requestImpl<RooAbsReal>(const std::string &objname)
896{
898 return retval;
899 if (isNumber(objname))
902 return pdf;
904 return var;
906 return retval;
907 auto it = _functionsByName.find(objname);
908 if (it != _functionsByName.end()) {
909 this->importFunction(*it->second, true);
911 return retval;
912 }
913 return nullptr;
914}
915
916// Export a single variable (RooRealVar or RooConstVar) `v` as a named child of `node`.
918{
919 auto *cv = dynamic_cast<const RooConstVar *>(v);
920 auto *rrv = dynamic_cast<const RooRealVar *>(v);
921 if (!cv && !rrv)
922 return;
923
924 // for RooConstVar, if name and value are the same, we don't need to do anything
925 if (cv && strcmp(cv->GetName(), TString::Format("%g", cv->getVal()).Data()) == 0) {
926 return;
927 }
928
929 JSONNode &var = appendNamedChild(node, v->GetName());
930
931 if (cv) {
932 var["value"] << cv->getVal();
933 var["const"] << true;
934 } else if (rrv) {
935 var["value"] << rrv->getVal();
936 if (storeConstant && (rrv->isConstant() || rrv->getMin() >= rrv->getMax())) {
937 var["const"] << true;
938 } else if (storeBins) {
939 var["min"] << rrv->getMin();
940 var["max"] << rrv->getMax();
941 }
942 if (rrv->getBins() != 100 && storeBins) {
943 var["nbins"] << rrv->getBins();
944 }
945 _domains->readVariable(*rrv);
946 }
947}
948
949// Export all variables in `allElems` as a sequence under `n`.
951{
952 // export a list of RooRealVar objects
953 n.set_seq();
954 for (RooAbsArg *arg : allElems) {
956 }
957}
958
960 const std::string &formula)
961{
962 std::string newname = std::string(original->GetName()) + suffix;
964 trafo_node["type"] << "generic";
965 trafo_node["expression"] << TString::Format(formula.c_str(), original->GetName()).Data();
966 this->setAttribute(newname, "roofit_skip"); // this function should not be imported back in
967 return newname;
968}
969
970// Export a single object `func` (pdf, function, variable or category) to the output JSON, recording its name in
971// `exportedObjectNames` to avoid exporting it twice.
972void RooJSONFactoryWSTool::exportObject(RooAbsArg const &func, std::set<std::string> &exportedObjectNames)
973{
974 std::string name = func.GetName();
975
976 // if this element was already exported, skip
978 return;
979
981
982 if (auto simPdf = dynamic_cast<RooSimultaneous const *>(&func)) {
983 // RooSimultaneous is not used in the HS3 standard, we only export the
984 // dependents and some ROOT internal information.
986
987 std::vector<std::string> channelNames;
988 for (auto const &item : simPdf->indexCat()) {
989 channelNames.push_back(item.first);
990 }
991
992 auto &infoNode = getRooFitInternal(*_rootnodeOutput, "combined_distributions").set_map();
993 auto &child = infoNode[simPdf->GetName()].set_map();
994 child["index_cat"] << simPdf->indexCat().GetName();
995 exportCategory(simPdf->indexCat(), child);
996 child["distributions"].set_seq();
997 for (auto const &item : simPdf->indexCat()) {
998 child["distributions"].append_child() << simPdf->getPdf(item.first.c_str())->GetName();
999 }
1000
1001 return;
1002 } else if (dynamic_cast<RooAbsCategory const *>(&func)) {
1003 // categories are created by the respective RooSimultaneous, so we're skipping the export here
1004 return;
1005 } else if (dynamic_cast<RooRealVar const *>(&func) || dynamic_cast<RooConstVar const *>(&func)) {
1006 exportVariable(&func, *_varsNode, true, false);
1007 return;
1008 }
1009
1010 auto &collectionNode = (*_rootnodeOutput)[dynamic_cast<RooAbsPdf const *>(&func) ? "distributions" : "functions"];
1011
1012 auto const &exporters = RooFit::JSONIO::exporters();
1013 auto const &exportKeys = RooFit::JSONIO::exportKeys();
1014
1015 TClass *cl = func.IsA();
1016
1018
1019 auto it = exporters.find(cl);
1020 if (it != exporters.end()) { // check if we have a specific exporter available
1021 for (auto &exp : it->second) {
1022 _serversToExport.clear();
1023 _serversToDelete.clear();
1024 if (!exp->exportObject(this, &func, elem)) {
1025 // The exporter might have messed with the content of the node
1026 // before failing. That's why we clear it and only reset the name.
1027 elem.clear();
1028 elem.set_map();
1030 elem["name"] << name;
1031 }
1032 continue;
1033 }
1034 if (exp->autoExportDependants()) {
1036 } else {
1038 }
1039 for (auto &s : _serversToDelete) {
1040 delete s;
1041 }
1042 return;
1043 }
1044 }
1045
1046 // generic export using the factory expressions
1047 const auto &dict = exportKeys.find(cl);
1048 if (dict == exportKeys.end()) {
1049 std::cerr << "unable to export class '" << cl->GetName() << "' - no export keys available!\n"
1050 << "there are several possible reasons for this:\n"
1051 << " 1. " << cl->GetName() << " is a custom class that you or some package you are using added.\n"
1052 << " 2. " << cl->GetName()
1053 << " is a ROOT class that nobody ever bothered to write a serialization definition for.\n"
1054 << " 3. something is wrong with your setup, e.g. you might have called "
1055 "RooFit::JSONIO::clearExportKeys() and/or never successfully read a file defining these "
1056 "keys with RooFit::JSONIO::loadExportKeys(filename)\n"
1057 << "either way, please make sure that:\n"
1058 << " 3: you are reading a file with export keys - call RooFit::JSONIO::printExportKeys() to "
1059 "see what is available\n"
1060 << " 2 & 1: you might need to write a serialization definition yourself. check "
1061 "https://root.cern/doc/master/group__roofit__dev__docs__hs3.html to "
1062 "see how to do this!\n";
1063 return;
1064 }
1065
1066 elem["type"] << dict->second.type;
1067
1068 size_t nprox = func.numProxies();
1069
1070 for (size_t i = 0; i < nprox; ++i) {
1071 RooAbsProxy *p = func.getProxy(i);
1072 if (!p)
1073 continue;
1074
1075 // some proxies start with a "!". This is a magic symbol that we don't want to stream
1076 std::string pname(p->name());
1077 if (pname[0] == '!')
1078 pname.erase(0, 1);
1079
1080 auto k = dict->second.proxies.find(pname);
1081 if (k == dict->second.proxies.end()) {
1082 std::cerr << "failed to find key matching proxy '" << pname << "' for type '" << dict->second.type
1083 << "', encountered in '" << func.GetName() << "', skipping" << std::endl;
1084 return;
1085 }
1086
1087 // empty string is interpreted as an instruction to ignore this value
1088 if (k->second.empty())
1089 continue;
1090
1091 if (auto l = dynamic_cast<RooAbsCollection *>(p)) {
1092 fillSeq(elem[k->second], *l);
1093 }
1094 if (auto r = dynamic_cast<RooArgProxy *>(p)) {
1095 if (isLiteralConstVar(*r->absArg())) {
1096 elem[k->second] << static_cast<RooConstVar *>(r->absArg())->getVal();
1097 } else {
1098 elem[k->second] << r->absArg()->GetName();
1099 }
1100 }
1101 }
1102
1103 // export all the servers of a given RooAbsArg
1104 for (RooAbsArg *s : func.servers()) {
1105 if (!s) {
1106 std::cerr << "unable to locate server of " << func.GetName() << std::endl;
1107 continue;
1108 }
1110 }
1111}
1112
1113/**
1114 * @brief Import a function from the JSONNode into the workspace.
1115 *
1116 * This function imports a function from the given JSONNode into the workspace.
1117 * The function's information is read from the JSONNode and added to the workspace.
1118 *
1119 * @param p The JSONNode representing the function to be imported.
1120 * @param importAllDependants A boolean flag indicating whether to import all dependants (servers) of the function.
1121 * @return void
1122 */
1124{
1125 std::string name(RooJSONFactoryWSTool::name(p));
1126
1127 // If this node if marked to be skipped by RooFit, exit
1128 if (hasAttribute(name, "roofit_skip")) {
1129 return;
1130 }
1131
1132 auto const &importers = RooFit::JSONIO::importers();
1134
1135 // some preparations: what type of function are we dealing with here?
1137
1138 // if the RooAbsArg already exists, we don't need to do anything
1139 if (_workspace.arg(name)) {
1140 return;
1141 }
1142 // if the key we found is not a map, it's an error
1143 if (!p.is_map()) {
1144 RooJSONFactoryWSTool::error("RooJSONFactoryWSTool() function node " + name + " is not a map!");
1145 }
1146 std::string prefix = genPrefix(p, true);
1147 if (!prefix.empty())
1148 name = prefix + name;
1149 if (!p.has_child("type")) {
1150 RooJSONFactoryWSTool::error("RooJSONFactoryWSTool() no type given for function '" + name + "', skipping.");
1151 }
1152
1153 std::string functype(p["type"].val());
1154
1155 // import all dependents if importing a workspace, not for creating new objects
1156 if (!importAllDependants) {
1157 this->importDependants(p);
1158 }
1159
1160 // check for specific implementations
1161 auto it = importers.find(functype);
1162 bool ok = false;
1163 if (it != importers.end()) {
1164 for (auto &imp : it->second) {
1165 try {
1166 ok = imp->importArg(this, p);
1167 } catch (const std::exception &e) {
1168 std::stringstream ss;
1169 const auto *ptr = imp.get();
1170 ss << "RooJSONFactoryWSTool() failed. The importer " << typeid(*ptr).name()
1171 << " emitted and error: " << e.what() << std::endl;
1173 }
1174 if (ok)
1175 break;
1176 }
1177 }
1178 if (!ok) { // generic import using the factory expressions
1179 auto expr = factoryExpressions.find(functype);
1180 if (expr != factoryExpressions.end()) {
1181 std::string expression = ::generate(expr->second, p, this);
1182 if (!_workspace.factory(expression)) {
1183 std::stringstream ss;
1184 ss << "RooJSONFactoryWSTool() failed to create " << expr->second.tclass->GetName() << " '" << name
1185 << "', skipping. expression was\n"
1186 << expression << std::endl;
1188 }
1189 } else {
1190 std::stringstream ss;
1191 ss << "RooJSONFactoryWSTool() no handling for type '" << functype << "' implemented, skipping."
1192 << "\n"
1193 << "there are several possible reasons for this:\n"
1194 << " 1. " << functype << " is a custom type that is not available in RooFit.\n"
1195 << " 2. " << functype
1196 << " is a ROOT class that nobody ever bothered to write a deserialization definition for.\n"
1197 << " 3. something is wrong with your setup, e.g. you might have called "
1198 "RooFit::JSONIO::clearFactoryExpressions() and/or never successfully read a file defining "
1199 "these expressions with RooFit::JSONIO::loadFactoryExpressions(filename)\n"
1200 << "either way, please make sure that:\n"
1201 << " 3: you are reading a file with factory expressions - call "
1202 "RooFit::JSONIO::printFactoryExpressions() "
1203 "to see what is available\n"
1204 << " 2 & 1: you might need to write a deserialization definition yourself. check "
1205 "https://root.cern/doc/master/group__roofit__dev__docs__hs3.html to see "
1206 "how to do this!"
1207 << std::endl;
1209 return;
1210 }
1211 }
1213 if (!func) {
1214 RooJSONFactoryWSTool::error("something went wrong importing function '" + name + "'.");
1215 }
1216}
1217
1218/**
1219 * @brief Import a function from a JSON string into the workspace.
1220 *
1221 * This function imports a function from the provided JSON string into the workspace.
1222 * The function's information is read from the JSON string and added to the workspace.
1223 *
1224 * @param jsonString The JSON string containing the function information.
1225 * @param importAllDependants A boolean flag indicating whether to import all dependants (servers) of the function.
1226 * @return void
1227 */
1229{
1230 this->importFunction((JSONTree::create(jsonString))->rootnode(), importAllDependants);
1231}
1232
1233/**
1234 * @brief Export the name and binning of a RooRealVar to a JSONNode.
1235 *
1236 * @param obsNode The JSONNode to which the axis information will be exported.
1237 * @param var The RooRealVar representing the axis to be exported.
1238 * @return void
1239 */
1241{
1242 std::string name = var.GetName();
1244 obsNode["name"] << name;
1245
1247}
1248
1249/**
1250 * @brief Export histogram data to a JSONNode.
1251 *
1252 * This function exports histogram data, represented by the provided variables and contents, to a JSONNode.
1253 * The histogram's axes information and bin contents are added as key-value pairs to the JSONNode.
1254 *
1255 * @param vars The RooArgSet representing the variables associated with the histogram.
1256 * @param n The number of bins in the histogram.
1257 * @param contents A pointer to the array containing the bin contents of the histogram.
1258 * @param output The JSONNode to which the histogram data will be exported.
1259 * @return void
1260 */
1261void RooJSONFactoryWSTool::exportHisto(RooArgSet const &vars, std::size_t n, double const *contents, JSONNode &output)
1262{
1263 auto &observablesNode = output["axes"].set_seq();
1264 // axes have to be ordered to get consistent bin indices
1265 for (auto *var : static_range_cast<RooRealVar *>(vars)) {
1266 exportAxis(observablesNode.append_child().set_map(), *var);
1267 }
1268
1269 return exportArray(n, contents, output["contents"]);
1270}
1271
1272/**
1273 * @brief Export an array of doubles to a JSONNode.
1274 *
1275 * This function exports an array of doubles, represented by the provided size and contents,
1276 * to a JSONNode. The array elements are added to the JSONNode as a sequence of values.
1277 *
1278 * @param n The size of the array.
1279 * @param contents A pointer to the array containing the double values.
1280 * @param output The JSONNode to which the array will be exported.
1281 * @return void
1282 */
1283void RooJSONFactoryWSTool::exportArray(std::size_t n, double const *contents, JSONNode &output)
1284{
1285 output.set_seq();
1286 for (std::size_t i = 0; i < n; ++i) {
1287 double w = contents[i];
1288 // To make sure there are no unnecessary floating points in the JSON
1289 if (int(w) == w) {
1290 output.append_child() << int(w);
1291 } else {
1292 output.append_child() << w;
1293 }
1294 }
1295}
1296
1297namespace {
1298
1299// Turn an arbitrary string into a valid variable name, but refuse to change the
1300// first character (which would silently rename the object).
1301std::string makeValidNameOrError(std::string const &in)
1302{
1303 if (!std::isalpha(in[0])) {
1304 RooJSONFactoryWSTool::error("refusing to change first character of string '" + in + "' to make a valid name!");
1305 }
1306 std::string out = RooFit::Detail::makeValidVarName(in);
1307 if (out != in) {
1308 oocoutW(nullptr, IO) << "RooFitHS3: changed '" << in << "' to '" << out << "' to become a valid name";
1309 }
1310 return out;
1311}
1312
1313} // namespace
1314
1315/**
1316 * @brief Export a RooAbsCategory object to a JSONNode.
1317 *
1318 * This function exports a RooAbsCategory object, represented by the provided categories and indices,
1319 * to a JSONNode. The category labels and corresponding indices are added to the JSONNode as key-value pairs.
1320 *
1321 * @param cat The RooAbsCategory object to be exported.
1322 * @param node The JSONNode to which the category data will be exported.
1323 * @return void
1324 */
1326{
1327 auto &labels = node["labels"].set_seq();
1328 auto &indices = node["indices"].set_seq();
1329
1330 for (auto const &item : cat) {
1331 labels.append_child() << makeValidNameOrError(item.first);
1332 indices.append_child() << item.second;
1333 }
1334}
1335
1336// Split `data` by its index category into per-channel datasets and export each, returning the resulting
1337// component-name map.
1339{
1341
1342 // prepare return value
1344
1345 if (!cat)
1346 return datamap;
1347 // this is a combined dataset
1348
1349 datamap.name = data.GetName();
1350
1351 // Write information necessary to reconstruct the combined dataset upon import
1352 auto &child = getRooFitInternal(*_rootnodeOutput, "combined_datasets").set_map()[data.GetName()].set_map();
1353 child["index_cat"] << cat->GetName();
1354 exportCategory(*cat, child);
1355
1356 // Find a RooSimultaneous model that would fit to this dataset
1357 RooSimultaneous const *simPdf = nullptr;
1358 auto *combinedPdfInfoNode = findRooFitInternal(*_rootnodeOutput, "combined_distributions");
1359 if (combinedPdfInfoNode) {
1360 for (auto &info : combinedPdfInfoNode->children()) {
1361 if (info["index_cat"].val() == cat->GetName()) {
1362 simPdf = static_cast<RooSimultaneous const *>(_workspace.pdf(info.key()));
1363 }
1364 }
1365 }
1366
1367 // If there is an associated simultaneous pdf for the index category, we
1368 // use the RooAbsData::split() overload that takes the RooSimultaneous.
1369 // Like this, the observables that are not relevant for a given channel
1370 // are automatically split from the component datasets.
1371 std::vector<std::unique_ptr<RooAbsData>> dataList{simPdf ? data.split(*simPdf, true) : data.split(*cat, true)};
1372
1373 for (std::unique_ptr<RooAbsData> const &absData : dataList) {
1374 std::string catName(absData->GetName());
1375 std::string dataName = makeValidNameOrError(catName);
1376 absData->SetName((std::string(data.GetName()) + "_" + dataName).c_str());
1377 datamap.components[catName] = absData->GetName();
1378 this->exportData(*absData);
1379 }
1380 return datamap;
1381}
1382
1383// Export a single dataset `data` (binned or unbinned) to the output JSON.
1385{
1387
1388 if (cat)
1389 return;
1390
1391 JSONNode &output = appendNamedChild((*_rootnodeOutput)["data"], data.GetName());
1392
1393 // This works around a problem in RooStats/HistFactory that was only fixed
1394 // in ROOT 6.30: until then, the weight variable of the observed dataset,
1395 // called "weightVar", was added to the observables. Therefore, it also got
1396 // added to the Asimov dataset. But the Asimov has its own weight variable,
1397 // called "binWeightAsimov", making "weightVar" an actual observable in the
1398 // Asimov data. But this is only by accident and should be removed.
1399 RooArgSet variables = *data.get();
1400 if (auto weightVar = variables.find("weightVar")) {
1401 variables.remove(*weightVar);
1402 }
1403
1404 // this is a regular binned dataset
1405 if (auto dh = dynamic_cast<RooDataHist const *>(&data)) {
1406 output["type"] << "binned";
1407 for (auto *var : static_range_cast<RooRealVar *>(variables)) {
1408 _domains->readVariable(*var);
1409 }
1410 return exportHisto(variables, dh->numEntries(), dh->weightArray(), output);
1411 }
1412
1413 // Check if this actually represents a binned dataset, and then import it
1414 // like a RooDataHist. This happens frequently when people create combined
1415 // RooDataSets from binned data to fit HistFactory models. In this case, it
1416 // doesn't make sense to export them like an unbinned dataset, because the
1417 // coordinates are redundant information with the binning. We only do this
1418 // for 1D data for now.
1419 if (data.isWeighted() && variables.size() == 1) {
1420 bool isBinnedData = false;
1421 auto &x = static_cast<RooRealVar const &>(*variables[0]);
1422 std::vector<double> contents;
1423 int i = 0;
1424 for (; i < data.numEntries(); ++i) {
1425 data.get(i);
1426 if (x.getBin() != i)
1427 break;
1428 contents.push_back(data.weight());
1429 }
1430 if (i == x.getBins())
1431 isBinnedData = true;
1432 if (isBinnedData) {
1433 output["type"] << "binned";
1434 for (auto *var : static_range_cast<RooRealVar *>(variables)) {
1435 _domains->readVariable(*var);
1436 }
1437 return exportHisto(variables, data.numEntries(), contents.data(), output);
1438 }
1439 }
1440
1441 // this really is an unbinned dataset
1442 output["type"] << "unbinned";
1443 auto &observablesNode = output["axes"].set_seq();
1444 for (auto *var : static_range_cast<RooRealVar *>(variables)) {
1445 _domains->readVariable(*var);
1446 exportAxis(observablesNode.append_child().set_map(), *var);
1447 }
1448 auto &coords = output["entries"].set_seq();
1449 std::vector<double> weightVals;
1450 bool hasNonUnityWeights = false;
1451 for (int i = 0; i < data.numEntries(); ++i) {
1452 data.get(i);
1453 coords.append_child().fill_seq(variables, [](auto x) { return static_cast<RooRealVar *>(x)->getVal(); });
1454 if (data.isWeighted()) {
1455 weightVals.push_back(data.weight());
1456 if (data.weight() != 1.)
1457 hasNonUnityWeights = true;
1458 }
1459 }
1460 if (data.isWeighted() && hasNonUnityWeights) {
1461 output["weights"].fill_seq(weightVals);
1462 }
1463}
1464
1465/**
1466 * @brief Read axes from the JSONNode and create a RooArgSet representing them.
1467 *
1468 * This function reads axes information from the given JSONNode and
1469 * creates a RooArgSet with variables representing these axes.
1470 *
1471 * @param topNode The JSONNode containing the axes information to be read.
1472 * @return RooArgSet A RooArgSet containing the variables created from the JSONNode.
1473 */
1475{
1476 RooArgSet vars;
1477
1478 for (JSONNode const &node : topNode["axes"].children()) {
1479 if (node.has_child("edges")) {
1480 std::vector<double> edges;
1481 for (auto const &bound : node["edges"].children()) {
1482 edges.push_back(bound.val_double());
1483 }
1484 auto obs = std::make_unique<RooRealVar>(node["name"].val().c_str(), node["name"].val().c_str(), edges[0],
1485 edges[edges.size() - 1]);
1486 RooBinning bins(obs->getMin(), obs->getMax());
1487 for (auto b : edges) {
1488 bins.addBoundary(b);
1489 }
1490 obs->setBinning(bins);
1491 vars.addOwned(std::move(obs));
1492 } else {
1493 auto obs = std::make_unique<RooRealVar>(node["name"].val().c_str(), node["name"].val().c_str(),
1494 node["min"].val_double(), node["max"].val_double());
1495 obs->setBins(node["nbins"].val_int());
1496 vars.addOwned(std::move(obs));
1497 }
1498 }
1499
1500 return vars;
1501}
1502
1503/**
1504 * @brief Read binned data from the JSONNode and create a RooDataHist object.
1505 *
1506 * This function reads binned data from the given JSONNode and creates a RooDataHist object.
1507 * The binned data is associated with the specified name and variables (RooArgSet) in the workspace.
1508 *
1509 * @param n The JSONNode representing the binned data to be read.
1510 * @param name The name to be associated with the created RooDataHist object.
1511 * @param vars The RooArgSet representing the variables associated with the binned data.
1512 * @return std::unique_ptr<RooDataHist> A unique pointer to the created RooDataHist object.
1513 */
1514std::unique_ptr<RooDataHist>
1515RooJSONFactoryWSTool::readBinnedData(const JSONNode &n, const std::string &name, RooArgSet const &vars)
1516{
1517 if (!n.has_child("contents"))
1518 RooJSONFactoryWSTool::error("no contents given");
1519
1520 JSONNode const &contents = n["contents"];
1521
1522 if (!contents.is_seq())
1523 RooJSONFactoryWSTool::error("contents are not in list form");
1524
1525 JSONNode const *errors = nullptr;
1526 if (n.has_child("errors")) {
1527 errors = &n["errors"];
1528 if (!errors->is_seq())
1529 RooJSONFactoryWSTool::error("errors are not in list form");
1530 }
1531
1532 auto bins = generateBinIndices(vars);
1533 if (contents.num_children() != bins.size()) {
1534 RooJSONFactoryWSTool::error("inconsistent bin numbers: contents=" + std::to_string(contents.num_children()) +
1535 ", bins=" + std::to_string(bins.size()));
1536 }
1537 auto dh = std::make_unique<RooDataHist>(name, name, vars);
1538 std::vector<double> contentVals;
1539 contentVals.reserve(contents.num_children());
1540 for (auto const &cont : contents.children()) {
1541 contentVals.push_back(cont.val_double());
1542 }
1543 std::vector<double> errorVals;
1544 if (errors) {
1545 errorVals.reserve(errors->num_children());
1546 for (auto const &err : errors->children()) {
1547 errorVals.push_back(err.val_double());
1548 }
1549 }
1550 for (size_t ibin = 0; ibin < bins.size(); ++ibin) {
1551 const double err = errors ? errorVals[ibin] : -1;
1552 dh->set(ibin, contentVals[ibin], err);
1553 }
1554 return dh;
1555}
1556
1557// Import a single variable (RooRealVar or RooConstVar) from the JSON node `p` into the workspace.
1559{
1560 // import a RooRealVar object
1561 std::string name(RooJSONFactoryWSTool::name(p));
1563
1564 if (_workspace.arg(name))
1565 return;
1566 if (!p.is_map()) {
1567 oocoutE(nullptr, InputArguments) << "RooJSONFactoryWSTool() node '" << name << "' is not a map, skipping."
1568 << std::endl;
1569 return;
1570 }
1571 if (config().importNoDomainParametersAsRooConstVars && !_domains->hasVariable(name.c_str())) {
1572 if (!p.has_child("value")) {
1573 RooJSONFactoryWSTool::error("cannot instantiate RooConstVar '" + name + "' without \"value\"!");
1574 }
1575 wsEmplace<RooConstVar>(name, p["value"].val_double());
1576 return;
1577 }
1579}
1580
1581// Import all dependants (variables, functions and distributions) of node `n` into the workspace.
1583{
1584 // import all the dependants of an object
1585 if (JSONNode const *varsNode = getVariablesNode(n)) {
1586 for (const auto &p : varsNode->children()) {
1588 }
1589 }
1590 if (auto seq = n.find("functions")) {
1591 for (const auto &p : seq->children()) {
1592 this->importFunction(p, true);
1593 }
1594 }
1595 if (auto seq = n.find("distributions")) {
1596 for (const auto &p : seq->children()) {
1597 this->importFunction(p, true);
1598 }
1599 }
1600}
1601
1603 const std::vector<CombinedData> &combDataSets,
1604 const std::vector<RooAbsData *> &singleDataSets)
1605{
1606 auto pdf = mc.GetPdf();
1607 auto simpdf = dynamic_cast<RooSimultaneous const *>(pdf);
1608 if (simpdf) {
1609 for (std::size_t i = 0; i < std::max(combDataSets.size(), std::size_t(1)); ++i) {
1610 const bool hasdata = i < combDataSets.size();
1611 if (hasdata && !matches(combDataSets.at(i), simpdf))
1612 continue;
1613
1614 std::string analysisName(simpdf->GetName());
1615 if (hasdata)
1616 analysisName += "_" + combDataSets[i].name;
1617
1618 exportSingleModelConfig(rootnode, mc, analysisName, hasdata ? &combDataSets[i].components : nullptr);
1619 }
1620 } else {
1621 RooArgSet observables(*mc.GetObservables());
1622 int founddata = 0;
1623 for (auto *data : singleDataSets) {
1624 if (observables.equals(*(data->get()))) {
1625 std::map<std::string, std::string> mapping;
1626 mapping[pdf->GetName()] = data->GetName();
1627 exportSingleModelConfig(rootnode, mc, std::string(pdf->GetName()) + "_" + data->GetName(), &mapping);
1628 ++founddata;
1629 }
1630 }
1631 if (founddata == 0) {
1632 exportSingleModelConfig(rootnode, mc, pdf->GetName(), nullptr);
1633 }
1634 }
1635}
1636
1638 std::string const &analysisName,
1639 std::map<std::string, std::string> const *dataComponents)
1640{
1641 auto pdf = mc.GetPdf();
1642
1643 JSONNode &analysisNode = appendNamedChild(rootnode["analyses"], analysisName);
1644
1645 auto &domains = analysisNode["domains"].set_seq();
1646
1647 analysisNode["likelihood"] << analysisName;
1648
1649 auto &nllNode = appendNamedChild(rootnode["likelihoods"], analysisName);
1650 nllNode["distributions"].set_seq();
1651 nllNode["data"].set_seq();
1652
1653 if (dataComponents) {
1654 auto simPdf = dynamic_cast<RooSimultaneous const *>(pdf);
1655 if (simPdf) {
1656 for (auto const &item : simPdf->indexCat()) {
1657 const auto &dataComp = dataComponents->find(item.first);
1658 nllNode["distributions"].append_child() << simPdf->getPdf(item.first)->GetName();
1659 nllNode["data"].append_child() << dataComp->second;
1660 }
1661 } else {
1662 for (auto it : *dataComponents) {
1663 nllNode["distributions"].append_child() << it.first;
1664 nllNode["data"].append_child() << it.second;
1665 }
1666 }
1667 } else {
1668 nllNode["distributions"].append_child() << pdf->GetName();
1669 nllNode["data"].append_child() << 0;
1670 }
1671
1672 if (mc.GetExternalConstraints()) {
1673 auto &extConstrNode = nllNode["aux_distributions"];
1674 extConstrNode.set_seq();
1675 for (const auto &constr : *mc.GetExternalConstraints()) {
1676 extConstrNode.append_child() << constr->GetName();
1677 }
1678 }
1679
1680 auto writeList = [&](const char *name, RooArgSet const *args) {
1681 if (!args || !args->size())
1682 return;
1683
1684 std::vector<std::string> names;
1685 names.reserve(args->size());
1686 for (RooAbsArg const *arg : *args)
1687 names.push_back(arg->GetName());
1688 std::sort(names.begin(), names.end());
1689 analysisNode[name].fill_seq(names);
1690 };
1691
1692 writeList("parameters_of_interest", mc.GetParametersOfInterest());
1693
1694 auto &domainsNode = rootnode["domains"];
1695
1696 auto writeProductDomain = [&](const char *suffix, RooArgSet const *args) {
1697 if (!args || args->empty())
1698 return;
1699 const std::string domainName = analysisName + suffix;
1700 domains.append_child() << domainName;
1702 for (auto *var : static_range_cast<const RooRealVar *>(*args)) {
1703 domain.readVariable(*var);
1704 }
1706 };
1707
1708 writeProductDomain("_nuisance_parameters", mc.GetNuisanceParameters());
1709 writeProductDomain("_global_observables", mc.GetGlobalObservables());
1710 writeProductDomain("_parameters_of_interest", mc.GetParametersOfInterest());
1711
1712 auto &modelConfigAux = getRooFitInternal(rootnode, "ModelConfigs", analysisName);
1713 modelConfigAux.set_map();
1714 modelConfigAux["pdfName"] << pdf->GetName();
1715 modelConfigAux["mcName"] << mc.GetName();
1716}
1717
1718// Export all top-level pdfs, functions, datasets and ModelConfigs of the workspace into `n`.
1720{
1721 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
1723 _rootnodeOutput = &n;
1724
1725 // export all toplevel pdfs
1726 std::vector<RooAbsPdf *> allpdfs;
1727 for (auto &arg : _workspace.allPdfs()) {
1728 if (isTopLevel(*arg, _workspace)) {
1729 if (auto *pdf = dynamic_cast<RooAbsPdf *>(arg)) {
1730 allpdfs.push_back(pdf);
1731 }
1732 }
1733 }
1735 std::set<std::string> exportedObjectNames;
1737
1738 // export all toplevel functions
1739 std::vector<RooAbsReal *> allfuncs;
1740 for (auto &arg : _workspace.allFunctions()) {
1741 if (isTopLevel(*arg, _workspace)) {
1742 if (auto *func = dynamic_cast<RooAbsReal *>(arg)) {
1743 allfuncs.push_back(func);
1744 }
1745 }
1746 }
1749
1750 // export attributes of all objects
1751 for (RooAbsArg *arg : _workspace.components()) {
1752 exportAttributes(arg, n);
1753 }
1754
1755 // collect all datasets
1756 std::vector<RooAbsData *> alldata;
1757 for (auto &d : _workspace.allData()) {
1758 alldata.push_back(d);
1759 }
1761 // first, take care of combined datasets
1762 std::vector<RooAbsData *> singleData;
1763 std::vector<RooJSONFactoryWSTool::CombinedData> combData;
1764 for (auto &d : alldata) {
1765 auto data = this->exportCombinedData(*d);
1766 if (!data.components.empty())
1767 combData.push_back(data);
1768 else
1769 singleData.push_back(d);
1770 }
1771 // next, take care datasets
1772 for (auto &d : alldata) {
1773 this->exportData(*d);
1774 }
1775
1776 // export all ModelConfig objects and attached Pdfs
1777 for (TObject *obj : _workspace.allGenericObjects()) {
1778 if (auto mc = dynamic_cast<RooFit::ModelConfig *>(obj)) {
1780 }
1781 }
1782
1784
1787 // We only want to add the variables that actually got exported and skip
1788 // the ones that the pdfs encoded implicitly (like in the case of
1789 // HistFactory).
1790 for (RooAbsArg *arg : *snsh) {
1791 bool do_export = false;
1792 for (const auto &pdf : allpdfs) {
1793 if (pdf->dependsOn(*arg)) {
1794 do_export = true;
1795 }
1796 }
1797 if (do_export) {
1798 RooJSONFactoryWSTool::testValidName(arg->GetName(), true);
1799 snapshotSorted.add(*arg);
1800 }
1801 }
1802 snapshotSorted.sort();
1803 std::string name(snsh->GetName());
1804 if (name != "default_values") {
1805 this->exportVariables(snapshotSorted, appendNamedChild(n["parameter_points"], name)["parameters"], true,
1806 false);
1807 }
1808 }
1809 _varsNode = nullptr;
1810 _domains->writeJSON(n["domains"]);
1811 _domains.reset();
1812 _rootnodeOutput = nullptr;
1813}
1814
1815/**
1816 * @brief Import the workspace from a JSON string.
1817 *
1818 * @param s The JSON string containing the workspace data.
1819 * @return bool Returns true on successful import, false otherwise.
1820 */
1822{
1823 std::stringstream ss(s);
1824 return importJSON(ss);
1825}
1826
1827/**
1828 * @brief Export the workspace to a JSON string.
1829 *
1830 * @return std::string The JSON string representing the exported workspace.
1831 */
1833{
1834 std::stringstream ss;
1835 exportJSON(ss);
1836 return ss.str();
1837}
1838
1839/**
1840 * @brief Create a new JSON tree with version information.
1841 *
1842 * @return std::unique_ptr<JSONTree> A unique pointer to the created JSON tree.
1843 */
1845{
1846 std::unique_ptr<JSONTree> tree = JSONTree::create();
1847 JSONNode &n = tree->rootnode();
1848 n.set_map();
1849 auto &metadata = n["metadata"].set_map();
1850
1851 // add the mandatory hs3 version number
1852 metadata["hs3_version"] << hs3VersionTag;
1853
1854 // Add information about the ROOT version that was used to generate this file
1855 auto &rootInfo = appendNamedChild(metadata["packages"], "ROOT");
1856 std::string versionName = gROOT->GetVersion();
1857 // We want to consistently use dots such that the version name can be easily
1858 // digested automatically.
1859 std::replace(versionName.begin(), versionName.end(), '/', '.');
1860 rootInfo["version"] << versionName;
1861
1862 return tree;
1863}
1864
1865/**
1866 * @brief Export the workspace to JSON format and write to the output stream.
1867 *
1868 * @param os The output stream to write the JSON data to.
1869 * @return bool Returns true on successful export, false otherwise.
1870 */
1872{
1873 std::unique_ptr<JSONTree> tree = createNewJSONTree();
1874 JSONNode &n = tree->rootnode();
1875 this->exportAllObjects(n);
1876 n.writeJSON(os);
1877 return true;
1878}
1879
1880/**
1881 * @brief Export the workspace to JSON format and write to the specified file.
1882 *
1883 * @param filename The name of the JSON file to create and write the data to.
1884 * @return bool Returns true on successful export, false otherwise.
1885 */
1887{
1888 std::ofstream out(filename.c_str());
1889 if (!out.is_open())
1890 RooJSONFactoryWSTool::error("RooJSONFactoryWSTool() invalid output file '" + filename + "'.");
1891 return this->exportJSON(out);
1892}
1893
1894bool RooJSONFactoryWSTool::hasAttribute(const std::string &obj, const std::string &attrib)
1895{
1896 if (!_attributesNode)
1897 return false;
1898 if (auto attrNode = _attributesNode->find(obj)) {
1899 if (auto seq = attrNode->find("tags")) {
1900 for (auto &a : seq->children()) {
1901 if (a.val() == attrib)
1902 return true;
1903 }
1904 }
1905 }
1906 return false;
1907}
1908void RooJSONFactoryWSTool::setAttribute(const std::string &obj, const std::string &attrib)
1909{
1910 auto node = &RooJSONFactoryWSTool::getRooFitInternal(*_rootnodeOutput, "attributes").set_map()[obj].set_map();
1911 auto &tags = (*node)["tags"];
1912 tags.set_seq();
1913 tags.append_child() << attrib;
1914}
1915
1916std::string RooJSONFactoryWSTool::getStringAttribute(const std::string &obj, const std::string &attrib)
1917{
1918 if (!_attributesNode)
1919 return "";
1920 if (auto attrNode = _attributesNode->find(obj)) {
1921 if (auto dict = attrNode->find("dict")) {
1922 if (auto *a = dict->find(attrib)) {
1923 return a->val();
1924 }
1925 }
1926 }
1927 return "";
1928}
1929void RooJSONFactoryWSTool::setStringAttribute(const std::string &obj, const std::string &attrib,
1930 const std::string &value)
1931{
1932 auto node = &RooJSONFactoryWSTool::getRooFitInternal(*_rootnodeOutput, "attributes").set_map()[obj].set_map();
1933 auto &dict = (*node)["dict"];
1934 dict.set_map();
1935 dict[attrib] << value;
1936}
1937
1938// Import all nodes of the JSON document rooted at `n` into the workspace.
1940{
1941 // Per HS3 standard, the hs3_version in the metadata is required. So we
1942 // error out if it is missing. TODO: now we are only checking if the
1943 // hs3_version tag exists, but in the future when the HS3 specification
1944 // versions are actually frozen, we should also check if the hs3_version is
1945 // one that RooFit can actually read.
1946 auto metadata = n.find("metadata");
1947 if (!metadata || !metadata->find("hs3_version")) {
1948 std::stringstream ss;
1949 ss << "The HS3 version is missing in the JSON!\n"
1950 << "Please include the HS3 version in the metadata field, e.g.:\n"
1951 << " \"metadata\" :\n"
1952 << " {\n"
1953 << " \"hs3_version\" : \"" << hs3VersionTag << "\"\n"
1954 << " }";
1955 error(ss.str());
1956 }
1957
1958 _rootnodeInput = &n;
1959
1961
1962 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
1963 if (auto domains = n.find("domains")) {
1964 _domains->readJSON(*domains);
1965 }
1966 _domains->populate(_workspace);
1967
1968 // Build name-keyed indices over the "functions" and "distributions"
1969 // sequences. Without these, every cross-reference resolved during import
1970 // (e.g. dependencies of a PiecewiseInterpolation, or factory-expression
1971 // arguments) triggers a linear scan over all sibling nodes via
1972 // findNamedChild(), which becomes O(N^2) on workspaces with thousands of
1973 // entries. Populating the maps up-front turns each lookup into O(1).
1974 auto buildIndex = [&n](const char *key, auto &index) {
1975 index.clear();
1976 auto seq = n.find(key);
1977 if (!seq || !seq->is_seq())
1978 return;
1979 index.reserve(seq->num_children());
1980 for (const auto &p : seq->children()) {
1981 index.emplace(RooJSONFactoryWSTool::name(p), &p);
1982 }
1983 };
1984 buildIndex("functions", _functionsByName);
1985 buildIndex("distributions", _distributionsByName);
1986
1987 this->importDependants(n);
1988
1989 if (auto paramPointsNode = n.find("parameter_points")) {
1990 for (const auto &snsh : paramPointsNode->children()) {
1991 std::string name = RooJSONFactoryWSTool::name(snsh);
1993
1994 RooArgSet vars;
1995 for (const auto &var : snsh["parameters"].children()) {
1998 vars.add(*rrv);
1999 }
2000 }
2002 }
2003 }
2004
2006
2007 // Import attributes
2008 if (_attributesNode) {
2009 for (const auto &elem : _attributesNode->children()) {
2010 if (RooAbsArg *arg = _workspace.arg(elem.key()))
2011 importAttributes(arg, elem);
2012 }
2013 }
2014
2015 _attributesNode = nullptr;
2016
2017 // We delay the import of the data to after combineDatasets(), because it
2018 // might be that some datasets are merged to combined datasets there. In
2019 // that case, we will remove the components from the "datasets" vector so they
2020 // don't get imported.
2021 std::vector<std::unique_ptr<RooAbsData>> datasets;
2022 if (auto dataNode = n.find("data")) {
2023 for (const auto &p : dataNode->children()) {
2024 datasets.push_back(loadData(p, _workspace));
2025 }
2026 }
2027
2028 // Now, read in analyses and likelihoods if there are any
2029
2030 if (auto analysesNode = n.find("analyses")) {
2031 for (JSONNode const &analysisNode : analysesNode->children()) {
2032 importAnalysis(*_rootnodeInput, analysisNode, n["likelihoods"], n["domains"], _workspace, datasets);
2033 }
2034 }
2035
2036 combineDatasets(*_rootnodeInput, datasets);
2037
2038 for (auto const &d : datasets) {
2039 if (d) {
2041 for (auto const &obs : *d->get()) {
2042 if (auto *rrv = dynamic_cast<RooRealVar *>(obs)) {
2043 _workspace.var(rrv->GetName())->setBinning(rrv->getBinning());
2044 }
2045 }
2046 }
2047 }
2048
2049 _rootnodeInput = nullptr;
2050 _domains.reset();
2051 _functionsByName.clear();
2052 _distributionsByName.clear();
2053}
2054
2055/**
2056 * @brief Imports a JSON file from the given input stream to the workspace.
2057 *
2058 * @param is The input stream containing the JSON data.
2059 * @return bool Returns true on successful import, false otherwise.
2060 */
2062{
2063 // import a JSON file to the workspace
2064 std::unique_ptr<JSONTree> tree = JSONTree::create(is);
2065 JSONNode const &rootnode = tree->rootnode();
2066 this->importAllNodes(rootnode);
2067 if (this->workspace()->getSnapshot("default_values")) {
2068 this->workspace()->loadSnapshot("default_values");
2069 }
2070 importParameterStepWidths(*this->workspace(), rootnode);
2071 return true;
2072}
2073
2074/**
2075 * @brief Imports a JSON file from the given filename to the workspace.
2076 *
2077 * @param filename The name of the JSON file to import.
2078 * @return bool Returns true on successful import, false otherwise.
2079 */
2081{
2082 // import a JSON file to the workspace
2083 std::ifstream infile(filename.c_str());
2084 if (!infile.is_open())
2085 RooJSONFactoryWSTool::error("RooJSONFactoryWSTool() invalid input file '" + filename + "'.");
2086 return this->importJSON(infile);
2087}
2088
2089void RooJSONFactoryWSTool::importJSONElement(const std::string &name, const std::string &jsonString)
2090{
2091 // Create the JSON Tree from the string
2092 std::unique_ptr<RooFit::Detail::JSONTree> tree = RooFit::Detail::JSONTree::create(jsonString);
2093 JSONNode &n = tree->rootnode();
2094
2095 // If the objects containts a parameter of interest, import it as a modelConfig
2096 if (n.find("poi")) {
2097
2098 RooStats::ModelConfig modelConfig{"ModelConfig"};
2099 std::string poi = n.find("poi")->val();
2100 std::string pdname = n.find("pdfName")->val();
2101 modelConfig.SetWS(_workspace);
2102 modelConfig.SetPdf(pdname.c_str());
2103 modelConfig.SetParametersOfInterest(_workspace.argSet(poi));
2105
2106 return;
2107 }
2108
2109 n["name"] << name;
2110
2111 bool isVariable = true;
2112 bool isData = false;
2113 // Check for the type of object, if it doesn't contain a type, it must be a variable
2114 if (n.find("type")) {
2115 isVariable = false;
2116 std::string elementType = n.find("type")->val();
2117 if (elementType == "binned" || elementType == "unbinned") {
2118 isData = true;
2119 }
2120 }
2121
2122 // Import the object to the workspace
2123 if (isVariable) {
2124 this->importVariableElement(n);
2125 } else if (isData) {
2126 auto absData = loadData(n, _workspace);
2128 } else {
2129 this->importFunction(n, false);
2130 }
2131}
2132
2134{
2135 std::unique_ptr<RooFit::Detail::JSONTree> tree = varJSONString(elementNode);
2136 JSONNode &n = tree->rootnode();
2137 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
2138 if (auto domains = n.find("domains"))
2139 _domains->readJSON(*domains);
2140
2141 _rootnodeInput = &n;
2143
2145 const auto &p = varsNode->child(0);
2147
2148 auto paramPointsNode = n.find("parameter_points");
2149 const auto &var = paramPointsNode->child(0)["parameters"].child(0);
2152 }
2153
2154 // Import attributes
2155 if (_attributesNode) {
2156 for (const auto &elem : _attributesNode->children()) {
2157 if (RooAbsArg *arg = _workspace.arg(elem.key()))
2158 importAttributes(arg, elem);
2159 }
2160 }
2161
2162 _attributesNode = nullptr;
2163 _rootnodeInput = nullptr;
2164 _domains.reset();
2165}
2166
2167/**
2168 * @brief Writes a warning message to the RooFit message service.
2169 *
2170 * @param str The warning message to be logged.
2171 * @return std::ostream& A reference to the output stream.
2172 */
2173std::ostream &RooJSONFactoryWSTool::warning(std::string const &str)
2174{
2175 return RooMsgService::instance().log(nullptr, RooFit::MsgLevel::WARNING, RooFit::IO) << str << std::endl;
2176}
2177
2178/**
2179 * @brief Writes an error message to the RooFit message service and throws a runtime_error.
2180 *
2181 * @param s The error message to be logged and thrown.
2182 * @return void
2183 */
2185{
2186 RooMsgService::instance().log(nullptr, RooFit::MsgLevel::ERROR, RooFit::IO) << s << std::endl;
2187 throw std::runtime_error(s);
2188}
2189
2190/**
2191 * @brief Cleans up names to the HS3 standard
2192 *
2193 * @param str The string to be sanitized.
2194 * @return std::string
2195 */
2196std::string RooJSONFactoryWSTool::sanitizeName(const std::string str)
2197{
2198 std::string result;
2199 if (RooJSONFactoryWSTool::config().allowSanitizeNames) {
2200 for (char c : str) {
2201 switch (c) {
2202 case '[':
2203 case '|':
2204 case ',':
2205 case '(': result += '_'; break;
2206 case ']':
2207 case ')':
2208 // skip these characters entirely
2209 break;
2210 case '.': result += "_dot_"; break;
2211 case '@': result += "at"; break;
2212 case '-': result += "minus"; break;
2213 case '/': result += "_div_"; break;
2214
2215 default: result += c; break;
2216 }
2217 }
2218 return result;
2219 }
2220 return str;
2221}
2222
2224{
2225 // Variables
2226
2228 if (onlyModelConfig) {
2229 for (auto *obj : ws.allGenericObjects()) {
2230 if (auto *mc = dynamic_cast<RooFit::ModelConfig *>(obj)) {
2231 tmpWS.import(*mc->GetPdf(), RooFit::RecycleConflictNodes(true));
2232 }
2233 }
2234
2235 } else {
2236
2237 for (auto *pdf : ws.allPdfs()) {
2238 if (isTopLevel(*pdf, ws)) {
2239 tmpWS.import(*pdf, RooFit::RecycleConflictNodes(true));
2240 }
2241 }
2242
2243 for (auto *func : ws.allFunctions()) {
2244 if (isTopLevel(*func, ws)) {
2245 tmpWS.import(*func, RooFit::RecycleConflictNodes(true));
2246 }
2247 }
2248 }
2249
2250 for (auto *data : ws.allData()) {
2251 tmpWS.import(*data);
2252 }
2253
2254 for (auto *obj : ws.allGenericObjects()) {
2255 tmpWS.import(*obj);
2256 }
2257
2258 for (auto *obj : ws.allResolutionModels()) {
2259 tmpWS.import(*obj);
2260 }
2261
2262 for (auto *snsh : ws.getSnapshots()) {
2263 auto *snshSet = dynamic_cast<RooArgSet *>(snsh);
2264 if (snshSet) {
2265 tmpWS.saveSnapshot(snshSet->GetName(), *snshSet, true);
2266 }
2267 }
2268
2269 return tmpWS;
2270}
2271
2277
2278// Sanitize all names in the workspace to be HS3 compliant
2280{
2281 // Variables
2282
2283 RooWorkspace tmpWS = cleanWS(ws, false);
2284
2285 auto sanitizeIfNeeded = [](auto const &list) {
2286 for (auto *obj : list) {
2287 if (!isValidName(obj->GetName())) {
2288 obj->SetName(sanitizeName(obj->GetName()).c_str());
2289 }
2290 }
2291 };
2292 sanitizeIfNeeded(tmpWS.allVars());
2293 sanitizeIfNeeded(tmpWS.allFunctions());
2294 sanitizeIfNeeded(tmpWS.allPdfs());
2295 sanitizeIfNeeded(tmpWS.allResolutionModels());
2296 // Datasets
2297 for (auto *data : tmpWS.allData()) {
2298 // Sanitize dataset name
2299 if (!isValidName(data->GetName())) {
2300 data->SetName(sanitizeName(data->GetName()).c_str());
2301 }
2302 for (auto *obj : *data->get()) {
2303 obj->SetName(sanitizeName(obj->GetName()).c_str());
2304 }
2305 }
2306 for (auto *data : tmpWS.allEmbeddedData()) {
2307 // Sanitize dataset name
2308 data->SetName(sanitizeName(data->GetName()).c_str());
2309 for (auto *obj : *data->get()) {
2310 obj->SetName(sanitizeName(obj->GetName()).c_str());
2311 }
2312 }
2313 for (auto *snshObj : tmpWS.getSnapshots()) {
2314 // Snapshots are stored as TObject*, but really they are RooArgSet*
2315 auto *snsh = dynamic_cast<RooArgSet *>(snshObj);
2316 if (!snsh) {
2317 std::cerr << "Warning: found snapshot that is not a RooArgSet, skipping\n";
2318 continue;
2319 }
2320
2321 // Sanitize snapshot name
2322 if (!isValidName(snsh->GetName())) {
2323 snsh->setName(sanitizeName(snsh->GetName()).c_str());
2324 }
2325
2326 // Sanitize the variables inside the snapshot
2327 for (auto *arg : *snsh) {
2328 if (!isValidName(arg->GetName())) {
2329 arg->SetName(sanitizeName(arg->GetName()).c_str());
2330 }
2331 }
2332 }
2333
2334 // Generic objects (ModelConfigs, attributes, etc.)
2335 for (auto *obj : tmpWS.allGenericObjects()) {
2336 if (!isValidName(obj->GetName())) {
2337 if (auto *named = dynamic_cast<TNamed *>(obj)) {
2338 named->SetName(sanitizeName(named->GetName()).c_str());
2339 } else {
2340 std::cerr << "Warning: object " << obj->GetName() << " is not TNamed, cannot rename.\n";
2341 }
2342 }
2343
2344 if (auto *mc = dynamic_cast<RooFit::ModelConfig *>(obj)) {
2345 // Sanitize ModelConfig name
2346 if (!isValidName(mc->GetName())) {
2347 mc->SetName(sanitizeName(mc->GetName()).c_str());
2348 }
2349
2350 // Sanitize the sets inside ModelConfig
2351 for (auto *obs : mc->GetObservables()->get()) {
2352 if (obs) {
2353 obs->SetName(sanitizeName(obs->GetName()).c_str());
2354 }
2355 }
2356 for (auto *poi : mc->GetParametersOfInterest()->get()) {
2357 if (poi) {
2358 poi->SetName(sanitizeName(poi->GetName()).c_str());
2359 }
2360 }
2361 for (auto *nuis : mc->GetNuisanceParameters()->get()) {
2362 if (nuis) {
2363 nuis->SetName(sanitizeName(nuis->GetName()).c_str());
2364 }
2365 }
2366 for (auto *glob : mc->GetGlobalObservables()->get()) {
2367 if (glob) {
2368 glob->SetName(sanitizeName(glob->GetName()).c_str());
2369 }
2370 }
2371 }
2372 }
2373 std::string wsName = std::string{ws.GetName()} + "_sanitized";
2374 RooWorkspace newWS = cleanWS(tmpWS, false);
2375 newWS.SetName(wsName.c_str());
2376
2377 return newWS;
2378}
std::unique_ptr< RooFit::Detail::JSONTree > varJSONString(const JSONNode &treeRoot)
void writeAxisBinning(JSONNode &node, const RooAbsBinning &binning)
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
double toDouble(const char *s)
constexpr auto hs3VersionTag
#define oocoutW(o, a)
#define oocoutE(o, a)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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 Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
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 char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
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 child
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 char Point_t Rectangle_t WindowAttributes_t attr
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
char name[80]
Definition TGX11.cxx:145
#define gROOT
Definition TROOT.h:426
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
TClass * IsA() const override
Definition RooAbsArg.h:678
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
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...
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
const std::set< std::string > & attributes() const
Definition RooAbsArg.h:258
const RefCountList_t & servers() const
List of all servers of this object.
Definition RooAbsArg.h:145
const std::map< std::string, std::string > & stringAttributes() const
Definition RooAbsArg.h:267
Int_t numProxies() const
Return the number of registered proxies.
const RefCountList_t & clients() const
List of all clients of this object.
Definition RooAbsArg.h:137
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
RooAbsProxy * getProxy(Int_t index) const
Return the nth proxy from the proxy list.
A space to attach TBranches.
Abstract container object that can hold multiple RooAbsArg objects.
bool equals(const RooAbsCollection &otherColl) const
Check if this and other collection have identically-named contents.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
virtual bool addOwned(RooAbsArg &var, bool silent=false)
Add an argument and transfer the ownership to the collection.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:56
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
std::unique_ptr< RooArgSet > getAllConstraints(const RooArgSet &observables, RooArgSet &constrainedParams, bool stripDisconnected=true) const
This helper function finds and collects all constraints terms of all component p.d....
Abstract interface for proxy classes.
Definition RooAbsProxy.h:37
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
Abstract interface for RooAbsArg proxy classes.
Definition RooArgProxy.h:24
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Implements a RooAbsBinning in terms of an array of boundary values, posing no constraints on the choi...
Definition RooBinning.h:27
bool addBoundary(double boundary)
Add bin boundary at given value.
Object to represent discrete states.
Definition RooCategory.h:28
Represents a constant real-valued object.
Definition RooConstVar.h:23
Container class to hold N-dimensional binned data.
Definition RooDataHist.h:40
virtual JSONNode & set_map()=0
virtual JSONNode & append_child()=0
virtual children_view children()
virtual size_t num_children() const =0
virtual JSONNode & set_seq()=0
virtual bool is_seq() const =0
virtual bool is_map() const =0
virtual std::string key() const =0
JSONNode const * find(std::string const &key) const
static std::unique_ptr< JSONTree > create()
void writeJSON(RooFit::Detail::JSONNode &) const
Definition Domains.cxx:248
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
static constexpr bool useListsInsteadOfDicts
std::string getStringAttribute(const std::string &obj, const std::string &attrib)
static void fillSeq(RooFit::Detail::JSONNode &node, RooAbsCollection const &coll, size_t nMax=-1)
void exportObjects(T const &args, std::set< std::string > &exportedObjectNames)
void exportCategory(RooAbsCategory const &cat, RooFit::Detail::JSONNode &node)
Export a RooAbsCategory object to a JSONNode.
RooJSONFactoryWSTool(RooWorkspace &ws)
void exportData(RooAbsData const &data)
void exportModelConfig(RooFit::Detail::JSONNode &rootnode, RooStats::ModelConfig const &mc, const std::vector< RooJSONFactoryWSTool::CombinedData > &combined, const std::vector< RooAbsData * > &single)
bool hasAttribute(const std::string &obj, const std::string &attrib)
bool importJSON(std::string const &filename)
Imports a JSON file from the given filename to the workspace.
void exportVariables(const RooArgSet &allElems, RooFit::Detail::JSONNode &n, bool storeConstant, bool storeBins)
static std::unique_ptr< RooDataHist > readBinnedData(const RooFit::Detail::JSONNode &n, const std::string &namecomp, RooArgSet const &vars)
Read binned data from the JSONNode and create a RooDataHist object.
static RooFit::Detail::JSONNode & appendNamedChild(RooFit::Detail::JSONNode &node, std::string const &name)
static RooFit::Detail::JSONNode & getRooFitInternal(RooFit::Detail::JSONNode &node, Keys_t const &...keys)
static void exportArray(std::size_t n, double const *contents, RooFit::Detail::JSONNode &output)
Export an array of doubles to a JSONNode.
static bool testValidName(const std::string &str, bool forcError)
RooFit::Detail::JSONNode * _rootnodeOutput
static void exportHisto(RooArgSet const &vars, std::size_t n, double const *contents, RooFit::Detail::JSONNode &output)
Export histogram data to a JSONNode.
std::vector< RooAbsArg const * > _serversToDelete
std::unordered_map< std::string, RooFit::Detail::JSONNode const * > _functionsByName
void exportSingleModelConfig(RooFit::Detail::JSONNode &rootnode, RooStats::ModelConfig const &mc, std::string const &analysisName, std::map< std::string, std::string > const *dataComponents)
static std::unique_ptr< RooFit::Detail::JSONTree > createNewJSONTree()
Create a new JSON tree with version information.
const RooFit::Detail::JSONNode * _rootnodeInput
RooJSONFactoryWSTool::CombinedData exportCombinedData(RooAbsData const &data)
std::string exportJSONtoString()
Export the workspace to a JSON string.
static RooWorkspace cleanWS(const RooWorkspace &ws, bool onlyModelConfig=false)
std::string exportTransformed(const RooAbsReal *original, const std::string &suffix, const std::string &formula)
const RooFit::Detail::JSONNode * _attributesNode
static bool isValidName(const std::string &str)
Check if a string is a valid name.
void importDependants(const RooFit::Detail::JSONNode &n)
static void exportAxis(RooFit::Detail::JSONNode &obsNode, RooRealVar const &var)
Export the name and binning of a RooRealVar to a JSONNode.
void importJSONElement(const std::string &name, const std::string &jsonString)
static RooWorkspace sanitizeWS(const RooWorkspace &ws)
static void error(const char *s)
Writes an error message to the RooFit message service and throws a runtime_error.
void setAttribute(const std::string &obj, const std::string &attrib)
void importVariable(const RooFit::Detail::JSONNode &p)
void exportVariable(const RooAbsArg *v, RooFit::Detail::JSONNode &n, bool storeConstant, bool storeBins)
void importFunction(const RooFit::Detail::JSONNode &p, bool importAllDependants)
Import a function from the JSONNode into the workspace.
bool importJSONfromString(const std::string &s)
Import the workspace from a JSON string.
RooFit::Detail::JSONNode * _varsNode
void exportObject(RooAbsArg const &func, std::set< std::string > &exportedObjectNames)
static RooFit::Detail::JSONNode & makeVariablesNode(RooFit::Detail::JSONNode &rootNode)
static std::string sanitizeName(const std::string str)
Cleans up names to the HS3 standard.
void importAllNodes(const RooFit::Detail::JSONNode &n)
static std::string name(const RooFit::Detail::JSONNode &n)
void exportAllObjects(RooFit::Detail::JSONNode &n)
bool exportJSON(std::string const &fileName)
Export the workspace to JSON format and write to the specified file.
static RooFit::Detail::JSONNode const * findNamedChild(RooFit::Detail::JSONNode const &node, std::string const &name)
std::unordered_map< std::string, RooFit::Detail::JSONNode const * > _distributionsByName
void setStringAttribute(const std::string &obj, const std::string &attrib, const std::string &value)
std::vector< RooAbsArg const * > _serversToExport
std::unique_ptr< RooFit::JSONIO::Detail::Domains > _domains
static std::ostream & warning(const std::string &s)
Writes a warning message to the RooFit message service.
static RooArgSet readAxes(const RooFit::Detail::JSONNode &node)
Read axes from the JSONNode and create a RooArgSet representing them.
void importVariableElement(const RooFit::Detail::JSONNode &n)
static RooMsgService & instance()
Return reference to singleton instance.
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
const RooAbsBinning & getBinning(const char *name=nullptr, bool verbose=true, bool createOnTheFly=false, bool shared=true) const override
Return binning definition with name.
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
const RooAbsCategoryLValue & indexCat() const
< A class that holds configuration information for a model using a workspace as a store
Definition ModelConfig.h:34
Persistable container for RooFit projects.
TObject * obj(RooStringView name) const
Return any type of object (RooAbsArg, RooAbsData or generic object) with given name)
const RooArgSet * getSnapshot(const char *name) const
Return the RooArgSet containing a snapshot of variables contained in the workspace.
RooAbsPdf * pdf(RooStringView name) const
Retrieve p.d.f (RooAbsPdf) with given name. A null pointer is returned if not found.
RooArgSet argSet(RooStringView nameList) const
Return set of RooAbsArgs matching to given list of names.
RooArgSet allResolutionModels() const
Return set with all resolution model objects.
bool saveSnapshot(RooStringView, const char *paramNames)
Save snapshot of values and attributes (including "Constant") of given parameters.
RooArgSet allPdfs() const
Return set with all probability density function objects.
std::list< RooAbsData * > allData() const
Return list of all dataset in the workspace.
RooLinkedList const & getSnapshots() const
std::list< TObject * > allGenericObjects() const
Return list of all generic objects in the workspace.
RooAbsReal * function(RooStringView name) const
Retrieve function (RooAbsReal) with given name. Note that all RooAbsPdfs are also RooAbsReals....
RooAbsArg * arg(RooStringView name) const
Return RooAbsArg with given name. A null pointer is returned if none is found.
const RooArgSet & components() const
RooArgSet allFunctions() const
Return set with all function objects.
RooFactoryWSTool & factory()
Return instance to factory tool.
RooRealVar * var(RooStringView name) const
Retrieve real-valued variable (RooRealVar) with given name. A null pointer is returned if not found.
bool loadSnapshot(const char *name)
Load the values and attributes of the parameters in the snapshot saved with the given name.
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.
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2385
RooCmdArg RecycleConflictNodes(bool flag=true)
RooConstVar & RooConst(double val)
RooCmdArg Silence(bool flag=true)
RooCmdArg Index(RooCategory &icat)
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg Import(const char *state, TH1 &histo)
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
std::string makeValidVarName(std::string const &in)
ImportMap & importers()
Definition JSONIO.cxx:59
ExportMap & exporters()
Definition JSONIO.cxx:81
ImportExpressionMap & importExpressions()
Definition JSONIO.cxx:108
ExportKeysMap & exportKeys()
Definition JSONIO.cxx:115
TLine l
Definition textangle.C:4
static void output()