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