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