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