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 <RooFit/ModelConfig.h>
28
29#include "JSONIOUtils.h"
30#include "Domains.h"
31
32#include <TROOT.h>
33
34#include <algorithm>
35#include <fstream>
36#include <iostream>
37#include <stack>
38#include <stdexcept>
39
40/** \class RooJSONFactoryWSTool
41\ingroup roofit
42
43When using `RooFit`, statistical models can be conveniently handled and
44stored as a `RooWorkspace`. However, for the sake of interoperability
45with other statistical frameworks, and also ease of manipulation, it
46may be useful to store statistical models in text form.
47
48The RooJSONFactoryWSTool is a helper class to achieve exactly this,
49exporting to and importing from JSON and YML.
50
51In order to import a workspace from a JSON file, you can do
52
53~~ {.py}
54ws = ROOT.RooWorkspace("ws")
55tool = ROOT.RooJSONFactoryWSTool(ws)
56tool.importJSON("myjson.json")
57~~
58
59Similarly, in order to export a workspace to a JSON file, you can do
60
61~~ {.py}
62tool = ROOT.RooJSONFactoryWSTool(ws)
63tool.exportJSON("myjson.json")
64~~
65
66For more details, consult the tutorial <a
67href="https://root.cern/doc/v626/rf515__hfJSON_8py.html">rf515_hfJSON</a>.
68
69In order to import and export YML files, `ROOT` needs to be compiled
70with the external dependency <a
71href="https://github.com/biojppm/rapidyaml">RapidYAML</a>, which needs
72to be installed on your system and enabled via the CMake option
73`roofit_hs3_ryml`.
74
75The RooJSONFactoryWSTool only knows about a limited set of classes for
76import and export. If import or export of a class you're interested in
77fails, you might need to add your own importer or exporter. Please
78consult the <a
79href="https://github.com/root-project/root/blob/master/roofit/hs3/README.md">README</a>
80to learn how to do that.
81
82You can always get a list of all the avialable importers and exporters by calling the following functions:
83~~ {.py}
84ROOT.RooFit.JSONIO.printImporters()
85ROOT.RooFit.JSONIO.printExporters()
86ROOT.RooFit.JSONIO.printFactoryExpressions()
87ROOT.RooFit.JSONIO.printExportKeys()
88~~
89
90Alternatively, you can generate a LaTeX version of the available importers and exporters by calling
91~~ {.py}
92tool = ROOT.RooJSONFactoryWSTool(ws)
93tool.writedoc("hs3.tex")
94~~
95
96*/
97
100
101namespace {
102
103bool matches(const RooJSONFactoryWSTool::CombinedData &data, const RooSimultaneous *pdf)
104{
105 return data.components.size() == pdf->indexCat().size();
106}
107
108struct Var {
109 int nbins;
110 double min;
111 double max;
112 std::vector<double> bounds;
113
114 Var(int n) : nbins(n), min(0), max(n) {}
115 Var(const JSONNode &val);
116};
117
118bool isNumber(const std::string &str)
119{
120 bool first = true;
121 for (char const &c : str) {
122 if (std::isdigit(c) == 0 && c != '.' && !(first && (c == '-' || c == '+')))
123 return false;
124 first = false;
125 }
126 return true;
127}
128
129void configureVariable(RooFit::JSONIO::Detail::Domains &domains, const JSONNode &p, RooRealVar &v)
130{
131 if (auto n = p.find("value"))
132 v.setVal(n->val_double());
133 domains.writeVariable(v);
134 if (auto n = p.find("nbins"))
135 v.setBins(n->val_int());
136 if (auto n = p.find("relErr"))
137 v.setError(v.getVal() * n->val_double());
138 if (auto n = p.find("err"))
139 v.setError(n->val_double());
140 if (auto n = p.find("const"))
141 v.setConstant(n->val_bool());
142 else
143 v.setConstant(false);
144}
145
146JSONNode const *getVariablesNode(JSONNode const &rootNode)
147{
148 auto paramPointsNode = rootNode.find("parameter_points");
149 if (!paramPointsNode)
150 return nullptr;
151 auto out = RooJSONFactoryWSTool::findNamedChild(*paramPointsNode, "default_values");
152 if (out == nullptr)
153 return nullptr;
154 return &((*out)["parameters"]);
155}
156
157void logInputArgumentsError(std::stringstream &&ss)
158{
159 oocoutE(nullptr, InputArguments) << ss.str() << std::endl;
160}
161
162Var::Var(const JSONNode &val)
163{
164 if (val.find("bounds")) {
165 for (auto const &child : val.children()) {
166 this->bounds.push_back(child.val_double());
167 }
168 this->nbins = this->bounds.size();
169 this->min = this->bounds[0];
170 this->max = this->bounds[this->nbins - 1];
171 } else {
172 if (!val.find("nbins"))
173 this->nbins = 1;
174 else
175 this->nbins = val["nbins"].val_int();
176 if (!val.find("min"))
177 this->min = 0;
178 else
179 this->min = val["min"].val_double();
180 if (!val.find("max"))
181 this->max = 1;
182 else
183 this->max = val["max"].val_double();
184 }
185}
186
187std::string genPrefix(const JSONNode &p, bool trailing_underscore)
188{
189 std::string prefix;
190 if (!p.is_map())
191 return prefix;
192 if (auto node = p.find("namespaces")) {
193 for (const auto &ns : node->children()) {
194 if (!prefix.empty())
195 prefix += "_";
196 prefix += ns.val();
197 }
198 }
199 if (trailing_underscore && !prefix.empty())
200 prefix += "_";
201 return prefix;
202}
203
204// helpers for serializing / deserializing binned datasets
205void genIndicesHelper(std::vector<std::vector<int>> &combinations, std::vector<int> &curr_comb,
206 const std::vector<int> &vars_numbins, size_t curridx)
207{
208 if (curridx == vars_numbins.size()) {
209 // we have filled a combination. Copy it.
210 combinations.emplace_back(curr_comb);
211 } else {
212 for (int i = 0; i < vars_numbins[curridx]; ++i) {
213 curr_comb[curridx] = i;
214 ::genIndicesHelper(combinations, curr_comb, vars_numbins, curridx + 1);
215 }
216 }
217}
218
219void importAttributes(RooAbsArg *arg, JSONNode const &node)
220{
221 if (auto seq = node.find("dict")) {
222 for (const auto &attr : seq->children()) {
223 arg->setStringAttribute(attr.key().c_str(), attr.val().c_str());
224 }
225 }
226 if (auto seq = node.find("tags")) {
227 for (const auto &attr : seq->children()) {
228 arg->setAttribute(attr.val().c_str());
229 }
230 }
231}
232
233// RooWSFactoryTool expression handling
234std::string generate(const RooFit::JSONIO::ImportExpression &ex, const JSONNode &p, RooJSONFactoryWSTool *tool)
235{
236 std::stringstream expression;
237 std::string classname(ex.tclass->GetName());
238 size_t colon = classname.find_last_of(":");
239 expression << (colon < classname.size() ? classname.substr(colon + 1) : classname);
240 bool first = true;
241 const auto &name = RooJSONFactoryWSTool::name(p);
242 for (auto k : ex.arguments) {
243 expression << (first ? "::" + name + "(" : ",");
244 first = false;
245 if (k == "true" || k == "false") {
246 expression << (k == "true" ? "1" : "0");
247 } else if (!p.has_child(k)) {
248 std::stringstream errMsg;
249 errMsg << "node '" << name << "' is missing key '" << k << "'";
250 RooJSONFactoryWSTool::error(errMsg.str());
251 } else if (p[k].is_seq()) {
252 bool firstInner = true;
253 for (RooAbsArg *arg : tool->requestArgList<RooAbsReal>(p, k)) {
254 expression << (firstInner ? "{" : ",") << arg->GetName();
255 firstInner = false;
256 }
257 expression << "}";
258 } else {
259 tool->requestArg<RooAbsReal>(p, p[k].key());
260 expression << p[k].val();
261 }
262 }
263 expression << ")";
264 return expression.str();
265}
266
267std::vector<std::vector<int>> generateBinIndices(const RooArgList &vars)
268{
269 std::vector<std::vector<int>> combinations;
270 std::vector<int> vars_numbins;
271 vars_numbins.reserve(vars.size());
272 for (const auto *absv : static_range_cast<RooRealVar *>(vars)) {
273 vars_numbins.push_back(absv->getBins());
274 }
275 std::vector<int> curr_comb(vars.size());
276 ::genIndicesHelper(combinations, curr_comb, vars_numbins, 0);
277 return combinations;
278}
279
280template <typename... Keys_t>
281JSONNode const *findRooFitInternal(JSONNode const &node, Keys_t const &...keys)
282{
283 return node.find("misc", "ROOT_internal", keys...);
284}
285
286bool isLiteralConstVar(RooAbsArg const &arg)
287{
288 bool isRooConstVar = dynamic_cast<RooConstVar const *>(&arg);
289 return isRooConstVar && isNumber(arg.GetName());
290}
291
292void exportAttributes(const RooAbsArg *arg, JSONNode &rootnode)
293{
294 // If this RooConst is a literal number, we don't need to export the attributes.
295 if (isLiteralConstVar(*arg)) {
296 return;
297 }
298
299 JSONNode *node = nullptr;
300
301 auto initializeNode = [&]() {
302 if (node)
303 return;
304
305 node = &RooJSONFactoryWSTool::getRooFitInternal(rootnode, "attributes").set_map()[arg->GetName()].set_map();
306 };
307
308 // We have to remember if the variable was a constant RooRealVar or a
309 // RooConstVar in RooFit to reconstruct the workspace correctly. The HS3
310 // standard does not make this distinction.
311 bool isRooConstVar = dynamic_cast<RooConstVar const *>(arg);
312 if (isRooConstVar) {
313 initializeNode();
314 (*node)["is_const_var"] << 1;
315 }
316
317 // export all string attributes of an object
318 if (!arg->stringAttributes().empty()) {
319 for (const auto &it : arg->stringAttributes()) {
320 // Skip some RooFit internals
321 if (it.first == "factory_tag" || it.first == "PROD_TERM_TYPE")
322 continue;
323 initializeNode();
324 (*node)["dict"].set_map()[it.first] << it.second;
325 }
326 }
327 if (!arg->attributes().empty()) {
328 for (auto const &attr : arg->attributes()) {
329 // Skip some RooFit internals
330 if (attr == "SnapShot_ExtRefClone" || attr == "RooRealConstant_Factory_Object")
331 continue;
332 initializeNode();
333 (*node)["tags"].set_seq().append_child() << attr;
334 }
335 }
336}
337
338///////////////////////////////////////////////////////////////////////////////////////////////////////
339// create several observables
340void getObservables(RooWorkspace const &ws, const JSONNode &node, RooArgSet &out)
341{
342 std::map<std::string, Var> vars;
343 for (const auto &p : node["axes"].children()) {
344 vars.emplace(RooJSONFactoryWSTool::name(p), Var(p));
345 }
346
347 for (auto v : vars) {
348 std::string name(v.first);
349 if (ws.var(name)) {
350 out.add(*ws.var(name));
351 } else {
352 std::stringstream errMsg;
353 errMsg << "The observable \"" << name << "\" could not be found in the workspace!";
354 RooJSONFactoryWSTool::error(errMsg.str());
355 }
356 }
357}
358
359///////////////////////////////////////////////////////////////////////////////////////////////////////
360// importing data
361std::unique_ptr<RooAbsData> loadData(const JSONNode &p, RooWorkspace &workspace)
362{
363 std::string name(RooJSONFactoryWSTool::name(p));
364 std::string const &type = p["type"].val();
365 if (type == "binned") {
366 // binned
368 } else if (type == "unbinned") {
369 // unbinned
370 RooArgSet vars;
371 getObservables(workspace, p, vars);
372 RooArgList varlist(vars);
373 auto data = std::make_unique<RooDataSet>(name, name, vars, RooFit::WeightVar());
374 auto &coords = p["entries"];
375 auto &weights = p["weights"];
376 if (coords.num_children() != weights.num_children()) {
377 RooJSONFactoryWSTool::error("inconsistent number of entries and weights!");
378 }
379 if (!coords.is_seq()) {
380 RooJSONFactoryWSTool::error("key 'entries' is not a list!");
381 }
382 std::vector<double> weightVals;
383 for (auto const &weight : weights.children()) {
384 weightVals.push_back(weight.val_double());
385 }
386 std::size_t i = 0;
387 for (auto const &point : coords.children()) {
388 if (!point.is_seq()) {
389 std::stringstream errMsg;
390 errMsg << "coordinate point '" << i << "' is not a list!";
391 RooJSONFactoryWSTool::error(errMsg.str());
392 }
393 if (point.num_children() != varlist.size()) {
394 RooJSONFactoryWSTool::error("inconsistent number of entries and observables!");
395 }
396 std::size_t j = 0;
397 for (auto const &pointj : point.children()) {
398 auto *v = static_cast<RooRealVar *>(varlist.at(j));
399 v->setVal(pointj.val_double());
400 ++j;
401 }
402 data->add(vars, weightVals[i]);
403 ++i;
404 }
405 return data;
406 }
407
408 std::stringstream ss;
409 ss << "RooJSONFactoryWSTool() failed to create dataset " << name << std::endl;
410 logInputArgumentsError(std::move(ss));
411 return nullptr;
412}
413
414void importAnalysis(const JSONNode &rootnode, const JSONNode &analysisNode, const JSONNode &likelihoodsNode,
415 RooWorkspace &workspace, const std::vector<std::unique_ptr<RooAbsData>> &datas)
416{
417 // if this is a toplevel pdf, also create a modelConfig for it
418 std::string const &analysisName = RooJSONFactoryWSTool::name(analysisNode);
419 JSONNode const *mcAuxNode = findRooFitInternal(rootnode, "ModelConfigs", analysisName);
420
421 JSONNode const *mcNameNode = mcAuxNode ? mcAuxNode->find("mcName") : nullptr;
422 std::string mcname = mcNameNode ? mcNameNode->val() : analysisName.c_str();
423 if (workspace.obj(mcname))
424 return;
425
426 workspace.import(RooStats::ModelConfig{mcname.c_str(), mcname.c_str()});
427 auto *mc = static_cast<RooStats::ModelConfig *>(workspace.obj(mcname));
428 mc->SetWS(workspace);
429
430 std::vector<std::string> nllDistNames;
431 std::vector<std::string> nllDataNames;
432
433 auto *nllNode = RooJSONFactoryWSTool::findNamedChild(likelihoodsNode, analysisNode["likelihood"].val());
434 if (!nllNode) {
435 throw std::runtime_error("likelihood node not found!");
436 }
437 for (auto &nameNode : (*nllNode)["distributions"].children()) {
438 nllDistNames.push_back(nameNode.val());
439 }
440 RooArgSet extConstraints;
441 for (auto &nameNode : (*nllNode)["aux_distributions"].children()) {
442 RooAbsArg *extConstraint = workspace.arg(nameNode.val());
443 if (extConstraint) {
444 extConstraints.add(*extConstraint);
445 }
446 }
447 RooArgSet observables;
448 for (auto &nameNode : (*nllNode)["data"].children()) {
449 nllDataNames.push_back(nameNode.val());
450 for (const auto &d : datas) {
451 if (d->GetName() == nameNode.val()) {
452 observables.add(*d->get());
453 }
454 }
455 }
456
457 JSONNode const *pdfNameNode = mcAuxNode ? mcAuxNode->find("pdfName") : nullptr;
458 std::string const pdfName = pdfNameNode ? pdfNameNode->val() : "simPdf";
459
460 auto *pdf = static_cast<RooSimultaneous *>(workspace.pdf(pdfName));
461 if (!pdf)
462 std::runtime_error("pdf not found!");
463
464 mc->SetPdf(*pdf);
465
466 if (!extConstraints.empty())
467 mc->SetExternalConstraints(extConstraints);
468
469 auto readArgSet = [&](std::string const &name) {
470 RooArgSet out;
471 for (auto const &child : analysisNode[name].children()) {
472 out.add(*workspace.arg(child.val()));
473 }
474 return out;
475 };
476
477 mc->SetParametersOfInterest(readArgSet("parameters_of_interest"));
478 mc->SetObservables(observables);
479 RooArgSet pars;
480 pdf->getParameters(&observables, pars);
481
482 // Figure out the set parameters that appear in the main measurement:
483 // getAllConstraints() has the side effect to remove all parameters from
484 // "mainPars" that are not part of any pdf over observables.
485 RooArgSet mainPars{pars};
486 pdf->getAllConstraints(observables, mainPars, /*stripDisconnected*/ true);
487
488 RooArgSet nps;
489 RooArgSet globs;
490 for (const auto &p : pars) {
491 if (mc->GetParametersOfInterest()->find(*p))
492 continue;
493 if (p->isConstant() && !mainPars.find(*p))
494 globs.add(*p);
495 else
496 nps.add(*p);
497 }
498 mc->SetGlobalObservables(globs);
499 mc->SetNuisanceParameters(nps);
500
501 if (mcAuxNode) {
502 if (auto found = mcAuxNode->find("combined_data_name")) {
503 pdf->setStringAttribute("combined_data_name", found->val().c_str());
504 }
505 }
506}
507
508void combinePdfs(const JSONNode &rootnode, RooWorkspace &ws)
509{
510 auto *combinedPdfInfoNode = findRooFitInternal(rootnode, "combined_distributions");
511
512 // If there is no info on combining pdfs
513 if (combinedPdfInfoNode == nullptr) {
514 return;
515 }
516
517 for (auto &info : combinedPdfInfoNode->children()) {
518
519 // parse the information
520 std::string combinedName = info.key();
521 std::string indexCatName = info["index_cat"].val();
522 std::vector<std::string> labels;
523 std::vector<int> indices;
524 std::vector<std::string> pdfNames;
525 for (auto &n : info["indices"].children()) {
526 indices.push_back(n.val_int());
527 }
528 for (auto &n : info["labels"].children()) {
529 labels.push_back(n.val());
530 }
531 for (auto &n : info["distributions"].children()) {
532 pdfNames.push_back(n.val());
533 }
534
535 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
536 std::map<std::string, RooAbsPdf *> pdfMap;
537
538 for (std::size_t iChannel = 0; iChannel < labels.size(); ++iChannel) {
539 indexCat.defineType(labels[iChannel], indices[iChannel]);
540 pdfMap[labels[iChannel]] = ws.pdf(pdfNames[iChannel]);
541 }
542
543 RooSimultaneous simPdf{combinedName.c_str(), combinedName.c_str(), pdfMap, indexCat};
544 ws.import(simPdf, RooFit::RecycleConflictNodes(true), RooFit::Silence(true));
545 }
546}
547
548void combineDatasets(const JSONNode &rootnode, std::vector<std::unique_ptr<RooAbsData>> &datas)
549{
550 auto *combinedDataInfoNode = findRooFitInternal(rootnode, "combined_datas");
551
552 // If there is no info on combining datasets
553 if (combinedDataInfoNode == nullptr) {
554 return;
555 }
556
557 for (auto &info : combinedDataInfoNode->children()) {
558
559 // parse the information
560 std::string combinedName = info.key();
561 std::string indexCatName = info["index_cat"].val();
562 std::vector<std::string> labels;
563 std::vector<int> indices;
564 for (auto &n : info["indices"].children()) {
565 indices.push_back(n.val_int());
566 }
567 for (auto &n : info["labels"].children()) {
568 labels.push_back(n.val());
569 }
570
571 // Create the combined dataset for RooFit
572 std::map<std::string, std::unique_ptr<RooAbsData>> dsMap;
573 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
574 RooArgSet allVars{indexCat};
575 for (std::size_t iChannel = 0; iChannel < labels.size(); ++iChannel) {
576 auto componentName = combinedName + "_" + labels[iChannel];
577 // We move the found channel data out of the "datas" vector, such that
578 // the data components don't get imported anymore.
579 std::unique_ptr<RooAbsData> &component =
580 *std::find_if(datas.begin(), datas.end(), [&](auto &d) { return d && d->GetName() == componentName; });
581 allVars.add(*component->get());
582 dsMap.insert({labels[iChannel], std::move(component)});
583 indexCat.defineType(labels[iChannel], indices[iChannel]);
584 }
585
586 auto combined = std::make_unique<RooDataSet>(combinedName.c_str(), combinedName.c_str(), allVars,
587 RooFit::Import(dsMap), RooFit::Index(indexCat));
588 datas.emplace_back(std::move(combined));
589 }
590}
591
592template <class T>
593void sortByName(T &coll)
594{
595 std::sort(coll.begin(), coll.end(), [](auto &l, auto &r) { return strcmp(l->GetName(), r->GetName()) < 0; });
596}
597
598} // namespace
599
601
603
605{
606 node.set_seq();
607 for (RooAbsArg const *arg : coll) {
608 if (isLiteralConstVar(*arg))
609 node.append_child() << static_cast<RooConstVar const *>(arg)->getVal();
610 else
611 node.append_child() << arg->GetName();
612 }
613}
614
616{
618 return node.set_map()[name].set_map();
619 }
621 child["name"] << name;
622 return child;
623}
624
625JSONNode const *RooJSONFactoryWSTool::findNamedChild(JSONNode const &node, std::string const &name)
626{
628 if (!node.is_map())
629 return nullptr;
630 return node.find(name);
631 }
632 if (!node.is_seq())
633 return nullptr;
634 for (JSONNode const &child : node.children()) {
635 if (child["name"].val() == name)
636 return &child;
637 }
638
639 return nullptr;
640}
641
643{
644 return useListsInsteadOfDicts ? n["name"].val() : n.key();
645}
646
648{
649 return appendNamedChild(rootNode["parameter_points"], "default_values")["parameters"];
650}
651
652template <>
653RooRealVar *RooJSONFactoryWSTool::requestImpl<RooRealVar>(const std::string &objname)
654{
655 if (RooRealVar *retval = _workspace.var(objname))
656 return retval;
657 if (JSONNode const *vars = getVariablesNode(*_rootnodeInput)) {
658 if (auto node = vars->find(objname)) {
659 this->importVariable(*node);
660 if (RooRealVar *retval = _workspace.var(objname))
661 return retval;
662 }
663 }
664 return nullptr;
665}
666
667template <>
668RooAbsPdf *RooJSONFactoryWSTool::requestImpl<RooAbsPdf>(const std::string &objname)
669{
670 if (RooAbsPdf *retval = _workspace.pdf(objname))
671 return retval;
672 if (auto distributionsNode = _rootnodeInput->find("distributions")) {
673 if (auto child = findNamedChild(*distributionsNode, objname)) {
674 this->importFunction(*child, true);
675 if (RooAbsPdf *retval = _workspace.pdf(objname))
676 return retval;
677 }
678 }
679 return nullptr;
680}
681
682template <>
683RooAbsReal *RooJSONFactoryWSTool::requestImpl<RooAbsReal>(const std::string &objname)
684{
685 if (RooAbsReal *retval = _workspace.function(objname))
686 return retval;
687 if (isNumber(objname))
688 return &RooFit::RooConst(std::stod(objname));
689 if (RooAbsPdf *pdf = requestImpl<RooAbsPdf>(objname))
690 return pdf;
691 if (RooRealVar *var = requestImpl<RooRealVar>(objname))
692 return var;
693 if (auto functionNode = _rootnodeInput->find("functions")) {
694 if (auto child = findNamedChild(*functionNode, objname)) {
695 this->importFunction(*child, true);
696 if (RooAbsReal *retval = _workspace.function(objname))
697 return retval;
698 }
699 }
700 return nullptr;
701}
702
704{
705 auto *cv = dynamic_cast<const RooConstVar *>(v);
706 auto *rrv = dynamic_cast<const RooRealVar *>(v);
707 if (!cv && !rrv)
708 return;
709
710 // for RooConstVar, if name and value are the same, we don't need to do anything
711 if (cv && strcmp(cv->GetName(), TString::Format("%g", cv->getVal()).Data()) == 0) {
712 return;
713 }
714
715 // this variable was already exported
716 if (findNamedChild(node, v->GetName())) {
717 return;
718 }
719
720 JSONNode &var = appendNamedChild(node, v->GetName());
721
722 if (cv) {
723 var["value"] << cv->getVal();
724 var["const"] << true;
725 } else if (rrv) {
726 var["value"] << rrv->getVal();
727 if (rrv->isConstant()) {
728 var["const"] << rrv->isConstant();
729 }
730 if (rrv->getBins() != 100) {
731 var["nbins"] << rrv->getBins();
732 }
733 _domains->readVariable(*rrv);
734 }
735}
736
738{
739 // export a list of RooRealVar objects
740 for (RooAbsArg *arg : allElems) {
741 exportVariable(arg, n);
742 }
743}
744
745void RooJSONFactoryWSTool::exportObject(RooAbsArg const &func, std::set<std::string> &exportedObjectNames)
746{
747 const std::string name = func.GetName();
748
749 // if this element was already exported, skip
750 if (exportedObjectNames.find(name) != exportedObjectNames.end())
751 return;
752
753 exportedObjectNames.insert(name);
754
755 if (auto simPdf = dynamic_cast<RooSimultaneous const *>(&func)) {
756 // RooSimultaneous is not used in the HS3 standard, we only export the
757 // dependents and some ROOT internal information.
758 for (RooAbsArg *s : func.servers()) {
759 this->exportObject(*s, exportedObjectNames);
760 }
761
762 std::vector<std::string> channelNames;
763 for (auto const &item : simPdf->indexCat()) {
764 channelNames.push_back(item.first);
765 }
766
767 auto &infoNode = getRooFitInternal(*_rootnodeOutput, "combined_distributions").set_map();
768 auto &child = infoNode[simPdf->GetName()].set_map();
769 child["index_cat"] << simPdf->indexCat().GetName();
770 exportCategory(simPdf->indexCat(), child);
771 child["distributions"].set_seq();
772 for (auto const &item : simPdf->indexCat()) {
773 child["distributions"].append_child() << simPdf->getPdf(item.first.c_str())->GetName();
774 }
775
776 return;
777 } else if (dynamic_cast<RooAbsCategory const *>(&func)) {
778 // categories are created by the respective RooSimultaneous, so we're skipping the export here
779 return;
780 } else if (dynamic_cast<RooRealVar const *>(&func) || dynamic_cast<RooConstVar const *>(&func)) {
781 exportVariable(&func, *_varsNode);
782 return;
783 }
784
785 auto &collectionNode = (*_rootnodeOutput)[dynamic_cast<RooAbsPdf const *>(&func) ? "distributions" : "functions"];
786
787 auto const &exporters = RooFit::JSONIO::exporters();
788 auto const &exportKeys = RooFit::JSONIO::exportKeys();
789
790 TClass *cl = func.IsA();
791
792 auto &elem = appendNamedChild(collectionNode, name);
793
794 auto it = exporters.find(cl);
795 if (it != exporters.end()) { // check if we have a specific exporter available
796 for (auto &exp : it->second) {
797 _serversToExport.clear();
798 if (!exp->exportObject(this, &func, elem)) {
799 // The exporter might have messed with the content of the node
800 // before failing. That's why we clear it and only reset the name.
801 elem.clear();
802 elem.set_map();
804 elem["name"] << name;
805 }
806 continue;
807 }
808 if (exp->autoExportDependants()) {
809 for (RooAbsArg *s : func.servers()) {
810 this->exportObject(*s, exportedObjectNames);
811 }
812 } else {
813 for (RooAbsArg const *s : _serversToExport) {
814 this->exportObject(*s, exportedObjectNames);
815 }
816 }
817 return;
818 }
819 }
820
821 // generic export using the factory expressions
822 const auto &dict = exportKeys.find(cl);
823 if (dict == exportKeys.end()) {
824 std::cerr << "unable to export class '" << cl->GetName() << "' - no export keys available!\n"
825 << "there are several possible reasons for this:\n"
826 << " 1. " << cl->GetName() << " is a custom class that you or some package you are using added.\n"
827 << " 2. " << cl->GetName()
828 << " is a ROOT class that nobody ever bothered to write a serialization definition for.\n"
829 << " 3. something is wrong with your setup, e.g. you might have called "
830 "RooFit::JSONIO::clearExportKeys() and/or never successfully read a file defining these "
831 "keys with RooFit::JSONIO::loadExportKeys(filename)\n"
832 << "either way, please make sure that:\n"
833 << " 3: you are reading a file with export keys - call RooFit::JSONIO::printExportKeys() to "
834 "see what is available\n"
835 << " 2 & 1: you might need to write a serialization definition yourself. check "
836 "https://github.com/root-project/root/blob/master/roofit/hs3/README.md to "
837 "see how to do this!\n";
838 return;
839 }
840
841 elem["type"] << dict->second.type;
842
843 size_t nprox = func.numProxies();
844
845 for (size_t i = 0; i < nprox; ++i) {
846 RooAbsProxy *p = func.getProxy(i);
847
848 // some proxies start with a "!". This is a magic symbol that we don't want to stream
849 std::string pname(p->name());
850 if (pname[0] == '!')
851 pname.erase(0, 1);
852
853 auto k = dict->second.proxies.find(pname);
854 if (k == dict->second.proxies.end()) {
855 std::cerr << "failed to find key matching proxy '" << pname << "' for type '" << dict->second.type
856 << "', encountered in '" << func.GetName() << "', skipping" << std::endl;
857 return;
858 }
859
860 // empty string is interpreted as an instruction to ignore this value
861 if (k->second.empty())
862 continue;
863
864 if (auto l = dynamic_cast<RooListProxy *>(p)) {
865 fillSeq(elem[k->second], *l);
866 }
867 if (auto r = dynamic_cast<RooRealProxy *>(p)) {
868 if (isLiteralConstVar(r->arg()))
869 elem[k->second] << r->arg().getVal();
870 else
871 elem[k->second] << r->arg().GetName();
872 }
873 }
874
875 // export all the servers of a given RooAbsArg
876 for (RooAbsArg *s : func.servers()) {
877 this->exportObject(*s, exportedObjectNames);
878 }
879}
880
881///////////////////////////////////////////////////////////////////////////////////////////////////////
882// importing functions
883void RooJSONFactoryWSTool::importFunction(const JSONNode &p, bool importAllDependants)
884{
885 auto const &importers = RooFit::JSONIO::importers();
886 auto const &factoryExpressions = RooFit::JSONIO::importExpressions();
887
888 // some preparations: what type of function are we dealing with here?
889 std::string name(RooJSONFactoryWSTool::name(p));
890
891 // if the RooAbsArg already exists, we don't need to do anything
892 if (_workspace.arg(name)) {
893 return;
894 }
895 // if the key we found is not a map, it's an error
896 if (!p.is_map()) {
897 std::stringstream ss;
898 ss << "RooJSONFactoryWSTool() function node " + name + " is not a map!";
899 logInputArgumentsError(std::move(ss));
900 return;
901 }
902 std::string prefix = genPrefix(p, true);
903 if (!prefix.empty())
904 name = prefix + name;
905 if (!p.has_child("type")) {
906 std::stringstream ss;
907 ss << "RooJSONFactoryWSTool() no type given for function '" << name << "', skipping." << std::endl;
908 logInputArgumentsError(std::move(ss));
909 return;
910 }
911
912 std::string functype(p["type"].val());
913
914 // import all dependents if importing a workspace, not for creating new objects
915 if (!importAllDependants) {
916 this->importDependants(p);
917 }
918
919 // check for specific implementations
920 auto it = importers.find(functype);
921 bool ok = false;
922 if (it != importers.end()) {
923 for (auto &imp : it->second) {
924 ok = imp->importArg(this, p);
925 if (ok)
926 break;
927 }
928 }
929 if (!ok) { // generic import using the factory expressions
930 auto expr = factoryExpressions.find(functype);
931 if (expr != factoryExpressions.end()) {
932 std::string expression = ::generate(expr->second, p, this);
933 if (!_workspace.factory(expression)) {
934 std::stringstream ss;
935 ss << "RooJSONFactoryWSTool() failed to create " << expr->second.tclass->GetName() << " '" << name
936 << "', skipping. expression was\n"
937 << expression << std::endl;
938 logInputArgumentsError(std::move(ss));
939 }
940 } else {
941 std::stringstream ss;
942 ss << "RooJSONFactoryWSTool() no handling for type '" << functype << "' implemented, skipping."
943 << "\n"
944 << "there are several possible reasons for this:\n"
945 << " 1. " << functype << " is a custom type that is not available in RooFit.\n"
946 << " 2. " << functype
947 << " is a ROOT class that nobody ever bothered to write a deserialization definition for.\n"
948 << " 3. something is wrong with your setup, e.g. you might have called "
949 "RooFit::JSONIO::clearFactoryExpressions() and/or never successfully read a file defining "
950 "these expressions with RooFit::JSONIO::loadFactoryExpressions(filename)\n"
951 << "either way, please make sure that:\n"
952 << " 3: you are reading a file with factory expressions - call "
953 "RooFit::JSONIO::printFactoryExpressions() "
954 "to see what is available\n"
955 << " 2 & 1: you might need to write a deserialization definition yourself. check "
956 "https://github.com/root-project/root/blob/master/roofit/hs3/README.md to see "
957 "how to do this!"
958 << std::endl;
959 logInputArgumentsError(std::move(ss));
960 return;
961 }
962 }
964 if (!func) {
965 std::stringstream err;
966 err << "something went wrong importing function '" << name << "'.";
968 }
969}
970
971void RooJSONFactoryWSTool::importFunction(const std::string &jsonString, bool importAllDependants)
972{
973 this->importFunction((JSONTree::create(jsonString))->rootnode(), importAllDependants);
974}
975
976void RooJSONFactoryWSTool::exportHisto(RooArgSet const &vars, std::size_t n, double const *contents, JSONNode &output)
977{
978 auto &observablesNode = output["axes"].set_seq();
979 // axes have to be ordered to get consistent bin indices
980 for (auto *var : static_range_cast<RooRealVar *>(vars)) {
981 JSONNode &obsNode = observablesNode.append_child().set_map();
982 obsNode["name"] << var->GetName();
983 if (var->getBinning().isUniform()) {
984 obsNode["min"] << var->getMin();
985 obsNode["max"] << var->getMax();
986 obsNode["nbins"] << var->getBins();
987 } else {
988 auto &bounds = obsNode["bounds"];
989 bounds.set_seq();
990 double val = var->getBinning().binLow(0);
991 bounds.append_child() << val;
992 for (int i = 0; i < var->getBinning().numBins(); ++i) {
993 val = var->getBinning().binHigh(i);
994 bounds.append_child() << val;
995 }
996 }
997 }
998
999 return exportArray(n, contents, output["contents"]);
1000}
1001
1002void RooJSONFactoryWSTool::exportArray(std::size_t n, double const *contents, JSONNode &output)
1003{
1004 output.set_seq();
1005 for (std::size_t i = 0; i < n; ++i) {
1006 double w = contents[i];
1007 // To make sure there are no unnecessary floating points in the JSON
1008 if (int(w) == w) {
1009 output.append_child() << int(w);
1010 } else {
1011 output.append_child() << w;
1012 }
1013 }
1014}
1015
1017{
1018 auto &labels = node["labels"].set_seq();
1019 auto &indices = node["indices"].set_seq();
1020
1021 for (auto const &item : cat) {
1022 labels.append_child() << item.first;
1023 indices.append_child() << item.second;
1024 }
1025}
1026
1027///////////////////////////////////////////////////////////////////////////////////////////////////////
1028// exporting combined data
1030{
1031 // find category observables
1032 RooAbsCategory *cat = nullptr;
1033 for (RooAbsArg *obs : *data.get()) {
1034 if (dynamic_cast<RooAbsCategory *>(obs)) {
1035 if (cat) {
1036 RooJSONFactoryWSTool::error("dataset '" + std::string(data.GetName()) +
1037 " has several category observables!");
1038 }
1039 cat = static_cast<RooAbsCategory *>(obs);
1040 }
1041 }
1042
1043 // prepare return value
1045
1046 if (!cat)
1047 return datamap;
1048 // this is a combined dataset
1049
1050 datamap.name = data.GetName();
1051
1052 // Write information necessary to reconstruct the combined dataset upon import
1053 auto &child = getRooFitInternal(*_rootnodeOutput, "combined_datas").set_map()[data.GetName()].set_map();
1054 child["index_cat"] << cat->GetName();
1055 exportCategory(*cat, child);
1056
1057 // Find a RooSimultaneous model that would fit to this dataset
1058 RooSimultaneous const *simPdf = nullptr;
1059 auto *combinedPdfInfoNode = findRooFitInternal(*_rootnodeOutput, "combined_distributions");
1060 if (combinedPdfInfoNode) {
1061 for (auto &info : combinedPdfInfoNode->children()) {
1062 if (info["index_cat"].val() == cat->GetName()) {
1063 simPdf = static_cast<RooSimultaneous const *>(_workspace.pdf(info.key()));
1064 }
1065 }
1066 }
1067
1068 // If there is an associated simultaneous pdf for the index category, we
1069 // use the RooAbsData::split() overload that takes the RooSimultaneous.
1070 // Like this, the observables that are not relevant for a given channel
1071 // are automatically split from the component datasets.
1072 std::unique_ptr<TList> dataList{simPdf ? data.split(*simPdf, true) : data.split(*cat, true)};
1073
1074 int i = 0;
1075 for (RooAbsData *absData : static_range_cast<RooAbsData *>(*dataList)) {
1076 absData->SetName((std::string(data.GetName()) + "_" + absData->GetName()).c_str());
1077 datamap.components[cat->lookupName(i)] = absData->GetName();
1078 this->exportData(*absData);
1079 ++i;
1080 }
1081 return datamap;
1082}
1083
1084///////////////////////////////////////////////////////////////////////////////////////////////////////
1085// exporting data
1087{
1088 // find category observables
1089 RooAbsCategory *cat = nullptr;
1090 for (RooAbsArg *obs : *data.get()) {
1091 if (dynamic_cast<RooAbsCategory *>(obs)) {
1092 if (cat) {
1093 RooJSONFactoryWSTool::error("dataset '" + std::string(data.GetName()) +
1094 " has several category observables!");
1095 }
1096 cat = static_cast<RooAbsCategory *>(obs);
1097 }
1098 }
1099
1100 if (cat)
1101 return;
1102
1103 JSONNode &output = appendNamedChild((*_rootnodeOutput)["data"], data.GetName());
1104
1105 // this is a binned dataset
1106 if (auto dh = dynamic_cast<RooDataHist const *>(&data)) {
1107 output["type"] << "binned";
1108 return exportHisto(*dh->get(), dh->numEntries(), dh->weightArray(), output);
1109 }
1110
1111 // this is a regular unbinned dataset
1112
1113 // This works around a problem in RooStats/HistFactory that was only fixed
1114 // in ROOT 6.30: until then, the weight variable of the observerd dataset,
1115 // called "weightVar", was added to the observables. Therefore, it also got
1116 // added to the Asimov dataset. But the Asimov has its own weight variable,
1117 // called "binWeightAsimov", making "weightVar" an actual observable in the
1118 // Asimov data. But this is only by accident and should be removed.
1119 RooArgSet variables = *data.get();
1120 if (auto weightVar = variables.find("weightVar")) {
1121 variables.remove(*weightVar);
1122 }
1123
1124 // Check if this actually represents a binned dataset, and then import it
1125 // like a RooDataHist. This happens frequently when people create combined
1126 // RooDataSets from binned data to fit HistFactory models. In this case, it
1127 // doesn't make sense to export them like an unbinned dataset, because the
1128 // coordinates are redundant information with the binning. We only do this
1129 // for 1D data for now.
1130 if (data.isWeighted() && variables.size() == 1) {
1131 bool isBinnedData = false;
1132 auto &x = static_cast<RooRealVar const &>(*variables[0]);
1133 std::vector<double> contents;
1134 int i = 0;
1135 for (; i < data.numEntries(); ++i) {
1136 data.get(i);
1137 if (x.getBin() != i)
1138 break;
1139 contents.push_back(data.weight());
1140 }
1141 if (i == x.getBins())
1142 isBinnedData = true;
1143 if (isBinnedData) {
1144 output["type"] << "binned";
1145 return exportHisto(variables, data.numEntries(), contents.data(), output);
1146 }
1147 }
1148
1149 output["type"] << "unbinned";
1150
1151 for (RooAbsArg *arg : variables) {
1152 exportVariable(arg, output["axes"]);
1153 }
1154 auto &coords = output["entries"].set_seq();
1155 auto *weights = data.isWeighted() ? &output["weights"].set_seq() : nullptr;
1156 for (int i = 0; i < data.numEntries(); ++i) {
1157 data.get(i);
1158 coords.append_child().fill_seq(variables, [](auto x) { return static_cast<RooRealVar *>(x)->getVal(); });
1159 if (weights)
1160 weights->append_child() << data.weight();
1161 }
1162}
1163
1164std::unique_ptr<RooDataHist> RooJSONFactoryWSTool::readBinnedData(const JSONNode &n, const std::string &name)
1165{
1166 RooArgList varlist;
1167
1168 for (JSONNode const &nd : n["axes"].children()) {
1169 if (nd.has_child("bounds")) {
1170 std::vector<double> bounds;
1171 for (auto const &bound : nd["bounds"].children()) {
1172 bounds.push_back(bound.val_double());
1173 }
1174 auto obs = std::make_unique<RooRealVar>(nd["name"].val().c_str(), nd["name"].val().c_str(), bounds[0],
1175 bounds[bounds.size() - 1]);
1176 RooBinning bins(obs->getMin(), obs->getMax());
1177 ;
1178 for (auto b : bounds) {
1179 bins.addBoundary(b);
1180 }
1181 obs->setBinning(bins);
1182 varlist.addOwned(std::move(obs));
1183 } else {
1184 auto obs = std::make_unique<RooRealVar>(nd["name"].val().c_str(), nd["name"].val().c_str(),
1185 nd["min"].val_double(), nd["max"].val_double());
1186 obs->setBins(nd["nbins"].val_int());
1187 varlist.addOwned(std::move(obs));
1188 }
1189 }
1190
1191 return readBinnedData(n, name, varlist);
1192}
1193
1194///////////////////////////////////////////////////////////////////////////////////////////////////////
1195// reading binned data
1196std::unique_ptr<RooDataHist>
1197RooJSONFactoryWSTool::readBinnedData(const JSONNode &n, const std::string &name, RooArgList const &varlist)
1198{
1199 if (!n.has_child("contents"))
1200 RooJSONFactoryWSTool::error("no contents given");
1201
1202 JSONNode const &contents = n["contents"];
1203
1204 if (!contents.is_seq())
1205 RooJSONFactoryWSTool::error("contents are not in list form");
1206
1207 JSONNode const *errors = nullptr;
1208 if (n.has_child("errors")) {
1209 errors = &n["errors"];
1210 if (!errors->is_seq())
1211 RooJSONFactoryWSTool::error("errors are not in list form");
1212 }
1213
1214 auto bins = generateBinIndices(varlist);
1215 if (contents.num_children() != bins.size()) {
1216 std::stringstream errMsg;
1217 errMsg << "inconsistent bin numbers: contents=" << contents.num_children() << ", bins=" << bins.size();
1218 RooJSONFactoryWSTool::error(errMsg.str());
1219 }
1220 auto dh = std::make_unique<RooDataHist>(name.c_str(), name.c_str(), varlist);
1221 std::vector<double> contentVals;
1222 contentVals.reserve(contents.num_children());
1223 for (auto const &cont : contents.children()) {
1224 contentVals.push_back(cont.val_double());
1225 }
1226 std::vector<double> errorVals;
1227 if (errors) {
1228 errorVals.reserve(errors->num_children());
1229 for (auto const &err : errors->children()) {
1230 errorVals.push_back(err.val_double());
1231 }
1232 }
1233 for (size_t ibin = 0; ibin < bins.size(); ++ibin) {
1234 const double err = errors ? errorVals[ibin] : -1;
1235 dh->set(ibin, contentVals[ibin], err);
1236 }
1237 return dh;
1238}
1239
1240///////////////////////////////////////////////////////////////////////////////////////////////////////
1241// importing variable
1243{
1244 // import a RooRealVar object
1245 std::string name(RooJSONFactoryWSTool::name(p));
1246 if (_workspace.var(name))
1247 return;
1248 if (!p.is_map()) {
1249 std::stringstream ss;
1250 ss << "RooJSONFactoryWSTool() node '" << name << "' is not a map, skipping.";
1251 oocoutE(nullptr, InputArguments) << ss.str() << std::endl;
1252 return;
1253 }
1254 if (_attributesNode) {
1255 if (auto *attrNode = _attributesNode->find(name)) {
1256 // We should not create RooRealVar objects for RooConstVars!
1257 if (attrNode->has_child("is_const_var") && (*attrNode)["is_const_var"].val_int() == 1) {
1258 wsEmplace<RooConstVar>(name, p["value"].val_double());
1259 return;
1260 }
1261 }
1262 }
1263 configureVariable(*_domains, p, wsEmplace<RooRealVar>(name, 1.));
1264}
1265
1266///////////////////////////////////////////////////////////////////////////////////////////////////////
1267// import all dependants (servers) of a node
1269{
1270 // import all the dependants of an object
1271 if (JSONNode const *varsNode = getVariablesNode(n)) {
1272 for (const auto &p : varsNode->children()) {
1274 }
1275 }
1276 if (auto seq = n.find("functions")) {
1277 for (const auto &p : seq->children()) {
1278 this->importFunction(p, true);
1279 }
1280 }
1281 if (auto seq = n.find("distributions")) {
1282 for (const auto &p : seq->children()) {
1283 this->importFunction(p, true);
1284 }
1285 }
1286}
1287
1289 const std::vector<CombinedData> &combDataSets)
1290{
1291 auto pdf = dynamic_cast<RooSimultaneous const *>(mc.GetPdf());
1292 if (pdf == nullptr) {
1294 << "RooFitHS3 only supports ModelConfigs with RooSimultaneous! Skipping ModelConfig.\n";
1295 return;
1296 }
1297
1298 for (std::size_t i = 0; i < std::max(combDataSets.size(), std::size_t(1)); ++i) {
1299 const bool hasdata = i < combDataSets.size();
1300 if (hasdata && !matches(combDataSets.at(i), pdf))
1301 continue;
1302
1303 std::string analysisName(pdf->GetName());
1304 if (hasdata)
1305 analysisName += "_" + combDataSets[i].name;
1306
1307 exportSingleModelConfig(rootnode, mc, analysisName, hasdata ? &combDataSets[i].components : nullptr);
1308 }
1309}
1310
1312 std::string const &analysisName,
1313 std::map<std::string, std::string> const *dataComponents)
1314{
1315 auto pdf = static_cast<RooSimultaneous const *>(mc.GetPdf());
1316
1317 JSONNode &analysisNode = appendNamedChild(rootnode["analyses"], analysisName);
1318
1319 analysisNode["domains"].set_seq().append_child() << "default_domain";
1320
1321 analysisNode["likelihood"] << analysisName;
1322
1323 auto &nllNode = appendNamedChild(rootnode["likelihoods"], analysisName);
1324 nllNode["distributions"].set_seq();
1325 nllNode["data"].set_seq();
1326
1327 for (auto const &item : pdf->indexCat()) {
1328 nllNode["distributions"].append_child() << pdf->getPdf(item.first)->GetName();
1329 if (dataComponents) {
1330 const auto &d = dataComponents->find(item.first);
1331 nllNode["data"].append_child() << d->second;
1332 }
1333 }
1334 if (mc.GetExternalConstraints()) {
1335 auto &extConstrNode = nllNode["aux_distributions"];
1336 extConstrNode.set_seq();
1337 for (const auto &constr : *mc.GetExternalConstraints()) {
1338 extConstrNode.append_child() << constr->GetName();
1339 }
1340 }
1341
1342 auto writeList = [&](const char *name, RooArgSet const *args) {
1343 if (!args)
1344 return;
1345
1346 std::vector<std::string> names;
1347 names.reserve(args->size());
1348 for (RooAbsArg const *arg : *args)
1349 names.push_back(arg->GetName());
1350 std::sort(names.begin(), names.end());
1351 analysisNode[name].fill_seq(names);
1352 };
1353
1354 writeList("parameters_of_interest", mc.GetParametersOfInterest());
1355
1356 auto &modelConfigAux = getRooFitInternal(rootnode, "ModelConfigs", analysisName);
1357 modelConfigAux.set_map();
1358 modelConfigAux["pdfName"] << pdf->GetName();
1359 modelConfigAux["mcName"] << mc.GetName();
1360}
1361
1363{
1364 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
1366 _rootnodeOutput = &n;
1367
1368 // export all toplevel pdfs
1369 std::vector<RooAbsPdf *> allpdfs;
1370 for (auto &arg : _workspace.allPdfs()) {
1371 if (!arg->hasClients()) {
1372 if (auto *pdf = dynamic_cast<RooAbsPdf *>(arg)) {
1373 allpdfs.push_back(pdf);
1374 }
1375 }
1376 }
1377 sortByName(allpdfs);
1378 std::set<std::string> exportedObjectNames;
1379 for (RooAbsPdf *p : allpdfs) {
1380 this->exportObject(*p, exportedObjectNames);
1381 }
1382
1383 // export attributes of exported objects
1384 for (std::string const &name : exportedObjectNames) {
1385 exportAttributes(_workspace.arg(name), n);
1386 }
1387
1388 // export all datasets
1389 std::vector<RooAbsData *> alldata;
1390 for (auto &d : _workspace.allData()) {
1391 alldata.push_back(d);
1392 }
1393 sortByName(alldata);
1394 // first, take care of combined datasets
1395 std::vector<RooJSONFactoryWSTool::CombinedData> combData;
1396 for (auto &d : alldata) {
1397 auto data = this->exportCombinedData(*d);
1398 if (data.components.size() > 0)
1399 combData.push_back(data);
1400 }
1401 // next, take care of regular datasets
1402 for (auto &d : alldata) {
1403 this->exportData(*d);
1404 }
1405
1406 // export all ModelConfig objects and attached Pdfs
1407 for (TObject *obj : _workspace.allGenericObjects()) {
1408 if (auto mc = dynamic_cast<RooStats::ModelConfig *>(obj)) {
1409 exportModelConfig(n, *mc, combData);
1410 }
1411 }
1412
1413 for (auto *snsh : static_range_cast<RooArgSet const *>(_workspace.getSnapshots())) {
1414 RooArgSet snapshotSorted;
1415 // We only want to add the variables that actually got exported and skip
1416 // the ones that the pdfs encoded implicitly (like in the case of
1417 // HistFactory).
1418 for (RooAbsArg *arg : *snsh) {
1419 if (exportedObjectNames.find(arg->GetName()) != exportedObjectNames.end())
1420 snapshotSorted.add(*arg);
1421 }
1422 snapshotSorted.sort();
1423 std::string name(snsh->GetName());
1424 if (name != "default_values") {
1425 this->exportVariables(snapshotSorted, appendNamedChild(n["parameter_points"], name)["parameters"]);
1426 }
1427 }
1428 _varsNode = nullptr;
1429 _domains->writeJSON(n["domains"]);
1430 _domains.reset();
1431 _rootnodeOutput = nullptr;
1432}
1433
1435{
1436 // import the workspace from JSON
1437 std::stringstream ss(s);
1438 return importJSON(ss);
1439}
1440
1442{
1443 // import the workspace from YML
1444 std::stringstream ss(s);
1445 return importYML(ss);
1446}
1447
1449{
1450 // export the workspace to JSON
1451 std::stringstream ss;
1452 exportJSON(ss);
1453 return ss.str();
1454}
1455
1457{
1458 // export the workspace to YML
1459 std::stringstream ss;
1460 exportYML(ss);
1461 return ss.str();
1462}
1463
1464/// Create a new JSON tree with version information.
1466{
1467 std::unique_ptr<JSONTree> tree = JSONTree::create();
1468 JSONNode &n = tree->rootnode();
1469 n.set_map();
1470 auto &metadata = n["metadata"];
1471 metadata.set_map();
1472
1473 // Bump to 0.2.0 once the HS3 v2 standard is final
1474 metadata["hs3_version"] << "0.1.90";
1475
1476 // Add information about the ROOT version that was used to generate this file
1477 auto &rootInfo = appendNamedChild(metadata["packages"], "ROOT");
1478 std::string versionName = gROOT->GetVersion();
1479 // We want to consistently use dots such that the version name can be easily
1480 // digested automatically.
1481 std::replace(versionName.begin(), versionName.end(), '/', '.');
1482 rootInfo["version"] << versionName;
1483
1484 return tree;
1485}
1486
1488{
1489 // export the workspace in JSON
1490 std::unique_ptr<JSONTree> tree = createNewJSONTree();
1491 JSONNode &n = tree->rootnode();
1492 this->exportAllObjects(n);
1493 n.writeJSON(os);
1494 return true;
1495}
1497{
1498 // export the workspace in JSON
1499 std::ofstream out(filename.c_str());
1500 if (!out.is_open()) {
1501 std::stringstream ss;
1502 ss << "RooJSONFactoryWSTool() invalid output file '" << filename << "'." << std::endl;
1503 logInputArgumentsError(std::move(ss));
1504 return false;
1505 }
1506 return this->exportJSON(out);
1507}
1508
1510{
1511 // export the workspace in YML
1512 std::unique_ptr<JSONTree> tree = createNewJSONTree();
1513 JSONNode &n = tree->rootnode();
1514 this->exportAllObjects(n);
1515 n.writeYML(os);
1516 return true;
1517}
1519{
1520 // export the workspace in YML
1521 std::ofstream out(filename.c_str());
1522 if (!out.is_open()) {
1523 std::stringstream ss;
1524 ss << "RooJSONFactoryWSTool() invalid output file '" << filename << "'." << std::endl;
1525 logInputArgumentsError(std::move(ss));
1526 return false;
1527 }
1528 return this->exportYML(out);
1529}
1530
1532{
1533 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
1534 if (auto domains = n.find("domains"))
1535 _domains->readJSON(*domains);
1536
1537 _rootnodeInput = &n;
1538
1539 _attributesNode = findRooFitInternal(*_rootnodeInput, "attributes");
1540
1541 this->importDependants(n);
1542
1543 if (auto paramPointsNode = n.find("parameter_points")) {
1544 for (const auto &snsh : paramPointsNode->children()) {
1545 std::string name = RooJSONFactoryWSTool::name(snsh);
1546 RooArgSet vars;
1547 for (const auto &var : snsh["parameters"].children()) {
1549 configureVariable(*_domains, var, *rrv);
1550 vars.add(*rrv);
1551 }
1552 }
1554 }
1555 }
1556
1557 combinePdfs(*_rootnodeInput, _workspace);
1558
1559 // Import attributes
1560 if (_attributesNode) {
1561 for (const auto &elem : _attributesNode->children()) {
1562 if (RooAbsArg *arg = _workspace.arg(elem.key()))
1563 importAttributes(arg, elem);
1564 }
1565 }
1566
1567 _attributesNode = nullptr;
1568
1569 // We delay the import of the data to after combineDatasets(), because it
1570 // might be that some datasets are merged to combined datasets there. In
1571 // that case, we will remove the components from the "datas" vector so they
1572 // don't get imported.
1573 std::vector<std::unique_ptr<RooAbsData>> datas;
1574 if (auto dataNode = n.find("data")) {
1575 for (const auto &p : dataNode->children()) {
1576 datas.push_back(loadData(p, _workspace));
1577 }
1578 }
1579
1580 // Now, read in analyses and likelihoods if there are any
1581
1582 if (auto analysesNode = n.find("analyses")) {
1583 for (JSONNode const &analysisNode : analysesNode->children()) {
1584 importAnalysis(*_rootnodeInput, analysisNode, n["likelihoods"], _workspace, datas);
1585 }
1586 }
1587
1588 combineDatasets(*_rootnodeInput, datas);
1589
1590 for (auto const &d : datas) {
1591 if (d)
1593 }
1594
1595 _rootnodeInput = nullptr;
1596 _domains.reset();
1597}
1598
1600{
1601 // import a JSON file to the workspace
1602 std::unique_ptr<JSONTree> tree = JSONTree::create(is);
1603 this->importAllNodes(tree->rootnode());
1604 return true;
1605}
1606
1608{
1609 // import a JSON file to the workspace
1610 std::ifstream infile(filename.c_str());
1611 if (!infile.is_open()) {
1612 std::stringstream ss;
1613 ss << "RooJSONFactoryWSTool() invalid input file '" << filename << "'." << std::endl;
1614 logInputArgumentsError(std::move(ss));
1615 return false;
1616 }
1617 return this->importJSON(infile);
1618}
1619
1621{
1622 // import a YML file to the workspace
1623 std::unique_ptr<JSONTree> tree = JSONTree::create(is);
1624 this->importAllNodes(tree->rootnode());
1625 return true;
1626}
1628{
1629 // import a YML file to the workspace
1630 std::ifstream infile(filename.c_str());
1631 if (!infile.is_open()) {
1632 std::stringstream ss;
1633 ss << "RooJSONFactoryWSTool() invalid input file '" << filename << "'." << std::endl;
1634 logInputArgumentsError(std::move(ss));
1635 return false;
1636 }
1637 return this->importYML(infile);
1638}
1639
1640void RooJSONFactoryWSTool::importJSONElement(const std::string &name, const std::string &jsonString)
1641{
1642 std::unique_ptr<RooFit::Detail::JSONTree> tree = RooFit::Detail::JSONTree::create(jsonString);
1643 JSONNode &n = tree->rootnode();
1644 n["name"] << name;
1645
1646 bool isVariable = true;
1647 if (n.find("type")) {
1648 isVariable = false;
1649 }
1650
1651 if (isVariable) {
1652 this->importVariableElement(n);
1653 } else {
1654 this->importFunction(n, false);
1655 }
1656}
1657
1659{
1660 std::unique_ptr<RooFit::Detail::JSONTree> tree = varJSONString(elementNode);
1661 JSONNode &n = tree->rootnode();
1662 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
1663 if (auto domains = n.find("domains"))
1664 _domains->readJSON(*domains);
1665
1666 _rootnodeInput = &n;
1667 _attributesNode = findRooFitInternal(*_rootnodeInput, "attributes");
1668
1669 JSONNode const *varsNode = getVariablesNode(n);
1670 const auto &p = varsNode->child(0);
1672
1673 auto paramPointsNode = n.find("parameter_points");
1674 const auto &snsh = paramPointsNode->child(0);
1675 std::string name = RooJSONFactoryWSTool::name(snsh);
1676 RooArgSet vars;
1677 const auto &var = snsh["parameters"].child(0);
1679 configureVariable(*_domains, var, *rrv);
1680 vars.add(*rrv);
1681 }
1682
1683 // Import attributes
1684 if (_attributesNode) {
1685 for (const auto &elem : _attributesNode->children()) {
1686 if (RooAbsArg *arg = _workspace.arg(elem.key()))
1687 importAttributes(arg, elem);
1688 }
1689 }
1690
1691 _attributesNode = nullptr;
1692 _rootnodeInput = nullptr;
1693 _domains.reset();
1694}
1695
1696std::ostream &RooJSONFactoryWSTool::log(int level)
1697{
1698 return RooMsgService::instance().log(nullptr, static_cast<RooFit::MsgLevel>(level), RooFit::IO);
1699}
1700
1702{
1703 RooMsgService::instance().log(nullptr, RooFit::MsgLevel::ERROR, RooFit::IO) << s << std::endl;
1704 throw std::runtime_error(s);
1705}
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
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
#define oocoutE(o, a)
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 Int_t Int_t Window_t child
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:110
#define gROOT
Definition TROOT.h:405
RooAbsArg is the common abstract base class for objects that represent a value and a "shape" in RooFi...
Definition RooAbsArg.h:74
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...
const std::set< std::string > & attributes() const
Definition RooAbsArg.h:337
const RefCountList_t & servers() const
List of all servers of this object.
Definition RooAbsArg.h:204
const std::map< std::string, std::string > & stringAttributes() const
Definition RooAbsArg.h:345
Int_t numProxies() const
Return the number of registered proxies.
bool hasClients() const
Definition RooAbsArg.h:124
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.
const std::string & lookupName(value_type index) const
Get the name corresponding to the given index.
std::size_t size() const
Number of states defined.
RooAbsCollection is an abstract container object that can hold multiple RooAbsArg objects.
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.
void sort(bool reverse=false)
Sort collection using std::sort and name comparison.
RooAbsArg * find(const char *name) const
Find object with given name in list.
RooAbsData is the common abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:59
RooArgSet * getAllConstraints(const RooArgSet &observables, RooArgSet &constrainedParams, bool stripDisconnected=true, bool removeConstraintsFromPdf=false) const
This helper function finds and collects all constraints terms of all component p.d....
RooAbsProxy is the abstact interface for proxy classes.
Definition RooAbsProxy.h:37
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:62
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
Class RooBinning is an implements RooAbsBinning in terms of an array of boundary values,...
Definition RooBinning.h:27
bool addBoundary(double boundary)
Add bin boundary at given value.
RooCategory is an object to represent discrete states.
Definition RooCategory.h:28
bool defineType(const std::string &label)
Define a state with given name.
RooConstVar represent a constant real-valued object.
Definition RooConstVar.h:26
The RooDataHist is a container class to hold N-dimensional binned data.
Definition RooDataHist.h:39
virtual std::string val() const =0
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 & child(size_t pos)=0
virtual JSONNode & set_seq()=0
virtual void writeJSON(std::ostream &os) const =0
virtual bool is_seq() const =0
virtual bool is_map() const =0
virtual double val_double() const
JSONNode const * find(std::string const &key) const
virtual int val_int() const
static std::unique_ptr< JSONTree > create()
void writeVariable(RooRealVar &) const
Definition Domains.cxx:33
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
void importFunction(const RooFit::Detail::JSONNode &n, bool importAllDependants)
static constexpr bool useListsInsteadOfDicts
bool importYML(std::string const &filename)
static void exportCategory(RooAbsCategory const &cat, RooFit::Detail::JSONNode &node)
T * requestArg(const RooFit::Detail::JSONNode &node, const std::string &key)
RooJSONFactoryWSTool(RooWorkspace &ws)
void importVariable(const RooFit::Detail::JSONNode &n)
void exportData(RooAbsData const &data)
static std::unique_ptr< RooDataHist > readBinnedData(const RooFit::Detail::JSONNode &n, const std::string &namecomp)
void exportVariables(const RooArgSet &allElems, RooFit::Detail::JSONNode &n)
bool importJSON(std::string const &filename)
static std::ostream & log(int level)
static void fillSeq(RooFit::Detail::JSONNode &node, RooAbsCollection const &coll)
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)
bool importYMLfromString(const std::string &s)
RooFit::Detail::JSONNode * _rootnodeOutput
static void exportHisto(RooArgSet const &vars, std::size_t n, double const *contents, RooFit::Detail::JSONNode &output)
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.
void exportVariable(const RooAbsArg *v, RooFit::Detail::JSONNode &n)
const RooFit::Detail::JSONNode * _rootnodeInput
RooJSONFactoryWSTool::CombinedData exportCombinedData(RooAbsData const &data)
const RooFit::Detail::JSONNode * _attributesNode
void importDependants(const RooFit::Detail::JSONNode &n)
void importJSONElement(const std::string &name, const std::string &jsonString)
static void error(const char *s)
void exportModelConfig(RooFit::Detail::JSONNode &rootnode, RooStats::ModelConfig const &mc, const std::vector< RooJSONFactoryWSTool::CombinedData > &d)
bool exportYML(std::string const &fileName)
bool importJSONfromString(const std::string &s)
RooFit::Detail::JSONNode * _varsNode
void exportObject(RooAbsArg const &func, std::set< std::string > &exportedObjectNames)
static RooFit::Detail::JSONNode & makeVariablesNode(RooFit::Detail::JSONNode &rootNode)
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)
static RooFit::Detail::JSONNode const * findNamedChild(RooFit::Detail::JSONNode const &node, std::string const &name)
std::vector< RooAbsArg const * > _serversToExport
std::unique_ptr< RooFit::JSONIO::Detail::Domains > _domains
void importVariableElement(const RooFit::Detail::JSONNode &n)
std::ostream & log(const RooAbsArg *self, RooFit::MsgLevel level, RooFit::MsgTopic facility, bool forceSkipPrefix=false)
Log error message associated with RooAbsArg object self at given level and topic.
static RooMsgService & instance()
Return reference to singleton instance.
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:40
void setVal(double value) override
Set value of variable to 'value'.
RooSimultaneous facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
RooAbsPdf * getPdf(RooStringView catName) const
Return the p.d.f associated with the given index category name.
const RooAbsCategoryLValue & indexCat() const
ModelConfig is a simple class that holds configuration information specifying how a model should be u...
Definition ModelConfig.h:35
const RooArgSet * GetParametersOfInterest() const
get RooArgSet containing the parameter of interest (return nullptr if not existing)
void SetWS(RooWorkspace &ws) override
Set a workspace that owns all the necessary components for the analysis.
const RooArgSet * GetExternalConstraints() const
get RooArgSet for global observables (return nullptr if not existing)
RooAbsPdf * GetPdf() const
get model PDF (return nullptr if pdf has not been specified or does not exist)
The RooWorkspace is a persistable container for RooFit projects.
TObject * obj(RooStringView name) const
Return any type of object (RooAbsArg, RooAbsData or generic object) with given name)
RooAbsPdf * pdf(RooStringView name) const
Retrieve p.d.f (RooAbsPdf) with given name. A null pointer is returned if not found.
bool import(const RooAbsArg &arg, const RooCmdArg &arg1=RooCmdArg(), const RooCmdArg &arg2=RooCmdArg(), const RooCmdArg &arg3=RooCmdArg(), const RooCmdArg &arg4=RooCmdArg(), const RooCmdArg &arg5=RooCmdArg(), const RooCmdArg &arg6=RooCmdArg(), const RooCmdArg &arg7=RooCmdArg(), const RooCmdArg &arg8=RooCmdArg(), const RooCmdArg &arg9=RooCmdArg())
Import a RooAbsArg object, e.g.
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.
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.
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
TClass * IsA() const override
Definition TNamed.h:58
Mother of all ROOT objects.
Definition TObject.h:41
const char * Data() const
Definition TString.h:380
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:2356
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 Var(const RVec< T > &v)
Get the variance of the elements of an RVec.
Definition RVec.hxx:2050
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
ImportMap & importers()
Definition JSONIO.cxx:42
ExportMap & exporters()
Definition JSONIO.cxx:48
ImportExpressionMap & importExpressions()
Definition JSONIO.cxx:54
ExportKeysMap & exportKeys()
Definition JSONIO.cxx:61
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
Definition first.py:1
Definition tree.py:1
std::map< std::string, std::string > components
TLine l
Definition textangle.C:4
static void output()