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
30#include "JSONIOUtils.h"
31#include "Domains.h"
32
33#include "RooFitImplHelpers.h"
34
35#include <TROOT.h>
36
37#include <algorithm>
38#include <fstream>
39#include <iostream>
40#include <stack>
41#include <stdexcept>
42
43/** \class RooJSONFactoryWSTool
44\ingroup roofit
45
46When using `RooFit`, statistical models can be conveniently handled and
47stored as a `RooWorkspace`. However, for the sake of interoperability
48with other statistical frameworks, and also ease of manipulation, it
49may be useful to store statistical models in text form.
50
51The RooJSONFactoryWSTool is a helper class to achieve exactly this,
52exporting to and importing from JSON and YML.
53
54In order to import a workspace from a JSON file, you can do
55
56~~~ {.py}
57ws = ROOT.RooWorkspace("ws")
58tool = ROOT.RooJSONFactoryWSTool(ws)
59tool.importJSON("myjson.json")
60~~~
61
62Similarly, in order to export a workspace to a JSON file, you can do
63
64~~~ {.py}
65tool = ROOT.RooJSONFactoryWSTool(ws)
66tool.exportJSON("myjson.json")
67~~~
68
69For more details, consult the tutorial <a
70href="https://root.cern/doc/v626/rf515__hfJSON_8py.html">rf515_hfJSON</a>.
71
72In order to import and export YML files, `ROOT` needs to be compiled
73with the external dependency <a
74href="https://github.com/biojppm/rapidyaml">RapidYAML</a>, which needs
75to be installed on your system when building `ROOT`.
76
77The RooJSONFactoryWSTool only knows about a limited set of classes for
78import and export. If import or export of a class you're interested in
79fails, you might need to add your own importer or exporter. Please
80consult the <a
81href="https://github.com/root-project/root/blob/master/roofit/hs3/README.md">README</a>
82to learn how to do that.
83
84You can always get a list of all the available importers and exporters by calling the following functions:
85~~~ {.py}
86ROOT.RooFit.JSONIO.printImporters()
87ROOT.RooFit.JSONIO.printExporters()
88ROOT.RooFit.JSONIO.printFactoryExpressions()
89ROOT.RooFit.JSONIO.printExportKeys()
90~~~
91
92Alternatively, you can generate a LaTeX version of the available importers and exporters by calling
93~~~ {.py}
94tool = ROOT.RooJSONFactoryWSTool(ws)
95tool.writedoc("hs3.tex")
96~~~
97
98*/
99
100constexpr auto hs3VersionTag = "0.2";
101
104
105namespace {
106
107/**
108 * @brief Check if the number of components in CombinedData matches the number of categories in the RooSimultaneous PDF.
109 *
110 * This function checks whether the number of components in the provided CombinedData 'data' matches the number of
111 * categories in the provided RooSimultaneous PDF 'pdf'.
112 *
113 * @param data The reference to the CombinedData to be checked.
114 * @param pdf The pointer to the RooSimultaneous PDF for comparison.
115 * @return bool Returns true if the number of components in 'data' matches the number of categories in 'pdf'; otherwise,
116 * returns false.
117 */
118bool matches(const RooJSONFactoryWSTool::CombinedData &data, const RooSimultaneous *pdf)
119{
120 return data.components.size() == pdf->indexCat().size();
121}
122
123/**
124 * @struct Var
125 * @brief Structure to store variable information.
126 *
127 * This structure represents variable information such as the number of bins, minimum and maximum values,
128 * and a vector of binning edges for a variable.
129 */
130struct Var {
131 int nbins; // Number of bins
132 double min; // Minimum value
133 double max; // Maximum value
134 std::vector<double> edges; // Vector of edges
135
136 /**
137 * @brief Constructor for Var.
138 * @param n Number of bins.
139 */
140 Var(int n) : nbins(n), min(0), max(n) {}
141
142 /**
143 * @brief Constructor for Var from JSONNode.
144 * @param val JSONNode containing variable information.
145 */
146 Var(const JSONNode &val);
147};
148
149/**
150 * @brief Check if a string represents a valid number.
151 *
152 * This function checks whether the provided string 'str' represents a valid number.
153 * The function returns true if the entire string can be parsed as a number (integer or floating-point); otherwise, it
154 * returns false.
155 *
156 * @param str The string to be checked.
157 * @return bool Returns true if the string 'str' represents a valid number; otherwise, returns false.
158 */
159bool isNumber(const std::string &str)
160{
161 bool first = true;
162 for (char const &c : str) {
163 if (std::isdigit(c) == 0 && c != '.' && !(first && (c == '-' || c == '+')))
164 return false;
165 first = false;
166 }
167 return true;
168}
169
170/**
171 * @brief Check if a string is a valid name.
172 *
173 * A valid name should start with a letter or an underscore, followed by letters, digits, or underscores.
174 * Only characters from the ASCII character set are allowed.
175 *
176 * @param str The string to be checked.
177 * @return bool Returns true if the string is a valid name; otherwise, returns false.
178 */
179bool isValidName(const std::string &str)
180{
181 // Check if the string is empty or starts with a non-letter/non-underscore character
182 if (str.empty() || !(std::isalpha(str[0]) || str[0] == '_')) {
183 return false;
184 }
185
186 // Check the remaining characters in the string
187 for (char c : str) {
188 // Allow letters, digits, and underscore
189 if (!(std::isalnum(c) || c == '_')) {
190 return false;
191 }
192 }
193
194 // If all characters are valid, the string is a valid name
195 return true;
196}
197
198/**
199 * @brief Configure a RooRealVar based on information from a JSONNode.
200 *
201 * This function configures the provided RooRealVar 'v' based on the information provided in the JSONNode 'p'.
202 * The JSONNode 'p' contains information about various properties of the RooRealVar, such as its value, error, number of
203 * bins, etc. The function reads these properties from the JSONNode and sets the corresponding properties of the
204 * RooRealVar accordingly.
205 *
206 * @param domains The reference to the RooFit::JSONIO::Detail::Domains containing domain information for variables (not
207 * used in this function).
208 * @param p The JSONNode containing information about the properties of the RooRealVar 'v'.
209 * @param v The reference to the RooRealVar to be configured.
210 * @return void
211 */
212void configureVariable(RooFit::JSONIO::Detail::Domains &domains, const JSONNode &p, RooRealVar &v)
213{
214 if (!p.has_child("name")) {
215 RooJSONFactoryWSTool::error("cannot instantiate variable without \"name\"!");
216 }
217 if (auto n = p.find("value"))
218 v.setVal(n->val_double());
219 domains.writeVariable(v);
220 if (auto n = p.find("nbins"))
221 v.setBins(n->val_int());
222 if (auto n = p.find("relErr"))
223 v.setError(v.getVal() * n->val_double());
224 if (auto n = p.find("err"))
225 v.setError(n->val_double());
226 if (auto n = p.find("const"))
227 v.setConstant(n->val_bool());
228 else
229 v.setConstant(false);
230}
231
232JSONNode const *getVariablesNode(JSONNode const &rootNode)
233{
234 auto paramPointsNode = rootNode.find("parameter_points");
235 if (!paramPointsNode)
236 return nullptr;
237 auto out = RooJSONFactoryWSTool::findNamedChild(*paramPointsNode, "default_values");
238 if (out == nullptr)
239 return nullptr;
240 return &((*out)["parameters"]);
241}
242
243Var::Var(const JSONNode &val)
244{
245 if (val.find("edges")) {
246 for (auto const &child : val.children()) {
247 this->edges.push_back(child.val_double());
248 }
249 this->nbins = this->edges.size();
250 this->min = this->edges[0];
251 this->max = this->edges[this->nbins - 1];
252 } else {
253 if (!val.find("nbins"))
254 this->nbins = 1;
255 else
256 this->nbins = val["nbins"].val_int();
257 if (!val.find("min"))
258 this->min = 0;
259 else
260 this->min = val["min"].val_double();
261 if (!val.find("max"))
262 this->max = 1;
263 else
264 this->max = val["max"].val_double();
265 }
266}
267
268std::string genPrefix(const JSONNode &p, bool trailing_underscore)
269{
270 std::string prefix;
271 if (!p.is_map())
272 return prefix;
273 if (auto node = p.find("namespaces")) {
274 for (const auto &ns : node->children()) {
275 if (!prefix.empty())
276 prefix += "_";
277 prefix += ns.val();
278 }
279 }
280 if (trailing_underscore && !prefix.empty())
281 prefix += "_";
282 return prefix;
283}
284
285// helpers for serializing / deserializing binned datasets
286void genIndicesHelper(std::vector<std::vector<int>> &combinations, std::vector<int> &curr_comb,
287 const std::vector<int> &vars_numbins, size_t curridx)
288{
289 if (curridx == vars_numbins.size()) {
290 // we have filled a combination. Copy it.
291 combinations.emplace_back(curr_comb);
292 } else {
293 for (int i = 0; i < vars_numbins[curridx]; ++i) {
294 curr_comb[curridx] = i;
295 ::genIndicesHelper(combinations, curr_comb, vars_numbins, curridx + 1);
296 }
297 }
298}
299
300/**
301 * @brief Import attributes from a JSONNode into a RooAbsArg.
302 *
303 * This function imports attributes, represented by the provided JSONNode 'node', into the provided RooAbsArg 'arg'.
304 * The attributes are read from the JSONNode and applied to the RooAbsArg.
305 *
306 * @param arg The pointer to the RooAbsArg to which the attributes will be imported.
307 * @param node The JSONNode containing information about the attributes to be imported.
308 * @return void
309 */
310void importAttributes(RooAbsArg *arg, JSONNode const &node)
311{
312 if (auto seq = node.find("dict")) {
313 for (const auto &attr : seq->children()) {
314 arg->setStringAttribute(attr.key().c_str(), attr.val().c_str());
315 }
316 }
317 if (auto seq = node.find("tags")) {
318 for (const auto &attr : seq->children()) {
319 arg->setAttribute(attr.val().c_str());
320 }
321 }
322}
323
324// RooWSFactoryTool expression handling
325std::string generate(const RooFit::JSONIO::ImportExpression &ex, const JSONNode &p, RooJSONFactoryWSTool *tool)
326{
327 std::stringstream expression;
328 std::string classname(ex.tclass->GetName());
329 size_t colon = classname.find_last_of(':');
330 expression << (colon < classname.size() ? classname.substr(colon + 1) : classname);
331 bool first = true;
332 const auto &name = RooJSONFactoryWSTool::name(p);
333 for (auto k : ex.arguments) {
334 expression << (first ? "::" + name + "(" : ",");
335 first = false;
336 if (k == "true" || k == "false") {
337 expression << (k == "true" ? "1" : "0");
338 } else if (!p.has_child(k)) {
339 std::stringstream errMsg;
340 errMsg << "node '" << name << "' is missing key '" << k << "'";
341 RooJSONFactoryWSTool::error(errMsg.str());
342 } else if (p[k].is_seq()) {
343 bool firstInner = true;
344 for (RooAbsArg *arg : tool->requestArgList<RooAbsReal>(p, k)) {
345 expression << (firstInner ? "{" : ",") << arg->GetName();
346 firstInner = false;
347 }
348 expression << "}";
349 } else {
350 tool->requestArg<RooAbsReal>(p, p[k].key());
351 expression << p[k].val();
352 }
353 }
354 expression << ")";
355 return expression.str();
356}
357
358/**
359 * @brief Generate bin indices for a set of RooRealVars.
360 *
361 * This function generates all possible combinations of bin indices for the provided RooArgSet 'vars' containing
362 * RooRealVars. Each bin index represents a possible bin selection for the corresponding RooRealVar. The bin indices are
363 * stored in a vector of vectors, where each inner vector represents a combination of bin indices for all RooRealVars.
364 *
365 * @param vars The RooArgSet containing the RooRealVars for which bin indices will be generated.
366 * @return std::vector<std::vector<int>> A vector of vectors containing all possible combinations of bin indices.
367 */
368std::vector<std::vector<int>> generateBinIndices(const RooArgSet &vars)
369{
370 std::vector<std::vector<int>> combinations;
371 std::vector<int> vars_numbins;
372 vars_numbins.reserve(vars.size());
373 for (const auto *absv : static_range_cast<RooRealVar *>(vars)) {
374 vars_numbins.push_back(absv->getBins());
375 }
376 std::vector<int> curr_comb(vars.size());
377 ::genIndicesHelper(combinations, curr_comb, vars_numbins, 0);
378 return combinations;
379}
380
381template <typename... Keys_t>
382JSONNode const *findRooFitInternal(JSONNode const &node, Keys_t const &...keys)
383{
384 return node.find("misc", "ROOT_internal", keys...);
385}
386
387/**
388 * @brief Check if a RooAbsArg is a literal constant variable.
389 *
390 * This function checks whether the provided RooAbsArg 'arg' is a literal constant variable.
391 * A literal constant variable is a RooConstVar with a numeric value as a name.
392 *
393 * @param arg The reference to the RooAbsArg to be checked.
394 * @return bool Returns true if 'arg' is a literal constant variable; otherwise, returns false.
395 */
396bool isLiteralConstVar(RooAbsArg const &arg)
397{
398 bool isRooConstVar = dynamic_cast<RooConstVar const *>(&arg);
399 return isRooConstVar && isNumber(arg.GetName());
400}
401
402/**
403 * @brief Export attributes of a RooAbsArg to a JSONNode.
404 *
405 * This function exports the attributes of the provided RooAbsArg 'arg' to the JSONNode 'rootnode'.
406 *
407 * @param arg The pointer to the RooAbsArg from which attributes will be exported.
408 * @param rootnode The JSONNode to which the attributes will be exported.
409 * @return void
410 */
411void exportAttributes(const RooAbsArg *arg, JSONNode &rootnode)
412{
413 // If this RooConst is a literal number, we don't need to export the attributes.
414 if (isLiteralConstVar(*arg)) {
415 return;
416 }
417
418 JSONNode *node = nullptr;
419
420 auto initializeNode = [&]() {
421 if (node)
422 return;
423
424 node = &RooJSONFactoryWSTool::getRooFitInternal(rootnode, "attributes").set_map()[arg->GetName()].set_map();
425 };
426
427 // We have to remember if the variable was a constant RooRealVar or a
428 // RooConstVar in RooFit to reconstruct the workspace correctly. The HS3
429 // standard does not make this distinction.
430 bool isRooConstVar = dynamic_cast<RooConstVar const *>(arg);
431 if (isRooConstVar) {
432 initializeNode();
433 (*node)["is_const_var"] << 1;
434 }
435
436 // export all string attributes of an object
437 if (!arg->stringAttributes().empty()) {
438 for (const auto &it : arg->stringAttributes()) {
439 // Skip some RooFit internals
440 if (it.first == "factory_tag" || it.first == "PROD_TERM_TYPE")
441 continue;
442 initializeNode();
443 (*node)["dict"].set_map()[it.first] << it.second;
444 }
445 }
446 if (!arg->attributes().empty()) {
447 for (auto const &attr : arg->attributes()) {
448 // Skip some RooFit internals
449 if (attr == "SnapShot_ExtRefClone" || attr == "RooRealConstant_Factory_Object")
450 continue;
451 initializeNode();
452 (*node)["tags"].set_seq().append_child() << attr;
453 }
454 }
455}
456
457/**
458 * @brief Create several observables in the workspace.
459 *
460 * This function obtains a list of observables from the provided
461 * RooWorkspace 'ws' based on their names given in the 'axes" field of
462 * the JSONNode 'node'. The observables are added to the RooArgSet
463 * 'out'.
464 *
465 * @param ws The RooWorkspace in which the observables will be created.
466 * @param node The JSONNode containing information about the observables to be created.
467 * @param out The RooArgSet to which the created observables will be added.
468 * @return void
469 */
470void getObservables(RooWorkspace const &ws, const JSONNode &node, RooArgSet &out)
471{
472 std::map<std::string, Var> vars;
473 for (const auto &p : node["axes"].children()) {
474 vars.emplace(RooJSONFactoryWSTool::name(p), Var(p));
475 }
476
477 for (auto v : vars) {
478 std::string name(v.first);
479 if (ws.var(name)) {
480 out.add(*ws.var(name));
481 } else {
482 std::stringstream errMsg;
483 errMsg << "The observable \"" << name << "\" could not be found in the workspace!";
484 RooJSONFactoryWSTool::error(errMsg.str());
485 }
486 }
487}
488
489/**
490 * @brief Import data from the JSONNode into the workspace.
491 *
492 * This function imports data, represented by the provided JSONNode 'p', into the workspace represented by the provided
493 * RooWorkspace. The data information is read from the JSONNode and added to the workspace.
494 *
495 * @param p The JSONNode representing the data to be imported.
496 * @param workspace The RooWorkspace to which the data will be imported.
497 * @return std::unique_ptr<RooAbsData> A unique pointer to the RooAbsData object representing the imported data.
498 * The caller is responsible for managing the memory of the returned object.
499 */
500std::unique_ptr<RooAbsData> loadData(const JSONNode &p, RooWorkspace &workspace)
501{
502 std::string name(RooJSONFactoryWSTool::name(p));
503
504 if (!::isValidName(name)) {
505 std::stringstream ss;
506 ss << "RooJSONFactoryWSTool() data name '" << name << "' is not valid!" << std::endl;
508 }
509
510 std::string const &type = p["type"].val();
511 if (type == "binned") {
512 // binned
514 } else if (type == "unbinned") {
515 // unbinned
516 RooArgSet vars;
517 getObservables(workspace, p, vars);
518 RooArgList varlist(vars);
519 auto data = std::make_unique<RooDataSet>(name, name, vars, RooFit::WeightVar());
520 auto &coords = p["entries"];
521 auto &weights = p["weights"];
522 if (coords.num_children() != weights.num_children()) {
523 RooJSONFactoryWSTool::error("inconsistent number of entries and weights!");
524 }
525 if (!coords.is_seq()) {
526 RooJSONFactoryWSTool::error("key 'entries' is not a list!");
527 }
528 std::vector<double> weightVals;
529 for (auto const &weight : weights.children()) {
530 weightVals.push_back(weight.val_double());
531 }
532 std::size_t i = 0;
533 for (auto const &point : coords.children()) {
534 if (!point.is_seq()) {
535 std::stringstream errMsg;
536 errMsg << "coordinate point '" << i << "' is not a list!";
537 RooJSONFactoryWSTool::error(errMsg.str());
538 }
539 if (point.num_children() != varlist.size()) {
540 RooJSONFactoryWSTool::error("inconsistent number of entries and observables!");
541 }
542 std::size_t j = 0;
543 for (auto const &pointj : point.children()) {
544 auto *v = static_cast<RooRealVar *>(varlist.at(j));
545 v->setVal(pointj.val_double());
546 ++j;
547 }
548 data->add(vars, weightVals[i]);
549 ++i;
550 }
551 return data;
552 }
553
554 std::stringstream ss;
555 ss << "RooJSONFactoryWSTool() failed to create dataset " << name << std::endl;
557 return nullptr;
558}
559
560/**
561 * @brief Import an analysis from the JSONNode into the workspace.
562 *
563 * This function imports an analysis, represented by the provided JSONNodes 'analysisNode' and 'likelihoodsNode',
564 * into the workspace represented by the provided RooWorkspace. The analysis information is read from the JSONNodes
565 * and added to the workspace as one or more RooStats::ModelConfig objects.
566 *
567 * @param rootnode The root JSONNode representing the entire JSON file.
568 * @param analysisNode The JSONNode representing the analysis to be imported.
569 * @param likelihoodsNode The JSONNode containing information about likelihoods associated with the analysis.
570 * @param workspace The RooWorkspace to which the analysis will be imported.
571 * @param datasets A vector of unique pointers to RooAbsData objects representing the data associated with the analysis.
572 * @return void
573 */
574void importAnalysis(const JSONNode &rootnode, const JSONNode &analysisNode, const JSONNode &likelihoodsNode,
575 RooWorkspace &workspace, const std::vector<std::unique_ptr<RooAbsData>> &datasets)
576{
577 // if this is a toplevel pdf, also create a modelConfig for it
578 std::string const &analysisName = RooJSONFactoryWSTool::name(analysisNode);
579 JSONNode const *mcAuxNode = findRooFitInternal(rootnode, "ModelConfigs", analysisName);
580
581 JSONNode const *mcNameNode = mcAuxNode ? mcAuxNode->find("mcName") : nullptr;
582 std::string mcname = mcNameNode ? mcNameNode->val() : analysisName;
583 if (workspace.obj(mcname))
584 return;
585
586 workspace.import(RooStats::ModelConfig{mcname.c_str(), mcname.c_str()});
587 auto *mc = static_cast<RooStats::ModelConfig *>(workspace.obj(mcname));
588 mc->SetWS(workspace);
589
590 std::vector<std::string> nllDistNames;
591 std::vector<std::string> nllDataNames;
592
593 auto *nllNode = RooJSONFactoryWSTool::findNamedChild(likelihoodsNode, analysisNode["likelihood"].val());
594 if (!nllNode) {
595 throw std::runtime_error("likelihood node not found!");
596 }
597 for (auto &nameNode : (*nllNode)["distributions"].children()) {
598 nllDistNames.push_back(nameNode.val());
599 }
600 RooArgSet extConstraints;
601 for (auto &nameNode : (*nllNode)["aux_distributions"].children()) {
602 RooAbsArg *extConstraint = workspace.arg(nameNode.val());
603 if (extConstraint) {
604 extConstraints.add(*extConstraint);
605 }
606 }
607 RooArgSet observables;
608 for (auto &nameNode : (*nllNode)["data"].children()) {
609 nllDataNames.push_back(nameNode.val());
610 for (const auto &d : datasets) {
611 if (d->GetName() == nameNode.val()) {
612 observables.add(*d->get());
613 }
614 }
615 }
616
617 JSONNode const *pdfNameNode = mcAuxNode ? mcAuxNode->find("pdfName") : nullptr;
618 std::string const pdfName = pdfNameNode ? pdfNameNode->val() : "simPdf";
619
620 RooAbsPdf *pdf = static_cast<RooSimultaneous *>(workspace.pdf(pdfName));
621
622 if (!pdf) {
623 // if there is no simultaneous pdf, we can check whether there is only one pdf in the list
624 if (nllDistNames.size() == 1) {
625 // if so, we can use that one to populate the ModelConfig
626 pdf = workspace.pdf(nllDistNames[0]);
627 } else {
628 // otherwise, we have no choice but to build a simPdf by hand
629 std::string simPdfName = analysisName + "_simPdf";
630 std::string indexCatName = analysisName + "_categoryIndex";
631 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
632 std::map<std::string, RooAbsPdf *> pdfMap;
633 for (std::size_t i = 0; i < nllDistNames.size(); ++i) {
634 indexCat.defineType(nllDistNames[i], i);
635 pdfMap[nllDistNames[i]] = workspace.pdf(nllDistNames[i]);
636 }
637 RooSimultaneous simPdf{simPdfName.c_str(), simPdfName.c_str(), pdfMap, indexCat};
638 workspace.import(simPdf, RooFit::RecycleConflictNodes(true), RooFit::Silence(true));
639 pdf = static_cast<RooSimultaneous *>(workspace.pdf(simPdfName));
640 }
641 }
642
643 mc->SetPdf(*pdf);
644
645 if (!extConstraints.empty())
646 mc->SetExternalConstraints(extConstraints);
647
648 auto readArgSet = [&](std::string const &name) {
649 RooArgSet out;
650 for (auto const &child : analysisNode[name].children()) {
651 out.add(*workspace.arg(child.val()));
652 }
653 return out;
654 };
655
656 mc->SetParametersOfInterest(readArgSet("parameters_of_interest"));
657 mc->SetObservables(observables);
658 RooArgSet pars;
659 pdf->getParameters(&observables, pars);
660
661 // Figure out the set parameters that appear in the main measurement:
662 // getAllConstraints() has the side effect to remove all parameters from
663 // "mainPars" that are not part of any pdf over observables.
664 RooArgSet mainPars{pars};
665 pdf->getAllConstraints(observables, mainPars, /*stripDisconnected*/ true);
666
667 RooArgSet nps;
668 RooArgSet globs;
669 for (const auto &p : pars) {
670 if (mc->GetParametersOfInterest()->find(*p))
671 continue;
672 if (p->isConstant() && !mainPars.find(*p))
673 globs.add(*p);
674 else
675 nps.add(*p);
676 }
677 mc->SetGlobalObservables(globs);
678 mc->SetNuisanceParameters(nps);
679
680 if (mcAuxNode) {
681 if (auto found = mcAuxNode->find("combined_data_name")) {
682 pdf->setStringAttribute("combined_data_name", found->val().c_str());
683 }
684 }
685}
686
687void combinePdfs(const JSONNode &rootnode, RooWorkspace &ws)
688{
689 auto *combinedPdfInfoNode = findRooFitInternal(rootnode, "combined_distributions");
690
691 // If there is no info on combining pdfs
692 if (combinedPdfInfoNode == nullptr) {
693 return;
694 }
695
696 for (auto &info : combinedPdfInfoNode->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;
702 std::vector<int> indices;
703 std::vector<std::string> pdfNames;
704 for (auto &n : info["indices"].children()) {
705 indices.push_back(n.val_int());
706 }
707 for (auto &n : info["labels"].children()) {
708 labels.push_back(n.val());
709 }
710 for (auto &n : info["distributions"].children()) {
711 pdfNames.push_back(n.val());
712 }
713
714 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
715 std::map<std::string, RooAbsPdf *> pdfMap;
716
717 for (std::size_t iChannel = 0; iChannel < labels.size(); ++iChannel) {
718 indexCat.defineType(labels[iChannel], indices[iChannel]);
719 pdfMap[labels[iChannel]] = ws.pdf(pdfNames[iChannel]);
720 }
721
722 RooSimultaneous simPdf{combinedName.c_str(), combinedName.c_str(), pdfMap, indexCat};
723 ws.import(simPdf, RooFit::RecycleConflictNodes(true), RooFit::Silence(true));
724 }
725}
726
727void combineDatasets(const JSONNode &rootnode, std::vector<std::unique_ptr<RooAbsData>> &datasets)
728{
729 auto *combinedDataInfoNode = findRooFitInternal(rootnode, "combined_datasets");
730
731 // If there is no info on combining datasets
732 if (combinedDataInfoNode == nullptr) {
733 return;
734 }
735
736 for (auto &info : combinedDataInfoNode->children()) {
737
738 // parse the information
739 std::string combinedName = info.key();
740 std::string indexCatName = info["index_cat"].val();
741 std::vector<std::string> labels;
742 std::vector<int> indices;
743 for (auto &n : info["indices"].children()) {
744 indices.push_back(n.val_int());
745 }
746 for (auto &n : info["labels"].children()) {
747 labels.push_back(n.val());
748 }
749 if (indices.size() != labels.size()) {
750 RooJSONFactoryWSTool::error("mismatch in number of indices and labels!");
751 }
752
753 // Create the combined dataset for RooFit
754 std::map<std::string, std::unique_ptr<RooAbsData>> dsMap;
755 RooCategory indexCat{indexCatName.c_str(), indexCatName.c_str()};
756 RooArgSet allVars{indexCat};
757 for (std::size_t iChannel = 0; iChannel < labels.size(); ++iChannel) {
758 auto componentName = combinedName + "_" + labels[iChannel];
759 // We move the found channel data out of the "datasets" vector, such that
760 // the data components don't get imported anymore.
761 std::unique_ptr<RooAbsData> &component = *std::find_if(
762 datasets.begin(), datasets.end(), [&](auto &d) { return d && d->GetName() == componentName; });
763 if (!component)
764 RooJSONFactoryWSTool::error("unable to obtain component matching component name '" + componentName + "'");
765 allVars.add(*component->get());
766 dsMap.insert({labels[iChannel], std::move(component)});
767 indexCat.defineType(labels[iChannel], indices[iChannel]);
768 }
769
770 auto combined = std::make_unique<RooDataSet>(combinedName, combinedName, allVars, RooFit::Import(dsMap),
771 RooFit::Index(indexCat));
772 datasets.emplace_back(std::move(combined));
773 }
774}
775
776template <class T>
777void sortByName(T &coll)
778{
779 std::sort(coll.begin(), coll.end(), [](auto &l, auto &r) { return strcmp(l->GetName(), r->GetName()) < 0; });
780}
781
782} // namespace
783
785
787
788void RooJSONFactoryWSTool::fillSeq(JSONNode &node, RooAbsCollection const &coll, size_t nMax)
789{
790 const size_t old_children = node.num_children();
791 node.set_seq();
792 size_t n = 0;
793 for (RooAbsArg const *arg : coll) {
794 if (n >= nMax)
795 break;
796 if (isLiteralConstVar(*arg))
797 node.append_child() << static_cast<RooConstVar const *>(arg)->getVal();
798 else
799 node.append_child() << arg->GetName();
800 ++n;
801 }
802 if (node.num_children() != old_children + coll.size()) {
803 error("unable to stream collection " + std::string(coll.GetName()) + " to " + node.key());
804 }
805}
806
808{
810 return node.set_map()[name].set_map();
811 }
813 child["name"] << name;
814 return child;
815}
816
817JSONNode const *RooJSONFactoryWSTool::findNamedChild(JSONNode const &node, std::string const &name)
818{
820 if (!node.is_map())
821 return nullptr;
822 return node.find(name);
823 }
824 if (!node.is_seq())
825 return nullptr;
826 for (JSONNode const &child : node.children()) {
827 if (child["name"].val() == name)
828 return &child;
829 }
830
831 return nullptr;
832}
833
835{
836 return useListsInsteadOfDicts ? n["name"].val() : n.key();
837}
838
840{
841 return appendNamedChild(rootNode["parameter_points"], "default_values")["parameters"];
842}
843
844template <>
845RooRealVar *RooJSONFactoryWSTool::requestImpl<RooRealVar>(const std::string &objname)
846{
847 if (RooRealVar *retval = _workspace.var(objname))
848 return retval;
849 if (JSONNode const *vars = getVariablesNode(*_rootnodeInput)) {
850 if (auto node = vars->find(objname)) {
851 this->importVariable(*node);
852 if (RooRealVar *retval = _workspace.var(objname))
853 return retval;
854 }
855 }
856 return nullptr;
857}
858
859template <>
860RooAbsPdf *RooJSONFactoryWSTool::requestImpl<RooAbsPdf>(const std::string &objname)
861{
862 if (RooAbsPdf *retval = _workspace.pdf(objname))
863 return retval;
864 if (auto distributionsNode = _rootnodeInput->find("distributions")) {
865 if (auto child = findNamedChild(*distributionsNode, objname)) {
866 this->importFunction(*child, true);
867 if (RooAbsPdf *retval = _workspace.pdf(objname))
868 return retval;
869 }
870 }
871 return nullptr;
872}
873
874template <>
875RooAbsReal *RooJSONFactoryWSTool::requestImpl<RooAbsReal>(const std::string &objname)
876{
877 if (RooAbsReal *retval = _workspace.function(objname))
878 return retval;
879 if (isNumber(objname))
880 return &RooFit::RooConst(std::stod(objname));
881 if (RooAbsPdf *pdf = requestImpl<RooAbsPdf>(objname))
882 return pdf;
883 if (RooRealVar *var = requestImpl<RooRealVar>(objname))
884 return var;
885 if (auto functionNode = _rootnodeInput->find("functions")) {
886 if (auto child = findNamedChild(*functionNode, objname)) {
887 this->importFunction(*child, true);
888 if (RooAbsReal *retval = _workspace.function(objname))
889 return retval;
890 }
891 }
892 return nullptr;
893}
894
895/**
896 * @brief Export a variable from the workspace to a JSONNode.
897 *
898 * This function exports a variable, represented by the provided RooAbsArg pointer 'v', from the workspace to a
899 * JSONNode. The variable's information is added to the JSONNode as key-value pairs.
900 *
901 * @param v The pointer to the RooAbsArg representing the variable to be exported.
902 * @param node The JSONNode to which the variable will be exported.
903 * @return void
904 */
906{
907 auto *cv = dynamic_cast<const RooConstVar *>(v);
908 auto *rrv = dynamic_cast<const RooRealVar *>(v);
909 if (!cv && !rrv)
910 return;
911
912 // for RooConstVar, if name and value are the same, we don't need to do anything
913 if (cv && strcmp(cv->GetName(), TString::Format("%g", cv->getVal()).Data()) == 0) {
914 return;
915 }
916
917 // this variable was already exported
918 if (findNamedChild(node, v->GetName())) {
919 return;
920 }
921
922 JSONNode &var = appendNamedChild(node, v->GetName());
923
924 if (cv) {
925 var["value"] << cv->getVal();
926 var["const"] << true;
927 } else if (rrv) {
928 var["value"] << rrv->getVal();
929 if (rrv->isConstant()) {
930 var["const"] << rrv->isConstant();
931 }
932 if (rrv->getBins() != 100) {
933 var["nbins"] << rrv->getBins();
934 }
935 _domains->readVariable(*rrv);
936 }
937}
938
939/**
940 * @brief Export variables from the workspace to a JSONNode.
941 *
942 * This function exports variables, represented by the provided RooArgSet, from the workspace to a JSONNode.
943 * The variables' information is added to the JSONNode as key-value pairs.
944 *
945 * @param allElems The RooArgSet representing the variables to be exported.
946 * @param n The JSONNode to which the variables will be exported.
947 * @return void
948 */
950{
951 // export a list of RooRealVar objects
952 for (RooAbsArg *arg : allElems) {
953 exportVariable(arg, n);
954 }
955}
956
957RooAbsReal *RooJSONFactoryWSTool::importTransformed(const std::string &name, const std::string &tag,
958 const std::string &operation_name, const std::string &formula)
959{
960 RooAbsReal *transformed = nullptr;
961 const std::string tagname = "autogen_transform_" + tag;
962 if (this->hasAttribute(name, tagname)) {
963 const std::string &original = this->getStringAttribute(name, tagname + "_original");
964 transformed = this->workspace()->function(original);
965 if (transformed)
966 return transformed;
967 }
968 const std::string newname = name + "_" + tag + "_" + operation_name;
969 transformed = this->workspace()->function(newname);
970 if (!transformed) {
971 auto *original = this->workspace()->arg(name);
972 if (!original) {
973 error("unable to import transformed of '" + name + "', original not present.");
974 }
975 RooArgSet components{*original};
976 const std::string &expression = TString::Format(formula.c_str(), name.c_str()).Data();
977 transformed = &wsEmplace<RooFormulaVar>(newname, expression.c_str(), components);
978 transformed->setAttribute(tagname.c_str());
979 }
980 return transformed;
981}
982
983std::string RooJSONFactoryWSTool::exportTransformed(const RooAbsReal *original, const std::string &tag,
984 const std::string &operation_name, const std::string &formula)
985{
986 const std::string tagname = "autogen_transform_" + tag;
987 if (original->getAttribute(tagname.c_str())) {
988 if (const RooFormulaVar *trafo = dynamic_cast<const RooFormulaVar *>(original)) {
989 return trafo->dependents().first()->GetName();
990 }
991 }
992
993 std::string newname = std::string(original->GetName()) + "_" + tag + "_" + operation_name;
994 auto &trafo_node = this->createAdHoc("functions", newname);
995 trafo_node["type"] << "generic_function";
996 trafo_node["expression"] << TString::Format(formula.c_str(), original->GetName()).Data();
997 this->setAttribute(newname, tagname);
998 this->setStringAttribute(newname, tagname + "_original", original->GetName());
999 return newname;
1000}
1001
1002RooFit::Detail::JSONNode &RooJSONFactoryWSTool::createAdHoc(const std::string &toplevel, const std::string &name)
1003{
1004 auto &collectionNode = (*_rootnodeOutput)[toplevel];
1005 return appendNamedChild(collectionNode, name);
1006}
1007
1008/**
1009 * @brief Export an object from the workspace to a JSONNode.
1010 *
1011 * This function exports an object, represented by the provided RooAbsArg, from the workspace to a JSONNode.
1012 * The object's information is added to the JSONNode as key-value pairs.
1013 *
1014 * @param func The RooAbsArg representing the object to be exported.
1015 * @param exportedObjectNames A set of strings containing names of previously exported objects to avoid duplicates.
1016 * This set is updated with the name of the newly exported object.
1017 * @return void
1018 */
1019void RooJSONFactoryWSTool::exportObject(RooAbsArg const &func, std::set<std::string> &exportedObjectNames)
1020{
1021 const std::string name = func.GetName();
1022
1023 // if this element was already exported, skip
1024 if (exportedObjectNames.find(name) != exportedObjectNames.end())
1025 return;
1026
1027 exportedObjectNames.insert(name);
1028
1029 if (auto simPdf = dynamic_cast<RooSimultaneous const *>(&func)) {
1030 // RooSimultaneous is not used in the HS3 standard, we only export the
1031 // dependents and some ROOT internal information.
1032 for (RooAbsArg *s : func.servers()) {
1033 this->exportObject(*s, exportedObjectNames);
1034 }
1035
1036 std::vector<std::string> channelNames;
1037 for (auto const &item : simPdf->indexCat()) {
1038 channelNames.push_back(item.first);
1039 }
1040
1041 auto &infoNode = getRooFitInternal(*_rootnodeOutput, "combined_distributions").set_map();
1042 auto &child = infoNode[simPdf->GetName()].set_map();
1043 child["index_cat"] << simPdf->indexCat().GetName();
1044 exportCategory(simPdf->indexCat(), child);
1045 child["distributions"].set_seq();
1046 for (auto const &item : simPdf->indexCat()) {
1047 child["distributions"].append_child() << simPdf->getPdf(item.first.c_str())->GetName();
1048 }
1049
1050 return;
1051 } else if (dynamic_cast<RooAbsCategory const *>(&func)) {
1052 // categories are created by the respective RooSimultaneous, so we're skipping the export here
1053 return;
1054 } else if (dynamic_cast<RooRealVar const *>(&func) || dynamic_cast<RooConstVar const *>(&func)) {
1055 exportVariable(&func, *_varsNode);
1056 return;
1057 }
1058
1059 auto &collectionNode = (*_rootnodeOutput)[dynamic_cast<RooAbsPdf const *>(&func) ? "distributions" : "functions"];
1060
1061 auto const &exporters = RooFit::JSONIO::exporters();
1062 auto const &exportKeys = RooFit::JSONIO::exportKeys();
1063
1064 TClass *cl = func.IsA();
1065
1066 auto &elem = appendNamedChild(collectionNode, name);
1067
1068 auto it = exporters.find(cl);
1069 if (it != exporters.end()) { // check if we have a specific exporter available
1070 for (auto &exp : it->second) {
1071 _serversToExport.clear();
1072 if (!exp->exportObject(this, &func, elem)) {
1073 // The exporter might have messed with the content of the node
1074 // before failing. That's why we clear it and only reset the name.
1075 elem.clear();
1076 elem.set_map();
1078 elem["name"] << name;
1079 }
1080 continue;
1081 }
1082 if (exp->autoExportDependants()) {
1083 for (RooAbsArg *s : func.servers()) {
1084 this->exportObject(*s, exportedObjectNames);
1085 }
1086 } else {
1087 for (RooAbsArg const *s : _serversToExport) {
1088 this->exportObject(*s, exportedObjectNames);
1089 }
1090 }
1091 return;
1092 }
1093 }
1094
1095 // generic export using the factory expressions
1096 const auto &dict = exportKeys.find(cl);
1097 if (dict == exportKeys.end()) {
1098 std::cerr << "unable to export class '" << cl->GetName() << "' - no export keys available!\n"
1099 << "there are several possible reasons for this:\n"
1100 << " 1. " << cl->GetName() << " is a custom class that you or some package you are using added.\n"
1101 << " 2. " << cl->GetName()
1102 << " is a ROOT class that nobody ever bothered to write a serialization definition for.\n"
1103 << " 3. something is wrong with your setup, e.g. you might have called "
1104 "RooFit::JSONIO::clearExportKeys() and/or never successfully read a file defining these "
1105 "keys with RooFit::JSONIO::loadExportKeys(filename)\n"
1106 << "either way, please make sure that:\n"
1107 << " 3: you are reading a file with export keys - call RooFit::JSONIO::printExportKeys() to "
1108 "see what is available\n"
1109 << " 2 & 1: you might need to write a serialization definition yourself. check "
1110 "https://github.com/root-project/root/blob/master/roofit/hs3/README.md to "
1111 "see how to do this!\n";
1112 return;
1113 }
1114
1115 elem["type"] << dict->second.type;
1116
1117 size_t nprox = func.numProxies();
1118
1119 for (size_t i = 0; i < nprox; ++i) {
1120 RooAbsProxy *p = func.getProxy(i);
1121
1122 // some proxies start with a "!". This is a magic symbol that we don't want to stream
1123 std::string pname(p->name());
1124 if (pname[0] == '!')
1125 pname.erase(0, 1);
1126
1127 auto k = dict->second.proxies.find(pname);
1128 if (k == dict->second.proxies.end()) {
1129 std::cerr << "failed to find key matching proxy '" << pname << "' for type '" << dict->second.type
1130 << "', encountered in '" << func.GetName() << "', skipping" << std::endl;
1131 return;
1132 }
1133
1134 // empty string is interpreted as an instruction to ignore this value
1135 if (k->second.empty())
1136 continue;
1137
1138 if (auto l = dynamic_cast<RooListProxy *>(p)) {
1139 fillSeq(elem[k->second], *l);
1140 }
1141 if (auto r = dynamic_cast<RooArgProxy *>(p)) {
1142 if (isLiteralConstVar(*r->absArg()))
1143 elem[k->second] << static_cast<RooConstVar *>(r->absArg())->getVal();
1144 else
1145 elem[k->second] << r->absArg()->GetName();
1146 }
1147 }
1148
1149 // export all the servers of a given RooAbsArg
1150 for (RooAbsArg *s : func.servers()) {
1151 this->exportObject(*s, exportedObjectNames);
1152 }
1153}
1154
1155/**
1156 * @brief Import a function from the JSONNode into the workspace.
1157 *
1158 * This function imports a function from the given JSONNode into the workspace.
1159 * The function's information is read from the JSONNode and added to the workspace.
1160 *
1161 * @param p The JSONNode representing the function to be imported.
1162 * @param importAllDependants A boolean flag indicating whether to import all dependants (servers) of the function.
1163 * @return void
1164 */
1165void RooJSONFactoryWSTool::importFunction(const JSONNode &p, bool importAllDependants)
1166{
1167 auto const &importers = RooFit::JSONIO::importers();
1168 auto const &factoryExpressions = RooFit::JSONIO::importExpressions();
1169
1170 // some preparations: what type of function are we dealing with here?
1171 std::string name(RooJSONFactoryWSTool::name(p));
1172 if (!::isValidName(name)) {
1173 std::stringstream ss;
1174 ss << "RooJSONFactoryWSTool() function name '" << name << "' is not valid!" << std::endl;
1176 }
1177
1178 // if the RooAbsArg already exists, we don't need to do anything
1179 if (_workspace.arg(name)) {
1180 return;
1181 }
1182 // if the key we found is not a map, it's an error
1183 if (!p.is_map()) {
1184 std::stringstream ss;
1185 ss << "RooJSONFactoryWSTool() function node " + name + " is not a map!";
1187 return;
1188 }
1189 std::string prefix = genPrefix(p, true);
1190 if (!prefix.empty())
1191 name = prefix + name;
1192 if (!p.has_child("type")) {
1193 std::stringstream ss;
1194 ss << "RooJSONFactoryWSTool() no type given for function '" << name << "', skipping." << std::endl;
1196 return;
1197 }
1198
1199 std::string functype(p["type"].val());
1200
1201 // import all dependents if importing a workspace, not for creating new objects
1202 if (!importAllDependants) {
1203 this->importDependants(p);
1204 }
1205
1206 // check for specific implementations
1207 auto it = importers.find(functype);
1208 bool ok = false;
1209 if (it != importers.end()) {
1210 for (auto &imp : it->second) {
1211 ok = imp->importArg(this, p);
1212 if (ok)
1213 break;
1214 }
1215 }
1216 if (!ok) { // generic import using the factory expressions
1217 auto expr = factoryExpressions.find(functype);
1218 if (expr != factoryExpressions.end()) {
1219 std::string expression = ::generate(expr->second, p, this);
1220 if (!_workspace.factory(expression)) {
1221 std::stringstream ss;
1222 ss << "RooJSONFactoryWSTool() failed to create " << expr->second.tclass->GetName() << " '" << name
1223 << "', skipping. expression was\n"
1224 << expression << std::endl;
1226 }
1227 } else {
1228 std::stringstream ss;
1229 ss << "RooJSONFactoryWSTool() no handling for type '" << functype << "' implemented, skipping."
1230 << "\n"
1231 << "there are several possible reasons for this:\n"
1232 << " 1. " << functype << " is a custom type that is not available in RooFit.\n"
1233 << " 2. " << functype
1234 << " is a ROOT class that nobody ever bothered to write a deserialization definition for.\n"
1235 << " 3. something is wrong with your setup, e.g. you might have called "
1236 "RooFit::JSONIO::clearFactoryExpressions() and/or never successfully read a file defining "
1237 "these expressions with RooFit::JSONIO::loadFactoryExpressions(filename)\n"
1238 << "either way, please make sure that:\n"
1239 << " 3: you are reading a file with factory expressions - call "
1240 "RooFit::JSONIO::printFactoryExpressions() "
1241 "to see what is available\n"
1242 << " 2 & 1: you might need to write a deserialization definition yourself. check "
1243 "https://github.com/root-project/root/blob/master/roofit/hs3/README.md to see "
1244 "how to do this!"
1245 << std::endl;
1247 return;
1248 }
1249 }
1251 if (!func) {
1252 std::stringstream err;
1253 err << "something went wrong importing function '" << name << "'.";
1254 RooJSONFactoryWSTool::error(err.str());
1255 }
1256}
1257
1258/**
1259 * @brief Import a function from a JSON string into the workspace.
1260 *
1261 * This function imports a function from the provided JSON string into the workspace.
1262 * The function's information is read from the JSON string and added to the workspace.
1263 *
1264 * @param jsonString The JSON string containing the function information.
1265 * @param importAllDependants A boolean flag indicating whether to import all dependants (servers) of the function.
1266 * @return void
1267 */
1268void RooJSONFactoryWSTool::importFunction(const std::string &jsonString, bool importAllDependants)
1269{
1270 this->importFunction((JSONTree::create(jsonString))->rootnode(), importAllDependants);
1271}
1272
1273/**
1274 * @brief Export histogram data to a JSONNode.
1275 *
1276 * This function exports histogram data, represented by the provided variables and contents, to a JSONNode.
1277 * The histogram's axes information and bin contents are added as key-value pairs to the JSONNode.
1278 *
1279 * @param vars The RooArgSet representing the variables associated with the histogram.
1280 * @param n The number of bins in the histogram.
1281 * @param contents A pointer to the array containing the bin contents of the histogram.
1282 * @param output The JSONNode to which the histogram data will be exported.
1283 * @return void
1284 */
1285void RooJSONFactoryWSTool::exportHisto(RooArgSet const &vars, std::size_t n, double const *contents, JSONNode &output)
1286{
1287 auto &observablesNode = output["axes"].set_seq();
1288 // axes have to be ordered to get consistent bin indices
1289 for (auto *var : static_range_cast<RooRealVar *>(vars)) {
1290 JSONNode &obsNode = observablesNode.append_child().set_map();
1291 obsNode["name"] << var->GetName();
1292 if (var->getBinning().isUniform()) {
1293 obsNode["min"] << var->getMin();
1294 obsNode["max"] << var->getMax();
1295 obsNode["nbins"] << var->getBins();
1296 } else {
1297 auto &edges = obsNode["edges"];
1298 edges.set_seq();
1299 double val = var->getBinning().binLow(0);
1300 edges.append_child() << val;
1301 for (int i = 0; i < var->getBinning().numBins(); ++i) {
1302 val = var->getBinning().binHigh(i);
1303 edges.append_child() << val;
1304 }
1305 }
1306 }
1307
1308 return exportArray(n, contents, output["contents"]);
1309}
1310
1311/**
1312 * @brief Export an array of doubles to a JSONNode.
1313 *
1314 * This function exports an array of doubles, represented by the provided size and contents,
1315 * to a JSONNode. The array elements are added to the JSONNode as a sequence of values.
1316 *
1317 * @param n The size of the array.
1318 * @param contents A pointer to the array containing the double values.
1319 * @param output The JSONNode to which the array will be exported.
1320 * @return void
1321 */
1322void RooJSONFactoryWSTool::exportArray(std::size_t n, double const *contents, JSONNode &output)
1323{
1324 output.set_seq();
1325 for (std::size_t i = 0; i < n; ++i) {
1326 double w = contents[i];
1327 // To make sure there are no unnecessary floating points in the JSON
1328 if (int(w) == w) {
1329 output.append_child() << int(w);
1330 } else {
1331 output.append_child() << w;
1332 }
1333 }
1334}
1335
1336/**
1337 * @brief Export a RooAbsCategory object to a JSONNode.
1338 *
1339 * This function exports a RooAbsCategory object, represented by the provided categories and indices,
1340 * to a JSONNode. The category labels and corresponding indices are added to the JSONNode as key-value pairs.
1341 *
1342 * @param cat The RooAbsCategory object to be exported.
1343 * @param node The JSONNode to which the category data will be exported.
1344 * @return void
1345 */
1347{
1348 auto &labels = node["labels"].set_seq();
1349 auto &indices = node["indices"].set_seq();
1350
1351 for (auto const &item : cat) {
1352 std::string label;
1353 if (std::isalpha(item.first[0])) {
1354 label = RooFit::Detail::makeValidVarName(item.first);
1355 if (label != item.first) {
1356 oocoutW(nullptr, IO) << "RooFitHS3: changed '" << item.first << "' to '" << label
1357 << "' to become a valid name";
1358 }
1359 } else {
1360 RooJSONFactoryWSTool::error("refusing to change first character of string '" + item.first +
1361 "' to make a valid name!");
1362 label = item.first;
1363 }
1364 labels.append_child() << label;
1365 indices.append_child() << item.second;
1366 }
1367}
1368
1369/**
1370 * @brief Export combined data from the workspace to a custom struct.
1371 *
1372 * This function exports combined data from the workspace, represented by the provided RooAbsData object,
1373 * to a CombinedData struct. The struct contains information such as variables, categories,
1374 * and bin contents of the combined data.
1375 *
1376 * @param data The RooAbsData object representing the combined data to be exported.
1377 * @return CombinedData A custom struct containing the exported combined data.
1378 */
1380{
1381 // find category observables
1382 RooAbsCategory *cat = nullptr;
1383 for (RooAbsArg *obs : *data.get()) {
1384 if (dynamic_cast<RooAbsCategory *>(obs)) {
1385 if (cat) {
1386 RooJSONFactoryWSTool::error("dataset '" + std::string(data.GetName()) +
1387 " has several category observables!");
1388 }
1389 cat = static_cast<RooAbsCategory *>(obs);
1390 }
1391 }
1392
1393 // prepare return value
1395
1396 if (!cat)
1397 return datamap;
1398 // this is a combined dataset
1399
1400 datamap.name = data.GetName();
1401
1402 // Write information necessary to reconstruct the combined dataset upon import
1403 auto &child = getRooFitInternal(*_rootnodeOutput, "combined_datasets").set_map()[data.GetName()].set_map();
1404 child["index_cat"] << cat->GetName();
1405 exportCategory(*cat, child);
1406
1407 // Find a RooSimultaneous model that would fit to this dataset
1408 RooSimultaneous const *simPdf = nullptr;
1409 auto *combinedPdfInfoNode = findRooFitInternal(*_rootnodeOutput, "combined_distributions");
1410 if (combinedPdfInfoNode) {
1411 for (auto &info : combinedPdfInfoNode->children()) {
1412 if (info["index_cat"].val() == cat->GetName()) {
1413 simPdf = static_cast<RooSimultaneous const *>(_workspace.pdf(info.key()));
1414 }
1415 }
1416 }
1417
1418 // If there is an associated simultaneous pdf for the index category, we
1419 // use the RooAbsData::split() overload that takes the RooSimultaneous.
1420 // Like this, the observables that are not relevant for a given channel
1421 // are automatically split from the component datasets.
1422 std::unique_ptr<TList> dataList{simPdf ? data.split(*simPdf, true) : data.split(*cat, true)};
1423
1424 for (RooAbsData *absData : static_range_cast<RooAbsData *>(*dataList)) {
1425 std::string catName(absData->GetName());
1426 std::string dataName;
1427 if (std::isalpha(catName[0])) {
1428 dataName = RooFit::Detail::makeValidVarName(catName);
1429 if (dataName != catName) {
1430 oocoutW(nullptr, IO) << "RooFitHS3: changed '" << catName << "' to '" << dataName
1431 << "' to become a valid name";
1432 }
1433 } else {
1434 RooJSONFactoryWSTool::error("refusing to change first character of string '" + catName +
1435 "' to make a valid name!");
1436 dataName = catName;
1437 }
1438 absData->SetName((std::string(data.GetName()) + "_" + dataName).c_str());
1439 datamap.components[catName] = absData->GetName();
1440 this->exportData(*absData);
1441 }
1442 return datamap;
1443}
1444
1445/**
1446 * @brief Export data from the workspace to a JSONNode.
1447 *
1448 * This function exports data represented by the provided RooAbsData object,
1449 * to a JSONNode. The data's information is added as key-value pairs to the JSONNode.
1450 *
1451 * @param data The RooAbsData object representing the data to be exported.
1452 * @return void
1453 */
1455{
1456 // find category observables
1457 RooAbsCategory *cat = nullptr;
1458 for (RooAbsArg *obs : *data.get()) {
1459 if (dynamic_cast<RooAbsCategory *>(obs)) {
1460 if (cat) {
1461 RooJSONFactoryWSTool::error("dataset '" + std::string(data.GetName()) +
1462 " has several category observables!");
1463 }
1464 cat = static_cast<RooAbsCategory *>(obs);
1465 }
1466 }
1467
1468 if (cat)
1469 return;
1470
1471 JSONNode &output = appendNamedChild((*_rootnodeOutput)["data"], data.GetName());
1472
1473 // this is a binned dataset
1474 if (auto dh = dynamic_cast<RooDataHist const *>(&data)) {
1475 output["type"] << "binned";
1476 return exportHisto(*dh->get(), dh->numEntries(), dh->weightArray(), output);
1477 }
1478
1479 // this is a regular unbinned dataset
1480
1481 // This works around a problem in RooStats/HistFactory that was only fixed
1482 // in ROOT 6.30: until then, the weight variable of the observed dataset,
1483 // called "weightVar", was added to the observables. Therefore, it also got
1484 // added to the Asimov dataset. But the Asimov has its own weight variable,
1485 // called "binWeightAsimov", making "weightVar" an actual observable in the
1486 // Asimov data. But this is only by accident and should be removed.
1487 RooArgSet variables = *data.get();
1488 if (auto weightVar = variables.find("weightVar")) {
1489 variables.remove(*weightVar);
1490 }
1491
1492 // Check if this actually represents a binned dataset, and then import it
1493 // like a RooDataHist. This happens frequently when people create combined
1494 // RooDataSets from binned data to fit HistFactory models. In this case, it
1495 // doesn't make sense to export them like an unbinned dataset, because the
1496 // coordinates are redundant information with the binning. We only do this
1497 // for 1D data for now.
1498 if (data.isWeighted() && variables.size() == 1) {
1499 bool isBinnedData = false;
1500 auto &x = static_cast<RooRealVar const &>(*variables[0]);
1501 std::vector<double> contents;
1502 int i = 0;
1503 for (; i < data.numEntries(); ++i) {
1504 data.get(i);
1505 if (x.getBin() != i)
1506 break;
1507 contents.push_back(data.weight());
1508 }
1509 if (i == x.getBins())
1510 isBinnedData = true;
1511 if (isBinnedData) {
1512 output["type"] << "binned";
1513 return exportHisto(variables, data.numEntries(), contents.data(), output);
1514 }
1515 }
1516
1517 output["type"] << "unbinned";
1518
1519 for (RooAbsArg *arg : variables) {
1520 exportVariable(arg, output["axes"]);
1521 }
1522 auto &coords = output["entries"].set_seq();
1523 auto *weights = data.isWeighted() ? &output["weights"].set_seq() : nullptr;
1524 for (int i = 0; i < data.numEntries(); ++i) {
1525 data.get(i);
1526 coords.append_child().fill_seq(variables, [](auto x) { return static_cast<RooRealVar *>(x)->getVal(); });
1527 if (weights)
1528 weights->append_child() << data.weight();
1529 }
1530}
1531
1532/**
1533 * @brief Read axes from the JSONNode and create a RooArgSet representing them.
1534 *
1535 * This function reads axes information from the given JSONNode and
1536 * creates a RooArgSet with variables representing these axes.
1537 *
1538 * @param topNode The JSONNode containing the axes information to be read.
1539 * @return RooArgSet A RooArgSet containing the variables created from the JSONNode.
1540 */
1542{
1543 RooArgSet vars;
1544
1545 for (JSONNode const &node : topNode["axes"].children()) {
1546 if (node.has_child("edges")) {
1547 std::vector<double> edges;
1548 for (auto const &bound : node["edges"].children()) {
1549 edges.push_back(bound.val_double());
1550 }
1551 auto obs = std::make_unique<RooRealVar>(node["name"].val().c_str(), node["name"].val().c_str(), edges[0],
1552 edges[edges.size() - 1]);
1553 RooBinning bins(obs->getMin(), obs->getMax());
1554 for (auto b : edges) {
1555 bins.addBoundary(b);
1556 }
1557 obs->setBinning(bins);
1558 vars.addOwned(std::move(obs));
1559 } else {
1560 auto obs = std::make_unique<RooRealVar>(node["name"].val().c_str(), node["name"].val().c_str(),
1561 node["min"].val_double(), node["max"].val_double());
1562 obs->setBins(node["nbins"].val_int());
1563 vars.addOwned(std::move(obs));
1564 }
1565 }
1566
1567 return vars;
1568}
1569
1570/**
1571 * @brief Read binned data from the JSONNode and create a RooDataHist object.
1572 *
1573 * This function reads binned data from the given JSONNode and creates a RooDataHist object.
1574 * The binned data is associated with the specified name and variables (RooArgSet) in the workspace.
1575 *
1576 * @param n The JSONNode representing the binned data to be read.
1577 * @param name The name to be associated with the created RooDataHist object.
1578 * @param vars The RooArgSet representing the variables associated with the binned data.
1579 * @return std::unique_ptr<RooDataHist> A unique pointer to the created RooDataHist object.
1580 */
1581std::unique_ptr<RooDataHist>
1582RooJSONFactoryWSTool::readBinnedData(const JSONNode &n, const std::string &name, RooArgSet const &vars)
1583{
1584 if (!n.has_child("contents"))
1585 RooJSONFactoryWSTool::error("no contents given");
1586
1587 JSONNode const &contents = n["contents"];
1588
1589 if (!contents.is_seq())
1590 RooJSONFactoryWSTool::error("contents are not in list form");
1591
1592 JSONNode const *errors = nullptr;
1593 if (n.has_child("errors")) {
1594 errors = &n["errors"];
1595 if (!errors->is_seq())
1596 RooJSONFactoryWSTool::error("errors are not in list form");
1597 }
1598
1599 auto bins = generateBinIndices(vars);
1600 if (contents.num_children() != bins.size()) {
1601 std::stringstream errMsg;
1602 errMsg << "inconsistent bin numbers: contents=" << contents.num_children() << ", bins=" << bins.size();
1603 RooJSONFactoryWSTool::error(errMsg.str());
1604 }
1605 auto dh = std::make_unique<RooDataHist>(name, name, vars);
1606 std::vector<double> contentVals;
1607 contentVals.reserve(contents.num_children());
1608 for (auto const &cont : contents.children()) {
1609 contentVals.push_back(cont.val_double());
1610 }
1611 std::vector<double> errorVals;
1612 if (errors) {
1613 errorVals.reserve(errors->num_children());
1614 for (auto const &err : errors->children()) {
1615 errorVals.push_back(err.val_double());
1616 }
1617 }
1618 for (size_t ibin = 0; ibin < bins.size(); ++ibin) {
1619 const double err = errors ? errorVals[ibin] : -1;
1620 dh->set(ibin, contentVals[ibin], err);
1621 }
1622 return dh;
1623}
1624
1625/**
1626 * @brief Import a variable from the JSONNode into the workspace.
1627 *
1628 * This function imports a variable from the given JSONNode into the workspace.
1629 * The variable's information is read from the JSONNode and added to the workspace.
1630 *
1631 * @param p The JSONNode representing the variable to be imported.
1632 * @return void
1633 */
1635{
1636 // import a RooRealVar object
1637 std::string name(RooJSONFactoryWSTool::name(p));
1638
1639 if (!::isValidName(name)) {
1640 std::stringstream ss;
1641 ss << "RooJSONFactoryWSTool() variable name '" << name << "' is not valid!" << std::endl;
1643 }
1644
1645 if (_workspace.var(name))
1646 return;
1647 if (!p.is_map()) {
1648 std::stringstream ss;
1649 ss << "RooJSONFactoryWSTool() node '" << name << "' is not a map, skipping.";
1650 oocoutE(nullptr, InputArguments) << ss.str() << std::endl;
1651 return;
1652 }
1653 if (_attributesNode) {
1654 if (auto *attrNode = _attributesNode->find(name)) {
1655 // We should not create RooRealVar objects for RooConstVars!
1656 if (attrNode->has_child("is_const_var") && (*attrNode)["is_const_var"].val_int() == 1) {
1657 wsEmplace<RooConstVar>(name, p["value"].val_double());
1658 return;
1659 }
1660 }
1661 }
1662 configureVariable(*_domains, p, wsEmplace<RooRealVar>(name, 1.));
1663}
1664
1665/**
1666 * @brief Import all dependants (servers) of a node into the workspace.
1667 *
1668 * This function imports all the dependants (servers) of the given JSONNode into the workspace.
1669 * The dependants' information is read from the JSONNode and added to the workspace.
1670 *
1671 * @param n The JSONNode representing the node whose dependants are to be imported.
1672 * @return void
1673 */
1675{
1676 // import all the dependants of an object
1677 if (JSONNode const *varsNode = getVariablesNode(n)) {
1678 for (const auto &p : varsNode->children()) {
1680 }
1681 }
1682 if (auto seq = n.find("functions")) {
1683 for (const auto &p : seq->children()) {
1684 this->importFunction(p, true);
1685 }
1686 }
1687 if (auto seq = n.find("distributions")) {
1688 for (const auto &p : seq->children()) {
1689 this->importFunction(p, true);
1690 }
1691 }
1692}
1693
1695 const std::vector<CombinedData> &combDataSets)
1696{
1697 auto pdf = dynamic_cast<RooSimultaneous const *>(mc.GetPdf());
1698 if (pdf == nullptr) {
1699 warning("RooFitHS3 only supports ModelConfigs with RooSimultaneous! Skipping ModelConfig.");
1700 return;
1701 }
1702
1703 for (std::size_t i = 0; i < std::max(combDataSets.size(), std::size_t(1)); ++i) {
1704 const bool hasdata = i < combDataSets.size();
1705 if (hasdata && !matches(combDataSets.at(i), pdf))
1706 continue;
1707
1708 std::string analysisName(pdf->GetName());
1709 if (hasdata)
1710 analysisName += "_" + combDataSets[i].name;
1711
1712 exportSingleModelConfig(rootnode, mc, analysisName, hasdata ? &combDataSets[i].components : nullptr);
1713 }
1714}
1715
1717 std::string const &analysisName,
1718 std::map<std::string, std::string> const *dataComponents)
1719{
1720 auto pdf = static_cast<RooSimultaneous const *>(mc.GetPdf());
1721
1722 JSONNode &analysisNode = appendNamedChild(rootnode["analyses"], analysisName);
1723
1724 analysisNode["domains"].set_seq().append_child() << "default_domain";
1725
1726 analysisNode["likelihood"] << analysisName;
1727
1728 auto &nllNode = appendNamedChild(rootnode["likelihoods"], analysisName);
1729 nllNode["distributions"].set_seq();
1730 nllNode["data"].set_seq();
1731
1732 if (dataComponents) {
1733 for (auto const &item : pdf->indexCat()) {
1734 const auto &dataComp = dataComponents->find(item.first);
1735 nllNode["distributions"].append_child() << pdf->getPdf(item.first)->GetName();
1736 nllNode["data"].append_child() << dataComp->second;
1737 }
1738 }
1739
1740 if (mc.GetExternalConstraints()) {
1741 auto &extConstrNode = nllNode["aux_distributions"];
1742 extConstrNode.set_seq();
1743 for (const auto &constr : *mc.GetExternalConstraints()) {
1744 extConstrNode.append_child() << constr->GetName();
1745 }
1746 }
1747
1748 auto writeList = [&](const char *name, RooArgSet const *args) {
1749 if (!args)
1750 return;
1751
1752 std::vector<std::string> names;
1753 names.reserve(args->size());
1754 for (RooAbsArg const *arg : *args)
1755 names.push_back(arg->GetName());
1756 std::sort(names.begin(), names.end());
1757 analysisNode[name].fill_seq(names);
1758 };
1759
1760 writeList("parameters_of_interest", mc.GetParametersOfInterest());
1761
1762 auto &modelConfigAux = getRooFitInternal(rootnode, "ModelConfigs", analysisName);
1763 modelConfigAux.set_map();
1764 modelConfigAux["pdfName"] << pdf->GetName();
1765 modelConfigAux["mcName"] << mc.GetName();
1766}
1767
1768/**
1769 * @brief Export all objects in the workspace to a JSONNode.
1770 *
1771 * This function exports all the objects in the workspace to the provided JSONNode.
1772 * The objects' information is added as key-value pairs to the JSONNode.
1773 *
1774 * @param n The JSONNode to which the objects will be exported.
1775 * @return void
1776 */
1778{
1779 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
1781 _rootnodeOutput = &n;
1782
1783 // export all toplevel pdfs
1784 std::vector<RooAbsPdf *> allpdfs;
1785 for (auto &arg : _workspace.allPdfs()) {
1786 if (!arg->hasClients()) {
1787 if (auto *pdf = dynamic_cast<RooAbsPdf *>(arg)) {
1788 allpdfs.push_back(pdf);
1789 }
1790 }
1791 }
1792 sortByName(allpdfs);
1793 std::set<std::string> exportedObjectNames;
1794 for (RooAbsPdf *p : allpdfs) {
1795 this->exportObject(*p, exportedObjectNames);
1796 }
1797
1798 // export attributes of all objects
1799 for (RooAbsArg *arg : _workspace.components()) {
1800 exportAttributes(arg, n);
1801 }
1802
1803 // export all datasets
1804 std::vector<RooAbsData *> alldata;
1805 for (auto &d : _workspace.allData()) {
1806 alldata.push_back(d);
1807 }
1808 sortByName(alldata);
1809 // first, take care of combined datasets
1810 std::vector<RooJSONFactoryWSTool::CombinedData> combData;
1811 for (auto &d : alldata) {
1812 auto data = this->exportCombinedData(*d);
1813 if (!data.components.empty())
1814 combData.push_back(data);
1815 }
1816 // next, take care of regular datasets
1817 for (auto &d : alldata) {
1818 this->exportData(*d);
1819 }
1820
1821 // export all ModelConfig objects and attached Pdfs
1822 for (TObject *obj : _workspace.allGenericObjects()) {
1823 if (auto mc = dynamic_cast<RooStats::ModelConfig *>(obj)) {
1824 exportModelConfig(n, *mc, combData);
1825 }
1826 }
1827
1828 for (auto *snsh : static_range_cast<RooArgSet const *>(_workspace.getSnapshots())) {
1829 RooArgSet snapshotSorted;
1830 // We only want to add the variables that actually got exported and skip
1831 // the ones that the pdfs encoded implicitly (like in the case of
1832 // HistFactory).
1833 for (RooAbsArg *arg : *snsh) {
1834 if (exportedObjectNames.find(arg->GetName()) != exportedObjectNames.end()) {
1835 bool do_export = false;
1836 for (const auto &pdf : allpdfs) {
1837 if (pdf->dependsOn(*arg)) {
1838 do_export = true;
1839 }
1840 }
1841 if (do_export && !::isValidName(arg->GetName())) {
1842 std::stringstream ss;
1843 ss << "RooJSONFactoryWSTool() variable '" << arg->GetName() << "' has an invalid name!" << std::endl;
1845 }
1846 if (do_export)
1847 snapshotSorted.add(*arg);
1848 }
1849 }
1850 snapshotSorted.sort();
1851 std::string name(snsh->GetName());
1852 if (name != "default_values") {
1853 this->exportVariables(snapshotSorted, appendNamedChild(n["parameter_points"], name)["parameters"]);
1854 }
1855 }
1856 _varsNode = nullptr;
1857 _domains->writeJSON(n["domains"]);
1858 _domains.reset();
1859 _rootnodeOutput = nullptr;
1860}
1861
1862/**
1863 * @brief Import the workspace from a JSON string.
1864 *
1865 * @param s The JSON string containing the workspace data.
1866 * @return bool Returns true on successful import, false otherwise.
1867 */
1869{
1870 std::stringstream ss(s);
1871 return importJSON(ss);
1872}
1873
1874/**
1875 * @brief Import the workspace from a YML string.
1876 *
1877 * @param s The YML string containing the workspace data.
1878 * @return bool Returns true on successful import, false otherwise.
1879 */
1881{
1882 std::stringstream ss(s);
1883 return importYML(ss);
1884}
1885
1886/**
1887 * @brief Export the workspace to a JSON string.
1888 *
1889 * @return std::string The JSON string representing the exported workspace.
1890 */
1892{
1893 std::stringstream ss;
1894 exportJSON(ss);
1895 return ss.str();
1896}
1897
1898/**
1899 * @brief Export the workspace to a YML string.
1900 *
1901 * @return std::string The YML string representing the exported workspace.
1902 */
1904{
1905 std::stringstream ss;
1906 exportYML(ss);
1907 return ss.str();
1908}
1909
1910/**
1911 * @brief Create a new JSON tree with version information.
1912 *
1913 * @return std::unique_ptr<JSONTree> A unique pointer to the created JSON tree.
1914 */
1916{
1917 std::unique_ptr<JSONTree> tree = JSONTree::create();
1918 JSONNode &n = tree->rootnode();
1919 n.set_map();
1920 auto &metadata = n["metadata"].set_map();
1921
1922 // add the mandatory hs3 version number
1923 metadata["hs3_version"] << hs3VersionTag;
1924
1925 // Add information about the ROOT version that was used to generate this file
1926 auto &rootInfo = appendNamedChild(metadata["packages"], "ROOT");
1927 std::string versionName = gROOT->GetVersion();
1928 // We want to consistently use dots such that the version name can be easily
1929 // digested automatically.
1930 std::replace(versionName.begin(), versionName.end(), '/', '.');
1931 rootInfo["version"] << versionName;
1932
1933 return tree;
1934}
1935
1936/**
1937 * @brief Export the workspace to JSON format and write to the output stream.
1938 *
1939 * @param os The output stream to write the JSON data to.
1940 * @return bool Returns true on successful export, false otherwise.
1941 */
1943{
1944 std::unique_ptr<JSONTree> tree = createNewJSONTree();
1945 JSONNode &n = tree->rootnode();
1946 this->exportAllObjects(n);
1947 n.writeJSON(os);
1948 return true;
1949}
1950
1951/**
1952 * @brief Export the workspace to JSON format and write to the specified file.
1953 *
1954 * @param filename The name of the JSON file to create and write the data to.
1955 * @return bool Returns true on successful export, false otherwise.
1956 */
1958{
1959 std::ofstream out(filename.c_str());
1960 if (!out.is_open()) {
1961 std::stringstream ss;
1962 ss << "RooJSONFactoryWSTool() invalid output file '" << filename << "'." << std::endl;
1964 return false;
1965 }
1966 return this->exportJSON(out);
1967}
1968
1969/**
1970 * @brief Export the workspace to YML format and write to the output stream.
1971 *
1972 * @param os The output stream to write the YML data to.
1973 * @return bool Returns true on successful export, false otherwise.
1974 */
1976{
1977 std::unique_ptr<JSONTree> tree = createNewJSONTree();
1978 JSONNode &n = tree->rootnode();
1979 this->exportAllObjects(n);
1980 n.writeYML(os);
1981 return true;
1982}
1983
1984/**
1985 * @brief Export the workspace to YML format and write to the specified file.
1986 *
1987 * @param filename The name of the YML file to create and write the data to.
1988 * @return bool Returns true on successful export, false otherwise.
1989 */
1991{
1992 std::ofstream out(filename.c_str());
1993 if (!out.is_open()) {
1994 std::stringstream ss;
1995 ss << "RooJSONFactoryWSTool() invalid output file '" << filename << "'." << std::endl;
1997 return false;
1998 }
1999 return this->exportYML(out);
2000}
2001
2002bool RooJSONFactoryWSTool::hasAttribute(const std::string &obj, const std::string &attrib)
2003{
2004 if (!_attributesNode)
2005 return false;
2006 if (auto attrNode = _attributesNode->find(obj)) {
2007 if (auto seq = attrNode->find("tags")) {
2008 for (auto &a : seq->children()) {
2009 if (a.val() == attrib)
2010 return true;
2011 }
2012 }
2013 }
2014 return false;
2015}
2016void RooJSONFactoryWSTool::setAttribute(const std::string &obj, const std::string &attrib)
2017{
2018 auto node = &RooJSONFactoryWSTool::getRooFitInternal(*_rootnodeOutput, "attributes").set_map()[obj].set_map();
2019 auto &tags = (*node)["tags"];
2020 tags.set_seq();
2021 tags.append_child() << attrib;
2022}
2023
2024std::string RooJSONFactoryWSTool::getStringAttribute(const std::string &obj, const std::string &attrib)
2025{
2026 if (!_attributesNode)
2027 return "";
2028 if (auto attrNode = _attributesNode->find(obj)) {
2029 if (auto dict = attrNode->find("dict")) {
2030 if (auto *a = dict->find(attrib)) {
2031 return a->val();
2032 }
2033 }
2034 }
2035 return "";
2036}
2037void RooJSONFactoryWSTool::setStringAttribute(const std::string &obj, const std::string &attrib,
2038 const std::string &value)
2039{
2040 auto node = &RooJSONFactoryWSTool::getRooFitInternal(*_rootnodeOutput, "attributes").set_map()[obj].set_map();
2041 auto &dict = (*node)["dict"];
2042 dict.set_map();
2043 dict[attrib] << value;
2044}
2045
2046/**
2047 * @brief Imports all nodes of the JSON data and adds them to the workspace.
2048 *
2049 * @param n The JSONNode representing the root node of the JSON data.
2050 * @return void
2051 */
2053{
2054 // Per HS3 standard, the hs3_version in the metadata is required. So we
2055 // error out if it is missing. TODO: now we are only checking if the
2056 // hs3_version tag exists, but in the future when the HS3 specification
2057 // versions are actually frozen, we should also check if the hs3_version is
2058 // one that RooFit can actually read.
2059 auto metadata = n.find("metadata");
2060 if (!metadata || !metadata->find("hs3_version")) {
2061 std::stringstream ss;
2062 ss << "The HS3 version is missing in the JSON!\n"
2063 << "Please include the HS3 version in the metadata field, e.g.:\n"
2064 << " \"metadata\" :\n"
2065 << " {\n"
2066 << " \"hs3_version\" : \"" << hs3VersionTag << "\"\n"
2067 << " }";
2068 error(ss.str());
2069 }
2070
2071 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
2072 if (auto domains = n.find("domains")) {
2073 _domains->readJSON(*domains);
2074 }
2075 _domains->populate(_workspace);
2076
2077 _rootnodeInput = &n;
2078
2079 _attributesNode = findRooFitInternal(*_rootnodeInput, "attributes");
2080
2081 this->importDependants(n);
2082
2083 if (auto paramPointsNode = n.find("parameter_points")) {
2084 for (const auto &snsh : paramPointsNode->children()) {
2085 std::string name = RooJSONFactoryWSTool::name(snsh);
2086
2087 if (!::isValidName(name)) {
2088 std::stringstream ss;
2089 ss << "RooJSONFactoryWSTool() node name '" << name << "' is not valid!" << std::endl;
2091 }
2092
2093 RooArgSet vars;
2094 for (const auto &var : snsh["parameters"].children()) {
2096 configureVariable(*_domains, var, *rrv);
2097 vars.add(*rrv);
2098 }
2099 }
2101 }
2102 }
2103
2104 combinePdfs(*_rootnodeInput, _workspace);
2105
2106 // Import attributes
2107 if (_attributesNode) {
2108 for (const auto &elem : _attributesNode->children()) {
2109 if (RooAbsArg *arg = _workspace.arg(elem.key()))
2110 importAttributes(arg, elem);
2111 }
2112 }
2113
2114 _attributesNode = nullptr;
2115
2116 // We delay the import of the data to after combineDatasets(), because it
2117 // might be that some datasets are merged to combined datasets there. In
2118 // that case, we will remove the components from the "datasets" vector so they
2119 // don't get imported.
2120 std::vector<std::unique_ptr<RooAbsData>> datasets;
2121 if (auto dataNode = n.find("data")) {
2122 for (const auto &p : dataNode->children()) {
2123 datasets.push_back(loadData(p, _workspace));
2124 }
2125 }
2126
2127 // Now, read in analyses and likelihoods if there are any
2128
2129 if (auto analysesNode = n.find("analyses")) {
2130 for (JSONNode const &analysisNode : analysesNode->children()) {
2131 importAnalysis(*_rootnodeInput, analysisNode, n["likelihoods"], _workspace, datasets);
2132 }
2133 }
2134
2135 combineDatasets(*_rootnodeInput, datasets);
2136
2137 for (auto const &d : datasets) {
2138 if (d)
2140 }
2141
2142 _rootnodeInput = nullptr;
2143 _domains.reset();
2144}
2145
2146/**
2147 * @brief Imports a JSON file from the given input stream to the workspace.
2148 *
2149 * @param is The input stream containing the JSON data.
2150 * @return bool Returns true on successful import, false otherwise.
2151 */
2153{
2154 // import a JSON file to the workspace
2155 std::unique_ptr<JSONTree> tree = JSONTree::create(is);
2156 this->importAllNodes(tree->rootnode());
2157 return true;
2158}
2159
2160/**
2161 * @brief Imports a JSON file from the given filename to the workspace.
2162 *
2163 * @param filename The name of the JSON file to import.
2164 * @return bool Returns true on successful import, false otherwise.
2165 */
2167{
2168 // import a JSON file to the workspace
2169 std::ifstream infile(filename.c_str());
2170 if (!infile.is_open()) {
2171 std::stringstream ss;
2172 ss << "RooJSONFactoryWSTool() invalid input file '" << filename << "'." << std::endl;
2174 return false;
2175 }
2176 return this->importJSON(infile);
2177}
2178
2179/**
2180 * @brief Imports a YML file from the given input stream to the workspace.
2181 *
2182 * @param is The input stream containing the YML data.
2183 * @return bool Returns true on successful import, false otherwise.
2184 */
2186{
2187 // import a YML file to the workspace
2188 std::unique_ptr<JSONTree> tree = JSONTree::create(is);
2189 this->importAllNodes(tree->rootnode());
2190 return true;
2191}
2192
2193/**
2194 * @brief Imports a YML file from the given filename to the workspace.
2195 *
2196 * @param filename The name of the YML file to import.
2197 * @return bool Returns true on successful import, false otherwise.
2198 */
2200{
2201 // import a YML file to the workspace
2202 std::ifstream infile(filename.c_str());
2203 if (!infile.is_open()) {
2204 std::stringstream ss;
2205 ss << "RooJSONFactoryWSTool() invalid input file '" << filename << "'." << std::endl;
2207 return false;
2208 }
2209 return this->importYML(infile);
2210}
2211
2212void RooJSONFactoryWSTool::importJSONElement(const std::string &name, const std::string &jsonString)
2213{
2214 std::unique_ptr<RooFit::Detail::JSONTree> tree = RooFit::Detail::JSONTree::create(jsonString);
2215 JSONNode &n = tree->rootnode();
2216 n["name"] << name;
2217
2218 bool isVariable = true;
2219 if (n.find("type")) {
2220 isVariable = false;
2221 }
2222
2223 if (isVariable) {
2224 this->importVariableElement(n);
2225 } else {
2226 this->importFunction(n, false);
2227 }
2228}
2229
2231{
2232 std::unique_ptr<RooFit::Detail::JSONTree> tree = varJSONString(elementNode);
2233 JSONNode &n = tree->rootnode();
2234 _domains = std::make_unique<RooFit::JSONIO::Detail::Domains>();
2235 if (auto domains = n.find("domains"))
2236 _domains->readJSON(*domains);
2237
2238 _rootnodeInput = &n;
2239 _attributesNode = findRooFitInternal(*_rootnodeInput, "attributes");
2240
2241 JSONNode const *varsNode = getVariablesNode(n);
2242 const auto &p = varsNode->child(0);
2244
2245 auto paramPointsNode = n.find("parameter_points");
2246 const auto &snsh = paramPointsNode->child(0);
2247 std::string name = RooJSONFactoryWSTool::name(snsh);
2248 RooArgSet vars;
2249 const auto &var = snsh["parameters"].child(0);
2251 configureVariable(*_domains, var, *rrv);
2252 vars.add(*rrv);
2253 }
2254
2255 // Import attributes
2256 if (_attributesNode) {
2257 for (const auto &elem : _attributesNode->children()) {
2258 if (RooAbsArg *arg = _workspace.arg(elem.key()))
2259 importAttributes(arg, elem);
2260 }
2261 }
2262
2263 _attributesNode = nullptr;
2264 _rootnodeInput = nullptr;
2265 _domains.reset();
2266}
2267
2268/**
2269 * @brief Writes a warning message to the RooFit message service.
2270 *
2271 * @param str The warning message to be logged.
2272 * @return std::ostream& A reference to the output stream.
2273 */
2274std::ostream &RooJSONFactoryWSTool::warning(std::string const &str)
2275{
2276 return RooMsgService::instance().log(nullptr, RooFit::MsgLevel::ERROR, RooFit::IO) << str << std::endl;
2277}
2278
2279/**
2280 * @brief Writes an error message to the RooFit message service and throws a runtime_error.
2281 *
2282 * @param s The error message to be logged and thrown.
2283 * @return void
2284 */
2286{
2287 RooMsgService::instance().log(nullptr, RooFit::MsgLevel::ERROR, RooFit::IO) << s << std::endl;
2288 throw std::runtime_error(s);
2289}
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
constexpr auto hs3VersionTag
#define oocoutW(o, a)
#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 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:110
#define gROOT
Definition TROOT.h:407
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:79
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:341
const RefCountList_t & servers() const
List of all servers of this object.
Definition RooAbsArg.h:208
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
const std::map< std::string, std::string > & stringAttributes() const
Definition RooAbsArg.h:349
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.
std::size_t size() const
Number of states defined.
Abstract container object that can hold multiple RooAbsArg objects.
const char * GetName() const override
Returns name of object.
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.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:57
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
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 abstract interface for proxy classes.
Definition RooAbsProxy.h:37
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
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: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.
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:23
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 std::string key() 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:43
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
void importFunction(const RooFit::Detail::JSONNode &n, bool importAllDependants)
Import a function from the JSONNode into the workspace.
static constexpr bool useListsInsteadOfDicts
std::string getStringAttribute(const std::string &obj, const std::string &attrib)
bool importYML(std::string const &filename)
Imports a YML file from the given filename to the workspace.
static void fillSeq(RooFit::Detail::JSONNode &node, RooAbsCollection const &coll, size_t nMax=-1)
void exportCategory(RooAbsCategory const &cat, RooFit::Detail::JSONNode &node)
Export a RooAbsCategory object to a JSONNode.
T * requestArg(const RooFit::Detail::JSONNode &node, const std::string &key)
RooJSONFactoryWSTool(RooWorkspace &ws)
void importVariable(const RooFit::Detail::JSONNode &n)
Import a variable from the JSONNode into the workspace.
void exportData(RooAbsData const &data)
Export data from the workspace to a JSONNode.
bool hasAttribute(const std::string &obj, const std::string &attrib)
void exportVariables(const RooArgSet &allElems, RooFit::Detail::JSONNode &n)
Export variables from the workspace to a JSONNode.
bool importJSON(std::string const &filename)
Imports a JSON file from the given filename to the workspace.
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)
RooAbsReal * importTransformed(const std::string &name, const std::string &tag, const std::string &operation_name, const std::string &formula)
std::string exportYMLtoString()
Export the workspace to a YML string.
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.
bool importYMLfromString(const std::string &s)
Import the workspace from a YML string.
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.
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)
Export a variable from the workspace to a JSONNode.
const RooFit::Detail::JSONNode * _rootnodeInput
RooJSONFactoryWSTool::CombinedData exportCombinedData(RooAbsData const &data)
Export combined data from the workspace to a custom struct.
std::string exportJSONtoString()
Export the workspace to a JSON string.
RooArgList requestArgList(const RooFit::Detail::JSONNode &node, const std::string &seqName)
const RooFit::Detail::JSONNode * _attributesNode
void importDependants(const RooFit::Detail::JSONNode &n)
Import all dependants (servers) of a node into the workspace.
void importJSONElement(const std::string &name, const std::string &jsonString)
static void error(const char *s)
Writes an error message to the RooFit message service and throws a runtime_error.
void exportModelConfig(RooFit::Detail::JSONNode &rootnode, RooStats::ModelConfig const &mc, const std::vector< RooJSONFactoryWSTool::CombinedData > &d)
void setAttribute(const std::string &obj, const std::string &attrib)
bool exportYML(std::string const &fileName)
Export the workspace to YML format and write to the specified file.
std::string exportTransformed(const RooAbsReal *original, const std::string &tag, const std::string &operation_name, const std::string &formula)
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)
Export an object from the workspace to a JSONNode.
static RooFit::Detail::JSONNode & makeVariablesNode(RooFit::Detail::JSONNode &rootNode)
void importAllNodes(const RooFit::Detail::JSONNode &n)
Imports all nodes of the JSON data and adds them to the workspace.
static std::string name(const RooFit::Detail::JSONNode &n)
void exportAllObjects(RooFit::Detail::JSONNode &n)
Export all objects in the workspace to a JSONNode.
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)
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
RooFit::Detail::JSONNode & createAdHoc(const std::string &toplevel, const std::string &name)
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)
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:37
void setVal(double value) override
Set value of variable to 'value'.
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
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)
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 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.
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.
const RooArgSet & components() const
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_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:42
ExportMap & exporters()
Definition JSONIO.cxx:48
ImportExpressionMap & importExpressions()
Definition JSONIO.cxx:54
ExportKeysMap & exportKeys()
Definition JSONIO.cxx:61
Definition first.py:1
Definition tree.py:1
std::map< std::string, std::string > components
TLine l
Definition textangle.C:4
static void output()