Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
HS3Validate.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * Jonas Rembser, CERN, Nov 2026
5 *
6 * Copyright (c) 2026, 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 "HS3Document.h"
14
15#include <algorithm>
16#include <cmath>
17#include <limits>
18#include <map>
19#include <optional>
20#include <set>
21#include <string>
22#include <vector>
23
24namespace RooFit {
25namespace HS3 {
26namespace {
27
28std::string at(const char *section, std::size_t index, std::string const &name)
29{
30 return std::string(section) + "[" + std::to_string(index) + "](" + name + ")";
31}
32
33class Validator {
34public:
35 Validator(HS3Document const &doc, Diagnostics &diagnostics, ValidateOptions const &options)
36 : _doc{doc}, _diagnostics{diagnostics}, _options{options}
37 {
38 }
39
40 void run()
41 {
42 collectNames();
43 checkFixedAxes();
44 checkLikelihoods();
45 checkAnalyses();
46 checkData();
47 for (std::size_t i = 0; i < _doc.distributions.size(); ++i) {
48 auto const &dist = _doc.distributions[i];
49 if (auto const *histFactory = std::get_if<HistFactoryDist>(&dist.body)) {
50 checkHistFactory(*histFactory, dist.name, at("distributions", i, dist.name));
51 }
52 }
53 for (auto const &item : _doc.rooFit.combinedDistributions) {
54 for (auto const &ref : item.second.distributions) {
55 requireDistribution(ref.name, "misc.ROOT_internal.combined_distributions." + item.first, "distribution");
56 }
57 }
58 for (auto const &item : _doc.rooFit.channelConstraints) {
59 requireDistribution(item.first, "misc.ROOT_internal.histfactory_constraints", "distribution");
60 for (auto const &ref : item.second) {
61 requireDistribution(ref.name, "misc.ROOT_internal.histfactory_constraints." + item.first, "constraint");
62 }
63 }
64 }
65
66 void error(std::string const &path, std::string const &message)
67 {
68 _diagnostics.push_back({Diagnostic::Severity::Error, path, message});
69 }
70
71 void warning(std::string const &path, std::string const &message)
72 {
73 _diagnostics.push_back({Diagnostic::Severity::Warning, path, message});
74 }
75
76 // An error in documents that follow the current standard, which older versions of RooFit didn't care about.
77 void strictError(std::string const &path, std::string const &message)
78 {
79 _diagnostics.push_back(
81 }
82
83 void checkHistFactory(HistFactoryDist const &dist, std::string const &name, std::string const &path);
84
85private:
86 bool isExternal(std::string const &name) const { return _options.isExternal && _options.isExternal(name); }
87
88 char const *sectionOf(std::string const &name) const
89 {
90 auto found = _sections.find(name);
91 return found != _sections.end() ? found->second.front() : nullptr;
92 }
93
94 bool isDefinedIn(std::string const &name, char const *section) const
95 {
96 auto found = _sections.find(name);
97 if (found == _sections.end()) {
98 return false;
99 }
100 return std::any_of(found->second.begin(), found->second.end(),
101 [&](char const *s) { return std::string(s) == section; });
102 }
103
104 void requireDistribution(std::string const &name, std::string const &path, std::string const &what)
105 {
106 // RooFit refers to a RooSimultaneous by name, like the likelihood of a ModelConfig without data does.
107 if (!isDefinedIn(name, "distributions") && !_doc.rooFit.combinedDistributions.count(name) && !isExternal(name)) {
108 error(path, "the " + what + " '" + name + "' does not exist");
109 }
110 }
111
112 void addParameter(std::string const &name) { _parameters.insert(name); }
113
114 // The number of bins of an axis, after reporting an invalid binning.
115 std::size_t checkAxis(BinnedAxis const &axis, std::string const &path);
116 std::size_t checkAxis(CategoricalAxis const &axis, std::string const &path);
117 template <class... Axis>
118 std::size_t checkAxis(std::variant<Axis...> const &axis, std::string const &path)
119 {
120 return std::visit([&](auto const &a) { return checkAxis(a, path); }, axis);
121 }
122 // The number of bins of all axes, or nothing if it can't be stored.
123 template <class Axes>
124 std::optional<std::size_t> numBins(Axes const &axes, std::string const &path);
125
126 void collectNames();
127 void checkFixedAxes();
128 void checkLikelihoods();
129 void checkAnalyses();
130 void checkData();
131
132 HS3Document const &_doc;
134 ValidateOptions const &_options;
135 std::map<std::string, std::vector<char const *>> _sections; ///< The sections where a name is defined.
136 std::set<std::string> _parameters;
137};
138
139// HS3 requires the names of all objects to be unique within a document, including the parameters. RooFit writes such
140// documents, e.g. for a range and a pdf of one name, so only ambiguous clashes in current documents are errors.
141void Validator::collectNames()
142{
143 auto check = [&](char const *section, std::string const &name) {
144 auto &sections = _sections[name];
145 sections.push_back(section);
146 if (sections.size() == 1) {
147 return;
148 }
149 std::string const firstSection = sections.front();
150 // RooFit used to give analyses and likelihoods the name of the pdf or the analysis. Accept this for backwards
151 // compatibility: analyses are not referenced by name, and likelihoods only from analyses, so there is no
152 // ambiguity.
153 auto isLegacyNamed = [](std::string const &s) { return s == "likelihoods" || s == "analyses"; };
154 // Within one section, and between distributions and functions (both can be referenced by name), a clash is
155 // ambiguous. Other clashes are produced by RooFit itself (a range, snapshot or data set named like a pdf).
156 auto isReferable = [](std::string const &s) { return s == "distributions" || s == "functions"; };
157 const bool ambiguous = firstSection == section || (isReferable(firstSection) && isReferable(section));
158 if (!_options.strict || !ambiguous || isLegacyNamed(firstSection) || isLegacyNamed(section)) {
159 warning("", "the name '" + name + "' is used in \"" + firstSection + "\" and in \"" + section +
160 "\", which is not allowed by the HS3 standard.");
161 return;
162 }
163 error("", "duplicate name '" + name + "': it is defined in \"" + firstSection + "\" and in \"" + section +
164 "\", but names must be unique within the document");
165 };
166 auto checkSection = [&](char const *section, auto const &objects) {
167 for (auto const &object : objects) {
168 check(section, object.name);
169 }
170 };
171 checkSection("distributions", _doc.distributions);
172 checkSection("functions", _doc.functions);
173 checkSection("data", _doc.data);
174 checkSection("domains", _doc.domains);
175 checkSection("parameter_points", _doc.parameterPoints);
176 checkSection("likelihoods", _doc.likelihoods);
177 checkSection("analyses", _doc.analyses);
178
179 // The parameters are the axes of the domains, the entries of the parameter points, and the parameters of
180 // HistFactory modifiers, which are created on the fly.
181 for (auto const &domain : _doc.domains) {
182 for (auto const &axis : domain.axes) {
183 addParameter(std::visit([](auto const &a) -> std::string const & { return a.name; }, axis));
184 }
185 }
186 for (auto const &point : _doc.parameterPoints) {
187 for (auto const &parameter : point.parameters) {
188 addParameter(parameter.name);
189 }
190 }
191 for (auto const &item : _doc.rooFit.combinedDistributions) {
192 addParameter(item.second.indexCat);
193 }
194 for (auto const &dist : _doc.distributions) {
195 auto const *histFactory = std::get_if<HistFactoryDist>(&dist.body);
196 if (!histFactory) {
197 continue;
198 }
199 for (auto const &sample : histFactory->samples) {
200 for (auto const &modifier : sample.modifiers) {
201 std::visit(overloaded{[&](NormFactor const &m) { addParameter(m.parameter.name); },
202 [&](NormSys const &m) { addParameter(m.parameter.name); },
203 [&](HistoSys const &m) { addParameter(m.parameter.name); },
204 [&](ShapeSys const &m) {
205 for (auto const &p : m.parameters) {
206 addParameter(p.name);
207 }
208 },
209 [&](StatError const &m) {
210 for (auto const &p : m.parameters) {
211 addParameter(p.name);
212 }
213 },
214 [](CustomModifier const &) {}},
215 modifier);
216 }
217 }
218 }
219 for (std::string const &parameter : _parameters) {
220 if (char const *section = sectionOf(parameter)) {
221 strictError("", "the name '" + parameter + "' is used in \"" + section +
222 "\" and for a parameter, which is not allowed by the HS3 standard.");
223 }
224 }
225}
226
227// A fixed axis says that the parameter only takes a single value. No other domain can give it a range or another
228// value, and no parameter point another value.
229void Validator::checkFixedAxes()
230{
231 struct FixedAxis {
232 double value = 0.0;
233 std::string const *domain = nullptr;
234 };
235 std::map<std::string, FixedAxis> fixedAxes;
236 auto forEachAxis = [&](auto &&fn) {
237 for (std::size_t i = 0; i < _doc.domains.size(); ++i) {
238 for (auto const &axis : _doc.domains[i].axes) {
239 // A categorical axis is passed as null.
240 fn(i, std::visit([](auto const &a) -> std::string const & { return a.name; }, axis),
241 std::get_if<DomainAxis>(&axis));
242 }
243 }
244 };
245 forEachAxis([&](std::size_t i, std::string const &, DomainAxis const *continuous) {
246 if (!continuous || !continuous->fixedValue) {
247 return;
248 }
249 DomainAxis const &axis = *continuous;
250 Domain const &domain = _doc.domains[i];
251 if (!std::isfinite(*axis.fixedValue)) {
252 error(at("domains", i, domain.name),
253 "the single value of the parameter '" + axis.name + "' is not finite, which JSON can't represent");
254 }
255 auto inserted = fixedAxes.insert({axis.name, FixedAxis{*axis.fixedValue, &domain.name}});
256 if (!inserted.second && inserted.first->second.value != *axis.fixedValue) {
257 error(at("domains", i, domain.name),
258 "the parameter '" + axis.name + "' has different single values in the domains '" +
259 *inserted.first->second.domain + "' and '" + domain.name + "'");
260 }
261 });
262 if (fixedAxes.empty()) {
263 return;
264 }
265 forEachAxis([&](std::size_t i, std::string const &name, DomainAxis const *continuous) {
266 auto found = fixedAxes.find(name);
267 if (found != fixedAxes.end() && !(continuous && continuous->fixedValue)) {
268 Domain const &domain = _doc.domains[i];
269 error(at("domains", i, domain.name),
270 "the parameter '" + name + "' has the single value " + std::to_string(found->second.value) +
271 " in the domain '" + *found->second.domain + "', but " + (continuous ? "a range" : "categories") +
272 " in the domain '" + domain.name + "'");
273 }
274 });
275 for (std::size_t i = 0; i < _doc.parameterPoints.size(); ++i) {
276 ParameterPoint const &point = _doc.parameterPoints[i];
277 for (ParameterValue const &parameter : point.parameters) {
278 auto found = fixedAxes.find(parameter.name);
279 double const *value = parameter.value ? std::get_if<double>(&*parameter.value) : nullptr;
280 if (value && found != fixedAxes.end() && *value != found->second.value) {
281 error(at("parameter_points", i, point.name),
282 "the parameter point '" + point.name + "' gives the parameter '" + parameter.name + "' the value " +
283 std::to_string(*value) + ", but its domain only has the value " +
284 std::to_string(found->second.value));
285 }
286 }
287 }
288}
289
290void Validator::checkLikelihoods()
291{
292 for (std::size_t i = 0; i < _doc.likelihoods.size(); ++i) {
293 auto const &likelihood = _doc.likelihoods[i];
294 const std::string path = at("likelihoods", i, likelihood.name);
295 for (auto const &term : likelihood.terms) {
296 requireDistribution(term.distribution.name, path + ".distributions", "distribution");
297 if (auto const *data = std::get_if<DataRef>(&term.data)) {
298 if (!isDefinedIn(data->name, "data")) {
299 error(path + ".data", "dataset '" + data->name + "' cannot be found!");
300 }
301 }
302 }
303 for (auto const &aux : likelihood.auxDistributions) {
304 if (!isDefinedIn(aux.name, "distributions") && !isExternal(aux.name)) {
305 strictError(path + ".aux_distributions", "the distribution '" + aux.name + "' does not exist");
306 }
307 }
308 }
309}
310
311void Validator::checkAnalyses()
312{
313 for (std::size_t i = 0; i < _doc.analyses.size(); ++i) {
314 auto const &analysis = _doc.analyses[i];
315 const std::string path = at("analyses", i, analysis.name);
316 if (!isDefinedIn(analysis.likelihood.name, "likelihoods")) {
317 error(path + ".likelihood", "likelihood node not found!");
318 }
319 for (auto const &poi : analysis.parametersOfInterest) {
320 if (char const *section = sectionOf(poi.name)) {
321 error(path, "the parameter of interest '" + poi.name + "' of analysis '" + analysis.name +
322 "' is defined in \"" + section + "\", but it must be a parameter");
323 } else if (!_parameters.count(poi.name) && !isExternal(poi.name)) {
324 error(path, "the object '" + poi.name + "' in \"parameters_of_interest\" of analysis '" + analysis.name +
325 "' does not exist");
326 }
327 }
328 for (auto const &domain : analysis.domains) {
329 if (!isDefinedIn(domain.name, "domains")) {
330 strictError(path + ".domains", "the domain '" + domain.name + "' does not exist");
331 }
332 }
333 if (analysis.init && !isDefinedIn(analysis.init->name, "parameter_points")) {
334 warning(path, "the parameter point '" + analysis.init->name + "' used as \"init\" of analysis '" +
335 analysis.name + "' does not exist.");
336 }
337 if (analysis.prior && !isDefinedIn(analysis.prior->name, "distributions") && !isExternal(analysis.prior->name)) {
338 strictError(path + ".prior", "the distribution '" + analysis.prior->name + "' does not exist");
339 }
340 }
341}
342
343std::size_t Validator::checkAxis(BinnedAxis const &axis, std::string const &path)
344{
345 if (auto const *regular = std::get_if<RegularBinning>(&axis.binning)) {
346 if (regular->nbins <= 0) {
347 error(path, "the axis '" + axis.name + "' needs a positive number of bins");
348 return 0;
349 }
350 if (!(axis.min < axis.max)) {
351 error(path, "the axis '" + axis.name + "' needs \"min\" smaller than \"max\"");
352 }
353 return regular->nbins;
354 }
355 auto const &edges = std::get<EdgesBinning>(axis.binning).edges;
356 return edges.empty() ? 0 : edges.size() - 1;
357}
358
359std::size_t Validator::checkAxis(CategoricalAxis const &axis, std::string const &path)
360{
361 if (axis.categories.empty()) {
362 error(path, "the axis '" + axis.name + "' needs at least one category");
363 }
364 return axis.categories.size();
365}
366
367template <class Axes>
368std::optional<std::size_t> Validator::numBins(Axes const &axes, std::string const &path)
369{
370 std::size_t nBins = 1;
371 bool valid = true;
372 for (std::size_t i = 0; i < axes.size(); ++i) {
373 const std::size_t n = checkAxis(axes[i], path + ".axes[" + std::to_string(i) + "]");
374 if (n == 0) {
375 valid = false; // reported already
376 } else if (nBins > std::numeric_limits<std::size_t>::max() / n) {
377 error(path + ".axes", "the axes have more bins than can be stored");
378 return std::nullopt;
379 }
380 nBins *= n;
381 }
382 return valid ? std::optional<std::size_t>{nBins} : std::nullopt;
383}
384
385void Validator::checkData()
386{
387 for (std::size_t i = 0; i < _doc.data.size(); ++i) {
388 auto const &data = _doc.data[i];
389 auto const *binned = std::get_if<BinnedData>(&data.body);
390 if (!binned) {
391 continue;
392 }
393 const std::string path = at("data", i, data.name);
394 std::optional<std::size_t> nBinsOrNone = numBins(binned->axes, path);
395 if (!nBinsOrNone) {
396 continue;
397 }
398 const std::size_t nBins = *nBinsOrNone;
399 if (binned->contents.size() != nBins) {
400 error(path, "inconsistent bin numbers: contents=" + std::to_string(binned->contents.size()) +
401 ", bins=" + std::to_string(nBins));
402 }
403 if (binned->uncertainty) {
404 auto const &sigma = binned->uncertainty->sigma;
405 if (sigma.size() != binned->contents.size()) {
406 error(path, "inconsistent number of errors and contents in '" + data.name +
407 "': errors=" + std::to_string(sigma.size()) +
408 ", contents=" + std::to_string(binned->contents.size()));
409 }
410 auto const &correlation = binned->uncertainty->correlation;
411 if (!correlation.empty()) {
412 bool square = correlation.size() == binned->contents.size();
413 for (auto const &row : correlation) {
414 square = square && row.size() == binned->contents.size();
415 }
416 if (!square) {
417 error(path + ".uncertainty.correlation", "must be a square matrix of the size of \"contents\"");
418 }
419 }
420 }
421 }
422}
423
424void Validator::checkHistFactory(HistFactoryDist const &dist, std::string const &name, std::string const &path)
425{
426 if (dist.axes.empty()) {
427 error(path + ".axes", "a HistFactory distribution needs at least one axis");
428 return;
429 }
430 std::optional<std::size_t> nBinsOrNone = numBins(dist.axes, path);
431 if (!nBinsOrNone) {
432 return;
433 }
434 const std::size_t nBins = *nBinsOrNone;
435
436 std::string channelName = name;
437 if (channelName.rfind("model_", 0) == 0) {
438 channelName.erase(0, 6);
439 }
440
441 auto checkSize = [&](std::string const &where, std::size_t size, char const *what) {
442 if (size != nBins) {
443 error(where, std::string(what) + " has " + std::to_string(size) + " entries, but the channel has " +
444 std::to_string(nBins) + " bins");
445 }
446 };
447
448 // Two modifiers with the same parameter must have the same constraint.
449 std::map<std::string, std::pair<Constraint, std::string>> constraintOfParameter;
450 auto checkCorrelated = [&](std::string const &where, std::string const &parameter, Constraint const &constraint) {
451 auto inserted = constraintOfParameter.emplace(parameter, std::make_pair(constraint, where));
452 if (inserted.second) {
453 return;
454 }
455 if (!(inserted.first->second.first == constraint)) {
456 warning(where, "the parameter '" + parameter + "' has a different constraint than in " +
457 inserted.first->second.second +
458 ", but modifiers with the same parameter must share the constraint");
459 }
460 };
461
462 auto checkConstraint = [&](std::string const &where, Constraint const &constraint) {
463 if (auto const *pdf = std::get_if<ConstraintPdf>(&constraint)) {
464 requireDistribution(pdf->pdf.name, where, "constraint");
465 }
466 };
467
468 // The constraint distributions of the gamma parameters of shape modifiers.
469 auto checkGammaConstraint = [&](std::string const &where, GammaConstraint const &constraint) {
470 auto const *pdfs = std::get_if<GammaConstraintPdfs>(&constraint);
471 if (!pdfs) {
472 return;
473 }
474 checkSize(where + ".constraints", pdfs->pdfs.size(), "\"constraints\"");
475 for (auto const &pdf : pdfs->pdfs) {
476 if (pdf) {
477 requireDistribution(pdf->name, where + ".constraints", "constraint");
478 }
479 }
480 };
481
482 auto checkParameter = [&](std::string const &where, std::string const &parameter, std::string const &modifier) {
483 if (char const *section = sectionOf(parameter)) {
484 error(where, "the parameter '" + parameter + "' of the modifier '" + modifier + "' is defined in \"" +
485 section + "\", but it must be a parameter");
486 }
487 };
488
489 checkGammaConstraint(path + ".staterror", dist.statError.constraint);
490
491 std::set<std::string> sampleNames;
492 for (std::size_t iSample = 0; iSample < dist.samples.size(); ++iSample) {
493 auto const &sample = dist.samples[iSample];
494 const std::string samplePath = path + ".samples[" + std::to_string(iSample) + "](" + sample.name + ")";
495 if (!sampleNames.insert(sample.name).second) {
496 error(samplePath, "the name of the sample is not unique in the channel");
497 }
498 checkSize(samplePath + ".data.contents", sample.contents.size(), "\"contents\"");
499 // These modifiers create objects that are named after them.
500 std::set<std::string> objectNames;
501 auto checkUniqueName = [&](std::string const &where, char const *type, std::string const &modifierName) {
502 if (!objectNames.insert(std::string(type) + " " + modifierName).second) {
503 error(where,
504 std::string("there is another ") + type + " modifier named '" + modifierName + "' in the sample");
505 }
506 };
507 if (sample.errors && sample.errors->size() != sample.contents.size()) {
508 error(samplePath + ".data", "inconsistent number of errors and contents in '" + sample.name +
509 "': errors=" + std::to_string(sample.errors->size()) +
510 ", contents=" + std::to_string(sample.contents.size()));
511 }
512 for (std::size_t iModifier = 0; iModifier < sample.modifiers.size(); ++iModifier) {
513 std::string modifierPath = samplePath + ".modifiers[" + std::to_string(iModifier) + "]";
514 auto checkInterpolation = [&](std::optional<Interpolation> const &interpolation, char const *type,
515 std::string const &modifierName) {
516 if (!interpolation && !dist.defaultInterpolation) {
517 error(modifierPath, std::string("interpolation for ") + type + " modifier '" + modifierName +
518 "' in sample '" + sample.name + "' of channel '" + channelName +
519 "' is not given, and the channel has no \"default_interpolation\"");
520 }
521 };
522 std::visit(overloaded{
523 [&](NormFactor const &m) {
524 modifierPath += "(" + m.name + ")";
525 checkParameter(modifierPath, m.parameter.name, m.name);
526 checkConstraint(modifierPath, m.constraint);
527 checkCorrelated(modifierPath, m.parameter.name, m.constraint);
528 },
529 [&](NormSys const &m) {
530 modifierPath += "(" + m.name + ")";
531 checkParameter(modifierPath, m.parameter.name, m.name);
532 checkConstraint(modifierPath, m.constraint);
533 checkCorrelated(modifierPath, m.parameter.name, m.constraint);
534 checkInterpolation(m.interpolation, "normsys", m.name);
535 },
536 [&](HistoSys const &m) {
537 modifierPath += "(" + m.name + ")";
538 checkUniqueName(modifierPath, "histosys", m.name);
539 checkParameter(modifierPath, m.parameter.name, m.name);
540 checkConstraint(modifierPath, m.constraint);
541 checkCorrelated(modifierPath, m.parameter.name, m.constraint);
542 checkInterpolation(m.interpolation, "histosys", m.name);
543 checkSize(modifierPath + ".data.hi.contents", m.hi.contents.size(), "\"contents\"");
544 checkSize(modifierPath + ".data.lo.contents", m.lo.contents.size(), "\"contents\"");
545 },
546 [&](ShapeSys const &m) {
547 modifierPath += "(" + m.name + ")";
548 checkUniqueName(modifierPath, "shapesys", m.name);
549 if (!m.vals.empty()) {
550 checkSize(modifierPath + ".data.vals", m.vals.size(), "\"vals\"");
551 }
552 if (!m.parameters.empty()) {
553 checkSize(modifierPath + ".parameters", m.parameters.size(), "\"parameters\"");
554 }
555 for (auto const &p : m.parameters) {
556 checkParameter(modifierPath, p.name, m.name);
557 }
558 checkGammaConstraint(modifierPath, m.constraint);
559 },
560 [&](StatError const &m) {
561 modifierPath += "(" + m.name + ")";
562 if (!m.parameters.empty()) {
563 checkSize(modifierPath + ".parameters", m.parameters.size(), "\"parameters\"");
564 }
565 checkGammaConstraint(modifierPath, m.constraint);
566 },
567 [&](CustomModifier const &m) {
568 modifierPath += "(" + m.name + ")";
569 if (!isDefinedIn(m.name, "functions") && !isDefinedIn(m.name, "distributions") &&
570 !isExternal(m.name)) {
571 error(modifierPath, "unable to find custom modifier '" + m.name + "'");
572 }
573 },
574 },
575 sample.modifiers[iModifier]);
576 }
577 }
578}
579
580} // namespace
581
582void checkDocument(HS3Document const &doc, Diagnostics &diagnostics, ValidateOptions const &options)
583{
584 Validator{doc, diagnostics, options}.run();
585}
586
587std::optional<ValidatedHS3Document> validate(HS3Document doc, Diagnostics &diagnostics, ValidateOptions const &options)
588{
589 checkDocument(doc, diagnostics, options);
590 if (hasErrors(diagnostics)) {
591 return std::nullopt;
592 }
593 return ValidatedHS3Document{std::move(doc)};
594}
595
596void validateHistFactoryDist(HistFactoryDist const &dist, std::string const &name, Diagnostics &diagnostics)
597{
598 // Without a document, all names are resolved later in the workspace.
599 HS3Document empty;
600 ValidateOptions options;
601 options.isExternal = [](std::string const &) { return true; };
602 Validator{empty, diagnostics, options}.checkHistFactory(dist, name, "");
603}
604
605} // namespace HS3
606} // namespace RooFit
std::string channelName
ValidateOptions const & _options
Diagnostics & _diagnostics
std::map< std::string, std::vector< char const * > > _sections
The sections where a name is defined.
HS3Document const & _doc
std::set< std::string > _parameters
#define a(i)
Definition RSha256.hxx:99
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
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 modifier
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:142
An HS3 document. It is move-only, because generic objects refer to the JSON tree that the document wa...
A document that passed validate().
const Double_t sigma
const Int_t n
Definition legend1.C:16
double dist(Rotation3D const &r1, Rotation3D const &r2)
Types that represent an HS3 document.
void validateHistFactoryDist(HistFactoryDist const &dist, std::string const &name, Diagnostics &diagnostics)
Same as validate(), but for a single HistFactory distribution that doesn't come with a document.
void checkDocument(HS3Document const &doc, Diagnostics &diagnostics, ValidateOptions const &options={})
The checks of validate(), for a document that is still needed if it doesn't pass them.
std::optional< ValidatedHS3Document > validate(HS3Document doc, Diagnostics &diagnostics, ValidateOptions const &options)
Checks what can be checked without knowing how the objects are built: unique names,...
bool hasErrors(Diagnostics const &diagnostics)
Whether there is a diagnostic with error severity.
Definition HS3Parser.cxx:94
std::vector< Diagnostic > Diagnostics
std::variant< Unconstrained, ConstraintType, ConstantGammas, GammaConstraintPdfs > GammaConstraint
std::variant< Unconstrained, ConstraintType, ConstraintPdf > Constraint
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73
static const char * what
Definition stlLoader.cc:5
Options for validate().
std::function< bool(std::string const &) isExternal)
Whether a name that is not defined in the document is known in some other way, e.g....
TMarker m
Definition textangle.C:8