Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
xRooHypoSpace.cxx
Go to the documentation of this file.
1/*
2 * Project: xRooFit
3 * Author:
4 * Will Buttinger, RAL 2022
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
14
15#include "RooArgSet.h"
16#include "RooArgList.h"
17#include "RooRealVar.h"
18#include "RooFitResult.h"
19#include "RooConstVar.h"
20#include "RooAbsPdf.h"
21
22#include "TCanvas.h"
23#include "TStopwatch.h"
24#include "TSystem.h"
25#include "TPRegexp.h"
26#include "TMemFile.h"
27#include "RooDataSet.h"
28#include "TKey.h"
29#include "TFile.h"
30#include "TGraphErrors.h"
31#include "TH1F.h"
32#include "TStyle.h"
33#include "TLegend.h"
34#include "TLine.h"
36
38
39// bool xRooNLLVar::xRooHypoSpace::AddWorkspace(const char* wsFilename, const char* extraPars){
40//
41// auto ws = std::make_shared<xRooNode>(wsFilename);
42//
43// ws->browse();
44// std::set<std::shared_ptr<xRooNode>> models;
45// for(auto n : *ws) {
46// if (n->fFolder == "!models") models.insert(n);
47// }
48// if (models.size()!=1) {
49// throw std::runtime_error("More than one model in workspace, use AddModel instead");
50// }
51//
52// auto out = AddModel(*models.begin(),extraPars);
53// if (out) {
54// fWorkspaces.insert(ws); // keep ws open
55// }
56//
57// }
58
59xRooNLLVar::xRooHypoSpace::xRooHypoSpace(const char *name, const char *title)
60 : TNamed(name, title), fPars(std::make_shared<RooArgSet>())
61{
62}
63
64std::shared_ptr<xRooNode> xRooNLLVar::xRooHypoSpace::pdf(const char *parValues) const
65{
66 return pdf(toArgs(parValues));
67}
68
69std::shared_ptr<xRooNode> xRooNLLVar::xRooHypoSpace::pdf(const RooAbsCollection &parValues) const
70{
71 RooArgList rhs;
72 rhs.add(parValues);
73 rhs.sort();
74
75 std::shared_ptr<xRooNode> out = nullptr;
76
77 for (auto &[_range, _pdf] : fPdfs) {
78 // any pars not in rhs are assumed to have infinite range in rhs
79 // and vice versa
80 bool collision = true;
81 for (auto &_lhs : *_range) {
82 auto _rhs = rhs.find(*_lhs);
83 if (!_rhs)
84 continue;
85 if (auto v = dynamic_cast<RooRealVar *>(_rhs); v) {
86 if (auto v2 = dynamic_cast<RooRealVar *>(_lhs)) {
87 if (!(v->getMin() <= v2->getMax() && v2->getMin() <= v->getMax())) {
88 collision = false;
89 break;
90 }
91 } else if (auto c2 = dynamic_cast<RooConstVar *>(_lhs)) {
92 if (!(v->getMin() <= c2->getVal() && c2->getVal() <= v->getMax())) {
93 collision = false;
94 break;
95 }
96 }
97 } else if (auto c = dynamic_cast<RooConstVar *>(_rhs); c) {
98 if (auto v2 = dynamic_cast<RooRealVar *>(_lhs)) {
99 if (!(c->getVal() <= v2->getMax() && v2->getMin() <= c->getVal())) {
100 collision = false;
101 break;
102 }
103 } else if (auto c2 = dynamic_cast<RooConstVar *>(_lhs)) {
104 if (!(c->getVal() == c2->getVal())) {
105 collision = false;
106 break;
107 }
108 }
109 }
110 }
111 if (collision) {
112 if (out) {
113 throw std::runtime_error("Multiple pdf possibilities");
114 }
115 out = _pdf;
116 }
117 }
118
119 return out;
120}
121
123{
124
125 RooArgList out;
126
127 TStringToken pattern(str, ";");
128 while (pattern.NextToken()) {
129 TString s = pattern;
130 // split by "=" sign
131 auto _idx = s.Index('=');
132 if (_idx == -1)
133 continue;
134 TString _name = s(0, _idx);
135 TString _val = s(_idx + 1, s.Length());
136
137 if (_val.IsFloat()) {
138 out.addClone(RooConstVar(_name, _name, _val.Atof()));
139 } else if (_val.BeginsWith('[')) {
140 _idx = _val.Index(',');
141 if (_idx == -1)
142 continue;
143 TString _min = _val(0, _idx);
144 TString _max = _val(_idx + 1, _val.Length() - _idx - 2);
145 out.addClone(RooRealVar(_name, _name, _min.Atof(), _max.Atof()));
146 }
147 }
148
149 return out;
150}
151
152int xRooNLLVar::xRooHypoSpace::AddPoints(const char *parName, size_t nPoints, double low, double high)
153{
154 if (nPoints == 0)
155 return nPoints;
156
157 auto _par = dynamic_cast<RooAbsRealLValue *>(fPars->find(parName));
158 if (!_par)
159 throw std::runtime_error("Unknown parameter");
160
161 if (nPoints == 1) {
162 _par->setVal((high + low) * 0.5);
163 AddPoint();
164 return nPoints;
165 }
166
167 double step = (high - low) / nPoints;
168 if (step <= 0)
169 throw std::runtime_error("Invalid steps");
170
171 for (double v = low + step * 0.5; v <= high; v += step) {
172 _par->setVal(v);
173 AddPoint();
174 }
175 return nPoints;
176}
177
178double round_to_digits(double value, int digits)
179{
180 if (value == 0.0)
181 return 0.0;
182 double factor = pow(10.0, digits - ceil(log10(std::abs(value))));
183 return std::round(value * factor) / factor;
184};
185double round_to_decimal(double value, int decimal_places)
186{
187 const double multiplier = std::pow(10.0, decimal_places);
188 return std::round(value * multiplier) / multiplier;
189}
190
191// rounds error to 1 or 2 sig fig and round value to match that precision
192std::pair<double, double> matchPrecision(const std::pair<double, double> &in)
193{
194 auto out = in;
195 if (!std::isinf(out.second)) {
196 auto tmp = out.second;
197 out.second = round_to_digits(out.second, 2);
198 int expo = (out.second == 0) ? 0 : (int)std::floor(std::log10(std::abs(out.second)));
199 if (TString::Format("%e", out.second)(0) != '1') {
200 out.second = round_to_digits(tmp, 1);
201 out.first = (expo >= 0) ? round(out.first) : round_to_decimal(out.first, -expo);
202 } else if (out.second != 0) {
203 out.first = (expo >= 0) ? round(out.first) : round_to_decimal(out.first, -expo + 1);
204 }
205 }
206 return out;
207}
208
209std::map<std::string, std::pair<double, double>> xRooNLLVar::xRooHypoSpace::limits(const char *opt, double relUncert)
210{
211 TString sOpt(opt);
212 if (sOpt.Contains("cls")) {
213 for (auto p : poi()) {
214 if (!p->hasRange("physical")) {
215 Info("limits", "No physical range set for %s, setting to [0,inf]", p->GetName());
216 dynamic_cast<RooRealVar *>(p)->setRange("physical", 0, std::numeric_limits<double>::infinity());
217 }
218 if (!p->getStringAttribute("altVal")) {
219 Info("limits", "No altVal set for %s, setting to 0", p->GetName());
220 p->setStringAttribute("altVal", "0");
221 }
222 // ensure range straddles altVal
223 double altVal = TString(p->getStringAttribute("altVal")).Atof();
224 auto v = dynamic_cast<RooRealVar *>(p);
225 if (v->getMin() >= altVal) {
226 Info("limits", "range of POI does not straddle alt value, adjusting minimum to %g", altVal - 1e-5);
227 v->setMin(altVal - 1e-5);
228 }
229 if (v->getMax() <= altVal) {
230 Info("limits", "range of POI does not straddle alt value, adjusting minimum to %g", altVal + 1e-5);
231 v->setMax(altVal + 1e-5);
232 }
233 for (auto &[pdf, nll] : fNlls) {
234 if (auto _v = dynamic_cast<RooRealVar *>(nll->pars()->find(*p))) {
235 _v->setRange(v->getMin(), v->getMax());
236 }
237 }
238 }
239 }
240
241 std::map<std::string, std::pair<double, double>> out;
242 std::shared_ptr<TMemFile> memFile;
243 if (!gDirectory->IsWritable()) {
244 memFile = std::make_shared<TMemFile>("memory", "RECREATE");
245 }
246 for (int nSigma : {0, 1, 2, -1, -2}) {
247 auto lim = FindLimit(TString::Format("%s exp%s%d", opt, nSigma > 0 ? "+" : "", nSigma), relUncert);
248 if (lim.second < 0)
249 lim.second = -lim.second; // make errors positive for this method
250 out[TString::Format("%d", nSigma).Data()] = matchPrecision(lim);
251 }
252
253 // don't do the observed limit if all the NLL datas are generated
254 bool doObs = true;
255#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
256 bool allGen = true;
257 for (auto &[pdf, nll] : fNlls) {
258 auto _d = dynamic_cast<RooDataSet *>(nll->data());
259 if (!_d || !_d->weightVar() || !_d->weightVar()->getStringAttribute("fitResult")) {
260 allGen = false;
261 break;
262 }
263 }
264 if (allGen)
265 doObs = false;
266#endif
267 if (doObs) {
268 auto lim = FindLimit(TString::Format("%s obs", opt), relUncert);
269 if (lim.second < 0)
270 lim.second = -lim.second;
271 out["obs"] = matchPrecision(lim);
272 }
273 return out;
274}
275
277{
278 // move to given coords, if any
279 fPars->assignValueOnly(toArgs(coords));
280
281 auto _pdf = pdf();
282
283 if (!_pdf)
284 throw std::runtime_error("no model at coordinates");
285
286 if (std::unique_ptr<RooAbsCollection>(fPars->selectByAttrib("poi", true))->size() == 0) {
287 throw std::runtime_error(
288 "No pars designated as POI - set with pars()->find(<parName>)->setAttribute(\"poi\",true)");
289 }
290
291 if (fNlls.find(_pdf) == fNlls.end()) {
292 fNlls[_pdf] = std::make_shared<xRooNLLVar>(_pdf->nll("" /*TODO:allow change dataset name and nll opts*/, {}));
293 }
294
295 xRooHypoPoint out;
296
297 out.nllVar = fNlls[_pdf];
298 out.data = fNlls[_pdf]->getData();
299
300 out.coords.reset(fPars->snapshot()); // should already have altVal prop on poi, and poi labelled
301 // ensure all poi are marked const ... required by xRooHypoPoint behaviour
302 out.poi().setAttribAll("Constant");
303 // and now remove anything that's marked floating
304 const_cast<RooAbsCollection *>(out.coords.get())
305 ->remove(*std::unique_ptr<RooAbsCollection>(out.coords->selectByAttrib("Constant", false)), true, true);
306 double value = out.fNullVal();
307 double alt_value = out.fAltVal();
308
309 auto _type = fTestStatType;
310 if (_type == xRooFit::Asymptotics::Unknown) {
311 // decide based on values
312 if (std::isnan(alt_value))
314 else if (value >= alt_value)
316 else
318 }
319
320 out.fPllType = _type;
321
322 // TODO: Check for equivalent point before adding
323
324 return emplace_back(out);
325}
326
327bool xRooNLLVar::xRooHypoSpace::AddModel(const xRooNode &_pdf, const char *validity)
328{
329
330 if (!_pdf.get<RooAbsPdf>()) {
331 throw std::runtime_error("Not a pdf");
332 }
333
334 auto pars = _pdf.pars().argList();
335
336 // replace any pars with validity pars and add new pars
337 auto vpars = toArgs(validity);
338 pars.replace(vpars);
339 vpars.remove(pars, true, true);
340 pars.add(vpars);
341
342 if (auto existing = pdf(pars)) {
343 throw std::runtime_error(std::string("Clashing model: ") + existing->GetName());
344 }
345
346 auto myPars = std::shared_ptr<RooArgList>(dynamic_cast<RooArgList *>(pars.snapshot()));
347 myPars->sort();
348
349 pars.remove(*fPars, true, true);
350
351 fPars->addClone(pars);
352
353 fPdfs.insert(std::make_pair(myPars, std::make_shared<xRooNode>(_pdf)));
354
355 return true;
356}
357
359{
360 // determine which pars are the minimal set to distinguish all points in the space
361 RooArgList out;
362 out.setName("axes");
363
364 bool clash;
365 do {
366 clash = false;
367
368 // add next best coordinate
369 std::map<std::string, std::set<double>> values;
370 for (auto &par : *pars()) {
371 if (out.find(*par))
372 continue;
373 for (auto p : *this) {
374 values[par->GetName()].insert(
375 p.coords->getRealValue(par->GetName(), std::numeric_limits<double>::quiet_NaN()));
376 }
377 }
378
379 std::string bestVar;
380 size_t maxDiff = 0;
381 bool isPOI = false;
382 for (auto &[k, v] : values) {
383 if (v.size() > maxDiff || (v.size() == maxDiff && !isPOI && pars()->find(k.c_str())->getAttribute("poi"))) {
384 bestVar = k;
385 isPOI = pars()->find(k.c_str())->getAttribute("poi");
386 maxDiff = std::max(maxDiff, v.size());
387 }
388 }
389
390 if (bestVar.empty()) {
391 break;
392 }
393
394 out.add(*pars()->find(bestVar.c_str()));
395
396 std::set<std::vector<double>> coords;
397 for (auto &p : *this) {
398 std::vector<double> p_coords;
399 for (auto o : out) {
400 p_coords.push_back(p.coords->getRealValue(o->GetName(), std::numeric_limits<double>::quiet_NaN()));
401 }
402 if (coords.find(p_coords) != coords.end()) {
403 clash = true;
404 break;
405 }
406 }
407
408 } while (clash);
409
410 // ensure poi are at the end
411 std::unique_ptr<RooAbsCollection> poi(out.selectByAttrib("poi", true));
412 out.remove(*poi);
413 out.add(*poi);
414
415 return out;
416}
417
419{
420 RooArgList out;
421 out.setName("poi");
422 out.add(*std::unique_ptr<RooAbsCollection>(pars()->selectByAttrib("poi", true)));
423 return out;
424}
425
427{
428
429 if (!gDirectory)
430 return;
431 auto dir = gDirectory->GetDirectory(apath);
432 if (!dir) {
433 // try open file first
434 TString s(apath);
435 auto f = TFile::Open(s.Contains(":") ? TString(s(0, s.Index(":"))) : s);
436 if (f) {
437 if (!s.Contains(":"))
438 s += ":";
439 dir = gDirectory->GetDirectory(s);
440 if (dir) {
441 LoadFits(s);
442 return;
443 }
444 }
445 if (!dir) {
446 Error("LoadFits", "Path not found %s", apath);
447 return;
448 }
449 }
450
451 // assume for now all fits in given path will have the same pars
452 // so can just look at the float and const pars of first fit result to get all of them
453 // tuple is: parName, parValue, parAltValue (blank if nan)
454 // key represents the ufit values, value represents the sets of poi for the available cfits (subfits of the ufit)
455
456 std::map<std::set<std::tuple<std::string, double, std::string>>, std::set<std::set<std::string>>> cfits;
457 std::set<std::string> allpois;
458
459 int nFits = 0;
460 std::function<void(TDirectory *)> processDir;
461 processDir = [&](TDirectory *_dir) {
462 std::cout << "Processing " << _dir->GetName() << std::endl;
463 if (auto keys = _dir->GetListOfKeys(); keys) {
464 // first check if dir doesn't contain any RooLinkedList ... this identifies it as not an nll dir
465 // so treat any sub-dirs as new nll
466 bool isNllDir = false;
467 for (auto &&k : *keys) {
468 TKey *key = dynamic_cast<TKey *>(k);
469 if (strcmp(key->GetClassName(), "RooLinkedList") == 0) {
470 isNllDir = true;
471 break;
472 }
473 }
474
475 for (auto &&k : *keys) {
476 if (auto subdir = _dir->GetDirectory(k->GetName()); subdir) {
477 if (!isNllDir) {
478 LoadFits(subdir->GetPath());
479 } else {
480 processDir(subdir);
481 }
482 continue;
483 }
484 auto cl = TClass::GetClass(((TKey *)k)->GetClassName());
485 if (cl->InheritsFrom("RooFitResult")) {
486 if (auto cachedFit = _dir->Get<RooFitResult>(k->GetName()); cachedFit) {
487 nFits++;
488 if (nFits == 1) {
489 // for first fit add any missing float pars
490 std::unique_ptr<RooAbsCollection> snap(cachedFit->floatParsFinal().snapshot());
491 snap->remove(*fPars, true, true);
492 fPars->addClone(*snap);
493 // add also the non-string const pars
494 for (auto &p : cachedFit->constPars()) {
495 if (p->getAttribute("global"))
496 continue; // don't consider globals
497 auto v = dynamic_cast<RooAbsReal *>(p);
498 if (!v) {
499 continue;
500 };
501 if (!fPars->contains(*v))
502 fPars->addClone(*v);
503 }
504 }
505 // get names of all the floats
506 std::set<std::string> floatPars;
507 for (auto &p : cachedFit->floatParsFinal())
508 floatPars.insert(p->GetName());
509 // see if
510
511 // build a set of the const par values
512 std::set<std::tuple<std::string, double, std::string>> constPars;
513 for (auto &p : cachedFit->constPars()) {
514 if (p->getAttribute("global"))
515 continue; // don't consider globals when looking for cfits
516 auto v = dynamic_cast<RooAbsReal *>(p);
517 if (!v) {
518 continue;
519 };
520 constPars.insert(
521 std::make_tuple(v->GetName(), v->getVal(),
522 v->getStringAttribute("altVal") ? v->getStringAttribute("altVal") : ""));
523 }
524 // now see if this is a subset of any existing cfit ...
525 for (auto &&[key, value] : cfits) {
526 if (constPars == key)
527 continue; // ignore cases where we already recorded this list of constPars
528 if (std::includes(constPars.begin(), constPars.end(), key.begin(), key.end())) {
529 // usual case ... cachedFit has more constPars than one of the fits we have already encountered
530 // (the ufit)
531 // => cachedFit is a cfit of key fr ...
532 std::set<std::string> pois;
533 for (auto &&par : constPars) {
534 if (key.find(par) == key.end()) {
535 pois.insert(std::get<0>(par));
536 allpois.insert(std::get<0>(par));
537 }
538 }
539 if (!pois.empty()) {
540 cfits[constPars].insert(pois);
541 // std::cout << cachedFit->GetName() << " ";
542 // for(auto ff: constPars) std::cout << ff.first << "=" <<
543 // ff.second << " "; std::cout << std::endl;
544 }
545 }
546 /* FOR NOW we will skip cases where we encounter the cfit before the ufit - usually should eval the
547 ufit first
548 * else if (std::includes(key.begin(), key.end(), constPars.begin(), constPars.end())) {
549 // constPars are subset of key
550 // => key is a ufit of the cachedFit
551 // add all par names of key that aren't in constPars ... these are the poi
552 std::set<std::string> pois;
553 for (auto &&par: key) {
554 if (constPars.find(par) == constPars.end()) {
555 pois.insert(std::get<0>(par));
556 allpois.insert(std::get<0>(par));
557 }
558 }
559 if (!pois.empty()) {
560 std::cout << "found cfit BEFORE ufit??" << std::endl;
561 value.insert(pois);
562 }
563 } */
564 }
565 // ensure that this combination of constPars has entry in map,
566 // even if it doesn't end up with any poi identified from cfits to it
567 cfits[constPars];
568 delete cachedFit;
569 }
570 }
571 }
572 }
573 };
574 processDir(dir);
575 Info("xRooHypoSpace", "%s - Loaded %d fits", apath, nFits);
576
577 if (allpois.size() == 1) {
578 Info("xRooHypoSpace", "Detected POI: %s", allpois.begin()->c_str());
579
580 auto nll = std::make_shared<xRooNLLVar>(nullptr, nullptr);
581 auto dummyNll = std::make_shared<RooRealVar>(apath, "Dummy NLL", std::numeric_limits<double>::quiet_NaN());
582 nll->std::shared_ptr<RooAbsReal>::operator=(dummyNll);
583 dummyNll->setAttribute("readOnly");
584 // add pars as 'servers' on the dummy NLL
585 if (fPars) {
586 for (auto &&p : *fPars) {
587 dummyNll->addServer(
588 *p); // this is ok provided fPars (i.e. hypoSpace) stays alive as long as the hypoPoint ...
589 }
590 // flag poi
591 for (auto &p : allpois) {
592 fPars->find(p.c_str())->setAttribute("poi", true);
593 }
594 }
595 nll->reinitialize(); // triggers filling of par lists etc
596
597 for (auto &&[key, value] : cfits) {
598 if (value.find(allpois) != value.end()) {
599 // get the value of the poi in the key set
600 auto _coords = std::make_shared<RooArgSet>();
601 for (auto &k : key) {
602 auto v = _coords->addClone(RooRealVar(std::get<0>(k).c_str(), std::get<0>(k).c_str(), std::get<1>(k)));
603 v->setAttribute("poi", allpois.find(std::get<0>(k)) != allpois.end());
604 if (!std::get<2>(k).empty()) {
605 v->setStringAttribute("altVal", std::get<2>(k).c_str());
606 }
607 }
609 // hp.fPOIName = allpois.begin()->c_str();
610 // hp.fNullVal = _coords->getRealValue(hp.fPOIName.c_str());
611 hp.coords = _coords;
612 hp.nllVar = nll;
613
614 // auto altVal =
615 // hp.null_cfit()->constPars().find(hp.fPOIName.c_str())->getStringAttribute("altVal");
616 // if(altVal) hp.fAltVal = TString(altVal).Atof();
617 // else hp.fAltVal = std::numeric_limits<double>::quiet_NaN();
618
619 // decide based on values
620 if (std::isnan(hp.fAltVal()))
622 else if (hp.fNullVal() >= hp.fAltVal())
624 else
626
627 emplace_back(hp);
628 }
629 }
630 } else if (nFits > 0) {
631 std::cout << "possible POI: ";
632 for (auto p : allpois)
633 std::cout << p << ",";
634 std::cout << std::endl;
635 }
636}
637
639{
640
641 auto _axes = axes();
642
643 size_t badFits = 0;
644
645 for (size_t i = 0; i < size(); i++) {
646 std::cout << i << ") ";
647 for (auto a : _axes) {
648 if (a != _axes.first())
649 std::cout << ",";
650 std::cout << a->GetName() << "="
651 << at(i).coords->getRealValue(a->GetName(), std::numeric_limits<double>::quiet_NaN());
652 }
653 std::cout << " status=[ufit:";
654 auto ufit = const_cast<xRooHypoPoint &>(at(i)).ufit(true);
655 if (!ufit)
656 std::cout << "-";
657 else {
658 std::cout << ufit->status();
659 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(ufit->status()) == 0);
660 }
661 std::cout << ",cfit_null:";
662 auto cfit = const_cast<xRooHypoPoint &>(at(i)).cfit_null(true);
663 if (!cfit)
664 std::cout << "-";
665 else {
666 std::cout << cfit->status();
667 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(cfit->status()) == 0);
668 }
669 std::cout << ",cfit_alt:";
670 auto afit = const_cast<xRooHypoPoint &>(at(i)).cfit_alt(true);
671 if (!afit)
672 std::cout << "-";
673 else {
674 std::cout << afit->status();
675 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(afit->status()) == 0);
676 }
677 std::cout << "]";
678 auto sigma_mu = const_cast<xRooHypoPoint &>(at(i)).sigma_mu(true);
679 if (!std::isnan(sigma_mu.first)) {
680 std::cout << " sigma_mu=" << sigma_mu.first;
681 if (sigma_mu.second)
682 std::cout << " +/- " << sigma_mu.second;
683 }
684 std::cout << std::endl;
685 }
686 std::cout << "--------------------------" << std::endl;
687 std::cout << "Number of bad fits: " << badFits << std::endl;
688}
689
690std::shared_ptr<TGraphErrors> xRooNLLVar::xRooHypoSpace::BuildGraph(const char *opt)
691{
692
693 TString sOpt(opt);
694 sOpt.ToLower();
695
696 bool doCLs = sOpt.Contains("cls");
697
698 double nSigma =
699 (sOpt.Contains("exp"))
700 ? (TString(sOpt(sOpt.Index("exp") + 3,
701 sOpt.Index(" ", sOpt.Index("exp")) == -1 ? sOpt.Length() : sOpt.Index(" ", sOpt.Index("exp"))))
702 .Atof())
703 : std::numeric_limits<double>::quiet_NaN();
704 bool expBand =
705 !std::isnan(nSigma) && nSigma && !(sOpt(sOpt.Index("exp") + 3) == '+' || sOpt(sOpt.Index("exp") + 3) == '-');
706
707 auto _axes = axes();
708 if (_axes.size() != 1)
709 return nullptr;
710
711 auto out = std::make_shared<TGraphErrors>();
712 out->SetName(GetName());
713 const char *sCL = (doCLs) ? "CLs" : "null";
714
715 TString title =
716 TString::Format("%s;%s;p_{%s}",
717 (std::isnan(nSigma))
718 ? "Observed"
719 : (!nSigma ? "Expected"
720 : TString::Format("%s%d#sigma Expected",
721 expBand || !nSigma ? "" : ((nSigma < 0) ? "-" : "+"), int(nSigma))
722 .Data()),
723 _axes.at(0)->GetTitle(), sCL);
724
725 if (std::isnan(nSigma)) {
726 out->SetNameTitle(TString::Format("obs_p%s", sCL), title);
727 out->SetMarkerStyle(20);
728 if (sOpt.Contains("ts"))
729 out->SetNameTitle("obs_ts", TString::Format("Observed;%s;Test Statistic", _axes.at(0)->GetTitle()));
730 } else {
731 out->SetNameTitle(TString::Format("exp%d_p%s", int(nSigma), sCL), title);
732 out->SetMarkerStyle(0);
733 out->SetLineStyle(2 + int(nSigma));
734 if (expBand && nSigma) {
735 out->SetFillColor((nSigma == 2) ? kYellow : kGreen);
736 // out->SetFillStyle(3005);
737 out->SetLineStyle(0);
738 out->SetLineWidth(0);
739 auto x = out->Clone("up");
740 x->SetBit(kCanDelete);
741 dynamic_cast<TAttFill *>(x)->SetFillStyle(nSigma == 2 ? 3005 : 3004);
742 dynamic_cast<TAttFill *>(x)->SetFillColor(kBlack);
743 out->GetListOfFunctions()->Add(x, "F");
744 x = out->Clone("down");
745 x->SetBit(kCanDelete);
746 // dynamic_cast<TAttFill*>(x)->SetFillColor((nSigma==2) ? kYellow : kGreen);
747 // dynamic_cast<TAttFill*>(x)->SetFillStyle(1001);
748 out->GetListOfFunctions()->Add(x, "F");
749 }
750 if (sOpt.Contains("ts"))
751 out->SetNameTitle(TString::Format("exp_ts%d", int(nSigma)),
752 TString::Format("Expected;%s;Test Statistic", _axes.at(0)->GetTitle()));
753 }
754
755 auto badPoints = [&]() {
756 auto badPoints2 = dynamic_cast<TGraph *>(out->GetListOfFunctions()->FindObject("badPoints"));
757 if (!badPoints2) {
758 badPoints2 = new TGraph;
759 badPoints2->SetBit(kCanDelete);
760 badPoints2->SetName("badPoints");
761 badPoints2->SetMarkerStyle(5);
762 badPoints2->SetMarkerColor(kRed);
763 badPoints2->SetMarkerSize(out->GetMarkerSize());
764 out->GetListOfFunctions()->Add(badPoints2, "P");
765 }
766 return badPoints2;
767 };
768 int nPointsDown = 0;
769 bool above = true;
770 for (auto &p : *this) {
771 double _x = p.coords->getRealValue(_axes.at(0)->GetName(), std::numeric_limits<double>::quiet_NaN());
772 auto pval = p.getVal(sOpt);
773 auto idx = out->GetN() - nPointsDown;
774
775 if (std::isnan(pval.first)) {
776 badPoints()->SetPoint(badPoints()->GetN(), _x, 0);
777 } else {
778 out->InsertPointBefore(idx, _x, pval.first);
779 out->SetPointError(idx, 0, pval.second);
780 }
781
782 if (expBand && nSigma) {
783 TString sOpt2 = sOpt;
784 sOpt2.ReplaceAll("exp", "exp-");
785 pval = p.getVal(sOpt2);
786 if (std::isnan(pval.first)) {
787 badPoints()->SetPoint(badPoints()->GetN(), _x, 0);
788 } else {
789 out->InsertPointBefore(idx + 1, _x, pval.first);
790 out->SetPointError(idx + 1, 0, pval.second);
791 nPointsDown++;
792 if (out->GetPointY(idx) < pval.first)
793 above = false; // the -sigma points are actually above +sigma
794 }
795 }
796 }
797
798 if (out->GetN() == 0)
799 return out;
800
801 if (!expBand) {
802 out->Sort();
803 } else {
804 out->Sort(&TGraph::CompareX, true, 0, out->GetN() - nPointsDown - 1); // sort first half
805 out->Sort(&TGraph::CompareX, false, out->GetN() - nPointsDown, out->GetN() - 1); // reverse sort second half
806
807 // now populate the up and down values
808 auto up = dynamic_cast<TGraph *>(out->GetListOfFunctions()->FindObject("up"));
809 auto down = dynamic_cast<TGraph *>(out->GetListOfFunctions()->FindObject("down"));
810
811 for (int i = 0; i < out->GetN(); i++) {
812 if (i < out->GetN() - nPointsDown) {
813 up->SetPoint(up->GetN(), out->GetPointX(i), out->GetPointY(i) + out->GetErrorY(i) * (above ? 1. : -1.));
814 down->SetPoint(down->GetN(), out->GetPointX(i), out->GetPointY(i) - out->GetErrorY(i) * (above ? 1. : -1.));
815 } else {
816 up->SetPoint(up->GetN(), out->GetPointX(i), out->GetPointY(i) - out->GetErrorY(i) * (above ? 1. : -1.));
817 down->SetPoint(down->GetN(), out->GetPointX(i), out->GetPointY(i) + out->GetErrorY(i) * (above ? 1. : -1.));
818 }
819 }
820 }
821
822 return out;
823}
824
825std::pair<double, double> xRooNLLVar::xRooHypoSpace::GetLimit(const TGraph &pValues, double target)
826{
827
828 auto gr = std::make_shared<TGraph>(pValues);
829 // remove any nan points and duplicates
830 int i = 0;
831 std::set<double> existingX;
832 while (i < gr->GetN()) {
833 if (std::isnan(gr->GetPointY(i)))
834 gr->RemovePoint(i);
835 else if (existingX.find(gr->GetPointX(i)) != existingX.end()) {
836 gr->RemovePoint(i);
837 } else {
838 existingX.insert(gr->GetPointX(i));
839 // convert to log ....
840 gr->SetPointY(i, log(std::max(gr->GetPointY(i), 1e-10)));
841 i++;
842 }
843 }
844
845 gr->Sort();
846
847 // simple linear extrapolation to critical value ... return nan if problem
848 if (gr->GetN() < 2) {
849 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0);
850 }
851
852 double alpha = log(target);
853
854 bool above = gr->GetPointY(0) > alpha;
855 for (int ii = 1; ii < gr->GetN(); ii++) {
856 if ((above && (gr->GetPointY(ii) <= alpha)) || (!above && (gr->GetPointY(ii) >= alpha))) {
857 // found the limit ... return linearly extrapolated point
858 double lim = gr->GetPointX(ii - 1) + (gr->GetPointX(ii) - gr->GetPointX(ii - 1)) *
859 (alpha - gr->GetPointY(ii - 1)) /
860 (gr->GetPointY(ii) - gr->GetPointY(ii - 1));
861 // use points either side as error
862 double err = std::max(lim - gr->GetPointX(ii - 1), gr->GetPointX(ii) - lim);
863 // give err negative sign to indicate if error due to negative side
864 if ((lim - gr->GetPointX(ii - 1)) > (gr->GetPointX(ii) - lim))
865 err *= -1;
866 return std::pair(lim, err);
867 }
868 }
869 // if reach here need to extrapolate ...
870 if ((above && gr->GetPointY(gr->GetN() - 1) <= gr->GetPointY(0)) ||
871 (!above && gr->GetPointY(gr->GetN() - 1) >= gr->GetPointY(0))) {
872 // extrapolating above based on last two points
873 double x1 = gr->GetPointX(gr->GetN() - 2);
874 double y1 = gr->GetPointY(gr->GetN() - 2);
875 double m = (gr->GetPointY(gr->GetN() - 1) - y1) / (gr->GetPointX(gr->GetN() - 1) - x1);
876 if (m == 0.)
877 return std::pair(2. * gr->GetPointX(gr->GetN() - 1) - x1, std::numeric_limits<double>::infinity());
878 return std::pair((alpha - y1) / m + x1, std::numeric_limits<double>::infinity());
879 } else {
880 // extrapolating below based on first two points
881 double x1 = gr->GetPointX(0);
882 double y1 = gr->GetPointY(0);
883 double m = (gr->GetPointY(1) - y1) / (gr->GetPointX(1) - x1);
884 if (m == 0.)
885 return std::pair(2. * x1 - gr->GetPointX(1), std::numeric_limits<double>::infinity());
886 return std::pair((alpha - y1) / m + x1, std::numeric_limits<double>::infinity());
887 }
888}
889
890std::pair<double, double> xRooNLLVar::xRooHypoSpace::FindLimit(const char *opt, double relUncert)
891{
892 std::shared_ptr<TGraphErrors> gr = BuildGraph(TString(opt) + " readonly");
893
894 // resync parameter boundaries from nlls (may have been modified by fits)
895 for (auto p : poi()) {
896 for (auto &[pdf, nll] : fNlls) {
897 if (auto _v = dynamic_cast<RooRealVar *>(nll->pars()->find(*p))) {
898 dynamic_cast<RooRealVar *>(p)->setRange(_v->getMin(), _v->getMax());
899 }
900 }
901 }
902
903 if (!gr || gr->GetN() < 2) {
904 auto v = (poi().empty()) ? nullptr : dynamic_cast<RooRealVar *>(poi().first());
905 if (!v)
906 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0);
907 double muMax = std::min(v->getMax(), v->getMax("physical"));
908 double muMin = std::max(v->getMin("physical"), v->getMin());
909 if (!gr || gr->GetN() < 1) {
910 if (std::isnan(AddPoint(TString::Format("%s=%g", v->GetName(), muMin)).getVal(opt).first)) {
911 // first point failed ... give up
912 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0);
913 }
914 return FindLimit(opt, relUncert); // do this to resync parameter limits
915 }
916
917 if (std::isnan(
918 AddPoint(TString::Format("%s=%g", v->GetName(), muMin + (muMax - muMin) / 50)).getVal(opt).first)) {
919 // second point failed ... give up
920 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0);
921 }
922 return FindLimit(opt, relUncert);
923 }
924
925 auto lim = GetLimit(*gr);
926
927 if (std::isnan(lim.first)) {
928 return lim;
929 }
930
931 if (std::abs(lim.second) <= relUncert * std::abs(lim.first))
932 return lim;
933
934 double nextPoint;
935 auto v = dynamic_cast<RooRealVar *>(poi().first());
936 double maxMu = std::min(v->getMax("physical"), v->getMax());
937 double minMu = std::max(v->getMin("physical"), v->getMin());
938 if (lim.second == std::numeric_limits<double>::infinity()) {
939 // limit was found by extrapolating to right
940 nextPoint = lim.first;
941 if (nextPoint > v->getMax("physical"))
942 nextPoint = gr->GetPointX(gr->GetN() - 1) + (maxMu - minMu) / 50;
943 if (nextPoint > v->getMax("physical"))
944 return lim;
945 } else if (lim.second == -std::numeric_limits<double>::infinity()) {
946 // limit from extrapolating to left
947 nextPoint = lim.first;
948 if (nextPoint < v->getMin("physical"))
949 nextPoint = gr->GetPointX(0) - (maxMu - minMu) / 50;
950 if (nextPoint < v->getMin("physical"))
951 return lim;
952 } else {
953 nextPoint = lim.first + lim.second * relUncert * 0.99;
954 }
955
956 // got here need a new point .... evaluate the estimated lim location +/- the relUncert (signed error takes care of
957 // direction)
958
959 Info("FindLimit", "%s -- Testing new point @ %s=%g", opt, v->GetName(), nextPoint);
960 if (std::isnan(AddPoint(TString::Format("%s=%g", v->GetName(), nextPoint)).getVal(opt).first)) {
961 return lim;
962 }
963
964 return FindLimit(opt, relUncert);
965}
966
968{
969
970 TString sOpt(opt);
971 sOpt.ToLower();
972
973 // split up by ; and call Draw for each (with 'same' appended)
974 auto _axes = axes();
975 if (_axes.empty())
976 return;
977
978 if (sOpt == "status") {
979 // draw the points in the space
980 if (_axes.size() <= 2) {
981 TGraphErrors *out = new TGraphErrors;
982 out->SetBit(kCanDelete);
983 out->SetName("points");
984 out->SetMarkerSize(0.5);
985 TGraph *tsAvail = new TGraph;
986 tsAvail->SetName("ts");
987 tsAvail->SetBit(kCanDelete);
988 tsAvail->SetMarkerStyle(20);
989 TGraph *expAvail = new TGraph;
990 expAvail->SetName("exp");
991 expAvail->SetBit(kCanDelete);
992 expAvail->SetMarkerStyle(25);
993 expAvail->SetMarkerSize(out->GetMarkerSize() * 1.5);
994 TGraph *badPoints = new TGraph;
995 badPoints->SetName("bad_ufit");
996 badPoints->SetBit(kCanDelete);
997 badPoints->SetMarkerStyle(5);
998 badPoints->SetMarkerColor(kRed);
999 badPoints->SetMarkerSize(out->GetMarkerSize());
1000 TGraph *badPoints2 = new TGraph;
1001 badPoints2->SetName("bad_cfit_null");
1002 badPoints2->SetBit(kCanDelete);
1003 badPoints2->SetMarkerStyle(2);
1004 badPoints2->SetMarkerColor(kRed);
1005 badPoints2->SetMarkerSize(out->GetMarkerSize());
1006
1007 out->SetTitle(TString::Format("%s;%s;%s", GetTitle(), _axes.at(0)->GetTitle(),
1008 (_axes.size() == 1) ? "" : _axes.at(1)->GetTitle()));
1009 for (auto &p : *this) {
1010 bool _readOnly = p.nllVar ? p.nllVar->get()->getAttribute("readOnly") : false;
1011 if (p.nllVar)
1012 p.nllVar->get()->setAttribute("readOnly", true);
1013 double x = p.coords->getRealValue(_axes.at(0)->GetName());
1014 double y = _axes.size() == 1 ? p.ts_asymp().first : p.coords->getRealValue(_axes.at(1)->GetName());
1015 out->SetPoint(out->GetN(), x, y);
1016 if (!std::isnan(p.ts_asymp().first)) {
1017 if (_axes.size() == 1)
1018 out->SetPointError(out->GetN() - 1, 0, p.ts_asymp().second);
1019 tsAvail->SetPoint(tsAvail->GetN(), x, y);
1020 } else if (p.fUfit && (std::isnan(p.fUfit->minNll()) ||
1021 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.fUfit->status()) ==
1023 badPoints->SetPoint(badPoints->GetN(), x, y);
1024 } else if (p.fNull_cfit && (std::isnan(p.fNull_cfit->minNll()) ||
1025 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.fNull_cfit->status()) ==
1027 badPoints2->SetPoint(badPoints2->GetN(), x, y);
1028 }
1029 if (!std::isnan(p.ts_asymp(0).first)) {
1030 expAvail->SetPoint(expAvail->GetN(), x, y);
1031 } else if (p.asimov() && p.asimov()->fUfit &&
1032 (std::isnan(p.asimov()->fUfit->minNll()) ||
1033 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.asimov()->fUfit->status()) ==
1035
1036 } else if (p.asimov() && p.asimov()->fNull_cfit &&
1037 (std::isnan(p.asimov()->fNull_cfit->minNll()) ||
1038 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.asimov()->fNull_cfit->status()) ==
1040 }
1041 if (p.nllVar)
1042 p.nllVar->get()->setAttribute("readOnly", _readOnly);
1043 }
1044
1045 if (_axes.size() == 1) {
1046 TGraph tmp;
1047 for (int i = 0; i < out->GetN(); i++) {
1048 if (!std::isnan(out->GetPointY(i)))
1049 tmp.SetPoint(tmp.GetN(), out->GetPointX(i), out->GetPointY(i));
1050 }
1051 auto fixPoints = [&](TGraph *g) {
1052 for (int i = 0; i < g->GetN(); i++) {
1053 if (std::isnan(g->GetPointY(i)))
1054 g->SetPointY(i, std::isnan(tmp.Eval(g->GetPointX(i))) ? 0. : tmp.Eval(g->GetPointX(i)));
1055 }
1056 };
1057 fixPoints(out);
1058 fixPoints(tsAvail);
1059 fixPoints(expAvail);
1060 fixPoints(badPoints);
1061 fixPoints(badPoints2);
1062 }
1063
1064 out->SetMarkerStyle(4);
1065 out->Draw("AP");
1066 auto leg = new TLegend(1. - gPad->GetRightMargin() - 0.3, 1. - gPad->GetTopMargin() - 0.3,
1067 1. - gPad->GetRightMargin() - 0.05, 1. - gPad->GetTopMargin() - 0.05);
1068 leg->SetName("legend");
1069 leg->AddEntry(out, "Uncomputed", "P");
1070
1071 if (tsAvail->GetN()) {
1072 out->GetListOfFunctions()->Add(tsAvail, "P");
1073 leg->AddEntry(tsAvail, "Computed", "P");
1074 } else {
1075 delete tsAvail;
1076 }
1077 if (expAvail->GetN()) {
1078 out->GetListOfFunctions()->Add(expAvail, "P");
1079 leg->AddEntry(expAvail, "Expected computed", "P");
1080 } else {
1081 delete expAvail;
1082 }
1083 if (badPoints->GetN()) {
1084 out->GetListOfFunctions()->Add(badPoints, "P");
1085 leg->AddEntry(badPoints, "Bad ufit", "P");
1086 } else {
1087 delete badPoints;
1088 }
1089 if (badPoints2->GetN()) {
1090 out->GetListOfFunctions()->Add(badPoints2, "P");
1091 leg->AddEntry(badPoints2, "Bad null cfit", "P");
1092 } else {
1093 delete badPoints2;
1094 }
1096 leg->Draw();
1097 // if(_axes.size()==1) out->GetHistogram()->GetYaxis()->SetRangeUser(0,1);
1098 gPad->SetGrid(true, _axes.size() > 1);
1099 if (_axes.size() == 1)
1100 gPad->SetLogy(false);
1101 }
1102
1103 return;
1104 }
1105
1106 if (sOpt.Contains("pcls") || sOpt.Contains("pnull")) {
1107 // bool doCLs = (sOpt.Contains("cls"));
1108 // const char* sCL = (doCLs) ? "CLs" : "null";
1109
1110 auto exp2 = BuildGraph(sOpt + " exp2");
1111 auto exp1 = BuildGraph(sOpt + " exp1");
1112 auto exp = BuildGraph(sOpt + " exp");
1113 auto obs = BuildGraph(sOpt);
1114
1115 if (!sOpt.Contains("same") && gPad) {
1116 gPad->Clear();
1117 }
1118 auto g = dynamic_cast<TGraphErrors *>(exp2->DrawClone("AF"));
1120 g->GetHistogram()->SetName(".axis");
1121 g->GetHistogram()->SetTitle("");
1122 g->GetHistogram()->SetBit(TH1::kNoTitle);
1123 exp2->DrawClone("F")->SetBit(kCanDelete);
1124 exp1->DrawClone("F")->SetBit(kCanDelete);
1125 exp->DrawClone("LP")->SetBit(kCanDelete);
1126 obs->DrawClone("LP")->SetBit(kCanDelete);
1127 TLine l;
1128 l.SetLineStyle(2);
1129 l.DrawLine(g->GetHistogram()->GetXaxis()->GetXmin(), 0.05, g->GetHistogram()->GetXaxis()->GetXmax(), 0.05);
1130 // auto l = gPad->BuildLegend(gPad->GetLeftMargin()+0.05,
1131 // gPad->GetBottomMargin()+0.05,gPad->GetLeftMargin()+0.35,gPad->GetBottomMargin()+0.25);l->SetName("legend");
1132
1133 auto leg = new TLegend(1. - gPad->GetRightMargin() - 0.3, 1. - gPad->GetTopMargin() - 0.3,
1134 1. - gPad->GetRightMargin() - 0.05, 1. - gPad->GetTopMargin() - 0.05);
1135 leg->SetName("legend");
1136 leg->AddEntry(g->GetListOfFunctions()->FindObject("down"), "", "F");
1137 leg->AddEntry(
1138 dynamic_cast<TGraph *>(gPad->GetPrimitive(exp1->GetName()))->GetListOfFunctions()->FindObject("down"), "",
1139 "F");
1140 leg->AddEntry(gPad->GetPrimitive(exp->GetName()), "", "LPE");
1141 leg->AddEntry(gPad->GetPrimitive(obs->GetName()), "", "LPE");
1142 leg->Draw();
1143 leg->SetBit(kCanDelete);
1144
1145 g->GetHistogram()->Draw("sameaxis"); // redraw axis
1146
1147 if (!sOpt.Contains("same")) {
1148 gPad->SetGrid(0, 0);
1149 gPad->SetLogy(1);
1150 }
1151
1153
1154 return;
1155 }
1156
1157 TGraphErrors *out = new TGraphErrors;
1158 out->SetName(GetName());
1159
1160 TString title = TString::Format(";%s", poi().first()->GetTitle());
1161
1162 auto pllType = xRooFit::Asymptotics::TwoSided;
1163 if (!empty() && axes().size() == 1) {
1164 auto v = dynamic_cast<RooRealVar *>(axes().first());
1165 for (auto &p : *this) {
1166 if (p.fPllType != xRooFit::Asymptotics::TwoSided) {
1167 pllType = p.fPllType;
1168 }
1169 }
1171 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity())
1172 title += TString::Format(";Lower-Bound One-Sided Limit PLR");
1173 else if (v)
1174 title += TString::Format(";One-Sided Limit PLR");
1175 else
1176 title += ";q";
1177 } else if (pllType == xRooFit::Asymptotics::TwoSided) {
1178 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity())
1179 title += TString::Format(";Lower-Bound PLR");
1180 else if (v)
1181 title += TString::Format(";PLR");
1182 else
1183 title += ";t";
1184 } else if (pllType == xRooFit::Asymptotics::OneSidedNegative) {
1185 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity())
1186 title += TString::Format(";Lower-Bound One-Sided Discovery PLR");
1187 else if (v)
1188 title += TString::Format(";One-Sided Discovery PLR");
1189 else
1190 title += ";r";
1191 } else if (pllType == xRooFit::Asymptotics::Uncapped) {
1192 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity())
1193 title += TString::Format(";Lower-Bound Uncapped PLR");
1194 else if (v)
1195 title += TString::Format(";Uncapped PLR");
1196 else
1197 title += ";s";
1198 } else {
1199 title += ";Test Statistic";
1200 }
1201 }
1202
1203 out->SetTitle(title);
1204 *dynamic_cast<TAttFill *>(out) = *this;
1205 *dynamic_cast<TAttLine *>(out) = *this;
1206 *dynamic_cast<TAttMarker *>(out) = *this;
1207 out->SetBit(kCanDelete);
1208
1209 if (!gPad)
1211 auto basePad = gPad;
1212 if (!sOpt.Contains("same"))
1213 basePad->Clear();
1214
1215 bool doFits = false;
1216 if (sOpt.Contains("fits")) {
1217 doFits = true;
1218 sOpt.ReplaceAll("fits", "");
1219 }
1220
1221 auto mainPad = gPad;
1222
1223 out->SetEditable(false);
1224
1225 if (doFits) {
1226 gPad->Divide(1, 2);
1227 gPad->cd(1);
1228 gPad->SetBottomMargin(gPad->GetBottomMargin() * 2.); // increase margin to be same as before
1229 gPad->SetGrid();
1230 out->Draw(sOpt);
1231 basePad->cd(2);
1232 mainPad = basePad->GetPad(1);
1233 } else {
1234 gPad->SetGrid();
1235 out->Draw("ALP");
1236 }
1237
1238 std::pair<double, double> minMax(std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity());
1239 for (auto &p : *this) {
1240 if (p.fPllType != pllType)
1241 continue; // must all have same pll type
1242 auto val = p.pll().first;
1243 minMax.first = std::min(minMax.first, val);
1244 minMax.second = std::max(minMax.second, val);
1245 }
1246 out->GetHistogram()->SetMinimum(minMax.first);
1247 out->GetHistogram()->SetMaximum(minMax.second);
1248
1249 TGraph *badPoints = nullptr;
1250
1251 TStopwatch s;
1252 s.Start();
1253 std::shared_ptr<const RooFitResult> ufr;
1254 for (auto &p : *this) {
1255 if (p.fPllType != pllType)
1256 continue; // must all have same pll type
1257 auto val = p.pll().first;
1258 if (!ufr)
1259 ufr = p.ufit();
1260 if (auto fr = p.fNull_cfit;
1261 fr && doFits) { // access member to avoid unnecessarily creating fit result if wasnt needed
1262 // create a new subpad and draw fitResult on it
1263 auto _pad = gPad;
1264 auto pad =
1265 new TPad(fr->GetName(), TString::Format("%s = %g", poi().first()->GetTitle(), p.fNullVal()), 0, 0, 1., 1);
1266 pad->SetNumber(out->GetN() + 1); // can't use "0" for a subpad
1267 pad->cd();
1268 xRooNode(fr).Draw("goff");
1269 _pad->cd();
1270 //_pad->GetListOfPrimitives()->AddFirst(pad);
1271 pad->AppendPad();
1272 }
1273 if (std::isnan(val)) {
1274 if (!badPoints) {
1275 badPoints = new TGraph;
1276 badPoints->SetBit(kCanDelete);
1277 badPoints->SetName("badPoints");
1278 badPoints->SetMarkerStyle(5);
1279 badPoints->SetMarkerColor(kRed);
1280 badPoints->SetMarkerSize(1);
1281 out->GetListOfFunctions()->Add(badPoints, "P");
1282 }
1283 badPoints->SetPoint(badPoints->GetN(), p.fNullVal(), out->Eval(p.fNullVal()));
1284 mainPad->Modified();
1285 } else {
1286 out->SetPoint(out->GetN(), p.fNullVal(), p.pll().first);
1287 out->SetPointError(out->GetN() - 1, 0, p.pll().second);
1288 out->Sort();
1289
1290 // reposition bad points
1291 if (badPoints) {
1292 for (int i = 0; i < badPoints->GetN(); i++)
1293 badPoints->SetPointY(i, out->Eval(badPoints->GetPointX(i)));
1294 }
1295
1296 mainPad->Modified();
1297 }
1298 if (s.RealTime() > 3) { // stops the clock
1299 basePad->Update();
1301 s.Reset();
1302 s.Start();
1303 }
1304 s.Continue();
1305 }
1306
1307 // finish by overlaying ufit
1308 if (ufr && doFits) {
1309 auto _pad = gPad;
1310 auto pad = new TPad(ufr->GetName(), "unconditional fit", 0, 0, 1., 1.);
1311 pad->SetNumber(-1);
1312 pad->cd();
1313 xRooNode(ufr).Draw("goff");
1314 _pad->cd();
1315 pad->AppendPad();
1316
1317 // draw one more pad to represent the selected, and draw the ufit pad onto that pad
1318 pad = new TPad("selected", "selected", 0, 0, 1, 1);
1319 pad->Draw();
1320 pad->cd();
1321 basePad->GetPad(2)->GetPad(-1)->AppendPad();
1322 pad->Modified();
1323 pad->Update();
1325
1326 basePad->cd();
1327 }
1328
1329 if (!xRooNode::gIntObj) {
1331 }
1332 gPad->GetCanvas()->Connect("Highlighted(TVirtualPad*,TObject*,Int_t,Int_t)", "xRooNode::InteractiveObject",
1333 xRooNode::gIntObj, "Interactive_PLLPlot(TVirtualPad*,TObject*,Int_t,Int_t)");
1334
1335 return;
1336}
1337
1339{
1340
1341 RooStats::HypoTestInverterResult *out = nullptr;
1342
1343 auto _axes = axes();
1344 if (_axes.empty())
1345 return out;
1346
1347 out = new RooStats::HypoTestInverterResult(GetName(), *dynamic_cast<RooRealVar *>(_axes.at(0)), 0.05);
1348
1349 for (auto &p : *this) {
1350 double _x = p.coords->getRealValue(_axes.at(0)->GetName(), std::numeric_limits<double>::quiet_NaN());
1351 out->Add(_x, p.result());
1352 }
1353
1354 return out;
1355}
1356
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Definition RtypesCore.h:66
@ kRed
Definition Rtypes.h:66
@ kBlack
Definition Rtypes.h:65
@ kGreen
Definition Rtypes.h:66
@ kYellow
Definition Rtypes.h:66
#define gDirectory
Definition TDirectory.h:386
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:230
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:197
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t SetFillStyle
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 target
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 x1
Option_t Option_t SetFillColor
Option_t Option_t TPoint TPoint const char y1
char name[80]
Definition TGX11.cxx:110
@ kCanDelete
Definition TObject.h:369
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
#define gPad
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
RooAbsCollection is an abstract container object that can hold multiple RooAbsArg objects.
RooAbsCollection * selectByAttrib(const char *name, bool value) const
Create a subset of the current collection, consisting only of those elements with the specified attri...
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
void setAttribAll(const Text_t *name, bool value=true)
Set given attribute in each element of the collection by calling each elements setAttribute() functio...
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
void sort(bool reverse=false)
Sort collection using std::sort and name comparison.
void setName(const char *name)
RooAbsArg * find(const char *name) const
Find object with given name in list.
RooAbsRealLValue is the common abstract base class for objects that represent a real value that may a...
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:62
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
RooConstVar represent a constant real-valued object.
Definition RooConstVar.h:26
RooDataSet is a container class to hold unbinned data.
Definition RooDataSet.h:57
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
std::shared_ptr< const RooAbsCollection > coords
Definition xRooNLLVar.h:169
std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > data
Definition xRooNLLVar.h:119
RooStats::HypoTestInverterResult * result()
bool AddModel(const xRooNode &pdf, const char *validity="")
int AddPoints(const char *parName, size_t nPoints, double low, double high)
std::shared_ptr< TGraphErrors > BuildGraph(const char *opt)
static std::pair< double, double > GetLimit(const TGraph &pValues, double target=0.05)
void Print(Option_t *opt="") const override
Print TNamed name and title.
xRooHypoPoint & AddPoint(const char *coords="")
std::shared_ptr< xRooNode > pdf(const RooAbsCollection &parValues) const
void Draw(Option_t *opt="") override
Default Draw method for all objects.
std::pair< double, double > FindLimit(const char *opt, double relUncert=std::numeric_limits< double >::infinity())
std::map< std::string, std::pair< double, double > > limits(const char *opt="cls", double relUncert=0.1)
std::shared_ptr< RooAbsPdf > pdf() const
Definition xRooNLLVar.h:283
std::shared_ptr< RooArgSet > pars(bool stripGlobalObs=true)
void Draw(Option_t *opt="") override
Default Draw method for all objects.
static InteractiveObject * gIntObj
Definition xRooNode.h:351
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:40
HypoTestInverterResult class holds the array of hypothesis test results and compute a confidence inte...
bool Add(const HypoTestInverterResult &otherResult)
merge with the content of another HypoTestInverterResult object
Fill Area Attributes class.
Definition TAttFill.h:19
Line Attributes class.
Definition TAttLine.h:18
virtual void SetLineStyle(Style_t lstyle)
Set the line style.
Definition TAttLine.h:42
Marker Attributes class.
Definition TAttMarker.h:19
virtual void SetMarkerColor(Color_t mcolor=1)
Set the marker color.
Definition TAttMarker.h:38
virtual Size_t GetMarkerSize() const
Return the marker size.
Definition TAttMarker.h:33
virtual void SetMarkerStyle(Style_t mstyle=1)
Set the marker style.
Definition TAttMarker.h:40
virtual void SetMarkerSize(Size_t msize=1)
Set the marker size.
Definition TAttMarker.h:45
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1504
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2968
Describe directory structure in memory.
Definition TDirectory.h:45
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
Definition TFile.cxx:4053
A TGraphErrors is a TGraph with error bars.
virtual void SetPointError(Double_t ex, Double_t ey)
Set ex and ey values for point pointed by the mouse.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
virtual Double_t GetPointX(Int_t i) const
Get x value for point i.
Definition TGraph.cxx:1528
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2325
static Bool_t CompareX(const TGraph *gr, Int_t left, Int_t right)
Return kTRUE if fX[left] > fX[right]. Can be used by Sort.
Definition TGraph.cxx:682
Int_t GetN() const
Definition TGraph.h:129
virtual void Sort(Bool_t(*greater)(const TGraph *, Int_t, Int_t)=&TGraph::CompareX, Bool_t ascending=kTRUE, Int_t low=0, Int_t high=-1111)
Sorts the points of this TGraph using in-place quicksort (see e.g.
Definition TGraph.cxx:2469
TList * GetListOfFunctions() const
Definition TGraph.h:123
virtual Double_t Eval(Double_t x, TSpline *spline=nullptr, Option_t *option="") const
Interpolate points in this graph at x using a TSpline.
Definition TGraph.cxx:926
virtual Int_t RemovePoint()
Delete point close to the mouse position Returns index of removed point (or -1 if nothing was changed...
Definition TGraph.cxx:2021
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2364
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:808
TH1F * GetHistogram() const
Returns a pointer to the histogram used to draw the axis Takes into account the two following cases.
Definition TGraph.cxx:1401
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1539
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2380
virtual void SetPointY(Int_t i, Double_t y)
Set y value for point i.
Definition TGraph.cxx:2357
virtual void SetEditable(Bool_t editable=kTRUE)
if editable=kFALSE, the graph cannot be modified with the mouse by default a TGraph is editable
Definition TGraph.cxx:2284
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:168
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:400
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:401
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
virtual const char * GetClassName() const
Definition TKey.h:75
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
Use the TLine constructor to create a simple line.
Definition TLine.h:22
virtual TLine * DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2)
Draw this line with new coordinates.
Definition TLine.cxx:102
TObject * FindObject(const char *name) const override
Find an object in this list using its name.
Definition TList.cxx:578
void Add(TObject *obj) override
Definition TList.h:81
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:439
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:774
The most important graphics class in the ROOT system.
Definition TPad.h:28
Bool_t Connect(const char *signal, const char *receiver_class, void *receiver, const char *slot)
Non-static method is used to connect from the signal of this object to the receiver slot.
Definition TQObject.cxx:869
Stopwatch class.
Definition TStopwatch.h:28
Double_t RealTime()
Stop the stopwatch (if it is running) and return the realtime (in seconds) passed between the start a...
void Start(Bool_t reset=kTRUE)
Start the stopwatch.
void Continue()
Resume a stopped stopwatch.
void Reset()
Definition TStopwatch.h:52
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Definition TPRegexp.cxx:975
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:421
void ToLower()
Change string to lower-case.
Definition TString.cxx:1170
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2032
Bool_t IsFloat() const
Returns kTRUE if string contains a floating point or integer number.
Definition TString.cxx:1836
const char * Data() const
Definition TString.h:380
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:704
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:627
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2356
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:636
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:651
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:419
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
TGraphErrors * gr
Definition legend1.C:25
leg
Definition legend1.C:34
return c2
Definition legend2.C:14
Definition first.py:1
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
double round_to_decimal(double value, int decimal_places)
double round_to_digits(double value, int digits)