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 "TROOT.h"
28#include "RooDataSet.h"
29#include "TKey.h"
30#include "TFile.h"
31#include "TGraphErrors.h"
32#include "TMultiGraph.h"
33#include "TH1F.h"
34#include "TStyle.h"
35#include "TLegend.h"
36#include "TLine.h"
38#include "TEnv.h"
39
41
42xRooNLLVar::xRooHypoSpace::xRooHypoSpace(const char *name, const char *title)
43 : TNamed(name, title), fPars(std::make_shared<RooArgSet>())
44{
45 if (name == nullptr || strlen(name) == 0) {
46 SetName(TUUID().AsString());
47 }
48}
49
51 : TNamed(), fPars(std::make_shared<RooArgSet>())
52{
53 if (!result)
54 return;
55
57
58 fPars->addClone(*std::unique_ptr<RooAbsCollection>(result->GetParameters()));
59 double spaceSize = 1;
60 for (auto p : *fPars) {
61 auto v = dynamic_cast<RooRealVar *>(p);
62 if (!v)
63 continue;
64 spaceSize *= (v->getMax() - v->getMin());
65 }
66 for (int i = 0; i < result->ArraySize(); i++) {
67 auto point = result->GetResult(i);
68 double xVal = result->GetXValue(i);
69 double ssize = spaceSize;
70 for (auto p : *fPars) {
71 auto v = dynamic_cast<RooRealVar *>(p);
72 if (!v)
73 continue;
74 ssize /= (v->getMax() - v->getMin());
75 double remain = std::fmod(xVal, ssize);
76 v->setVal((xVal - remain) / ssize);
77 xVal = remain;
78 }
79 emplace_back(xRooHypoPoint(std::make_shared<RooStats::HypoTestResult>(*point), fPars.get()));
80 }
81 // add any pars we might have missed
82 for (auto &p : *this) {
83 for (auto a : *p.coords) {
84 if (!fPars->find(a->GetName()))
85 fPars->addClone(*a);
86 }
87 }
88}
89
90std::shared_ptr<xRooNode> xRooNLLVar::xRooHypoSpace::pdf(const char *parValues) const
91{
92 return pdf(toArgs(parValues));
93}
94
95std::shared_ptr<xRooNode> xRooNLLVar::xRooHypoSpace::pdf(const RooAbsCollection &parValues) const
96{
97 RooArgList rhs;
98 rhs.add(parValues);
99 rhs.sort();
100
101 std::shared_ptr<xRooNode> out = nullptr;
102
103 for (auto &[_range, _pdf] : fPdfs) {
104 // any pars not in rhs are assumed to have infinite range in rhs
105 // and vice versa
106 bool collision = true;
107 for (auto &_lhs : *_range) {
108 auto _rhs = rhs.find(*_lhs);
109 if (!_rhs)
110 continue;
111 if (auto v = dynamic_cast<RooRealVar *>(_rhs); v) {
112 if (auto v2 = dynamic_cast<RooRealVar *>(_lhs)) {
113 if (!(v->getMin() <= v2->getMax() && v2->getMin() <= v->getMax())) {
114 collision = false;
115 break;
116 }
117 } else if (auto c2 = dynamic_cast<RooConstVar *>(_lhs)) {
118 if (!(v->getMin() <= c2->getVal() && c2->getVal() <= v->getMax())) {
119 collision = false;
120 break;
121 }
122 }
123 } else if (auto c = dynamic_cast<RooConstVar *>(_rhs); c) {
124 if (auto v2 = dynamic_cast<RooRealVar *>(_lhs)) {
125 if (!(c->getVal() <= v2->getMax() && v2->getMin() <= c->getVal())) {
126 collision = false;
127 break;
128 }
129 } else if (auto c2 = dynamic_cast<RooConstVar *>(_lhs)) {
130 if (!(c->getVal() == c2->getVal())) {
131 collision = false;
132 break;
133 }
134 }
135 }
136 }
137 if (collision) {
138 if (out) {
139 throw std::runtime_error("Multiple pdf possibilities");
140 }
141 out = _pdf;
142 }
143 }
144
145 return out;
146}
147
149{
150
151 RooArgList out;
152
153 TStringToken pattern(str, ";");
154 while (pattern.NextToken()) {
155 TString s = pattern;
156 // split by "=" sign
157 auto _idx = s.Index('=');
158 if (_idx == -1)
159 continue;
160 TString _name = s(0, _idx);
161 TString _val = s(_idx + 1, s.Length());
162
163 if (_val.IsFloat()) {
164 out.addClone(RooConstVar(_name, _name, _val.Atof()));
165 } else if (_val.BeginsWith('[')) {
166 _idx = _val.Index(',');
167 if (_idx == -1)
168 continue;
169 TString _min = _val(0, _idx);
170 TString _max = _val(_idx + 1, _val.Length() - _idx - 2);
171 out.addClone(RooRealVar(_name, _name, _min.Atof(), _max.Atof()));
172 }
173 }
174
175 return out;
176}
177
178int xRooNLLVar::xRooHypoSpace::AddPoints(const char *parName, size_t nPoints, double low, double high)
179{
180 if (nPoints == 0)
181 return nPoints;
182
183 auto _par = dynamic_cast<RooAbsRealLValue *>(fPars->find(parName));
184 if (!_par)
185 throw std::runtime_error("Unknown parameter");
186 _par->setAttribute("axis");
187
188 if (nPoints == 1) {
189 _par->setVal((high + low) * 0.5);
190 AddPoint();
191 return nPoints;
192 }
193
194 double step = (high - low) / (nPoints - 1);
195 if (step <= 0)
196 throw std::runtime_error("Invalid steps");
197
198 for (size_t i = 0; i < nPoints; i++) {
199 _par->setVal((i == nPoints - 1) ? high : (low + step * i));
200 AddPoint();
201 }
202 return nPoints;
203}
204
206{
207 if (axes().empty()) {
208 // set the first poi as the axis variable to scan
209 if (poi().empty()) {
210 throw std::runtime_error("No POI to scan");
211 } else {
212 poi().first()->setAttribute("axis");
213 }
214 }
215
216 if (empty()) {
217 // promote all axes to being poi and demote all non-axes to non-poi
218 poi().setAttribAll("poi", false);
219 axes().setAttribAll("poi");
220 }
221
222 return AddPoint(TString::Format("%s=%f", axes().first()->GetName(), value));
223}
224
225int xRooNLLVar::xRooHypoSpace::scan(const char *type, size_t nPoints, double low, double high,
226 const std::vector<double> &nSigmas, double relUncert)
227{
228
229 TString sType(type);
230 sType.ToLower();
231 if (sType.Contains("cls") && !sType.Contains("pcls"))
232 sType.ReplaceAll("cls", "pcls");
233 if (!sType.Contains("pcls") && !sType.Contains("ts") && !sType.Contains("pnull") && !sType.Contains("plr")) {
234 throw std::runtime_error("scan type must be equal to one of: plr, cls, ts, pnull");
235 }
236
237 // will scan the first axes variable ... if there is none, specify the first poi as the axis var
238 if (axes().empty()) {
239 // set the first poi as the axis variable to scan
240 if (poi().empty()) {
241 throw std::runtime_error("No POI to scan");
242 } else {
243 poi().first()->setAttribute("axis");
244 }
245 }
246
247 // promote all axes to being poi and demote all non-axes to non-poi
248 poi().setAttribAll("poi", false);
249 axes().setAttribAll("poi");
250
251 auto p = dynamic_cast<RooRealVar *>(axes().first());
252 if (!p) {
253 throw std::runtime_error(TString::Format("%s not scannable", axes().first()->GetName()));
254 }
255
256 if (sType.Contains("cls")) {
257 if (empty() && relUncert == std::numeric_limits<double>::infinity()) {
258 // use default uncertainty precision of 10%
259 ::Info("xRooHypoSpace::scan", "Using default precision of 10%% for auto-scan");
260 relUncert = 0.1;
261 }
262 for (auto a : axes()) {
263 if (!a->hasRange("physical")) {
264 ::Info("xRooHypoSpace::scan", "No physical range set for %s, setting to [0,inf]", p->GetName());
265 dynamic_cast<RooRealVar *>(a)->setRange("physical", 0, std::numeric_limits<double>::infinity());
266 }
267 if (!a->getStringAttribute("altVal") || !strlen(p->getStringAttribute("altVal"))) {
268 ::Info("xRooHypoSpace::scan", "No altVal set for %s, setting to 0", a->GetName());
269 a->setStringAttribute("altVal", "0");
270 }
271 // ensure range straddles altVal
272 double altVal = TString(a->getStringAttribute("altVal")).Atof();
273 auto v = dynamic_cast<RooRealVar *>(a);
274 if (v->getMin() >= altVal) {
275 ::Info("xRooHypoSpace::scan", "range of POI does not straddle alt value, adjusting minimum to %g",
276 altVal - 1e-5);
277 v->setMin(altVal - 1e-5);
278 }
279 if (v->getMax() <= altVal) {
280 ::Info("xRooHypoSpace::scan", "range of POI does not straddle alt value, adjusting maximum to %g",
281 altVal + 1e-5);
282 v->setMax(altVal + 1e-5);
283 }
284 for (auto &[pdf, nll] : fNlls) {
285 if (auto _v = dynamic_cast<RooRealVar *>(nll->pars()->find(*a))) {
286 _v->setRange(v->getMin(), v->getMax());
287 }
288 }
289 }
290 } else if (sType.Contains("plr")) {
291 // force use of two-sided test statistic for any new points
292 fTestStatType = xRooFit::Asymptotics::TwoSided;
293 sType.ReplaceAll("plr", "ts");
294 }
295
296 if (p && high <= low) {
297 // take from parameter
298 low = p->getMin("scan");
299 high = p->getMax("scan");
300 ::Info("xRooHypoSpace::scan", "Using %s range: %g - %g", p->GetName(), low, high);
301 }
302 if (!std::isnan(low) && !std::isnan(high) && !(std::isinf(low) && std::isinf(high))) {
303 p->setRange("scan", low, high);
304 }
305
306 bool doObs = false;
307 for (auto nSigma : nSigmas) {
308 if (std::isnan(nSigma)) {
309 doObs = true;
310 break;
311 }
312 }
313
314 if (fNlls.empty()) {
315 // this happens when loaded hypoSpace from a hypoSpaceInverterResult
316 // set relUncert to infinity so that we don't test any new points
317 relUncert = std::numeric_limits<double>::infinity(); // no NLL available so just get whatever limit we can
318
319 // if any of the defined points are 'expected' data don't do obs
320 // for(auto& hp : *this) {
321 // if(hp.isExpected) {
322 // doObs = false; break;
323 // }
324 // }
325 } else {
326#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
327// bool allGen = true;
328// for (auto &[pdf, nll]: fNlls) {
329// auto _d = dynamic_cast<RooDataSet *>(nll->data());
330// if (!_d || !_d->weightVar() || !_d->weightVar()->getStringAttribute("fitResult") ||
331// !_d->weightVar()->getAttribute("expected")) {
332// allGen = false;
333// break;
334// }
335// }
336// if (allGen)
337// doObs = false;
338#endif
339 }
340
341 // create a fitDatabase if required
342 TDirectory *origDir = gDirectory;
343 if (!gDirectory || !gDirectory->IsWritable()) {
344 // locate a TMemFile in the open list of files and move to that
345 // or create one if cannot find
346 for (auto file : *gROOT->GetListOfFiles()) {
347 if (auto f = dynamic_cast<TMemFile *>(file)) {
348 f->cd();
349 break;
350 }
351 }
352 if (!gDirectory || !gDirectory->IsWritable()) {
353 new TMemFile("fitDatabase", "RECREATE");
354 }
355 }
356
357 int out = 0;
358
359 if (nPoints == 0) {
360 // automatic scan
361 if (sType.Contains("cls")) {
362 for (double nSigma : nSigmas) {
363 xValueWithError res(std::make_pair(0., 0.));
364 if (std::isnan(nSigma)) {
365 if (!doObs)
366 continue;
367 res = findlimit(TString::Format("%s obs", sType.Data()), relUncert);
368 } else {
369 res =
370 findlimit(TString::Format("%s exp%s%d", sType.Data(), nSigma > 0 ? "+" : "", int(nSigma)), relUncert);
371 }
372 if (std::isnan(res.first) || std::isnan(res.second))
373 out = 1;
374 else if (std::isinf(res.second))
375 out = 2;
376 }
377 } else {
378 throw std::runtime_error(TString::Format("Automatic scanning not yet supported for %s", type));
379 }
380 } else {
381 // add the required points and then compute the required value
382 if (nPoints == 1) {
383 AddPoint(TString::Format("%s=%g", poi().first()->GetName(), (high + low) / 2.));
384 graphs(sType); // triggers computation
385 } else {
386 double step = (high - low) / (nPoints - 1);
387 for (size_t i = 0; i < nPoints; i++) {
388 AddPoint(TString::Format("%s=%g", poi().first()->GetName(), low + step * i));
389 graphs(sType); // triggers computation
390 }
391 }
392 }
393
394 if (origDir)
395 origDir->cd();
396
397 return out;
398}
399
400std::map<std::string, std::pair<double, double>>
401xRooNLLVar::xRooHypoSpace::limits(const char *opt, const std::vector<double> &nSigmas, double relUncert)
402{
403
404 if (fNlls.empty()) {
405 // this happens when loaded hypoSpace from a hypoSpaceInverterResult
406 // set relUncert to infinity so that we don't test any new points
407 relUncert = std::numeric_limits<double>::infinity(); // no NLL available so just get whatever limit we can
408 }
409
410 scan(opt, nSigmas, relUncert);
411
412 std::map<std::string, std::pair<double, double>> out;
413 for (auto nSigma : nSigmas) {
414 auto lim = limit(opt, nSigma);
415 if (lim.second < 0)
416 lim.second = -lim.second; // make errors positive for this method
417 out[std::isnan(nSigma) ? "obs" : TString::Format("%d", int(nSigma)).Data()] = xRooFit::matchPrecision(lim);
418 }
419 return out;
420}
421
423{
424 // move to given coords, if any ... will mark them const too
425 std::unique_ptr<RooAbsCollection, std::function<void(RooAbsCollection *)>> _snap(fPars->snapshot(),
426 [&](RooAbsCollection *c) {
427 *fPars = *c;
428 delete c;
429 });
430 TStringToken pattern(coords, ";");
431 while (pattern.NextToken()) {
432 TString s = pattern;
433 // split by "=" sign
434 auto _idx = s.Index('=');
435 if (_idx == -1)
436 continue;
437 TString _name = s(0, _idx);
438 TString _val = s(_idx + 1, s.Length());
439 auto _v = dynamic_cast<RooRealVar *>(fPars->find(_name));
440 if (!_v)
441 continue;
442
443 if (_val.IsFloat()) {
444 _v->setConstant();
445 _v->setVal(_val.Atof());
446 }
447 }
448
449 auto _pdf = pdf();
450
451 if (!_pdf)
452 throw std::runtime_error("no model at coordinates");
453
454 // if (std::unique_ptr<RooAbsCollection>(fPars->selectByAttrib("poi", true))->size() == 0) {
455 // throw std::runtime_error(
456 // "No pars designated as POI - set with pars()->find(<parName>)->setAttribute(\"poi\",true)");
457 // }
458
459 if (fNlls.find(_pdf) == fNlls.end()) {
460 fNlls[_pdf] = std::make_shared<xRooNLLVar>(_pdf->nll("" /*TODO:allow change dataset name and nll opts*/, {}));
461 }
462
463 xRooHypoPoint out;
464
465 out.nllVar = fNlls[_pdf];
466 out.fData = fNlls[_pdf]->getData();
467 out.isExpected = dynamic_cast<RooDataSet *>(out.fData.first.get()) &&
468 dynamic_cast<RooDataSet *>(out.fData.first.get())->weightVar()->getAttribute("expected");
469 // TODO: need to access the genfit of the data and add that to the point, somehow ...
470
471 out.coords.reset(fPars->snapshot()); // should already have altVal prop on poi, and poi labelled
472 // ensure all poi are marked const ... required by xRooHypoPoint behaviour
473 out.poi().setAttribAll("Constant");
474 // and now remove anything that's marked floating
475
476 // do to bug in remove have to ensure not using the hash map otherwise will be doing an invalid read after the
477 // deletion of the owned pars
478 const_cast<RooAbsCollection *>(out.coords.get())->useHashMapForFind(false);
479 const_cast<RooAbsCollection *>(out.coords.get())
480 ->remove(*std::unique_ptr<RooAbsCollection>(out.coords->selectByAttrib("Constant", false)), true, true);
481
482 double value = out.fNullVal();
483 double alt_value = out.fAltVal();
484
485 auto _type = fTestStatType;
486 if (_type == xRooFit::Asymptotics::Unknown) {
487 // decide based on values
488 if (std::isnan(alt_value))
490 else if (value >= alt_value)
492 else
494 }
495
496 out.fPllType = _type;
497
498 // look for a matching point
499 for (auto &p : *this) {
500 if (p.nllVar != out.nllVar)
501 continue;
502 if (p.fData != out.fData)
503 continue;
504 if (!p.alt_poi().equals(out.alt_poi()))
505 continue;
506 bool match = true;
507 for (auto c : p.alt_poi()) {
508 if (auto v = dynamic_cast<RooAbsReal *>(c);
509 v && std::abs(v->getVal() - out.alt_poi().getRealValue(v->GetName())) > 1e-12) {
510 match = false;
511 break;
512 }
513 }
514 if (!match)
515 continue;
516 if (!p.coords->equals(*out.coords))
517 continue;
518 for (auto c : *p.coords) {
519 if (c->getAttribute("poi")) {
520 continue; // check poi below
521 }
522 if (auto v = dynamic_cast<RooAbsReal *>(c);
523 v && std::abs(v->getVal() - out.coords->getRealValue(v->GetName())) > 1e-12) {
524 match = false;
525 break;
526 }
527 }
528 if (!match)
529 continue;
530 // if reached here we can copy over the asimov dataset to save re-generating it
531 // first copy over cfit_alt (if its there) because that is also the same since coords and alt_poi values same
532 if (auto cfit = p.cfit_alt(true)) {
533 out.fAlt_cfit = cfit;
534 }
535 if (p.asimov(true) && p.asimov(true)->fData.first && (!out.asimov(true) || !out.asimov(true)->fData.first)) {
536 out.asimov()->fData = p.asimov(true)->fData;
537 }
538 if (!p.poi().equals(out.poi()))
539 continue;
540 for (auto c : p.poi()) {
541 if (auto v = dynamic_cast<RooAbsReal *>(c);
542 v && std::abs(v->getVal() - out.poi().getRealValue(v->GetName())) > 1e-12) {
543 match = false;
544 break;
545 }
546 }
547 if (match) {
548 // found a duplicate point, return that!
549 return p;
550 }
551 }
552
553 std::string coordString;
554 for (auto a : axes()) {
555 coordString += TString::Format("%s=%g", a->GetName(), out.coords->getRealValue(a->GetName()));
556 coordString += ",";
557 }
558 coordString.erase(coordString.end() - 1);
559
560 ::Info("xRooHypoSpace::AddPoint", "Added new point @ %s", coordString.c_str());
561 return emplace_back(out);
562}
563
564bool xRooNLLVar::xRooHypoSpace::AddModel(const xRooNode &_pdf, const char *validity)
565{
566
567 if (!_pdf.get<RooAbsPdf>()) {
568 throw std::runtime_error("Not a pdf");
569 }
570
571 auto pars = _pdf.pars().argList();
572
573 // replace any pars with validity pars and add new pars
574 auto vpars = toArgs(validity);
575 pars.replace(vpars);
576 vpars.remove(pars, true, true);
577 pars.add(vpars);
578
579 if (auto existing = pdf(pars)) {
580 throw std::runtime_error(std::string("Clashing model: ") + existing->GetName());
581 }
582
583 auto myPars = std::shared_ptr<RooArgList>(dynamic_cast<RooArgList *>(pars.snapshot()));
584 myPars->sort();
585
586 pars.remove(*fPars, true, true);
587
588 fPars->addClone(pars);
589
590 fPdfs.insert(std::make_pair(myPars, std::make_shared<xRooNode>(_pdf)));
591
592 return true;
593}
594
596{
597 // determine which pars are the minimal set to distinguish all points in the space
598 RooArgList out;
599 out.setName("axes");
600
601 out.add(*std::unique_ptr<RooAbsCollection>(
602 fPars->selectByAttrib("axis", true))); // start with any pars explicitly designated as axes
603
604 bool clash;
605 do {
606 clash = false;
607
608 std::set<std::vector<double>> coords;
609 for (auto &p : *this) {
610 std::vector<double> p_coords;
611 for (auto o : out) {
612 auto _v = dynamic_cast<RooRealVar *>(p.coords->find(o->GetName()));
613 p_coords.push_back(
614 (_v && _v->isConstant())
615 ? _v->getVal()
616 : std::numeric_limits<double>::infinity()); // non-const coords are treating as non-existent
617 // p_coords.push_back(p.coords->getRealValue(o->GetName(), std::numeric_limits<double>::quiet_NaN()));
618 }
619 if (coords.find(p_coords) != coords.end()) {
620 clash = true;
621 break;
622 }
623 coords.insert(p_coords);
624 }
625
626 if (clash) {
627 // add next best coordinate
628 std::map<std::string, std::unordered_set<double>> values;
629 for (auto &par : *pars()) {
630 if (out.find(*par))
631 continue;
632 for (auto p : *this) {
633 auto _v = dynamic_cast<RooRealVar *>(p.coords->find(par->GetName()));
634 values[par->GetName()].insert(
635 (_v && _v->isConstant())
636 ? _v->getVal()
637 : std::numeric_limits<double>::infinity()); // non-const coords are treating as non-existent
638 // values[par->GetName()].insert(
639 // p.coords->getRealValue(par->GetName(), std::numeric_limits<double>::quiet_NaN()));
640 }
641 }
642
643 std::string bestVar;
644 size_t maxDiff = 0;
645 bool isPOI = false;
646 for (auto &[k, v] : values) {
647 if (v.size() > maxDiff || (v.size() == maxDiff && !isPOI && pars()->find(k.c_str())->getAttribute("poi"))) {
648 bestVar = k;
649 isPOI = pars()->find(k.c_str())->getAttribute("poi");
650 maxDiff = std::max(maxDiff, v.size());
651 }
652 }
653 if (bestVar.empty()) {
654 break;
655 }
656
657 out.add(*pars()->find(bestVar.c_str()));
658 }
659 } while (clash);
660
661 // ensure poi are at the end
662 std::unique_ptr<RooAbsCollection> poi(out.selectByAttrib("poi", true));
663 out.remove(*poi);
664 out.add(*poi);
665
666 return out;
667}
668
670{
671 RooArgList out;
672 out.setName("poi");
673 out.add(*std::unique_ptr<RooAbsCollection>(pars()->selectByAttrib("poi", true)));
674 return out;
675}
676
678{
679
680 if (!gDirectory)
681 return;
682 auto dir = gDirectory->GetDirectory(apath);
683 if (!dir) {
684 // try open file first
685 TString s(apath);
686 auto f = TFile::Open(s.Contains(":") ? TString(s(0, s.Index(":"))) : s);
687 if (f) {
688 if (!s.Contains(":"))
689 s += ":";
690 dir = gDirectory->GetDirectory(s);
691 if (dir) {
692 LoadFits(s);
693 return;
694 }
695 }
696 if (!dir) {
697 Error("LoadFits", "Path not found %s", apath);
698 return;
699 }
700 }
701
702 // assume for now all fits in given path will have the same pars
703 // so can just look at the float and const pars of first fit result to get all of them
704 // tuple is: parName, parValue, parAltValue (blank if nan)
705 // key represents the ufit values, value represents the sets of poi for the available cfits (subfits of the ufit)
706
707 std::map<std::set<std::tuple<std::string, double, std::string>>, std::set<std::set<std::string>>> cfits;
708 std::set<std::string> allpois;
709
710 int nFits = 0;
711 std::function<void(TDirectory *)> processDir;
712 processDir = [&](TDirectory *_dir) {
713 std::cout << "Processing " << _dir->GetName() << std::endl;
714 if (auto keys = _dir->GetListOfKeys(); keys) {
715 // first check if dir doesn't contain any RooLinkedList ... this identifies it as not an nll dir
716 // so treat any sub-dirs as new nll
717 bool isNllDir = false;
718 for (auto &&k : *keys) {
719 TKey *key = dynamic_cast<TKey *>(k);
720 if (strcmp(key->GetClassName(), "RooLinkedList") == 0) {
721 isNllDir = true;
722 break;
723 }
724 }
725
726 for (auto &&k : *keys) {
727 if (auto subdir = _dir->GetDirectory(k->GetName()); subdir) {
728 if (!isNllDir) {
729 LoadFits(subdir->GetPath());
730 } else {
731 processDir(subdir);
732 }
733 continue;
734 }
735 auto cl = TClass::GetClass(((TKey *)k)->GetClassName());
736 if (cl->InheritsFrom("RooFitResult")) {
737 if (auto cachedFit = _dir->Get<RooFitResult>(k->GetName()); cachedFit) {
738 nFits++;
739 if (nFits == 1) {
740 // for first fit add any missing float pars
741 std::unique_ptr<RooAbsCollection> snap(cachedFit->floatParsFinal().snapshot());
742 snap->remove(*fPars, true, true);
743 fPars->addClone(*snap);
744 // add also the non-string const pars
745 for (auto &p : cachedFit->constPars()) {
746 if (p->getAttribute("global"))
747 continue; // don't consider globals
748 auto v = dynamic_cast<RooAbsReal *>(p);
749 if (!v) {
750 continue;
751 };
752 if (!fPars->contains(*v))
753 fPars->addClone(*v);
754 }
755 }
756 // get names of all the floats
757 std::set<std::string> floatPars;
758 for (auto &p : cachedFit->floatParsFinal())
759 floatPars.insert(p->GetName());
760 // see if
761
762 // build a set of the const par values
763 std::set<std::tuple<std::string, double, std::string>> constPars;
764 for (auto &p : cachedFit->constPars()) {
765 if (p->getAttribute("global"))
766 continue; // don't consider globals when looking for cfits
767 auto v = dynamic_cast<RooAbsReal *>(p);
768 if (!v) {
769 continue;
770 };
771 constPars.insert(
772 std::make_tuple(v->GetName(), v->getVal(),
773 v->getStringAttribute("altVal") ? v->getStringAttribute("altVal") : ""));
774 }
775 // now see if this is a subset of any existing cfit ...
776 for (auto &&[key, value] : cfits) {
777 if (constPars == key)
778 continue; // ignore cases where we already recorded this list of constPars
779 if (std::includes(constPars.begin(), constPars.end(), key.begin(), key.end())) {
780 // usual case ... cachedFit has more constPars than one of the fits we have already encountered
781 // (the ufit)
782 // => cachedFit is a cfit of key fr ...
783 std::set<std::string> pois;
784 for (auto &&par : constPars) {
785 if (key.find(par) == key.end()) {
786 pois.insert(std::get<0>(par));
787 allpois.insert(std::get<0>(par));
788 }
789 }
790 if (!pois.empty()) {
791 cfits[constPars].insert(pois);
792 // std::cout << cachedFit->GetName() << " ";
793 // for(auto ff: constPars) std::cout << ff.first << "=" <<
794 // ff.second << " "; std::cout << std::endl;
795 }
796 }
797 /* FOR NOW we will skip cases where we encounter the cfit before the ufit - usually should eval the
798 ufit first
799 * else if (std::includes(key.begin(), key.end(), constPars.begin(), constPars.end())) {
800 // constPars are subset of key
801 // => key is a ufit of the cachedFit
802 // add all par names of key that aren't in constPars ... these are the poi
803 std::set<std::string> pois;
804 for (auto &&par: key) {
805 if (constPars.find(par) == constPars.end()) {
806 pois.insert(std::get<0>(par));
807 allpois.insert(std::get<0>(par));
808 }
809 }
810 if (!pois.empty()) {
811 std::cout << "found cfit BEFORE ufit??" << std::endl;
812 value.insert(pois);
813 }
814 } */
815 }
816 // ensure that this combination of constPars has entry in map,
817 // even if it doesn't end up with any poi identified from cfits to it
818 cfits[constPars];
819 delete cachedFit;
820 }
821 }
822 }
823 }
824 };
825 processDir(dir);
826 ::Info("xRooHypoSpace::xRooHypoSpace", "%s - Loaded %d fits", apath, nFits);
827
828 if (allpois.size() == 1) {
829 ::Info("xRooHypoSpace::xRooHypoSpace", "Detected POI: %s", allpois.begin()->c_str());
830
831 auto nll = std::make_shared<xRooNLLVar>(nullptr, nullptr);
832 auto dummyNll = std::make_shared<RooRealVar>(apath, "Dummy NLL", std::numeric_limits<double>::quiet_NaN());
833 nll->std::shared_ptr<RooAbsReal>::operator=(dummyNll);
834 dummyNll->setAttribute("readOnly");
835 // add pars as 'servers' on the dummy NLL
836 if (fPars) {
837 for (auto &&p : *fPars) {
838 dummyNll->addServer(
839 *p); // this is ok provided fPars (i.e. hypoSpace) stays alive as long as the hypoPoint ...
840 }
841 // flag poi
842 for (auto &p : allpois) {
843 fPars->find(p.c_str())->setAttribute("poi", true);
844 }
845 }
846 nll->reinitialize(); // triggers filling of par lists etc
847
848 for (auto &&[key, value] : cfits) {
849 if (value.find(allpois) != value.end()) {
850 // get the value of the poi in the key set
851 auto _coords = std::make_shared<RooArgSet>();
852 for (auto &k : key) {
853 auto v = _coords->addClone(RooRealVar(std::get<0>(k).c_str(), std::get<0>(k).c_str(), std::get<1>(k)));
854 v->setAttribute("poi", allpois.find(std::get<0>(k)) != allpois.end());
855 if (!std::get<2>(k).empty()) {
856 v->setStringAttribute("altVal", std::get<2>(k).c_str());
857 }
858 }
860 // hp.fPOIName = allpois.begin()->c_str();
861 // hp.fNullVal = _coords->getRealValue(hp.fPOIName.c_str());
862 hp.coords = _coords;
863 hp.nllVar = nll;
864
865 // auto altVal =
866 // hp.null_cfit()->constPars().find(hp.fPOIName.c_str())->getStringAttribute("altVal");
867 // if(altVal) hp.fAltVal = TString(altVal).Atof();
868 // else hp.fAltVal = std::numeric_limits<double>::quiet_NaN();
869
870 // decide based on values
871 if (std::isnan(hp.fAltVal()))
873 else if (hp.fNullVal() >= hp.fAltVal())
875 else
877
878 emplace_back(hp);
879 }
880 }
881 } else if (nFits > 0) {
882 std::cout << "possible POI: ";
883 for (auto p : allpois)
884 std::cout << p << ",";
885 std::cout << std::endl;
886 }
887}
888
890{
891
892 auto _axes = axes();
893
894 size_t badFits = 0;
895
896 for (size_t i = 0; i < size(); i++) {
897 std::cout << i << ") ";
898 for (auto a : _axes) {
899 if (a != _axes.first())
900 std::cout << ",";
901 std::cout << a->GetName() << "="
902 << at(i).coords->getRealValue(a->GetName(), std::numeric_limits<double>::quiet_NaN());
903 }
904 std::cout << " status=[ufit:";
905 auto ufit = const_cast<xRooHypoPoint &>(at(i)).ufit(true);
906 if (!ufit)
907 std::cout << "-";
908 else {
909 std::cout << ufit->status();
910 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(ufit->status()) == 0);
911 }
912 std::cout << ",cfit_null:";
913 auto cfit = const_cast<xRooHypoPoint &>(at(i)).cfit_null(true);
914 if (!cfit)
915 std::cout << "-";
916 else {
917 std::cout << cfit->status();
918 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(cfit->status()) == 0);
919 }
920 std::cout << ",cfit_alt:";
921 auto afit = const_cast<xRooHypoPoint &>(at(i)).cfit_alt(true);
922 if (!afit)
923 std::cout << "-";
924 else {
925 std::cout << afit->status();
926 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(afit->status()) == 0);
927 }
928 if (auto asiPoint = const_cast<xRooHypoPoint &>(at(i)).asimov(true)) {
929 std::cout << ",asimov.ufit:";
930 auto asi_ufit = asiPoint->ufit(true);
931 if (!asi_ufit) {
932 std::cout << "-";
933 } else {
934 std::cout << asi_ufit->status();
935 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(asi_ufit->status()) == 0);
936 }
937 std::cout << ",asimov.cfit_null:";
938 auto asi_cfit = asiPoint->cfit_null(true);
939 if (!asi_cfit) {
940 std::cout << "-";
941 } else {
942 std::cout << asi_cfit->status();
943 badFits += (xRooNLLVar::xRooHypoPoint::allowedStatusCodes.count(asi_cfit->status()) == 0);
944 }
945 }
946 std::cout << "]";
947 auto sigma_mu = const_cast<xRooHypoPoint &>(at(i)).sigma_mu(true);
948 if (!std::isnan(sigma_mu.first)) {
949 std::cout << " sigma_mu=" << sigma_mu.first;
950 if (sigma_mu.second)
951 std::cout << " +/- " << sigma_mu.second;
952 }
953 std::cout << std::endl;
954 }
955 std::cout << "--------------------------" << std::endl;
956 std::cout << "Number of bad fits: " << badFits << std::endl;
957}
958
959std::shared_ptr<TGraphErrors> xRooNLLVar::xRooHypoSpace::graph(
960 const char *opt /*, const std::function<void(xRooNLLVar::xRooHypoSpace*)>& progress*/) const
961{
962
963 TString sOpt(opt);
964 sOpt.ToLower();
965
966 bool doCLs = sOpt.Contains("cls");
967 bool readOnly = sOpt.Contains("readonly");
968 bool visualize = sOpt.Contains("visualize") && !readOnly;
969
970 double nSigma =
971 (sOpt.Contains("exp"))
972 ? (TString(sOpt(sOpt.Index("exp") + 3,
973 sOpt.Index(" ", sOpt.Index("exp")) == -1 ? sOpt.Length() : sOpt.Index(" ", sOpt.Index("exp"))))
974 .Atof())
975 : std::numeric_limits<double>::quiet_NaN();
976 bool expBand =
977 !std::isnan(nSigma) && nSigma && !(sOpt(sOpt.Index("exp") + 3) == '+' || sOpt(sOpt.Index("exp") + 3) == '-');
978
979 auto _axes = axes();
980 if (_axes.size() != 1)
981 return nullptr;
982
983 auto out = std::make_shared<TGraphErrors>();
984 out->SetName(GetName());
985 out->SetEditable(false);
986 const char *sCL = (doCLs) ? "CLs" : "null";
987
988 TString title =
989 TString::Format("%s;%s;p_{%s}",
990 (std::isnan(nSigma))
991 ? "Observed"
992 : (!nSigma ? "Expected"
993 : TString::Format("%s%d#sigma Expected",
994 expBand || !nSigma ? "" : ((nSigma < 0) ? "-" : "+"), int(nSigma))
995 .Data()),
996 _axes.at(0)->GetTitle(), sCL);
997
998 if (std::isnan(nSigma)) {
999 out->SetNameTitle(TString::Format("obs_p%s", sCL), title);
1000 out->SetMarkerStyle(20);
1001 out->SetMarkerSize(0.5);
1002 if (sOpt.Contains("ts"))
1003 out->SetNameTitle("obs_ts", TString::Format("Observed;%s;%s", _axes.at(0)->GetTitle(),
1004 (empty() ? "" : front().tsTitle(true).Data())));
1005 } else {
1006 out->SetNameTitle(TString::Format("exp%d_p%s", int(nSigma), sCL), title);
1007 out->SetMarkerStyle(0);
1008 out->SetMarkerSize(0);
1009 out->SetLineStyle(2 + int(nSigma));
1010 if (expBand && nSigma) {
1011 out->SetFillColor((nSigma == 2) ? kYellow : kGreen);
1012 // out->SetFillStyle(3005);
1013 out->SetLineStyle(0);
1014 out->SetLineWidth(0);
1015 auto x = out->Clone("up");
1016 x->SetBit(kCanDelete);
1017 dynamic_cast<TAttFill *>(x)->SetFillStyle(nSigma == 2 ? 3005 : 3004);
1018 dynamic_cast<TAttFill *>(x)->SetFillColor(kBlack);
1019 out->GetListOfFunctions()->Add(x, "F");
1020 x = out->Clone("down");
1021 x->SetBit(kCanDelete);
1022 // dynamic_cast<TAttFill*>(x)->SetFillColor((nSigma==2) ? kYellow : kGreen);
1023 // dynamic_cast<TAttFill*>(x)->SetFillStyle(1001);
1024 out->GetListOfFunctions()->Add(x, "F");
1025 }
1026 if (sOpt.Contains("ts"))
1027 out->SetNameTitle(TString::Format("exp_ts%d", int(nSigma)),
1028 TString::Format("Expected;%s;%s", _axes.at(0)->GetTitle(), front().tsTitle(true).Data()));
1029 }
1030
1031 auto badPoints = [&]() {
1032 auto badPoints2 = dynamic_cast<TGraph *>(out->GetListOfFunctions()->FindObject("badPoints"));
1033 if (!badPoints2) {
1034 badPoints2 = new TGraph;
1035 badPoints2->SetBit(kCanDelete);
1036 badPoints2->SetName("badPoints");
1037 badPoints2->SetMarkerStyle(5);
1038 badPoints2->SetMarkerColor(std::isnan(nSigma) ? kRed : kBlue);
1039 badPoints2->SetMarkerSize(1);
1040 out->GetListOfFunctions()->Add(badPoints2, "P");
1041 }
1042 return badPoints2;
1043 };
1044 int nPointsDown = 0;
1045 bool above = true;
1046 TStopwatch s;
1047 s.Start();
1048 size_t nDone = 0;
1049 for (auto &p : *this) {
1050 if (s.RealTime() > 5) {
1051 if (visualize) {
1052 // draw readonly version of the graph
1053 auto gra = graph(sOpt + " readOnly");
1054 if (gra && gra->GetN()) {
1055 if (!gPad && gROOT->GetSelectedPad())
1056 gROOT->GetSelectedPad()->cd();
1057 if (gPad)
1058 gPad->Clear();
1059 gra->DrawClone(expBand ? "AF" : "ALP")->SetBit(kCanDelete);
1060#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1061 if (auto pad = gROOT->GetSelectedPad()) {
1062 pad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1063 }
1064#endif
1066 }
1067 } else {
1068 ::Info("xRooHypoSpace::graph", "Completed %d/%d points for %s", int(nDone), int(size()), sOpt.Data());
1069 }
1070 s.Start();
1071 } else {
1072 s.Continue();
1073 }
1074 double _x = p.coords->getRealValue(_axes.at(0)->GetName(), std::numeric_limits<double>::quiet_NaN());
1075 auto pval = const_cast<xRooHypoPoint &>(p).getVal(sOpt);
1076 auto idx = out->GetN() - nPointsDown;
1077
1078 if (std::isnan(pval.first)) {
1079 if (p.status() != 0) { // if status is 0 then bad pval is really just absence of fits, not bad fits
1080 badPoints()->SetPoint(badPoints()->GetN(), _x, 0);
1081 }
1082 } else {
1083 out->InsertPointBefore(idx, _x, pval.first);
1084 out->SetPointError(idx, 0, pval.second);
1085 }
1086
1087 if (expBand && nSigma) {
1088 TString sOpt2 = sOpt;
1089 sOpt2.ReplaceAll("exp", "exp-");
1090 pval = const_cast<xRooHypoPoint &>(p).getVal(sOpt2);
1091 if (std::isnan(pval.first)) {
1092 if (p.status() != 0) { // if status is 0 then bad pval is really just absence of fits, not bad fits
1093 badPoints()->SetPoint(badPoints()->GetN(), _x, 0);
1094 }
1095 } else {
1096 out->InsertPointBefore(idx + 1, _x, pval.first);
1097 out->SetPointError(idx + 1, 0, pval.second);
1098 nPointsDown++;
1099 if (out->GetPointY(idx) < pval.first)
1100 above = false; // the -sigma points are actually above +sigma
1101 }
1102 }
1103 nDone++;
1104 }
1105
1106 if (out->GetN() == 0)
1107 return out;
1108
1109 if (!expBand) {
1110 out->Sort();
1111 if (out->GetListOfFunctions()->FindObject("badPoints")) {
1112 // try to interpolate the points
1113 for (int i = 0; i < badPoints()->GetN(); i++) {
1114 badPoints()->SetPointY(i, out->Eval(badPoints()->GetPointX(i)));
1115 }
1116 }
1117 } else {
1118 out->Sort(&TGraph::CompareX, true, 0, out->GetN() - nPointsDown - 1); // sort first half
1119 out->Sort(&TGraph::CompareX, false, out->GetN() - nPointsDown, out->GetN() - 1); // reverse sort second half
1120
1121 // now populate the up and down values
1122 auto up = dynamic_cast<TGraph *>(out->GetListOfFunctions()->FindObject("up"));
1123 auto down = dynamic_cast<TGraph *>(out->GetListOfFunctions()->FindObject("down"));
1124
1125 for (int i = 0; i < out->GetN(); i++) {
1126 if (i < out->GetN() - nPointsDown) {
1127 up->SetPoint(up->GetN(), out->GetPointX(i), out->GetPointY(i) + out->GetErrorY(i) * (above ? 1. : -1.));
1128 down->SetPoint(down->GetN(), out->GetPointX(i), out->GetPointY(i) - out->GetErrorY(i) * (above ? 1. : -1.));
1129 } else {
1130 up->SetPoint(up->GetN(), out->GetPointX(i), out->GetPointY(i) - out->GetErrorY(i) * (above ? 1. : -1.));
1131 down->SetPoint(down->GetN(), out->GetPointX(i), out->GetPointY(i) + out->GetErrorY(i) * (above ? 1. : -1.));
1132 }
1133 }
1134 }
1135
1136 if (visualize) {
1137 // draw result
1138 if (!gPad && gROOT->GetSelectedPad())
1139 gROOT->GetSelectedPad()->cd();
1140 if (gPad)
1141 gPad->Clear();
1142 out->DrawClone(expBand ? "AF" : "ALP")->SetBit(kCanDelete);
1143#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1144 if (auto pad = gROOT->GetSelectedPad()) {
1145 pad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1146 }
1147#endif
1149 }
1150
1151 return out;
1152}
1153
1154std::shared_ptr<TMultiGraph> xRooNLLVar::xRooHypoSpace::graphs(const char *opt)
1155{
1156 TString sOpt(opt);
1157 sOpt.ToLower();
1158 std::shared_ptr<TMultiGraph> out;
1159 if (sOpt.Contains("pcls") || sOpt.Contains("pnull") || sOpt.Contains("ts")) {
1160
1161 bool visualize = sOpt.Contains("visualize");
1162 sOpt.ReplaceAll("visualize", "");
1163
1164 auto exp2 = graph(sOpt + " exp2");
1165 auto exp1 = graph(sOpt + " exp1");
1166 auto exp = graph(sOpt + " exp");
1167 bool doObs = true;
1168 // for(auto& hp : *this) { if(hp.isExpected) {doObs=false; break;} }
1169 auto obs = (doObs) ? graph(sOpt) : nullptr;
1170
1171 out = std::make_shared<TMultiGraph>(GetName(), GetTitle());
1172 if (exp2 && exp2->GetN() > 1)
1173 out->Add(static_cast<TGraph *>(exp2->Clone()), "FP");
1174 if (exp1 && exp1->GetN() > 1)
1175 out->Add(static_cast<TGraph *>(exp1->Clone()), "FP");
1176 if (exp && exp->GetN() > 1)
1177 out->Add(static_cast<TGraph *>(exp->Clone()), "LP");
1178 if (obs && obs->GetN() > 1)
1179 out->Add(static_cast<TGraph *>(obs->Clone()), "LP");
1180
1181 if (!out->GetListOfGraphs()) {
1182 return nullptr;
1183 }
1184
1185 TGraph *testedPoints = nullptr;
1186 if (sOpt.Contains("pcls")) {
1187 TGraph *line = new TGraph;
1188 line->SetName("alpha");
1189 line->SetLineStyle(2);
1190 line->SetEditable(false);
1191 line->SetPoint(line->GetN(), out->GetHistogram()->GetXaxis()->GetXmin() - 10, 0.05);
1192 testedPoints = new TGraph;
1193 testedPoints->SetName("hypoPoints");
1194 testedPoints->SetEditable(false);
1195 testedPoints->SetMarkerStyle(24);
1196 testedPoints->SetMarkerSize(0.4); // use line to indicate tested points
1197 if (exp) {
1198 for (int i = 0; i < exp->GetN(); i++) {
1199 testedPoints->SetPoint(testedPoints->GetN(), exp->GetPointX(i), 0.05);
1200 }
1201 }
1202 line->SetPoint(line->GetN(), out->GetHistogram()->GetXaxis()->GetXmax() + 10, 0.05);
1204 out->GetListOfFunctions()->Add(line, "L");
1205 }
1206 if (exp) {
1207 out->GetHistogram()->GetXaxis()->SetTitle(exp->GetHistogram()->GetXaxis()->GetTitle());
1208 out->GetHistogram()->GetYaxis()->SetTitle(exp->GetHistogram()->GetYaxis()->GetTitle());
1209 }
1210 auto leg = new TLegend(1. - gStyle->GetPadRightMargin() - 0.3, 1. - gStyle->GetPadTopMargin() - 0.35,
1211 1. - gStyle->GetPadRightMargin() - 0.05, 1. - gStyle->GetPadTopMargin() - 0.05);
1212 leg->SetName("legend");
1213 leg->SetBit(kCanDelete);
1214
1215 out->GetListOfFunctions()->Add(leg);
1216 // out->GetListOfFunctions()->Add(out->GetHistogram()->Clone(".axis"),"sameaxis"); // redraw axis
1217
1218 for (auto g : *out->GetListOfGraphs()) {
1219 if (auto o = dynamic_cast<TGraph *>(g)->GetListOfFunctions()->FindObject("down")) {
1220 leg->AddEntry(o, "", "F");
1221 } else {
1222 leg->AddEntry(g, "", "LPE");
1223 }
1224 }
1225
1226 if (sOpt.Contains("pcls")) {
1227 // add current limit estimates to legend
1228 if (exp2 && exp2->GetN() > 1) {
1229 auto l = xRooFit::matchPrecision(GetLimit(*graph(sOpt + "exp-2")));
1230 leg->AddEntry((TObject *)nullptr, TString::Format("-2#sigma: %g +/- %g", l.first, l.second), "");
1231 }
1232 if (exp1 && exp1->GetN() > 1) {
1233 auto l = xRooFit::matchPrecision(GetLimit(*graph(sOpt + "exp-1")));
1234 leg->AddEntry((TObject *)nullptr, TString::Format("-1#sigma: %g +/- %g", l.first, l.second), "");
1235 }
1236 if (exp && exp->GetN() > 1) {
1237 auto l = xRooFit::matchPrecision(GetLimit(*exp));
1238 leg->AddEntry((TObject *)nullptr, TString::Format("0#sigma: %g +/- %g", l.first, l.second), "");
1239 }
1240 if (exp1 && exp1->GetN() > 1) {
1241 auto l = xRooFit::matchPrecision(GetLimit(*graph(sOpt + "exp+1")));
1242 leg->AddEntry((TObject *)nullptr, TString::Format("+1#sigma: %g +/- %g", l.first, l.second), "");
1243 }
1244 if (exp2 && exp2->GetN() > 1) {
1245 auto l = xRooFit::matchPrecision(GetLimit(*graph(sOpt + "exp+2")));
1246 leg->AddEntry((TObject *)nullptr, TString::Format("+2#sigma: %g +/- %g", l.first, l.second), "");
1247 }
1248 if (obs && obs->GetN() > 1) {
1249 auto l = xRooFit::matchPrecision(GetLimit(*obs));
1250 leg->AddEntry((TObject *)nullptr, TString::Format("Observed: %g +/- %g", l.first, l.second), "");
1251 }
1252 }
1253 if (testedPoints)
1254 out->Add(testedPoints, "P");
1255
1256 if (visualize) {
1257 if (!gPad && gROOT->GetSelectedPad())
1258 gROOT->GetSelectedPad()->cd();
1259 if (gPad)
1260 gPad->Clear();
1261 auto gra2 = static_cast<TMultiGraph *>(out->DrawClone("A"));
1262 gra2->SetBit(kCanDelete);
1263 if (sOpt.Contains("pcls") || sOpt.Contains("pnull")) {
1264 gra2->GetHistogram()->SetMinimum(1e-6);
1265 }
1266 if (gPad) {
1267 gPad->RedrawAxis();
1268 gPad->GetCanvas()->Paint();
1269 gPad->GetCanvas()->Update();
1270#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1271 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1272#endif
1273 }
1275 }
1276 }
1277
1278 return out;
1279}
1280
1281std::pair<double, double> xRooNLLVar::xRooHypoSpace::GetLimit(const TGraph &pValues, double target)
1282{
1283
1284 if (std::isnan(target)) {
1285 target = 1. - gEnv->GetValue("xRooHypoSpace.CL", 95.) / 100.;
1286 }
1287
1288 auto gr = std::make_shared<TGraph>(pValues);
1289 // remove any nan points and duplicates
1290 int i = 0;
1291 std::set<double> existingX;
1292 while (i < gr->GetN()) {
1293 if (std::isnan(gr->GetPointY(i)))
1294 gr->RemovePoint(i);
1295 else if (existingX.find(gr->GetPointX(i)) != existingX.end()) {
1296 gr->RemovePoint(i);
1297 } else {
1298 existingX.insert(gr->GetPointX(i));
1299 // convert to log ....
1300 gr->SetPointY(i, log(std::max(gr->GetPointY(i), 1e-10)));
1301 i++;
1302 }
1303 }
1304
1305 gr->Sort();
1306
1307 // simple linear extrapolation to critical value ... return nan if problem
1308 if (gr->GetN() < 2) {
1309 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0);
1310 }
1311
1312 double alpha = log(target);
1313
1314 bool above = gr->GetPointY(0) > alpha;
1315 for (int ii = 1; ii < gr->GetN(); ii++) {
1316 if ((above && (gr->GetPointY(ii) <= alpha)) || (!above && (gr->GetPointY(ii) >= alpha))) {
1317 // found the limit ... return linearly extrapolated point
1318 double lim = gr->GetPointX(ii - 1) + (gr->GetPointX(ii) - gr->GetPointX(ii - 1)) *
1319 (alpha - gr->GetPointY(ii - 1)) /
1320 (gr->GetPointY(ii) - gr->GetPointY(ii - 1));
1321 // use points either side as error
1322 double err = std::max(lim - gr->GetPointX(ii - 1), gr->GetPointX(ii) - lim);
1323 // give err negative sign to indicate if error due to negative side
1324 if ((lim - gr->GetPointX(ii - 1)) > (gr->GetPointX(ii) - lim))
1325 err *= -1;
1326 return std::pair(lim, err);
1327 }
1328 }
1329 // if reach here need to extrapolate ...
1330 if ((above && gr->GetPointY(gr->GetN() - 1) <= gr->GetPointY(0)) ||
1331 (!above && gr->GetPointY(gr->GetN() - 1) >= gr->GetPointY(0))) {
1332 // extrapolating above based on last two points
1333 // in fact, if 2nd last point is a p=1 (log(p)=0) then go back
1334 int offset = 2;
1335 while (offset < gr->GetN() && gr->GetPointY(gr->GetN() - offset) == 0)
1336 offset++;
1337 double x1 = gr->GetPointX(gr->GetN() - offset);
1338 double y1 = gr->GetPointY(gr->GetN() - offset);
1339 double m = (gr->GetPointY(gr->GetN() - 1) - y1) / (gr->GetPointX(gr->GetN() - 1) - x1);
1340 if (m == 0.)
1341 return std::pair(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity());
1342 return std::pair((alpha - y1) / m + x1, std::numeric_limits<double>::infinity());
1343 } else {
1344 // extrapolating below based on first two points
1345 double x1 = gr->GetPointX(0);
1346 double y1 = gr->GetPointY(0);
1347 double m = (gr->GetPointY(1) - y1) / (gr->GetPointX(1) - x1);
1348 if (m == 0.)
1349 return std::pair(-std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity());
1350 return std::pair((alpha - y1) / m + x1, -std::numeric_limits<double>::infinity());
1351 }
1352}
1353
1355{
1356 TString sOpt = TString::Format("p%s", type);
1357 if (std::isnan(nSigma)) {
1358 sOpt += "obs";
1359 } else {
1360 sOpt += TString::Format("exp%s%d", nSigma > 0 ? "+" : "", int(nSigma));
1361 }
1362 return GetLimit(*graph(sOpt + " readonly"));
1363}
1364
1366xRooNLLVar::xRooHypoSpace::findlimit(const char *opt, double relUncert, unsigned int maxTries)
1367{
1368 TString sOpt(opt);
1369 bool visualize = sOpt.Contains("visualize");
1370 sOpt.ReplaceAll("visualize", "");
1371 std::shared_ptr<TGraphErrors> gr = graph(sOpt + " readonly");
1372 if (visualize) {
1373 auto gra = graphs(sOpt.Contains("toys") ? "pcls readonly toys" : "pcls readonly");
1374 if (gra) {
1375 if (!gPad)
1376 gra->Draw(); // in 6.28 DrawClone wont make the gPad defined :( ... so Draw then clear and Draw Clone
1377 gPad->Clear();
1378 gra->DrawClone("A")->SetBit(kCanDelete);
1379 gPad->RedrawAxis();
1380 gra->GetHistogram()->SetMinimum(1e-9);
1381 gra->GetHistogram()->GetYaxis()->SetRangeUser(1e-9, 1);
1382 gPad->Modified();
1383#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1384 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1385#endif
1387 }
1388 }
1389
1390 // resync parameter boundaries from nlls (may have been modified by fits)
1391 for (auto p : axes()) {
1392 for (auto &[pdf, nll] : fNlls) {
1393 if (auto _v = dynamic_cast<RooRealVar *>(nll->pars()->find(*p))) {
1394 dynamic_cast<RooRealVar *>(p)->setRange(_v->getMin(), _v->getMax());
1395 }
1396 }
1397 }
1398
1399 if (!gr || gr->GetN() < 2) {
1400 auto v = (axes().empty()) ? nullptr : dynamic_cast<RooRealVar *>(*axes().rbegin());
1401 if (!v)
1402 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0.);
1403 double muMax = std::min(std::min(v->getMax("physical"), v->getMax()), v->getMax("scan"));
1404 double muMin = std::max(std::max(v->getMin("physical"), v->getMin()), v->getMin("scan"));
1405 if (!gr || gr->GetN() < 1) {
1406 if (maxTries == 0 || std::isnan(AddPoint(TString::Format("%s=%g", v->GetName(), muMin)).getVal(sOpt).first)) {
1407 // first point failed ... give up
1408 Error("findlimit", "Problem evaluating %s @ %s=%g", sOpt.Data(), v->GetName(), muMin);
1409 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0.);
1410 }
1411 gr.reset();
1412 return findlimit(opt, relUncert, maxTries - 1); // do this to resync parameter limits
1413 }
1414
1415 // can approximate expected limit using
1416 // mu_hat + sigma_mu*ROOT::Math::gaussian_quantile(1.-alpha/2.,1) for cls
1417 // or mu_hat + sigma_mu*ROOT::Math::gaussian_quantile((1.-alpha),1) for cls+b
1418 // get a very first estimate of sigma_mu from ufit to expected data, take error on mu as sigma_mu
1419 double nextPoint = muMin + (muMax - muMin) / 50;
1420
1421 // if done an expected limit, assume data is like expected and choose expected limit point as first test point
1422 if (sOpt.Contains("obs")) {
1423 TString sOpt2 = sOpt;
1424 sOpt2.ReplaceAll("obs", "exp");
1425 auto expLim = findlimit(sOpt2, std::numeric_limits<double>::infinity(), 0);
1426 if (!std::isnan(expLim.first) && expLim.first < nextPoint)
1427 nextPoint = expLim.first;
1428 }
1429
1430 auto point =
1431 (sOpt.Contains("exp")) ? back().asimov() : std::shared_ptr<xRooHypoPoint>(&back(), [](xRooHypoPoint *) {});
1432 point = nullptr;
1433 if (point && point->ufit()) {
1434 double rough_sigma_mu = point->mu_hat().getError();
1435 double another_estimate = point->mu_hat().getVal() + rough_sigma_mu * ROOT::Math::gaussian_quantile(0.95, 1);
1436 // if (another_estimate < nextPoint) {
1437 nextPoint = another_estimate;
1438 ::Info("xRooHypoSpace::findlimit", "Guessing %g based on rough sigma_mu = %g", nextPoint, rough_sigma_mu);
1439 //}
1440 }
1441
1442 if (maxTries == 0 || std::isnan(AddPoint(TString::Format("%s=%g", v->GetName(), nextPoint)).getVal(sOpt).first)) {
1443 // second point failed ... give up
1444 Error("findlimit", "Problem evaluating %s @ %s=%g", sOpt.Data(), v->GetName(), nextPoint);
1445 return std::pair(std::numeric_limits<double>::quiet_NaN(), 0.);
1446 }
1447 gr.reset();
1448 return findlimit(opt, relUncert, maxTries - 1);
1449 }
1450
1451 auto lim = GetLimit(*gr);
1452
1453 if (std::isnan(lim.first)) {
1454 return lim;
1455 }
1456
1457 auto v = dynamic_cast<RooRealVar *>(*axes().rbegin());
1458 double maxMu = std::min(std::min(v->getMax("physical"), v->getMax()), v->getMax("scan"));
1459 double minMu = std::max(std::max(v->getMin("physical"), v->getMin()), v->getMin("scan"));
1460
1461 // static double MIN_LIMIT_UNCERT = 1e-4; // stop iterating once uncert gets this small
1462 if (lim.first > -std::numeric_limits<double>::infinity() && lim.first < std::numeric_limits<double>::infinity() &&
1463 (std::abs(lim.second) <= relUncert * std::abs(lim.first) /* || std::abs(lim.second)<MIN_LIMIT_UNCERT*/))
1464 return lim;
1465
1466 double nextPoint;
1467
1468 if (lim.second == std::numeric_limits<double>::infinity()) {
1469 // limit was found by extrapolating to right
1470 nextPoint = lim.first;
1471 if (nextPoint == std::numeric_limits<double>::infinity() || nextPoint > maxMu) {
1472 nextPoint = gr->GetPointX(gr->GetN() - 1) + (maxMu - minMu) / 50;
1473 }
1474
1475 // prefer extrapolation with sigma_mu, if available, if it takes us further
1476 // as shape of p-value curve is usually
1477 auto point =
1478 (sOpt.Contains("exp")) ? back().asimov() : std::shared_ptr<xRooHypoPoint>(&back(), [](xRooHypoPoint *) {});
1479 point = nullptr;
1480 if (point && point->ufit()) {
1481 double rough_sigma_mu = point->mu_hat().getError();
1482 double another_estimate = point->mu_hat().getVal() + rough_sigma_mu * ROOT::Math::gaussian_quantile(0.95, 1);
1483 // if (another_estimate < nextPoint) {
1484 nextPoint = std::max(nextPoint, another_estimate);
1485 ::Info("xRooHypoSpace::findlimit", "Guessing %g based on rough sigma_mu = %g", nextPoint, rough_sigma_mu);
1486 //}
1487 }
1488 nextPoint = std::min(nextPoint + nextPoint * relUncert * 0.99, maxMu); // ensure we step over location if possible
1489
1490 if (nextPoint > maxMu)
1491 return lim;
1492 } else if (lim.second == -std::numeric_limits<double>::infinity()) {
1493 // limit from extrapolating to left
1494 nextPoint = lim.first;
1495 if (nextPoint < minMu)
1496 nextPoint = gr->GetPointX(0) - (maxMu - minMu) / 50;
1497 if (nextPoint < minMu)
1498 return lim;
1499 } else {
1500 nextPoint = lim.first + lim.second * relUncert * 0.99;
1501 }
1502
1503 // got here need a new point .... evaluate the estimated lim location +/- the relUncert (signed error takes care of
1504 // direction)
1505
1506 ::Info("xRooHypoSpace::findlimit", "%s -- Testing new point @ %s=%g (delta=%g)", sOpt.Data(), v->GetName(),
1507 nextPoint, lim.second);
1508 if (maxTries == 0 || std::isnan(AddPoint(TString::Format("%s=%g", v->GetName(), nextPoint)).getVal(sOpt).first)) {
1509 if (maxTries == 0) {
1510 Warning("findlimit", "Reached max number of point evaluations");
1511 } else {
1512 Error("findlimit", "Problem evaluating %s @ %s=%g", sOpt.Data(), v->GetName(), nextPoint);
1513 }
1514 return lim;
1515 }
1516 gr.reset();
1517 return findlimit(opt, relUncert, maxTries - 1);
1518}
1519
1521{
1522
1523 TString sOpt(opt);
1524 sOpt.ToLower();
1525
1526 if ((sOpt == "" || sOpt == "same") && !empty()) {
1527 if (front().fPllType == xRooFit::Asymptotics::OneSidedPositive) {
1528 sOpt += "pcls"; // default to showing cls p-value scan if drawing a limit
1529 for (auto &hp : *this) {
1530 if (hp.nullToys.size() || hp.altToys.size()) {
1531 sOpt += " toys";
1532 break; // default to toys if done toys
1533 }
1534 }
1535 } else if (front().fPllType == xRooFit::Asymptotics::TwoSided) {
1536 sOpt += "ts";
1537 }
1538 }
1539
1540 // split up by ; and call Draw for each (with 'same' appended)
1541 auto _axes = axes();
1542 if (_axes.empty())
1543 return;
1544
1545 if (sOpt == "status") {
1546 // draw the points in the space
1547 if (_axes.size() <= 2) {
1548 TGraphErrors *out = new TGraphErrors;
1549 out->SetBit(kCanDelete);
1550 out->SetName("points");
1551 out->SetMarkerSize(0.5);
1552 TGraph *tsAvail = new TGraph;
1553 tsAvail->SetName("ts");
1554 tsAvail->SetBit(kCanDelete);
1555 tsAvail->SetMarkerStyle(20);
1556 TGraph *expAvail = new TGraph;
1557 expAvail->SetName("exp");
1558 expAvail->SetBit(kCanDelete);
1559 expAvail->SetMarkerStyle(25);
1560 expAvail->SetMarkerSize(out->GetMarkerSize() * 1.5);
1561 TGraph *badPoints = new TGraph;
1562 badPoints->SetName("bad_ufit");
1563 badPoints->SetBit(kCanDelete);
1564 badPoints->SetMarkerStyle(5);
1565 badPoints->SetMarkerColor(kRed);
1566 badPoints->SetMarkerSize(out->GetMarkerSize());
1567 TGraph *badPoints2 = new TGraph;
1568 badPoints2->SetName("bad_cfit_null");
1569 badPoints2->SetBit(kCanDelete);
1570 badPoints2->SetMarkerStyle(2);
1571 badPoints2->SetMarkerColor(kRed);
1572 badPoints2->SetMarkerSize(out->GetMarkerSize());
1573
1574 out->SetTitle(TString::Format("%s;%s;%s", GetTitle(), _axes.at(0)->GetTitle(),
1575 (_axes.size() == 1) ? "" : _axes.at(1)->GetTitle()));
1576 for (auto &p : *this) {
1577 bool _readOnly = p.nllVar ? p.nllVar->get()->getAttribute("readOnly") : false;
1578 if (p.nllVar)
1579 p.nllVar->get()->setAttribute("readOnly", true);
1580 double x = p.coords->getRealValue(_axes.at(0)->GetName());
1581 double y = _axes.size() == 1 ? p.ts_asymp().first : p.coords->getRealValue(_axes.at(1)->GetName());
1582 out->SetPoint(out->GetN(), x, y);
1583 if (!std::isnan(p.ts_asymp().first)) {
1584 if (_axes.size() == 1)
1585 out->SetPointError(out->GetN() - 1, 0, p.ts_asymp().second);
1586 tsAvail->SetPoint(tsAvail->GetN(), x, y);
1587 } else if (p.fUfit && (std::isnan(p.fUfit->minNll()) ||
1588 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.fUfit->status()) ==
1590 badPoints->SetPoint(badPoints->GetN(), x, y);
1591 } else if (p.fNull_cfit && (std::isnan(p.fNull_cfit->minNll()) ||
1592 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.fNull_cfit->status()) ==
1594 badPoints2->SetPoint(badPoints2->GetN(), x, y);
1595 }
1596 if (!std::isnan(p.ts_asymp(0).first)) {
1597 expAvail->SetPoint(expAvail->GetN(), x, y);
1598 } else if (p.asimov() && p.asimov()->fUfit &&
1599 (std::isnan(p.asimov()->fUfit->minNll()) ||
1600 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.asimov()->fUfit->status()) ==
1602
1603 } else if (p.asimov() && p.asimov()->fNull_cfit &&
1604 (std::isnan(p.asimov()->fNull_cfit->minNll()) ||
1605 xRooNLLVar::xRooHypoPoint::allowedStatusCodes.find(p.asimov()->fNull_cfit->status()) ==
1607 }
1608 if (p.nllVar)
1609 p.nllVar->get()->setAttribute("readOnly", _readOnly);
1610 }
1611
1612 if (_axes.size() == 1) {
1613 TGraph tmp;
1614 for (int i = 0; i < out->GetN(); i++) {
1615 if (!std::isnan(out->GetPointY(i)))
1616 tmp.SetPoint(tmp.GetN(), out->GetPointX(i), out->GetPointY(i));
1617 }
1618 auto fixPoints = [&](TGraph *g) {
1619 for (int i = 0; i < g->GetN(); i++) {
1620 if (std::isnan(g->GetPointY(i)))
1621 g->SetPointY(i, std::isnan(tmp.Eval(g->GetPointX(i))) ? 0. : tmp.Eval(g->GetPointX(i)));
1622 }
1623 };
1624 fixPoints(out);
1625 fixPoints(tsAvail);
1626 fixPoints(expAvail);
1627 fixPoints(badPoints);
1628 fixPoints(badPoints2);
1629 }
1630
1631 out->SetMarkerStyle(4);
1632 out->Draw("AP");
1633 auto leg = new TLegend(1. - gPad->GetRightMargin() - 0.3, 1. - gPad->GetTopMargin() - 0.35,
1634 1. - gPad->GetRightMargin() - 0.05, 1. - gPad->GetTopMargin() - 0.05);
1635 leg->SetName("legend");
1636 leg->AddEntry(out, "Uncomputed", "P");
1637
1638 if (tsAvail->GetN()) {
1639 out->GetListOfFunctions()->Add(tsAvail, "P");
1640 leg->AddEntry(tsAvail, "Computed", "P");
1641 } else {
1642 delete tsAvail;
1643 }
1644 if (expAvail->GetN()) {
1645 out->GetListOfFunctions()->Add(expAvail, "P");
1646 leg->AddEntry(expAvail, "Expected computed", "P");
1647 } else {
1648 delete expAvail;
1649 }
1650 if (badPoints->GetN()) {
1651 out->GetListOfFunctions()->Add(badPoints, "P");
1652 leg->AddEntry(badPoints, "Bad ufit", "P");
1653 } else {
1654 delete badPoints;
1655 }
1656 if (badPoints2->GetN()) {
1657 out->GetListOfFunctions()->Add(badPoints2, "P");
1658 leg->AddEntry(badPoints2, "Bad null cfit", "P");
1659 } else {
1660 delete badPoints2;
1661 }
1663 leg->Draw();
1664 // if(_axes.size()==1) out->GetHistogram()->GetYaxis()->SetRangeUser(0,1);
1665 gPad->SetGrid(true, _axes.size() > 1);
1666 if (_axes.size() == 1)
1667 gPad->SetLogy(false);
1668 }
1669
1671
1672 return;
1673 }
1674
1675 if (sOpt.Contains("pcls") || sOpt.Contains("pnull") || sOpt.Contains("ts")) {
1676 auto gra = graphs(sOpt + " readonly");
1677 if (!gPad && gROOT->GetSelectedPad())
1678 gROOT->GetSelectedPad()->cd();
1679 if (!sOpt.Contains("same") && gPad) {
1680 gPad->Clear();
1681 }
1682 if (gra) {
1683 auto gra2 = static_cast<TMultiGraph *>(gra->DrawClone(sOpt.Contains("same") ? "" : "A"));
1684 gra2->SetBit(kCanDelete);
1685 if (sOpt.Contains("pcls") || sOpt.Contains("pnull")) {
1686 gra2->GetHistogram()->SetMinimum(1e-6);
1687 }
1688 if (gPad) {
1689 gPad->RedrawAxis();
1690 gPad->GetCanvas()->Paint();
1691 gPad->GetCanvas()->Update();
1692#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1693 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1694#endif
1696 }
1697 }
1698 if (!sOpt.Contains("same") && gPad) {
1699 // auto mg = static_cast<TMultiGraph*>(gPad->GetPrimitive(gra->GetName()));
1700 // mg->GetHistogram()->SetMinimum(1e-9);
1701 // mg->GetHistogram()->GetYaxis()->SetRangeUser(1e-9,1);
1702 // gPad->SetGrid(0, 0);
1703 // gPad->SetLogy(1);
1704 }
1705
1707
1708 return;
1709 }
1710
1711 // graphs("ts visualize");return;
1712
1713 TGraphErrors *out = new TGraphErrors;
1714 out->SetName(GetName());
1715
1716 TString title = (!axes().empty()) ? TString::Format(";%s", axes().first()->GetTitle()) : "";
1717
1718 auto pllType = xRooFit::Asymptotics::TwoSided;
1719 if (!empty() && axes().size() == 1) {
1720 for (auto &p : *this) {
1721 if (p.fPllType != xRooFit::Asymptotics::TwoSided) {
1722 pllType = p.fPllType;
1723 }
1724 }
1725 title += ";";
1726 title += front().tsTitle(true);
1727 }
1728
1729 out->SetTitle(title);
1730 *dynamic_cast<TAttFill *>(out) = *this;
1731 *dynamic_cast<TAttLine *>(out) = *this;
1732 *dynamic_cast<TAttMarker *>(out) = *this;
1733 out->SetBit(kCanDelete);
1734
1735 if (!gPad)
1737 TVirtualPad *basePad = gPad;
1738 if (!sOpt.Contains("same"))
1739 basePad->Clear();
1740
1741 bool doFits = false;
1742 if (sOpt.Contains("fits")) {
1743 doFits = true;
1744 sOpt.ReplaceAll("fits", "");
1745 }
1746
1747 auto mainPad = gPad;
1748
1749 out->SetEditable(false);
1750
1751 if (doFits) {
1752 gPad->Divide(1, 2);
1753 gPad->cd(1);
1754 gPad->SetBottomMargin(gPad->GetBottomMargin() * 2.); // increase margin to be same as before
1755 gPad->SetGrid();
1756 out->Draw(sOpt);
1757 basePad->cd(2);
1758 mainPad = basePad->GetPad(1);
1759 } else {
1760 gPad->SetGrid();
1761 out->Draw("ALP");
1762 }
1763
1764 std::pair<double, double> minMax(std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity());
1765 for (auto &p : *this) {
1766 if (p.fPllType != pllType)
1767 continue; // must all have same pll type
1768 auto val = p.pll(true).first;
1769 if (std::isnan(val))
1770 continue;
1771 minMax.first = std::min(minMax.first, val);
1772 minMax.second = std::max(minMax.second, val);
1773 }
1774 if (minMax.first < std::numeric_limits<double>::infinity())
1775 out->GetHistogram()->SetMinimum(minMax.first);
1776 if (minMax.second > -std::numeric_limits<double>::infinity())
1777 out->GetHistogram()->SetMaximum(minMax.second);
1778
1779 TGraph *badPoints = nullptr;
1780
1781 TStopwatch s;
1782 s.Start();
1783 std::shared_ptr<const RooFitResult> ufr;
1784 for (auto &p : *this) {
1785 if (p.fPllType != pllType)
1786 continue; // must all have same pll type
1787 auto val = p.pll().first;
1788 if (!ufr)
1789 ufr = p.ufit();
1790 if (out->GetN() == 0 && ufr && ufr->status() == 0) {
1791 out->SetPoint(out->GetN(),
1792 ufr->floatParsFinal().getRealValue(axes().first()->GetName(),
1793 ufr->constPars().getRealValue(axes().first()->GetName())),
1794 0.);
1795 out->SetPointError(out->GetN() - 1, 0, ufr->edm());
1796 }
1797 if (auto fr = p.fNull_cfit;
1798 fr && doFits) { // access member to avoid unnecessarily creating fit result if wasnt needed
1799 // create a new subpad and draw fitResult on it
1800 auto _pad = gPad;
1801 auto pad =
1802 new TPad(fr->GetName(), TString::Format("%s = %g", poi().first()->GetTitle(), p.fNullVal()), 0, 0, 1., 1);
1803 pad->SetNumber(out->GetN() + 1); // can't use "0" for a subpad
1804 pad->cd();
1805 xRooNode(fr).Draw("goff");
1806 _pad->cd();
1807 //_pad->GetListOfPrimitives()->AddFirst(pad);
1808 pad->AppendPad();
1809 }
1810 if (std::isnan(val) && p.status() != 0) {
1811 if (!badPoints) {
1812 badPoints = new TGraph;
1813 badPoints->SetBit(kCanDelete);
1814 badPoints->SetName("badPoints");
1815 badPoints->SetMarkerStyle(5);
1816 badPoints->SetMarkerColor(kRed);
1817 badPoints->SetMarkerSize(1);
1818 out->GetListOfFunctions()->Add(badPoints, "P");
1819 }
1820 badPoints->SetPoint(badPoints->GetN(), p.fNullVal(), out->Eval(p.fNullVal()));
1821 mainPad->Modified();
1822 } else if (!std::isnan(val)) {
1823 out->SetPoint(out->GetN(), p.coords->getRealValue(axes().first()->GetName()), p.pll().first);
1824 out->SetPointError(out->GetN() - 1, 0, p.pll().second);
1825 out->Sort();
1826
1827 // reposition bad points
1828 if (badPoints) {
1829 for (int i = 0; i < badPoints->GetN(); i++)
1830 badPoints->SetPointY(i, out->Eval(badPoints->GetPointX(i)));
1831 }
1832
1833 mainPad->Modified();
1834 }
1835 if (s.RealTime() > 3) { // stops the clock
1836 basePad->GetCanvas()->Paint();
1837 basePad->GetCanvas()->Update();
1839 s.Reset();
1840 s.Start();
1841 }
1842 s.Continue();
1843 }
1844 basePad->GetCanvas()->Paint();
1845 basePad->GetCanvas()->Update();
1846#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1847 basePad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1848#endif
1850
1851 // finish by overlaying ufit
1852 if (ufr && doFits) {
1853 auto _pad = gPad;
1854 auto pad = new TPad(ufr->GetName(), "unconditional fit", 0, 0, 1., 1.);
1855 pad->SetNumber(-1);
1856 pad->cd();
1857 xRooNode(ufr).Draw("goff");
1858 _pad->cd();
1859 pad->AppendPad();
1860
1861 // draw one more pad to represent the selected, and draw the ufit pad onto that pad
1862 pad = new TPad("selected", "selected", 0, 0, 1, 1);
1863 pad->Draw();
1864 pad->cd();
1865 basePad->GetPad(2)->GetPad(-1)->AppendPad();
1866 pad->Modified();
1867 pad->Update();
1869
1870 basePad->cd();
1871 }
1872
1873 if (doFits) {
1874 if (!xRooNode::gIntObj) {
1876 }
1877 gPad->GetCanvas()->Connect("Highlighted(TVirtualPad*,TObject*,Int_t,Int_t)", "xRooNode::InteractiveObject",
1878 xRooNode::gIntObj, "Interactive_PLLPlot(TVirtualPad*,TObject*,Int_t,Int_t)");
1879 }
1880
1881 return;
1882}
1883
1885{
1886
1887 RooStats::HypoTestInverterResult *out = nullptr;
1888
1889 auto _axes = axes();
1890 if (_axes.empty())
1891 return out;
1892
1893 out = new RooStats::HypoTestInverterResult(GetName(), *dynamic_cast<RooRealVar *>(_axes.at(0)), 0.95);
1894 out->SetTitle(GetTitle());
1895
1896 for (auto &p : *this) {
1897 double _x = p.coords->getRealValue(_axes.at(0)->GetName(), std::numeric_limits<double>::quiet_NaN());
1898 out->Add(_x, p.result());
1899 }
1900
1901 return out;
1902}
1903
#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
@ kBlue
Definition Rtypes.h:66
@ kYellow
Definition Rtypes.h:66
#define gDirectory
Definition TDirectory.h:384
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:218
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:185
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:229
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 offset
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 char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
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 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
Option_t Option_t TPoint TPoint const char y1
char name[80]
Definition TGX11.cxx:110
@ kCanDelete
Definition TObject.h:367
#define gROOT
Definition TROOT.h:407
R__EXTERN TStyle * gStyle
Definition TStyle.h:433
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
#define gPad
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:1857
std::shared_ptr< const RooAbsCollection > coords
Definition xRooNLLVar.h:247
xValueWithError limit(const char *type="cls", double nSigma=std::numeric_limits< double >::quiet_NaN()) const
bool AddModel(const xRooNode &pdf, const char *validity="")
std::shared_ptr< TMultiGraph > graphs(const char *opt)
xValueWithError findlimit(const char *opt, double relUncert=std::numeric_limits< double >::infinity(), unsigned int maxTries=20)
int AddPoints(const char *parName, size_t nPoints, double low, double high)
static std::pair< double, double > GetLimit(const TGraph &pValues, double target=std::numeric_limits< double >::quiet_NaN())
std::shared_ptr< TGraphErrors > graph(const char *opt) const
void Print(Option_t *opt="") const override
Print TNamed name and title.
xRooHypoSpace(const char *name="", const char *title="")
int scan(const char *type, size_t nPoints, double low=std::numeric_limits< double >::quiet_NaN(), double high=std::numeric_limits< double >::quiet_NaN(), const std::vector< double > &nSigmas={0, 1, 2, -1, -2, std::numeric_limits< double >::quiet_NaN()}, double relUncert=0.1)
std::shared_ptr< xRooNode > pdf(const RooAbsCollection &parValues) const
void Draw(Option_t *opt="") override
Default Draw method for all objects.
std::map< std::string, std::pair< double, double > > limits(const char *opt="cls", const std::vector< double > &nSigmas={0, 1, 2, -1, -2, std::numeric_limits< double >::quiet_NaN()}, double relUncert=std::numeric_limits< double >::infinity())
std::shared_ptr< RooAbsPdf > pdf() const
Definition xRooNLLVar.h:429
std::shared_ptr< RooArgSet > pars(bool stripGlobalObs=true) const
The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacti...
Definition xRooNode.h:51
static InteractiveObject * gIntObj
Definition xRooNode.h:465
void Draw(Option_t *opt="") override
Default Draw method for all objects.
xRooNode pars() const
List of parameters (non-observables) of this node.
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
Abstract container object that can hold multiple RooAbsArg objects.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
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 interface for all probability density functions.
Definition RooAbsPdf.h:40
RooAbsRealLValue is the common abstract base class for objects that represent a real value that may a...
void setConstant(bool value=true)
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
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:23
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.
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:37
HypoTestInverterResult class holds the array of hypothesis test results and compute a confidence inte...
HypoTestResult * GetResult(int index) const
return a pointer to the i^th result object
int ArraySize() const
number of entries in the results array
double GetXValue(int index) const
function to return the value of the parameter of interest for the i^th entry in the results
RooArgSet * GetParameters() const override
return a cloned list with the parameter of interest
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 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
void ResetUpdated()
Definition TCanvas.h:193
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1514
void Paint(Option_t *option="") override
Paint canvas.
Definition TCanvas.cxx:1541
void Update() override
Update canvas pad buffers.
Definition TCanvas.cxx:2475
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
virtual Bool_t cd()
Change current directory to "this" directory.
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:491
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:4075
A TGraphErrors is a TGraph with error bars.
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:1518
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2315
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:683
Int_t GetN() const
Definition TGraph.h:130
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:2459
TList * GetListOfFunctions() const
Definition TGraph.h:124
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:927
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:2011
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2354
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1529
virtual void SetPointY(Int_t i, Double_t y)
Set y value for point i.
Definition TGraph.cxx:2347
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:2274
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
TObject * FindObject(const char *name) const override
Find an object in this list using its name.
Definition TList.cxx:578
A TMemFile is like a normal TFile except that it reads and writes only from memory.
Definition TMemFile.h:19
A TMultiGraph is a collection of TGraph (or derived) objects.
Definition TMultiGraph.h:34
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
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:48
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:140
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition TNamed.cxx:154
Mother of all ROOT objects.
Definition TObject.h:41
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:439
virtual void AppendPad(Option_t *option="")
Append graphics object to current pad.
Definition TObject.cxx:184
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:780
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
Float_t GetPadRightMargin() const
Definition TStyle.h:212
Float_t GetPadTopMargin() const
Definition TStyle.h:210
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:403
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual void SetGrid(Int_t valuex=1, Int_t valuey=1)=0
virtual TVirtualPad * cd(Int_t subpadnumber=0)=0
virtual TVirtualPad * GetPad(Int_t subpadnumber) const =0
virtual TCanvas * GetCanvas() const =0
void Clear(Option_t *option="") override=0
TLine * line
double gaussian_quantile(double z, double sigma)
Inverse ( ) of the cumulative distribution function of the lower tail of the normal (Gaussian) distri...
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 file.py:1
Definition first.py:1
Definition graph.py:1
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
BEGIN_XROOFIT_NAMESPACE