Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
xRooNLLVar.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
13/** \class ROOT::Experimental::XRooFit::xRooNLLVar
14\ingroup xroofit
15
16This xRooNLLVar object has several special methods, e.g. for fitting and toy dataset generation.
17
18 */
19
20#include "RVersion.h"
21
22#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
23#define protected public
24#endif
25
26#include "RooFitResult.h"
27
28#if ROOT_VERSION_CODE < ROOT_VERSION(6, 33, 00)
29#include "RooNLLVar.h"
30#endif
31
32#ifdef protected
33#undef protected
34#endif
35
36#include "xRooFit/xRooFit.h"
37
38#include "RooCmdArg.h"
39#include "RooAbsPdf.h"
40#include "RooAbsData.h"
41
42#include "RooConstraintSum.h"
43#include "RooSimultaneous.h"
45#include "TPRegexp.h"
46#include "TEfficiency.h"
47
48#include "RooRealVar.h"
49#include "Math/ProbFunc.h"
50#include "RooRandom.h"
51
52#include "TPad.h"
53#include "TSystem.h"
54
55#include "coutCapture.h"
56
57#include <chrono>
58
59#include "Math/GenAlgoOptions.h"
60
61#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
62#define private public
63#define GETWS(a) a->_myws
64#define GETWSSETS(w) w->_namedSets
65#else
66#define GETWS(a) a->workspace()
67#define GETWSSETS(w) w->sets()
68#endif
69#include "RooWorkspace.h"
70#ifdef private
71#undef private
72#endif
73
74#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
75#define protected public
76#endif
78#ifdef protected
79#undef protected
80#endif
81
82#include "TMultiGraph.h"
83#include "TCanvas.h"
84#include "TArrow.h"
85#include "RooStringVar.h"
86#include "TDirectory.h"
87#include "TStyle.h"
88#include "TH1D.h"
89#include "TLegend.h"
90#include "RooCategory.h"
91#include "TTree.h"
92#include "TGraph2D.h"
93
94#include "RooGaussian.h"
95#include "RooPoisson.h"
96
97#include "TROOT.h"
98#include "TKey.h"
99#include "TRegexp.h"
100#include "TStopwatch.h"
101
103
104std::set<int> xRooNLLVar::xRooHypoPoint::allowedStatusCodes = {0};
105
107public:
108 AutoRestorer(const RooAbsCollection &s, xRooNLLVar *nll = nullptr) : fSnap(s.snapshot()), fNll(nll)
109 {
110 fPars.add(s);
111 if (fNll) {
112 // if (!fNll->kReuseNLL) fOldNll = *fNll;
113 fOldData = fNll->getData();
114 fOldName = fNll->get()->GetName();
115 fOldTitle = fNll->get()->getStringAttribute("fitresultTitle");
116 }
117 }
119 {
121 if (fNll) {
122 // commented out code was attempt to speed up things avoid unnecessarily reinitializing things over and over
123 // if (!fNll->kReuseNLL) {
124 // // can be faster just by putting back in old nll
125 // fNll->std::shared_ptr<RooAbsReal>::operator=(fOldNll);
126 // fNll->fData = fOldData.first;
127 // fNll->fGlobs = fOldData.second;
128 // } else {
129 // fNll->setData(fOldData);
130 // fNll->get()->SetName(fOldName);
131 // fNll->get()->setStringAttribute("fitresultTitle", (fOldTitle == "") ? nullptr : fOldTitle);
132 // }
133 fNll->fGlobs = fOldData.second; // will mean globs matching checks are skipped in setData
134 fNll->setData(fOldData);
135 fNll->get()->SetName(fOldName);
136 fNll->get()->setStringAttribute("fitresultTitle", (fOldTitle == "") ? nullptr : fOldTitle);
137 }
138 }
140 std::unique_ptr<RooAbsCollection> fSnap;
141 xRooNLLVar *fNll = nullptr;
142 // std::shared_ptr<RooAbsReal> fOldNll;
143 std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> fOldData;
145};
146
147xRooNLLVar::~xRooNLLVar() {}
148
149xRooNLLVar::xRooNLLVar(RooAbsPdf &pdf, const std::pair<RooAbsData *, const RooAbsCollection *> &data,
150 const RooLinkedList &nllOpts)
151 : xRooNLLVar(std::shared_ptr<RooAbsPdf>(&pdf, [](RooAbsPdf *) {}),
152 std::make_pair(std::shared_ptr<RooAbsData>(data.first, [](RooAbsData *) {}),
153 std::shared_ptr<const RooAbsCollection>(data.second, [](const RooAbsCollection *) {})),
154 nllOpts)
155{
156}
157
158xRooNLLVar::xRooNLLVar(const std::shared_ptr<RooAbsPdf> &pdf,
159 const std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> &data,
160 const RooLinkedList &opts)
161 : fPdf(pdf), fData(data.first), fGlobs(data.second)
162{
163
164#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 37, 00)
166#else
168#endif
169 fOpts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
170 if (l)
171 l->Delete();
172 delete l;
173 });
174 fOpts->SetName("");
175
176 // we *must* take global observables from the model even if they are included in the dataset
177 // this is because the way xRooNLLVar is coded up it assumes the globs in the funcVars *ARE*
178 // part of the model
179 fOpts->Add(RooFit::GlobalObservablesSource("model").Clone(nullptr));
180
181 for (int i = 0; i < opts.GetSize(); i++) {
182 if (strlen(opts.At(i)->GetName()) == 0)
183 continue; // skipping "none" cmds
184 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
185 // will skip here to add with the obs from the function below
186 // must match global observables
187 auto gl = dynamic_cast<RooCmdArg *>(opts.At(i))->getSet(0);
188 if (!fGlobs || !fGlobs->equals(*gl)) {
189 throw std::runtime_error("GlobalObservables mismatch");
190 }
191 } else {
192 SetOption(dynamic_cast<RooCmdArg &>(*opts.At(i)));
193 }
194 }
195 if (fGlobs) {
196 // add global observables opt with function obs
197 auto _vars = std::unique_ptr<RooArgSet>(fPdf->getVariables());
198 if (auto extCon = dynamic_cast<RooCmdArg *>(fOpts->find("ExternalConstraints"))) {
199 for (auto con : *extCon->getSet(0)) {
200 _vars->add(*std::unique_ptr<RooArgSet>(con->getVariables()));
201 }
202 }
203 auto _funcGlobs = std::unique_ptr<RooArgSet>(dynamic_cast<RooArgSet *>(_vars->selectCommon(*fGlobs)));
204 fOpts->Add(RooFit::GlobalObservables(*_funcGlobs).Clone());
205 }
206
207 if (auto flag = dynamic_cast<RooCmdArg *>(fOpts->find("ReuseNLL"))) {
208 kReuseNLL = flag->getInt(0);
209 }
210
211 // if fit range specified, and pdf is a RooSimultaneous, may need to 'reduce' the model if some of the pdfs are in
212 // range and others are not
213 if (auto range = dynamic_cast<RooCmdArg *>(fOpts->find("RangeWithName"))) {
214 TString rangeName = range->getString(0);
215
216 // reduce the data here for convenience, not really necessary because will happen inside RooNLLVar but still
217 // fData.reset( fData->reduce(RooFit::SelectVars(*fData->get()),RooFit::CutRange(rangeName)) );
218
219 if (auto s = dynamic_cast<RooSimultaneous *>(fPdf.get()); s) {
220 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
221 std::vector<TString> chanPatterns;
222 TStringToken pattern(rangeName, ",");
223 bool hasRange(false);
224 std::string noneCatRanges;
225 while (pattern.NextToken()) {
226 chanPatterns.emplace_back(pattern);
227 if (_cat.hasRange(chanPatterns.back())) {
228 hasRange = true;
229 } else {
230 if (!noneCatRanges.empty())
231 noneCatRanges += ",";
232 noneCatRanges += chanPatterns.back();
233 }
234 }
235 if (hasRange) {
236 // must remove the ranges that referred to selections on channel category
237 // otherwise RooFit will incorrectly evaluate the NLL (it creates a partition for each range given in the
238 // list, which all end up being equal) the NLL would become scaled by the number of ranges given
239 if (noneCatRanges.empty()) {
240 fOpts->Remove(range);
242 } else {
243 range->setString(0, noneCatRanges.c_str());
244 }
245 // must reduce because category var has one of the ranges
246 auto newPdf =
247 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", s->GetName()), "Reduced model", _cat);
248 for (auto &c : _cat) {
249 auto _pdf = s->getPdf(c.first.c_str());
250 if (!_pdf)
251 continue;
252 _cat.setIndex(c.second);
253 bool matchAny = false;
254 for (auto &p : chanPatterns) {
255 if (_cat.hasRange(p) && _cat.inRange(p)) {
256 matchAny = true;
257 break;
258 }
259 }
260 if (matchAny) {
261 newPdf->addPdf(*_pdf, c.first.c_str());
262 }
263 }
264 fPdf = newPdf;
265 }
266 }
267 }
268
269 // if (fGlobs) {
270 // // must check GlobalObservables is in the list
271 // }
272 //
273 // if (auto globs = dynamic_cast<RooCmdArg*>(fOpts->find("GlobalObservables"))) {
274 // // first remove any obs the pdf doesnt depend on
275 // auto _vars = std::unique_ptr<RooAbsCollection>( fPdf->getVariables() );
276 // auto _funcGlobs = std::unique_ptr<RooAbsCollection>(_vars->selectCommon(*globs->getSet(0)));
277 // fGlobs.reset( std::unique_ptr<RooAbsCollection>(globs->getSet(0)->selectCommon(*_funcGlobs))->snapshot() );
278 // globs->setSet(0,dynamic_cast<const RooArgSet&>(*_funcGlobs)); // globs in linked list has its own argset
279 // but args need to live as long as the func
280 // /*RooArgSet toRemove;
281 // for(auto a : *globs->getSet(0)) {
282 // if (!_vars->find(*a)) toRemove.add(*a);
283 // }
284 // const_cast<RooArgSet*>(globs->getSet(0))->remove(toRemove);
285 // fGlobs.reset( globs->getSet(0)->snapshot() );
286 // fGlobs->setAttribAll("Constant",true);
287 // const_cast<RooArgSet*>(globs->getSet(0))->replace(*fGlobs);*/
288 // }
289}
290
291xRooNLLVar::xRooNLLVar(const std::shared_ptr<RooAbsPdf> &pdf, const std::shared_ptr<RooAbsData> &data,
292 const RooLinkedList &opts)
293 : xRooNLLVar(
294 pdf,
295 std::make_pair(data, std::shared_ptr<const RooAbsCollection>(
296 (opts.find("GlobalObservables"))
297 ? dynamic_cast<RooCmdArg *>(opts.find("GlobalObservables"))->getSet(0)->snapshot()
298 : nullptr)),
299 opts)
300{
301}
302
304{
305 std::cout << "PDF: ";
306 if (fPdf) {
307 fPdf->Print();
308 } else {
309 std::cout << "<null>" << std::endl;
310 }
311 std::cout << "Data: ";
312 if (fData) {
313 fData->Print();
314 } else {
315 std::cout << "<null>" << std::endl;
316 }
317 std::cout << "NLL Options: " << std::endl;
318 for (int i = 0; i < fOpts->GetSize(); i++) {
319 auto c = dynamic_cast<RooCmdArg *>(fOpts->At(i));
320 if (!c)
321 continue;
322 std::cout << " " << c->GetName() << " : ";
323 if (c->getString(0)) {
324 std::cout << c->getString(0);
325 } else if (c->getSet(0) && !c->getSet(0)->empty()) {
326 std::cout << (c->getSet(0)->contentsString());
327 } else {
328 std::cout << c->getInt(0);
329 }
330 std::cout << std::endl;
331 }
332 if (fFitConfig) {
333 std::cout << "Fit Config: " << std::endl;
334 std::cout << " UseParabErrors: " << (fFitConfig->ParabErrors() ? "True" : "False")
335 << " [toggles HESSE algorithm]" << std::endl;
336 std::cout << " MinimizerOptions: " << std::endl;
337 fFitConfig->MinimizerOptions().Print();
338 }
339 std::cout << "Last Rebuild Log Output: " << fFuncCreationLog << std::endl;
340}
341
343{
344 TString oldName = "";
345 if (std::shared_ptr<RooAbsReal>::get())
346 oldName = std::shared_ptr<RooAbsReal>::get()->GetName();
347 if (fPdf) {
349 // need to find all RooRealSumPdf nodes and mark them binned or unbinned as required
350 RooArgSet s;
351 fPdf->treeNodeServerList(&s, nullptr, true, false);
352 s.add(*fPdf); // ensure include self in case fitting a RooRealSumPdf
353 bool isBinned = false;
354 bool hasBinned = false; // if no binned option then 'auto bin' ...
355 if (auto a = dynamic_cast<RooCmdArg *>(fOpts->find("Binned")); a) {
356 hasBinned = true;
357 isBinned = a->getInt(0);
358 }
359 std::map<RooAbsArg *, bool> origValues;
360 if (hasBinned) {
361 for (auto a : s) {
362 if (a->InheritsFrom("RooRealSumPdf")) {
363 // since RooNLLVar will assume binBoundaries available (not null), we should check bin boundaries
364 // available
365 bool setBinned = false;
366 if (isBinned) {
367 std::unique_ptr<RooArgSet> obs(a->getObservables(fData->get()));
368 if (obs->size() == 1) { // RooNLLVar requires exactly 1 obs
369 auto *var = static_cast<RooRealVar *>(obs->first());
370 std::unique_ptr<std::list<double>> boundaries{dynamic_cast<RooAbsReal *>(a)->binBoundaries(
371 *var, -std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())};
372 if (boundaries) {
373 if (!std::shared_ptr<RooAbsReal>::get()) {
374 Info("xRooNLLVar", "%s will be evaluated as a Binned PDF (%d bins)", a->GetName(),
375 int(boundaries->size() - 1));
376 }
377 setBinned = true;
378 }
379 }
380 }
381 origValues[a] = a->getAttribute("BinnedLikelihood");
382 a->setAttribute("BinnedLikelihood", setBinned);
383 }
384 }
385 }
386 std::map<RooAbsPdf *, std::string> normRanges;
387 std::set<TObject *> removedOpts;
388 if (auto range = dynamic_cast<RooCmdArg *>(fOpts->find("RangeWithName"))) {
389 TString rangeName = range->getString(0);
390 if (auto sr = dynamic_cast<RooCmdArg *>(fOpts->find("SplitRange"));
391 sr && sr->getInt(0) && dynamic_cast<RooSimultaneous *>(fPdf.get())) {
392 if (auto special = fOpts->find("RangeOptimize")) {
393 removedOpts.insert(sr);
394 fOpts->Remove(sr);
395 removedOpts.insert(range);
396 fOpts->Remove(range);
397 removedOpts.insert(special);
398 fOpts->Remove(special);
399 }
400 // doing split range ... need to loop over categories of simpdf and apply range to each
401 auto simPdf = dynamic_cast<RooSimultaneous *>(fPdf.get());
402 for (auto cat : simPdf->indexCat()) {
403 auto subpdf = simPdf->getPdf(cat.first.c_str());
404 if (!subpdf)
405 continue; // state not in pdf
407 srangeName.ReplaceAll(",", "_" + cat.first + ",");
408 srangeName += "_" + cat.first;
410 subpdf->treeNodeServerList(&ss, nullptr, true, false);
411 ss.add(*subpdf);
412 for (auto a : ss) {
413 if (a->InheritsFrom("RooAddPdf")) {
414 auto p = dynamic_cast<RooAbsPdf *>(a);
415 normRanges[p] = p->normRange() ? p->normRange() : "";
416 p->setNormRange(srangeName);
417 }
418 }
419 }
420 } else {
421 // set range on all AddPdfs before creating - needed in cases where coefs are present and need fractioning
422 // based on fit range bugfix needed: roofit needs to propagate the normRange to AddPdfs child nodes (used in
423 // createExpectedEventsFunc)
424 for (auto a : s) {
425 if (a->InheritsFrom("RooAddPdf")) {
426 auto p = dynamic_cast<RooAbsPdf *>(a);
427 normRanges[p] = p->normRange() ? p->normRange() : "";
428 p->setNormRange(rangeName);
429 }
430 }
431 }
432 }
433 // before creating, clear away caches if any if pdf is in ws
434 if (GETWS(fPdf)) {
435 std::set<std::string> setNames;
436 for (auto &a : GETWSSETS(GETWS(fPdf))) {
437 if (TString(a.first.c_str()).BeginsWith("CACHE_")) {
438 setNames.insert(a.first);
439 }
440 }
441 for (auto &a : setNames) {
442 GETWS(fPdf)->removeSet(a.c_str());
443 }
444 }
445 std::set<std::string> attribs;
446 if (std::shared_ptr<RooAbsReal>::get())
447 attribs = std::shared_ptr<RooAbsReal>::get()->attributes();
448 this->reset(std::unique_ptr<RooAbsReal>{fPdf->createNLL(*fData, *fOpts)}.release());
449 std::shared_ptr<RooAbsReal>::get()->SetName(TString::Format("nll_%s/%s", fPdf->GetName(), fData->GetName()));
450 for (auto o : removedOpts)
451 fOpts->Add(o);
452 // RooFit only swaps in what it calls parameters, this misses out the RooConstVars which we treat as pars as well
453 // so swap those in ... question: is recursiveRedirectServers usage in RooAbsOptTestStatic (and here) a memory
454 // leak?? where do the replaced servers get deleted??
455
456 for (auto &[k, v] : normRanges)
457 k->setNormRange(v == "" ? nullptr : v.c_str());
458
459 for (auto &a : attribs)
460 std::shared_ptr<RooAbsReal>::get()->setAttribute(a.c_str());
461 // create parent on next line to avoid triggering workspace initialization code in constructor of xRooNode
462 if (GETWS(fPdf)) {
463 xRooNode(*GETWS(fPdf), std::make_shared<xRooNode>()).sterilize();
464 } // there seems to be a nasty bug somewhere that can make the cache become invalid, so clear it here
465 if (oldName != "")
466 std::shared_ptr<RooAbsReal>::get()->SetName(oldName);
467 if (!origValues.empty()) {
468 // need to evaluate NOW so that slaves are created while the BinnedLikelihood settings are in place
469 std::shared_ptr<RooAbsReal>::get()->getVal();
470 for (auto &[o, v] : origValues)
471 o->setAttribute("BinnedLikelihood", v);
472 }
473 }
474
475 fFuncVars = std::unique_ptr<RooArgSet>{std::shared_ptr<RooAbsReal>::get()->getVariables()};
476 if (fGlobs) {
477 fFuncGlobs.reset(fFuncVars->selectCommon(*fGlobs));
478 fFuncGlobs->setAttribAll("Constant", true);
479 }
480 fConstVars.reset(fFuncVars->selectByAttrib("Constant", true)); // will check if any of these have floated
481}
482
483std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>>
484xRooNLLVar::generate(bool expected, int seed)
485{
486 if (!fPdf)
487 return std::pair(nullptr, nullptr);
488 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
489 fr->setFinalParList(RooArgList());
491 l.add((fFuncVars) ? *fFuncVars : *std::unique_ptr<RooAbsCollection>(fPdf->getParameters(*fData)));
492 fr->setConstParList(l);
493 const_cast<RooArgList &>(fr->constPars()).setAttribAll("global", false);
494 if (fGlobs)
495 std::unique_ptr<RooAbsCollection>(fr->constPars().selectCommon(*fGlobs))->setAttribAll("global", true);
496 return xRooFit::generateFrom(*fPdf, *fr, expected, seed);
497}
498
500
501xRooNLLVar::xRooFitResult::xRooFitResult(const std::shared_ptr<xRooNode> &in, const std::shared_ptr<xRooNLLVar> &nll)
502 : std::shared_ptr<const RooFitResult>(std::dynamic_pointer_cast<const RooFitResult>(in->fComp)),
503 fNode(in),
504 fNll(nll),
505 fCfits(std::make_shared<std::map<std::string, xRooFitResult>>())
506{
507}
509{
510 return fNode->get<RooFitResult>();
511}
512// xRooNLLVar::xRooFitResult::operator std::shared_ptr<const RooFitResult>() const { return
513// std::dynamic_pointer_cast<const RooFitResult>(fNode->fComp); }
514xRooNLLVar::xRooFitResult::operator const RooFitResult *() const
515{
516 return fNode->get<const RooFitResult>();
517}
519{
520 fNode->Draw(opt);
521}
522
524{
525
526 // create a hypoPoint with ufit equal to this fit
527 // and poi equal to given poi
528 if (!fNll)
529 throw std::runtime_error("xRooFitResult::cfit: Cannot create cfit without nll");
530
531 // see if fit already done
532 if (alias) {
533 if (auto res = fCfits->find(alias); res != fCfits->end()) {
534 return res->second;
535 }
536 }
537 if (auto res = fCfits->find(poiValues); res != fCfits->end()) {
538 return res->second;
539 }
540
541 AutoRestorer s(*fNll->fFuncVars);
542 *fNll->fFuncVars = get()->floatParsFinal();
543 fNll->fFuncVars->assignValueOnly(get()->constPars());
544 std::unique_ptr<RooAbsCollection>(fNll->fFuncVars->selectCommon(get()->floatParsFinal()))
545 ->setAttribAll("Constant", false);
546 std::unique_ptr<RooAbsCollection>(fNll->fFuncVars->selectCommon(get()->constPars()))->setAttribAll("Constant", true);
547
548 auto hp = fNll->hypoPoint(poiValues, std::numeric_limits<double>::quiet_NaN(), xRooFit::Asymptotics::Unknown);
549 hp.fUfit = *this;
550 auto out = xRooNLLVar::xRooFitResult(std::make_shared<xRooNode>(hp.cfit_null(), fNode->fParent), fNll);
551 fCfits->insert(std::pair((alias) ? alias : poiValues, out));
552 return out;
553}
555{
556 RooRealVar *npVar = dynamic_cast<RooRealVar *>((prefit ? get()->floatParsInit() : get()->floatParsFinal()).find(np));
557 if (!npVar)
558 throw std::runtime_error("xRooFitResult::ifit: par not found");
559 return cfit(TString::Format("%s=%f", np, npVar->getVal() + (up ? npVar->getErrorHi() : npVar->getErrorLo())));
560}
561double xRooNLLVar::xRooFitResult::impact(const char *poi, const char *np, bool up, bool prefit, bool covApprox)
562{
563 if (!covApprox) {
564 // get the ifit and get the difference between the postFit poi values
565 RooRealVar *poiHat = dynamic_cast<RooRealVar *>((get()->floatParsFinal()).find(poi));
566 if (!poiHat)
567 throw std::runtime_error("xRooFitResult::impact: poi not found");
568 auto _ifit = ifit(np, up, prefit);
569 if (!_ifit)
570 throw std::runtime_error("xRooFitResult::impact: null ifit");
571 if (_ifit->status() != 0)
572 fNode->Warning("impact", "ifit status code is %d", _ifit->status());
573 return _ifit->floatParsFinal().getRealValue(poi) - poiHat->getVal();
574 } else {
575 // estimate impact from the covariance matrix ....
576 int iPoi = get()->floatParsFinal().index(poi);
577 int iNp = get()->floatParsFinal().index(np);
578 if (iPoi == -1)
579 throw std::runtime_error("xRooFitResult::impact: poi not found");
580 if (iNp == -1)
581 throw std::runtime_error("xRooFitResult::impact: np not found");
583 dynamic_cast<RooRealVar *>((prefit ? get()->floatParsInit() : get()->floatParsFinal()).find(np));
584 return get()->covarianceMatrix()(iPoi, iNp) / (up ? npVar->getErrorHi() : npVar->getErrorLo());
585 }
586 return std::numeric_limits<double>::quiet_NaN();
587}
588
589double xRooNLLVar::xRooFitResult::conditionalError(const char *poi, const char *nps, bool up, bool covApprox)
590{
591 // run a fit with given NPs held constant, return quadrature difference
592
594 RooArgList vars;
595 RooAbsArg *poiVar = nullptr;
596 for (auto p : get()->floatParsFinal()) {
597 if (strcmp(p->GetName(), poi) == 0) {
598 vars.add(*p);
599 poiVar = p;
600 continue;
601 }
602 TStringToken pattern(nps, ",");
603 bool matches = false;
604 while (pattern.NextToken()) {
605 TString s(pattern);
606 if ((p->getStringAttribute("group") && s == p->getStringAttribute("group")) ||
607 TString(p->GetName()).Contains(TRegexp(s, true)) || p->getAttribute(s)) {
608 matches = true;
609 break;
610 }
611 }
612 if (matches) {
613 if (npNames.Length())
614 npNames += ",";
615 npNames += p->GetName();
616 } else {
617 vars.add(*p); // keeping in reduced cov matrix
618 }
619 }
620 if (!poiVar) {
621 throw std::runtime_error(TString::Format("Could not find poi: %s", poi));
622 }
623 if (npNames == "") {
624 fNode->Warning("conditionalError", "No parameters selected by: %s", nps);
625 return (up) ? static_cast<RooRealVar *>(poiVar)->getErrorHi() : static_cast<RooRealVar *>(poiVar)->getErrorLo();
626 }
627
628 if (covApprox) {
629 int idx = vars.index(poi);
630 return sqrt(get()->conditionalCovarianceMatrix(vars)(idx, idx));
631 }
632
633 auto _cfit = cfit(npNames.Data(), nps);
634
635 auto _poi = _cfit->floatParsFinal().find(poi);
636
637 return (up) ? static_cast<RooRealVar *>(_poi)->getErrorHi() : static_cast<RooRealVar *>(_poi)->getErrorLo();
638}
639
641{
642
643 RooRealVar *poiHat = dynamic_cast<RooRealVar *>((get()->floatParsFinal()).find(poi));
644 if (!poiHat)
645 throw std::runtime_error("xRooFitResult::ranknp: poi not found");
646
647 std::vector<std::pair<std::string, double>> ranks;
648 // first do with the covariance approximation, since that's always available
649 for (auto par : get()->floatParsFinal()) {
650 if (par == poiHat)
651 continue;
652 ranks.emplace_back(std::pair(par->GetName(), impact(poi, par->GetName(), up, prefit, true)));
653 }
654
655 std::sort(ranks.begin(), ranks.end(), [](auto &left, auto &right) {
656 if (std::isnan(left.second) && !std::isnan(right.second))
657 return false;
658 if (!std::isnan(left.second) && std::isnan(right.second))
659 return true;
660 return fabs(left.second) > fabs(right.second);
661 });
662
663 // now redo the ones above the threshold
664 for (auto &[n, v] : ranks) {
665 if (v >= approxThreshold) {
666 try {
667 v = impact(poi, n.c_str(), up, prefit);
668 } catch (...) {
669 v = std::numeric_limits<double>::quiet_NaN();
670 };
671 }
672 }
673
674 // resort
675 std::sort(ranks.begin(), ranks.end(), [](auto &left, auto &right) {
676 if (std::isnan(left.second) && !std::isnan(right.second))
677 return false;
678 if (!std::isnan(left.second) && std::isnan(right.second))
679 return true;
680 return fabs(left.second) > fabs(right.second);
681 });
682
683 RooArgList out;
684 out.setName("rankings");
685 for (auto &[n, v] : ranks) {
686 out.addClone(*get()->floatParsFinal().find(n.c_str()));
687 auto vv = static_cast<RooRealVar *>(out.at(out.size() - 1));
688 vv->setVal(v);
689 vv->removeError();
690 vv->removeMin();
691 vv->removeMax(); // vv->removeRange();
692 }
693 return out;
694}
695
696xRooNLLVar::xRooFitResult xRooNLLVar::minimize(const std::shared_ptr<ROOT::Fit::FitConfig> &_config)
697{
698 auto &nll = *get();
699 auto out = xRooFit::minimize(nll, (_config) ? _config : fitConfig(), fOpts);
700 // add any pars that are const here that aren't in constPars list because they may have been
701 // const-optimized and their values cached with the dataset, so if subsequently floated the
702 // nll wont evaluate correctly
703 // fConstVars.reset( fFuncVars->selectByAttrib("Constant",true) );
704
705 // before returning, flag which of the constPars were actually global observables
706 if (out) {
707 const_cast<RooArgList &>(out->constPars()).setAttribAll("global", false);
708 if (fGlobs)
709 std::unique_ptr<RooAbsCollection>(out->constPars().selectCommon(*fGlobs))->setAttribAll("global", true);
710 }
711
712 if (fOpts->find("GoF")) {
713 // add pgof to the fit result
714 const_cast<RooArgList &>(out->constPars()).addClone(RooRealVar(".pgof", "GoF p-value", pgof()));
715 // and just main term
716 const_cast<RooArgList &>(out->constPars())
717 .addClone(RooRealVar(".mainterm_pgof", "MainTerm GoF p-value", mainTermPgof()));
718 }
719
720 return xRooFitResult(std::make_shared<xRooNode>(out, fPdf), std::make_shared<xRooNLLVar>(*this));
721}
722
723std::shared_ptr<ROOT::Fit::FitConfig> xRooNLLVar::fitConfig()
724{
725 if (!fFitConfig)
727 return fFitConfig;
728}
729
731{
732 if (auto conf = fitConfig(); conf)
733 return const_cast<ROOT::Math::IOptions *>(conf->MinimizerOptions().ExtraOptions());
734 return nullptr;
735}
736
737double xRooNLLVar::getEntryVal(size_t entry) const
738{
739 auto _data = data();
740 if (!_data)
741 return 0;
742 if (size_t(_data->numEntries()) <= entry)
743 return 0;
744 auto _pdf = pdf();
745 *std::unique_ptr<RooAbsCollection>(_pdf->getObservables(_data)) = *_data->get(entry);
746 // if (auto s = dynamic_cast<RooSimultaneous*>(_pdf.get());s) return
747 // -_data->weight()*s->getPdf(s->indexCat().getLabel())->getLogVal(_data->get());
748 return (_data->weight() == 0) ? 0 : (-_data->weight() * _pdf->getLogVal(_data->get()));
749}
750
751std::set<std::string> xRooNLLVar::binnedChannels() const
752{
753 std::set<std::string> out;
754
755 auto binnedOpt = dynamic_cast<RooCmdArg *>(fOpts->find("Binned")); // the binned option, if explicitly specified
756
757 if (auto s = dynamic_cast<RooSimultaneous *>(pdf().get())) {
758 xRooNode simPdf(*s);
759 bool allChannels = true;
760 for (auto c : simPdf.bins()) {
761 // see if there's a RooRealSumPdf in the channel - if there is, if it has BinnedLikelihood set
762 // then assume is a BinnedLikelihood channel
763 RooArgSet nodes;
764 c->get<RooAbsArg>()->treeNodeServerList(&nodes, nullptr, true, false);
765 bool isBinned = false;
766 for (auto a : nodes) {
767 if (a->InheritsFrom("RooRealSumPdf") &&
768 ((binnedOpt && binnedOpt->getInt(0)) || (!binnedOpt && a->getAttribute("BinnedLikelihood")))) {
769 TString chanName(c->GetName());
770 out.insert(chanName(chanName.Index("=") + 1, chanName.Length()).Data());
771 isBinned = true;
772 break;
773 }
774 }
775 if (!isBinned) {
776 allChannels = false;
777 }
778 }
779 if (allChannels) {
780 out.clear();
781 out.insert("*");
782 }
783 } else {
784 RooArgSet nodes;
785 pdf()->treeNodeServerList(&nodes, nullptr, true, false);
786 for (auto a : nodes) {
787 if (a->InheritsFrom("RooRealSumPdf") &&
788 ((binnedOpt && binnedOpt->getInt(0)) || (!binnedOpt && a->getAttribute("BinnedLikelihood")))) {
789 out.insert("*");
790 break;
791 }
792 }
793 }
794 return out;
795}
796
798{
799
800 auto _data = data();
801 if (!_data)
802 return 0;
803 if (size_t(_data->numEntries()) <= entry)
804 return 0;
805 auto _pdf = pdf().get();
806 std::unique_ptr<RooAbsCollection> _robs(_pdf->getObservables(_data->get()));
807 *_robs = *_data->get(entry); // only set robs
808 if (auto s = dynamic_cast<RooSimultaneous *>(_pdf); s) {
809 _pdf = s->getPdf(s->indexCat().getCurrentLabel());
810 }
811 double volume = 1.;
812 for (auto o : *_robs) {
813
814 if (auto a = dynamic_cast<RooAbsRealLValue *>(o);
815 a && _pdf->dependsOn(*a)) { // dependsOn check needed until ParamHistFunc binBoundaries method fixed
816 std::unique_ptr<std::list<double>> bins(
817 _pdf->binBoundaries(*a, -std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()));
818 if (bins) {
819 double lowEdge = -std::numeric_limits<double>::infinity();
820 for (auto b : *bins) {
821 if (b > a->getVal()) {
822 volume *= (b - lowEdge);
823 break;
824 }
825 lowEdge = b;
826 }
827 }
828 }
829 }
830
831 return volume;
832}
833
835{
836 // for each global observable in the dataset, determine which constraint term is associated to it
837 // and given its type, add the necessary saturated term...
838
839 double out = 0;
840
841 if (!fGlobs)
842 return 0;
843
844 auto cTerm = constraintTerm();
845 if (!cTerm)
846 return 0;
847
848 for (auto c : cTerm->list()) {
849 if (std::string(c->ClassName()) == "RooAbsPdf" ||
850 std::string(c->ClassName()).find("RooNormalizedPdf") != std::string::npos) {
851 // in ROOT 6.32 the constraintTerm is full of RooNormalizedPdfs which aren't public
852 // became public in 6.34, hence now also check for RooNormalizedPdf explicitly
853 // in this case use the first server
854 c = c->servers()[0];
855 }
856 if (auto gaus = dynamic_cast<RooGaussian *>(c)) {
857 auto v = dynamic_cast<RooAbsReal *>(fGlobs->find(gaus->getX().GetName()));
858 if (!v) {
859 v = dynamic_cast<RooAbsReal *>(fGlobs->find(
860 gaus->getMean().GetName())); // shouldn't really happen but does for at least ws made by pyhf
861 }
862 if (!v)
863 continue;
864 out -= std::log(ROOT::Math::gaussian_pdf(v->getVal(), gaus->getSigma().getVal(), v->getVal()));
865 } else if (auto pois = dynamic_cast<RooPoisson *>(c)) {
866 auto v = dynamic_cast<RooAbsReal *>(fGlobs->find(pois->getX().GetName()));
867 if (!v)
868 continue;
869 out -= std::log(TMath::Poisson(v->getVal(), v->getVal()));
870 }
871 }
872
873 return out;
874}
875
876double xRooNLLVar::ndof() const
877{
878 return data()->numEntries() + (fFuncGlobs ? fFuncGlobs->size() : 0) -
879 std::unique_ptr<RooAbsCollection>(pars()->selectByAttrib("Constant", false))->size();
880}
881
882double xRooNLLVar::pgof() const
883{
884 // note that if evaluating this for a single channel, until 6.30 is available if you are using Binned mode the pdf
885 // will need to be part of a Simultaneous
886 return TMath::Prob(2. * (get()->getVal() - saturatedVal()), ndof());
887}
888
890{
891 // need to count number of floating unconstrained parameters
892 // which are floating parameters not featured in the constraintTerm
893 std::unique_ptr<RooAbsCollection> _floats(pars()->selectByAttrib("Constant", false));
894 if (auto _constraintTerm = constraintTerm()) {
895 _floats->remove(*std::unique_ptr<RooAbsCollection>(_constraintTerm->getVariables()));
896 }
897 return data()->numEntries() - _floats->size();
898}
899
901{
902 // using totVal - constraintTerm while new evalbackend causes mainTerm() to return nullptr
903 return get()->getVal() - constraintTermVal();
904}
905
907{
908 if (auto _constraintTerm = constraintTerm()) {
909 return _constraintTerm->getVal();
910 }
911 return 0;
912}
913
915{
917}
918
923
925{
926
927 // Use this term to create a goodness-of-fit metric, which is approx chi2 distributed with numEntries (data) d.o.f:
928 // prob = TMath::Prob( 2.*(nll.mainTerm()->getVal() - nll.saturatedNllTerm()), nll.data()->numEntries() )
929
930 // note that need to construct nll with explicit Binned(1 or 0) option otherwise will pick up nll eval
931 // from attributes in model already, so many get binned mainTerm eval when thinking not binned because didnt specify
932 // Binned(1)
933
934 auto _data = data();
935 if (!_data)
936 return std::numeric_limits<double>::quiet_NaN();
937
938 std::set<std::string> _binnedChannels = binnedChannels();
939
940 // for binned case each entry is: -(-N + Nlog(N) - TMath::LnGamma(N+1))
941 // for unbinned case each entry is: -(N*log(N/(sumN*binW))) = -N*logN + N*log(sumN) + N*log(binW)
942 // but unbinned gets extendedTerm = sumN - sumN*log(sumN)
943 // so resulting sum is just sumN - sum[ N*logN - N*log(binW) ]
944 // which is the same as the binned case without the LnGamma part and with the extra sum[N*log(binW)] part
945
946 const RooAbsCategoryLValue *cat = (dynamic_cast<RooSimultaneous *>(pdf().get()))
947 ? &dynamic_cast<RooSimultaneous *>(pdf().get())->indexCat()
948 : nullptr;
949
950 double out = _data->sumEntries();
951 for (int i = 0; i < _data->numEntries(); i++) {
952 _data->get(i);
953 double w = _data->weight();
954 if (w == 0)
955 continue;
956 out -= w * std::log(w);
957 if (_binnedChannels.count("*")) {
958 out += TMath::LnGamma(w + 1);
959 } else if (_binnedChannels.empty()) {
960 out += w * std::log(getEntryBinWidth(i));
961 } else if (cat) {
962 // need to determine which channel we are in for this entry to decide if binned or unbinned active
963 if (_binnedChannels.count(_data->get()->getCatLabel(cat->GetName()))) {
964 out += TMath::LnGamma(w + 1);
965 } else {
966 out += w * std::log(getEntryBinWidth(i));
967 }
968 } else {
969 throw std::runtime_error("Cannot determine category of RooSimultaneous pdf");
970 }
971 }
972
973 out += simTermVal();
974
975 return out;
976}
977
978std::shared_ptr<RooArgSet> xRooNLLVar::pars(bool stripGlobalObs) const
979{
980 auto out = std::shared_ptr<RooArgSet>(get()->getVariables());
981 if (stripGlobalObs && fGlobs) {
982 out->remove(*fGlobs, true, true);
983 }
984 return out;
985}
986
987TObject *
988xRooNLLVar::Scan(const char *scanPars, const std::vector<std::vector<double>> &coords, const RooArgList &profilePars)
989{
990 return Scan(*std::unique_ptr<RooAbsCollection>(get()->getVariables()->selectByName(scanPars)), coords, profilePars);
991}
992
993TObject *xRooNLLVar::Scan(const RooArgList &scanPars, const std::vector<std::vector<double>> &coords,
994 const RooArgList &profilePars)
995{
996
997 if (scanPars.size() > 2 || scanPars.empty())
998 return nullptr;
999
1000 TGraph2D *out2d = (scanPars.size() == 2) ? new TGraph2D() : nullptr;
1001 TGraph *out1d = (out2d) ? nullptr : new TGraph();
1002 TNamed *out = (out2d) ? static_cast<TNamed *>(out2d) : static_cast<TNamed *>(out1d);
1003 out->SetName(get()->GetName());
1004 out->SetTitle(TString::Format("%s;%s%s%s", get()->GetTitle(), scanPars.first()->GetTitle(), out2d ? ";" : "",
1005 out2d ? scanPars.at(1)->GetTitle() : ""));
1006
1007 std::unique_ptr<RooAbsCollection> funcVars(get()->getVariables());
1009
1010 for (auto &coord : coords) {
1011 if (coord.size() != scanPars.size()) {
1012 throw std::runtime_error("Invalid coordinate");
1013 }
1014 for (size_t i = 0; i < coord.size(); i++) {
1015 static_cast<RooAbsRealLValue &>(scanPars[i]).setVal(coord[i]);
1016 }
1017
1018 if (profilePars.empty()) {
1019 // just evaluate
1020 if (out2d) {
1021 out2d->SetPoint(out2d->GetN(), coord[0], coord[1], get()->getVal());
1022 } else {
1023 out1d->SetPoint(out1d->GetN(), coord[0], get()->getVal());
1024 }
1025 }
1026 }
1027
1028 return out;
1029}
1030
1032{
1033 TString sOpt(opt);
1034
1035 auto _pars = pars();
1036
1037 if (sOpt == "sensitivity") {
1038
1039 // will make a plot of DeltaNLL
1040 }
1041
1042 if (sOpt == "floating") {
1043 // start scanning floating pars
1044 auto floats = std::unique_ptr<RooAbsCollection>(_pars->selectByAttrib("Constant", false));
1045 TVirtualPad *pad = gPad;
1046 if (!pad) {
1048 pad = gPad;
1049 }
1050 TMultiGraph *gr = new TMultiGraph;
1051 gr->SetName("multigraph");
1052 gr->SetTitle(TString::Format("%s;Normalized Parameter Value;#Delta NLL", get()->GetTitle()));
1053 /*((TPad*)pad)->DivideSquare(floats->size());
1054 int i=0;
1055 for(auto a : *floats) {
1056 i++;
1057 pad->cd(i);
1058 Draw(a->GetName());
1059 }*/
1060 return;
1061 }
1062
1063 RooArgList vars;
1064 TStringToken pattern(sOpt, ":");
1065 while (pattern.NextToken()) {
1066 TString s(pattern);
1067 if (auto a = _pars->find(s); a)
1068 vars.add(*a);
1069 }
1070
1071 if (vars.size() == 1) {
1072 TGraph *out = new TGraph;
1073 out->SetBit(kCanDelete);
1074 TGraph *bad = new TGraph;
1075 bad->SetBit(kCanDelete);
1076 bad->SetMarkerColor(kRed);
1077 bad->SetMarkerStyle(5);
1078 TMultiGraph *gr = (gPad) ? dynamic_cast<TMultiGraph *>(gPad->GetPrimitive("multigraph")) : nullptr;
1079 bool normRange = false;
1080 if (!gr) {
1081 gr = new TMultiGraph;
1082 gr->Add(out, "LP");
1084 } else {
1085 normRange = true;
1086 }
1087 out->SetName(get()->GetName());
1088 gr->SetTitle(TString::Format("%s;%s;#Delta NLL", get()->GetTitle(), vars.at(0)->GetTitle()));
1089 // scan outwards from current value towards limits
1090 auto v = dynamic_cast<RooRealVar *>(vars.at(0));
1091 double low = v->getVal();
1092 double high = low;
1093 double step = (v->getMax() - v->getMin()) / 100;
1094 double init = v->getVal();
1095 double initVal = func()->getVal();
1096 // double xscale = (normRange) ? (2.*(v->getMax() - v->getMin())) : 1.;
1097 auto currTime = std::chrono::steady_clock::now();
1098 while (out->GetN() < 100 && (low > v->getMin() || high < v->getMax())) {
1099 if (out->GetN() == 0) {
1100 out->SetPoint(out->GetN(), low, 0);
1101 low -= step;
1102 high += step;
1103 if (!normRange) {
1104 gr->Draw("A");
1105 gPad->SetGrid();
1106 }
1107 continue;
1108 }
1109 if (low > v->getMin()) {
1110 v->setVal(low);
1111 auto _v = func()->getVal();
1112 if (std::isnan(_v) || std::isinf(_v)) {
1113 if (bad->GetN() == 0)
1114 gr->Add(bad, "P");
1115 bad->SetPoint(bad->GetN(), low, out->GetPointY(0));
1116 } else {
1117 out->SetPoint(out->GetN(), low, _v - initVal);
1118 }
1119 low -= step;
1120 }
1121 if (high < v->getMax()) {
1122 v->setVal(high);
1123 auto _v = func()->getVal();
1124 if (std::isnan(_v) || std::isinf(_v)) {
1125 if (bad->GetN() == 0)
1126 gr->Add(bad, "P");
1127 bad->SetPoint(bad->GetN(), high, out->GetPointY(0));
1128 } else {
1129 out->SetPoint(out->GetN(), high, _v - initVal);
1130 }
1131 high += step;
1132 }
1133 out->Sort();
1134 // should only do processEvents once every second in case using x11 (which is slow)
1135 gPad->Modified();
1136 if (std::chrono::steady_clock::now() - currTime > std::chrono::seconds(1)) {
1137 currTime = std::chrono::steady_clock::now();
1138 gPad->Update();
1140 }
1141 }
1142 // add arrow to show current par value
1143 TArrow a;
1144 a.DrawArrow(init, 0, init, -0.1);
1145 gPad->Update();
1146#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1147 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1148#endif
1150 v->setVal(init);
1151 } else {
1152 Error("Draw", "Name a parameter to scan over: Draw(<name>) , choose from: %s",
1153 _pars->empty() ? "" : _pars->contentsString().c_str());
1154 }
1155}
1156
1157std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> xRooNLLVar::getData() const
1158{
1159 return std::make_pair(fData, fGlobs);
1160}
1161
1163{
1164 if (data.fComp && !data.get<RooAbsData>()) {
1165 return false;
1166 }
1167 return setData(std::dynamic_pointer_cast<RooAbsData>(data.fComp),
1168 std::shared_ptr<const RooAbsCollection>(data.globs().argList().snapshot()));
1169}
1170
1171bool xRooNLLVar::setData(const std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> &_data)
1172{
1173
1174 if (fData == _data.first && fGlobs == _data.second)
1175 return true;
1176
1177 auto _globs = fGlobs; // done to keep globs alive while NLL might still be alive.
1178
1179 auto _dglobs = (_data.second) ? _data.second
1180 : std::shared_ptr<const RooAbsCollection>(_data.first->getGlobalObservables(),
1181 [](const RooAbsCollection *) {});
1182
1183 if (fGlobs && !(fGlobs->empty() && !_dglobs) && _data.first &&
1184 fGlobs != _dglobs) { // second condition allows for no globs being a nullptr, third allow globs to remain if
1185 // nullifying data
1186 if (!_dglobs)
1187 throw std::runtime_error("Missing globs");
1188 // ignore 'extra' globs
1189 RooArgSet s;
1190 s.add(*fGlobs);
1191 std::unique_ptr<RooAbsCollection> _actualGlobs(fPdf->getObservables(s));
1192 RooArgSet s2;
1193 s2.add(*_dglobs);
1194 std::unique_ptr<RooAbsCollection> _actualGlobs2(fPdf->getObservables(s2));
1195 if (!_actualGlobs->equals(*_actualGlobs2)) {
1196 RooArgSet rC;
1197 rC.add(*_actualGlobs2);
1198 rC.remove(*std::unique_ptr<RooAbsCollection>(rC.selectCommon(*_actualGlobs)));
1199 TString r = (!rC.empty()) ? rC.contentsString() : "";
1200 RooArgSet lC;
1201 lC.add(*_actualGlobs);
1202 lC.remove(*std::unique_ptr<RooAbsCollection>(lC.selectCommon(*_actualGlobs2)));
1203 TString l = (!lC.empty()) ? lC.contentsString() : "";
1204 throw std::runtime_error(TString::Format("globs mismatch: adding %s removing %s", r.Data(), l.Data()));
1205 }
1206 fGlobs = _dglobs;
1207 }
1208
1209 if (!std::shared_ptr<RooAbsReal>::get()) {
1210 fData = _data.first;
1211 return true; // not loaded yet so nothing to do
1212 }
1213
1214 try {
1215 if (!kReuseNLL /*|| !mainTerm()*/
1216 /*|| mainTerm()->operMode() == RooAbsTestStatistic::MPMaster*/) { // lost access to RooAbsTestStatistic
1217 // in 6.34, but MP-mode will still throw
1218 // exception, so we will still catch it
1219 // happens when using MP need to rebuild the nll instead
1220 // also happens if there's no mainTerm(), which is the case in 6.32 where RooNLLVar is partially deprecated
1222 // ensure the const state is back where it was at nll construction time;
1223 fFuncVars->setAttribAll("Constant", false);
1224 fConstVars->setAttribAll("Constant", true);
1225 std::shared_ptr<RooAbsData> __data = fData; // do this just to keep fData alive while killing previous NLLVar
1226 // (can't kill data while NLL constructed with it)
1227 fData = _data.first;
1228 reinitialize();
1229 return true;
1230 }
1231 bool out = false;
1232 if (_data.first) {
1233 // replace in all terms
1234 out = get()->setData(*_data.first, false /* clone data */);
1235 // get()->setValueDirty();
1236 // if (_data.first->getGlobalObservables()) {
1237 // // replace in all terms
1238 // out = get()->setData(*_data.first, false);
1239 // get()->setValueDirty();
1240 // } else {
1241 // // replace just in mainTerm ... note to self: why not just replace in all like above? should
1242 // test! auto _mainTerm = mainTerm(); out = _mainTerm->setData(*_data.first, false /* clone data?
1243 // */); _mainTerm->setValueDirty();
1244 // }
1245 } else {
1246 reset();
1247 }
1248 fData = _data.first;
1249 return out;
1250 } catch (std::runtime_error &) {
1251 // happens when using MP need to rebuild the nll instead
1252 // also happens if there's no mainTerm(), which is the case in 6.32 where RooNLLVar is partially deprecated
1254 // ensure the const state is back where it was at nll construction time;
1255 fFuncVars->setAttribAll("Constant", false);
1256 fConstVars->setAttribAll("Constant", true);
1257 std::shared_ptr<RooAbsData> __data = fData; // do this just to keep fData alive while killing previous NLLVar
1258 // (can't kill data while NLL constructed with it)
1259 fData = _data.first;
1260 reinitialize();
1261 return true;
1262 }
1263 throw std::runtime_error("Unable to setData");
1264}
1265
1266std::shared_ptr<RooAbsReal> xRooNLLVar::func() const
1267{
1268 if (!(*this)) {
1269 const_cast<xRooNLLVar *>(this)->reinitialize();
1270 } else if (auto f = std::unique_ptr<RooAbsCollection>(fConstVars->selectByAttrib("Constant", false)); !f->empty()) {
1271 // have to reinitialize if const par values have changed - const optimization forces this
1272 // TODO: currently changes to globs also triggers this since the vars includes globs (vars are the non-obs pars)
1273 // std::cout << "Reinitializing because of change of const parameters:" << f->contentsString() << std::endl;
1274 const_cast<xRooNLLVar *>(this)->reinitialize();
1275
1276 // note ... it may be sufficient here to do:
1277 // nll.constOptimizeTestStatistic(RooAbsArg::ConfigChange, constOptimize>1 /* do tracking too if >1 */); //
1278 // trigger a re-evaluate of which nodes to cache-and-track nll.constOptimizeTestStatistic(RooAbsArg::ValueChange,
1279 // constOptimize>1); // update the cache values -- is this needed??
1280 // this forces the optimization to be redone
1281 // for now leave as a reinitialize though, until had a chance to test this properly
1282 }
1283 if (fGlobs && fFuncGlobs) {
1284 *fFuncGlobs = *fGlobs;
1285 fFuncGlobs->setAttribAll("Constant", true);
1286 }
1287 return *this;
1288}
1289
1291{
1292
1293 if (strcmp(opt.GetName(), "Hesse") == 0) {
1294 fitConfig()->SetParabErrors(opt.getInt(0)); // controls hesse
1295 } else if (strcmp(opt.GetName(), "Minos") == 0) {
1296 fitConfig()->SetMinosErrors(opt.getInt(0)); // controls minos
1297 } else if (strcmp(opt.GetName(), "Strategy") == 0) {
1298 fitConfig()->MinimizerOptions().SetStrategy(opt.getInt(0));
1299 } else if (strcmp(opt.GetName(), "StrategySequence") == 0) {
1300 fitConfigOptions()->SetNamedValue("StrategySequence", opt.getString(0));
1301 } else if (strcmp(opt.GetName(), "Tolerance") == 0) {
1302 fitConfig()->MinimizerOptions().SetTolerance(opt.getDouble(0));
1303 } else if (strcmp(opt.GetName(), "MaxCalls") == 0) {
1304 fitConfig()->MinimizerOptions().SetMaxFunctionCalls(opt.getInt(0));
1305 } else if (strcmp(opt.GetName(), "MaxIterations") == 0) {
1306 fitConfig()->MinimizerOptions().SetMaxIterations(opt.getInt(0));
1307 } else if (strcmp(opt.GetName(), "PrintLevel") == 0) {
1308 fitConfig()->MinimizerOptions().SetPrintLevel(opt.getInt(0));
1309 } else {
1310 if (strcmp(opt.GetName(), "Optimize") == 0) {
1311 // this flag will trigger constOptimizeTestStatistic to be called on the nll in createNLL method
1312 // we should ensure that the fitconfig setting is consistent with it ...
1313 fitConfigOptions()->SetValue("OptimizeConst", opt.getInt(0));
1314 }
1315 if (auto prevObject = fOpts->FindObject(opt.GetName()); prevObject) {
1316 // replace previous option
1317 fOpts->Replace(prevObject, opt.Clone(nullptr));
1318 delete prevObject;
1319 } else {
1320 fOpts->Add(opt.Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
1321 // "" instead, so doesnt copy names
1322 }
1323 if (std::shared_ptr<RooAbsReal>::get()) {
1324 reinitialize(); // do this way to keep name of nll if user set
1325 } else {
1326 reset(); // will trigger reinitialize
1327 }
1328 }
1329}
1330
1332{
1333 return fData.get();
1334 /*
1335#if ROOT_VERSION_CODE < ROOT_VERSION(6, 33, 00)
1336 auto _nll = mainTerm();
1337 if (!_nll)
1338 return fData.get();
1339 RooAbsData *out = &static_cast<RooAbsOptTestStatistic*>(_nll)->data();
1340#else
1341 RooAbsData* out = nullptr; // new backends not conducive to having a reference to a RooAbsData in them (they use
1342buffers instead) #endif if (!out) return fData.get(); return out;
1343 */
1344}
1345
1346/*
1347RooAbsReal *xRooNLLVar::mainTerm() const
1348{
1349 return nullptr;
1350 // the main term is the "other term" in a RooAddition alongside a ConstraintSum
1351 // if can't find the ConstraintSum, just return the function
1352
1353 RooAbsArg* _func = func().get();
1354 if(!_func->InheritsFrom("RooAddition")) {
1355 _func = nullptr;
1356 // happens with new 6.32 backend, where the top-level function is an EvaluatorWrapper
1357 for (auto s : func()->servers()) {
1358 if(s->InheritsFrom("RooAddition")) {
1359 _func = s; break;
1360 }
1361 }
1362 if(!_func) {
1363 return func().get();
1364 }
1365 }
1366 std::set<RooAbsArg*> others,constraints;
1367 for (auto s : _func->servers()) {
1368 if(s->InheritsFrom("RooConstraintSum")) {
1369 constraints.insert(s);
1370 } else {
1371 others.insert(s);
1372 }
1373 }
1374 if(constraints.size()==1 && others.size()==1) {
1375 return static_cast<RooAbsReal*>(*others.begin());
1376 }
1377 return nullptr; // failed to find the right term?
1378
1379
1380}
1381 */
1382
1384{
1385 // returns Nexp - Nobs*log(Nexp)
1386 return fPdf->extendedTerm(fData->sumEntries(), fData->get());
1387}
1388
1390{
1391 // comes from the _simCount code inside RooNLLVar
1392 // is this actually only appropriate if the roosimultaneous is not extended?
1393 // i.e. then this term represents the probability the entry belongs to a given state, and given
1394 // all the states are normalized to 1, this probability is assumed to just be 1/N_states
1395 if (auto s = dynamic_cast<RooSimultaneous *>(fPdf.get()); s) {
1396 return fData->sumEntries() * log(1.0 * (s->servers().size() - 1)); // one of the servers is the cat
1397 }
1398 return 0;
1399}
1400
1402{
1403 // this is only relevant if BinnedLikelihood active
1404 // = sum[ N_i! ] since LnGamma(N_i+1) ~= N_i!
1405 // need to also subtract off sum[ N_i*log(width_i) ] in order to have formula: binnedLL = unbinnedLL + binnedDataTerm
1406 // note this is 0 if all the bin widths are 1
1407 double out = 0;
1408 for (int i = 0; i < fData->numEntries(); i++) {
1409 fData->get(i);
1410 out += TMath::LnGamma(fData->weight() + 1) - fData->weight() * std::log(getEntryBinWidth(i));
1411 }
1412
1413 return out;
1414}
1415
1417{
1418 auto _func = func();
1419 if (auto a = dynamic_cast<RooConstraintSum *>(_func.get()); a)
1420 return a;
1421 for (auto s : _func->servers()) {
1422 if (auto a = dynamic_cast<RooConstraintSum *>(s); a)
1423 return a;
1424 // allow one more depth to support 6.32 (where sum is hidden inside the first server)
1425 for (auto s2 : s->servers()) {
1426 if (auto a2 = dynamic_cast<RooConstraintSum *>(s2); a2)
1427 return a2;
1428 }
1429 }
1430 return nullptr;
1431}
1432
1433/*xRooNLLVar::operator RooAbsReal &() const {
1434 // this works in c++ but not in python
1435 std::cout << "implicit conversion" << std::endl;
1436 return *fFunc;
1437}*/
1438
1440{
1442 sWhat.ToLower();
1443 sWhat.ReplaceAll(" =", "=").ReplaceAll("= ", "="); // remove spaces around equals signs
1444 bool doTS = sWhat.Contains("ts");
1445 bool doCLs = sWhat.Contains("pcls");
1446 bool doNull = sWhat.Contains("pnull");
1447 bool doAlt = sWhat.Contains("palt");
1448 double nSigma = (sWhat.Contains("exp"))
1449 ? (TString(sWhat(sWhat.Index("exp") + 3, sWhat.Index(" ", sWhat.Index("exp")) == -1
1450 ? sWhat.Length()
1451 : sWhat.Index(" ", sWhat.Index("exp"))))
1452 .Atof())
1453 : std::numeric_limits<double>::quiet_NaN();
1454
1455 bool toys = sWhat.Contains("toys");
1456
1457 // bool asymp = sWhat.Contains("asymp");
1458
1459 bool readOnly = sWhat.Contains("readonly");
1460
1461 if (!readOnly) {
1462 if (toys) {
1463 sigma_mu(); // means we will be able to evaluate the asymptotic values too
1464 }
1465 // only add toys if actually required
1466 if (getVal(sWhat + " readonly").second != 0) {
1467 if (sWhat.Contains("toys=")) {
1468 // extract number of toys required ... format is "nullToys.altToysFraction" if altToysFraction=0 then use
1469 // same for both, unless explicitly set (i.e. N.0) then means we want no alt toys
1470 // e.g. if doing just pnull significance
1471 TString toyNum = sWhat(sWhat.Index("toys=") + 5, sWhat.Length());
1472 size_t nToys = toyNum.Atoi();
1473 size_t nToysAlt = (toyNum.Atof() - nToys) * nToys;
1474 if (nToysAlt == 0 && !toyNum.Contains('.') && !doNull)
1475 nToysAlt = nToys;
1476 if (nullToys.size() < nToys) {
1477 addNullToys(nToys - nullToys.size());
1478 }
1479 if (altToys.size() < nToysAlt) {
1480 addAltToys(nToysAlt - altToys.size());
1481 }
1482 } else if (doCLs && toys) {
1483 // auto toy-generating for limits .. do in blocks of 100
1484 addCLsToys(100, 0, 0.05, nSigma);
1485 } else if (toys) {
1486 throw std::runtime_error("Auto-generating toys for anything other than CLs not yet supported, please "
1487 "specify number of toys with 'toys=N' ");
1488 }
1489 }
1490 }
1491
1492 struct RestoreNll {
1493 RestoreNll(std::shared_ptr<xRooNLLVar> &v, bool r) : rr(r), var(v)
1494 {
1495 if (rr && var && var->get()) {
1496 _readOnly = var->get()->getAttribute("readOnly");
1497 var->get()->setAttribute("readOnly", rr);
1498 } else {
1499 rr = false;
1500 }
1501 };
1502 ~RestoreNll()
1503 {
1504 if (rr)
1505 var->get()->setAttribute("readOnly", _readOnly);
1506 };
1507
1508 bool rr = false;
1509 bool _readOnly = false;
1510
1511 std::shared_ptr<xRooNLLVar> &var;
1512 };
1513
1514 RestoreNll rest(nllVar, readOnly);
1515
1516 if (doTS)
1517 return (toys) ? ts_toys(nSigma) : ts_asymp(nSigma);
1518 if (doNull)
1519 return (toys) ? pNull_toys(nSigma) : pNull_asymp(nSigma);
1520 if (doAlt)
1521 return (toys) ? pAlt_toys(nSigma) : pAlt_asymp(nSigma);
1522 if (doCLs)
1523 return (toys) ? pCLs_toys(nSigma) : pCLs_asymp(nSigma);
1524
1525 throw std::runtime_error(std::string("Unknown: ") + what);
1526}
1527
1529{
1530 RooArgList out;
1531 out.setName("poi");
1532 out.add(*std::unique_ptr<RooAbsCollection>(coords->selectByAttrib("poi", true)));
1533 return out;
1534}
1535
1537{
1538 RooArgList out;
1539 out.setName("alt_poi");
1540 out.addClone(*std::unique_ptr<RooAbsCollection>(coords->selectByAttrib("poi", true)));
1541 for (auto a : out) {
1542 auto v = dynamic_cast<RooAbsRealLValue *>(a);
1543 if (!v)
1544 continue;
1545 if (auto s = a->getStringAttribute("altVal"); s && strlen(s)) {
1546 v->setVal(TString(s).Atof());
1547 } else {
1548 v->setVal(std::numeric_limits<double>::quiet_NaN());
1549 }
1550 }
1551 return out;
1552}
1553
1555{
1556 auto &me = const_cast<xRooHypoPoint &>(*this);
1557 int out = 0;
1558 if (me.ufit(true) && !allowedStatusCodes.count(me.ufit(true)->status()))
1559 out += 1;
1560 if (me.cfit_null(true) && !allowedStatusCodes.count(me.cfit_null(true)->status()))
1561 out += 1 << 1;
1562 if (me.cfit_alt(true) && !allowedStatusCodes.count(me.cfit_alt(true)->status()))
1563 out += 1 << 2;
1564 if (me.asimov(true))
1565 out += me.asimov(true)->status() << 3;
1566 return out;
1567}
1568
1570{
1571 auto _poi = const_cast<xRooHypoPoint *>(this)->poi();
1572 auto _alt_poi = const_cast<xRooHypoPoint *>(this)->alt_poi();
1573 std::cout << "POI: " << _poi.contentsString() << " , null: ";
1574 bool first = true;
1575 for (auto a : _poi) {
1576 auto v = dynamic_cast<RooAbsReal *>(a);
1577 if (!a)
1578 continue;
1579 if (!first)
1580 std::cout << ",";
1581 std::cout << v->getVal();
1582 first = false;
1583 }
1584 std::cout << " , alt: ";
1585 first = true;
1586 bool any_alt = false;
1587 for (auto a : _alt_poi) {
1588 auto v = dynamic_cast<RooAbsReal *>(a);
1589 if (!a)
1590 continue;
1591 if (!first)
1592 std::cout << ",";
1593 std::cout << v->getVal();
1594 first = false;
1595 if (!std::isnan(v->getVal()))
1596 any_alt = true;
1597 }
1598 std::cout << " , pllType: ";
1599 switch (fPllType) {
1600 case 0: std::cout << "tmu"; break;
1601 case 1: std::cout << "qmu or qmutilde"; break; // should check for 'physical' to decide if is latter
1602 case 2: std::cout << "q0"; break;
1603 case 4: std::cout << "u0"; break;
1604 default: std::cout << "unknown"; break;
1605 }
1606 std::cout << std::endl;
1607
1608 if (fPllType == xRooFit::Asymptotics::Unknown) {
1609 std::cout << " obs ts: " << obs_ts << " +/- " << obs_ts_err << std::endl;
1610 }
1611
1612 std::cout << " - ufit: ";
1613 if (fUfit) {
1614 std::cout << fUfit->GetName() << " " << fUfit->minNll() << " (status=" << fUfit->status() << ") (";
1615 first = true;
1616 for (auto a : _poi) {
1617 auto v = dynamic_cast<RooRealVar *>(fUfit->floatParsFinal().find(a->GetName()));
1618 if (!v)
1619 continue;
1620 if (!first)
1621 std::cout << ",";
1622 std::cout << v->GetName() << "_hat: " << v->getVal() << " +/- " << v->getError();
1623 first = false;
1624 }
1625 std::cout << ")" << std::endl;
1626 } else {
1627 std::cout << "Not calculated" << std::endl;
1628 }
1629 std::cout << " - cfit_null: ";
1630 if (fNull_cfit) {
1631 std::cout << fNull_cfit->GetName() << " " << fNull_cfit->minNll() << " (status=" << fNull_cfit->status() << ")";
1632 } else {
1633 std::cout << "Not calculated";
1634 }
1635 if (any_alt) {
1636 std::cout << std::endl << " - cfit_alt: ";
1637 if (fAlt_cfit) {
1638 std::cout << fAlt_cfit->GetName() << " " << fAlt_cfit->minNll() << " (status=" << fAlt_cfit->status() << ")"
1639 << std::endl;
1640 } else {
1641 std::cout << "Not calculated" << std::endl;
1642 }
1643 std::cout << " sigma_mu: ";
1644 const_cast<xRooHypoPoint *>(this)->asimov(true); // will trigger construction of fAsimov hypoPoint if possible
1645 if (!fAsimov || !fAsimov->fUfit || !fAsimov->fNull_cfit) {
1646 std::cout << "Not calculated";
1647 } else {
1648 std::cout << const_cast<xRooHypoPoint *>(this)->sigma_mu().first << " +/- "
1649 << const_cast<xRooHypoPoint *>(this)->sigma_mu().second;
1650 }
1651 if (fAsimov) {
1652 std::cout << std::endl;
1653 std::cout << " - asimov ufit: ";
1654 if (fAsimov->fUfit) {
1655 std::cout << fAsimov->fUfit->GetName() << " " << fAsimov->fUfit->minNll()
1656 << " (status=" << fAsimov->fUfit->status() << ")";
1657 } else {
1658 std::cout << "Not calculated";
1659 }
1660 std::cout << std::endl << " - asimov cfit_null: ";
1661 if (fAsimov->fNull_cfit) {
1662 std::cout << fAsimov->fNull_cfit->GetName() << " " << fAsimov->fNull_cfit->minNll()
1663 << " (status=" << fAsimov->fNull_cfit->status() << ")";
1664 } else {
1665 std::cout << "Not calculated";
1666 }
1667 }
1668 std::cout << std::endl;
1669 } else {
1670 std::cout << std::endl;
1671 }
1672 if (fLbound_cfit) {
1673 std::cout << " - cfit_lbound: " << fLbound_cfit->GetName() << " " << fLbound_cfit->minNll()
1674 << " (status=" << fLbound_cfit->status() << ")" << std::endl;
1675 }
1676 if (fGenFit)
1677 std::cout << " - gfit: " << fGenFit->GetName() << std::endl;
1678 if (!nullToys.empty() || !altToys.empty()) {
1679 std::cout << " * null toys: " << nullToys.size();
1680 size_t firstToy = 0;
1681 while (firstToy < nullToys.size() && std::isnan(std::get<1>(nullToys[firstToy])))
1682 firstToy++;
1683 if (firstToy > 0)
1684 std::cout << " [ of which " << firstToy << " are bad]";
1685 std::cout << " , alt toys: " << altToys.size();
1686 firstToy = 0;
1687 while (firstToy < altToys.size() && std::isnan(std::get<1>(altToys[firstToy])))
1688 firstToy++;
1689 if (firstToy > 0)
1690 std::cout << " [ of which " << firstToy << " are bad]";
1691 std::cout << std::endl;
1692 }
1693 // std::cout << " nllVar: " << nllVar << std::endl;
1694}
1695
1697{
1698 if (ufit()) {
1699 auto var = dynamic_cast<RooRealVar *>(ufit()->floatParsFinal().find(fPOIName()));
1700 if (var) {
1701 return *var;
1702 } else {
1703 throw std::runtime_error(TString::Format("Cannot find POI: %s", fPOIName()));
1704 }
1705 }
1706 throw std::runtime_error("Unconditional fit unavailable");
1707}
1708
1709std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> xRooNLLVar::xRooHypoPoint::data()
1710{
1711 if (fData.first)
1712 return fData;
1713 if (fGenFit && isExpected) {
1714 // std::cout << "Generating asimov" << std::endl;poi().Print("v");
1715 fData = xRooFit::generateFrom(*nllVar->fPdf, *fGenFit, true);
1716 }
1717 return fData;
1718}
1719
1720xRooNLLVar::xRooHypoPoint::xRooHypoPoint(std::shared_ptr<RooStats::HypoTestResult> htr, const RooAbsCollection *_coords)
1721 : hypoTestResult(htr)
1722{
1723 if (hypoTestResult) {
1724 // load the pllType
1725 fPllType =
1726 xRooFit::Asymptotics::PLLType(hypoTestResult->GetFitInfo()->getGlobalObservables()->getCatIndex("pllType"));
1727 isExpected = hypoTestResult->GetFitInfo()->getGlobalObservables()->getRealValue("isExpected");
1728
1729 // load obsTS value
1730 obs_ts = hypoTestResult->GetTestStatisticData();
1731 obs_ts_err = hypoTestResult->GetFitInfo()->getGlobalObservables()->getRealValue("obs_ts_err");
1732
1733 // load the toys
1734 auto toys = hypoTestResult->GetNullDetailedOutput();
1735 if (toys) {
1736 // load coords from the nullDist globs list
1737 if (toys->getGlobalObservables()) {
1738 coords = std::shared_ptr<RooAbsCollection>(toys->getGlobalObservables()->snapshot());
1739 }
1740 for (int i = 0; i < toys->numEntries(); i++) {
1741 auto toy = toys->get(i);
1742 nullToys.emplace_back(
1743 std::make_tuple(int(toy->getRealValue("seed")), toy->getRealValue("ts"), toys->weight()));
1744 }
1745 }
1746 toys = hypoTestResult->GetAltDetailedOutput();
1747 if (toys) {
1748 for (int i = 0; i < toys->numEntries(); i++) {
1749 auto toy = toys->get(i);
1750 altToys.emplace_back(
1751 std::make_tuple(int(toy->getRealValue("seed")), toy->getRealValue("ts"), toys->weight()));
1752 }
1753 }
1754 }
1755 if (!coords && _coords)
1756 coords.reset(_coords->snapshot());
1757}
1758
1759std::shared_ptr<xRooNLLVar::xRooHypoPoint> xRooNLLVar::xRooHypoPoint::asimov(bool readOnly)
1760{
1761
1762 if (!fAsimov && (nllVar || hypoTestResult)) {
1763 auto theFit = (!fData.first && fGenFit && !isExpected)
1764 ? fGenFit
1765 : cfit_alt(readOnly); // first condition allows genFit to be used as the altFit *if* the data is
1766 // entirely absent, provided not expected data because we postpone data
1767 // creation til later in that case (see below)
1768 if (!theFit || allowedStatusCodes.find(theFit->status()) == allowedStatusCodes.end())
1769 return fAsimov;
1770 fAsimov = std::make_shared<xRooHypoPoint>(*this);
1771 fAsimov->coords.reset(fAsimov->coords->snapshot()); // create a copy so can remove the physical range below
1772 fAsimov->hypoTestResult.reset();
1773 fAsimov->fPllType = xRooFit::Asymptotics::TwoSided;
1774 for (auto p : fAsimov->poi()) {
1775 // dynamic_cast<RooRealVar *>(p)->removeRange("physical"); -- can't use this as will modify shared property
1776 if (auto v = dynamic_cast<RooRealVar *>(p)) {
1777 v->deleteSharedProperties(); // effectively removes all custom ranges
1778 if (v->getVal() == 0) {
1779 // for discovery tests, we generate asimov at mu!=0 and then evaluate the two sided
1780 // at some value of mu. Normally we would use mu=0 but if we have a bin
1781 // with only signal contribution (no bkg) will get asimov data in that bin
1782 // and no prediction ... the cfit(mu=0) will never succeed on this
1783 // so lets move to half the alt value instead (the value used to generate)
1784 v->setVal(theFit->constPars().getRealValue(v->GetName()) * 0.5);
1785 }
1786 }
1787 }
1788
1789 fAsimov->nullToys.clear();
1790 fAsimov->altToys.clear();
1791 fAsimov->fUfit = retrieveFit(3);
1792 fAsimov->fNull_cfit = retrieveFit(4);
1793 fAsimov->fAlt_cfit.reset();
1794 fAsimov->fData =
1795 std::make_pair(nullptr, nullptr); // postpone generating expected data until we definitely need it
1796 fAsimov->fGenFit = theFit;
1797 fAsimov->isExpected = true;
1798 }
1799
1800 return fAsimov;
1801}
1802
1804{
1805 if (fPllType != xRooFit::Asymptotics::Uncapped && ts_asymp(nSigma).first == 0)
1806 return std::pair<double, double>(1, 0);
1807 auto first_poi = dynamic_cast<RooRealVar *>(poi().first());
1808 if (!first_poi)
1809 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1810 double lowBound = first_poi->getMin("physical");
1811 double hiBound = first_poi->getMax("physical");
1812 // don't need to calculate sigma_mu if physical boundaries at infinity, PValue doesn't depend on it
1813 auto _sigma_mu =
1814 (lowBound == -std::numeric_limits<double>::infinity() && hiBound == std::numeric_limits<double>::infinity())
1815 ? std::pair<double, double>(0, 0)
1816 : sigma_mu();
1817 double nom = xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first, fNullVal(), fNullVal(), _sigma_mu.first,
1818 lowBound, hiBound);
1819 double up = xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first + ts_asymp(nSigma).second, fNullVal(),
1820 fNullVal(), _sigma_mu.first, lowBound, hiBound);
1821 double down = xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first - ts_asymp(nSigma).second, fNullVal(),
1822 fNullVal(), _sigma_mu.first, lowBound, hiBound);
1823 return std::pair(nom, std::max(std::abs(up - nom), std::abs(down - nom)));
1824}
1825
1827{
1828 if (fPllType != xRooFit::Asymptotics::Uncapped && ts_asymp(nSigma).first == 0)
1829 return std::pair<double, double>(1, 0);
1830 auto first_poi = dynamic_cast<RooRealVar *>(poi().first());
1831 if (!first_poi)
1832 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1833 auto _sigma_mu = sigma_mu();
1834 double nom = xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first, fNullVal(), fAltVal(), _sigma_mu.first,
1835 first_poi->getMin("physical"), first_poi->getMax("physical"));
1836 double up =
1837 xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first + ts_asymp(nSigma).second, fNullVal(), fAltVal(),
1838 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1839 double down =
1840 xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first - ts_asymp(nSigma).second, fNullVal(), fAltVal(),
1841 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1842
1843 return std::pair(nom, std::max(std::abs(up - nom), std::abs(down - nom)));
1844}
1845
1847{
1848 if (fNullVal() == fAltVal())
1849 return std::pair<double, double>(1, 0); // by construction
1850
1851 if (fPllType != xRooFit::Asymptotics::Uncapped && ts_asymp(nSigma).first == 0)
1852 return std::pair<double, double>(1, 0);
1853 auto first_poi = dynamic_cast<RooRealVar *>(poi().first());
1854 if (!first_poi)
1855 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1856
1857 auto _ts_asymp = ts_asymp(nSigma);
1858 auto _sigma_mu = sigma_mu();
1859 double nom1 = xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first, fNullVal(), fNullVal(), _sigma_mu.first,
1860 first_poi->getMin("physical"), first_poi->getMax("physical"));
1861 double up1 =
1862 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first + _ts_asymp.second, fNullVal(), fNullVal(),
1863 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1864 double down1 =
1865 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first - _ts_asymp.second, fNullVal(), fNullVal(),
1866 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1867 double nom2 = xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first, fNullVal(), fAltVal(), _sigma_mu.first,
1868 first_poi->getMin("physical"), first_poi->getMax("physical"));
1869 double up2 =
1870 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first + _ts_asymp.second, fNullVal(), fAltVal(), _sigma_mu.first,
1871 first_poi->getMin("physical"), first_poi->getMax("physical"));
1872 double down2 =
1873 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first - _ts_asymp.second, fNullVal(), fAltVal(), _sigma_mu.first,
1874 first_poi->getMin("physical"), first_poi->getMax("physical"));
1875
1876 auto nom = (nom1 == 0) ? 0 : nom1 / nom2;
1877 auto up = (up1 == 0) ? 0 : up1 / up2;
1878 auto down = (down1 == 0) ? 0 : down1 / down2;
1879
1880 return std::pair(nom, std::max(std::abs(up - nom), std::abs(down - nom)));
1881}
1882
1884{
1885 if (std::isnan(nSigma)) {
1886 return (fPllType == xRooFit::Asymptotics::Unknown) ? std::make_pair(obs_ts, obs_ts_err) : pll();
1887 }
1888
1889 auto first_poi = dynamic_cast<RooRealVar *>(poi().first());
1890 auto _sigma_mu = sigma_mu();
1891 if (!first_poi || (!std::isnan(nSigma) && std::isnan(_sigma_mu.first)))
1892 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1893 double nom = xRooFit::Asymptotics::k(fPllType, ROOT::Math::gaussian_cdf(nSigma), fNullVal(), fAltVal(),
1894 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1895 double up = xRooFit::Asymptotics::k(fPllType, ROOT::Math::gaussian_cdf(nSigma), fNullVal(), fAltVal(),
1896 _sigma_mu.first + _sigma_mu.second, first_poi->getMin("physical"),
1897 first_poi->getMax("physical"));
1898 double down = xRooFit::Asymptotics::k(fPllType, ROOT::Math::gaussian_cdf(nSigma), fNullVal(), fAltVal(),
1899 _sigma_mu.first - _sigma_mu.second, first_poi->getMin("physical"),
1900 first_poi->getMax("physical"));
1901 return std::pair<double, double>(nom, std::max(std::abs(nom - up), std::abs(nom - down)));
1902}
1903
1905{
1906 if (std::isnan(nSigma)) {
1907 return ts_asymp();
1908 }
1909 // nans should appear in the alt toys first ... so loop until past nans
1910 size_t firstToy = 0;
1911 while (firstToy < altToys.size() && std::isnan(std::get<1>(altToys[firstToy])))
1912 firstToy++;
1913 if (firstToy >= altToys.size())
1914 return std::pair(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
1915 int targetIdx =
1916 (altToys.size() - firstToy) * ROOT::Math::gaussian_cdf_c(nSigma) + firstToy; // TODO: Account for weights
1917 return std::pair(std::get<1>(altToys[targetIdx]), (std::get<1>(altToys[std::min(int(altToys.size()), targetIdx)]) -
1918 std::get<1>(altToys[std::max(0, targetIdx)])) /
1919 2.);
1920}
1921
1923{
1924 auto _ufit = ufit(readOnly);
1925 if (!_ufit) {
1926 if (hypoTestResult)
1927 return std::pair<double, double>(hypoTestResult->GetTestStatisticData(), 0);
1928 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1929 }
1930 if (allowedStatusCodes.find(_ufit->status()) == allowedStatusCodes.end()) {
1931 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1932 }
1933 if (auto _first_poi = dynamic_cast<RooRealVar *>(poi().first());
1934 _first_poi && _first_poi->getMin("physical") > _first_poi->getMin() &&
1935 mu_hat().getVal() < _first_poi->getMin("physical")) {
1936 // replace _ufit with fit "boundary" conditional fit
1937 _ufit = cfit_lbound(readOnly);
1938 if (!_ufit) {
1939 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1940 }
1941 }
1942 auto cFactor = (fPllType == xRooFit::Asymptotics::TwoSided)
1943 ? 1.
1944 : xRooFit::Asymptotics::CompatFactor(fPllType, fNullVal(), mu_hat().getVal());
1945 if (cFactor == 0)
1946 return std::pair<double, double>(0, 0);
1947 auto _cfit_null = cfit_null(readOnly);
1948 if (!_cfit_null || allowedStatusCodes.find(_cfit_null->status()) == allowedStatusCodes.end())
1949 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1950 // std::cout << cfit->minNll() << ":" << cfit->edm() << " " << ufit->minNll() << ":" << ufit->edm() << std::endl;
1951 double diff = _cfit_null->minNll() - _ufit->minNll();
1952 if (diff < 0) {
1953 // use edm to attempt a small correction to the diff, i.e. assume edm is an additional correction required to
1954 // minNll
1955 diff += (_ufit->edm() - _cfit_null->edm());
1956 }
1957 return std::pair<double, double>(2. * cFactor * diff,
1958 2. * cFactor * sqrt(pow(cfit_null(readOnly)->edm(), 2) + pow(_ufit->edm(), 2)));
1959 // return 2.*cFactor*(cfit->minNll()+cfit->edm() - ufit->minNll()+ufit->edm());
1960}
1961
1962std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::retrieveFit(int type)
1963{
1964 if (!hypoTestResult)
1965 return nullptr;
1966 // see if can retrieve from that ....
1967 if (auto fits = hypoTestResult->GetFitInfo()) {
1968 for (int i = 0; i < fits->numEntries(); i++) {
1969 auto fit = fits->get(i);
1970 if (fit->getCatIndex("type") != type)
1971 continue;
1972 // found ufit ... construct
1973 std::string _name =
1974 fits->getGlobalObservables()->getStringValue(TString::Format("%s.name", fit->getCatLabel("type")));
1975 // see if can retrieve from any open file ....
1977 for (auto file : *gROOT->GetListOfFiles()) {
1978 if (auto k = static_cast<TDirectory *>(file)->FindKeyAny(_name.c_str())) {
1979 // use pre-retrieved fits if available
1981 k->GetMotherDir()->GetList()
1982 ? dynamic_cast<xRooFit::StoredFitResult *>(k->GetMotherDir()->GetList()->FindObject(k->GetName()))
1983 : nullptr;
1984 if (auto cachedFit = (storedFr) ? storedFr->fr.get() : k->ReadObject<RooFitResult>(); cachedFit) {
1985 if (!storedFr) {
1987 k->GetMotherDir()->Add(storedFr);
1988 }
1989 gDirectory = tmp; // one of the above calls moves to key's directory ... i didn't check which
1990 return storedFr->fr;
1991 }
1992 }
1993 }
1994 auto rfit = std::make_shared<RooFitResult>(_name.c_str(), TUUID(_name.c_str()).GetTime().AsString());
1995 rfit->setStatus(fit->getRealValue("status"));
1996 rfit->setMinNLL(fit->getRealValue("minNll"));
1997 rfit->setEDM(fit->getRealValue("edm"));
1998 if (type == 0) {
1999 std::unique_ptr<RooAbsCollection> par_hats(
2000 hypoTestResult->GetFitInfo()->getGlobalObservables()->selectByName(coords->contentsString().c_str()));
2001 par_hats->setName("floatParsFinal");
2002 rfit->setFinalParList(*par_hats);
2003 } else {
2004 rfit->setFinalParList(RooArgList());
2005 }
2006 rfit->setConstParList(RooArgList());
2007 rfit->setInitParList(RooArgList());
2008 TMatrixDSym cov(0);
2009 rfit->setCovarianceMatrix(cov);
2010 rfit->setCovQual(fit->getRealValue("covQual"));
2011 return rfit;
2012 }
2013 }
2014 return nullptr;
2015}
2016
2017std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::ufit(bool readOnly)
2018{
2019 if (fUfit)
2020 return fUfit;
2021 if (auto rfit = retrieveFit(0)) {
2022 return fUfit = rfit;
2023 }
2024 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2025 return nullptr;
2026 if (!nllVar->fFuncVars)
2027 nllVar->reinitialize();
2028 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2029 if (!fData.first) {
2030 if (!readOnly && isExpected && fGenFit) {
2031 // can try to do a readOnly in case can load from cache
2032 bool tmp = nllVar->get()->getAttribute("readOnly");
2033 nllVar->get()->setAttribute("readOnly");
2034 auto out = ufit(true);
2035 nllVar->get()->setAttribute("readOnly", tmp);
2036 if (out) {
2037 // retrieve from cache worked, no need to generate dataset
2038 return out;
2039 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2040 nllVar->setData(data());
2041 }
2042 }
2043 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2044 nllVar->setData(fData);
2045 }
2046 nllVar->fFuncVars->setAttribAll("Constant", false);
2047 *nllVar->fFuncVars = *coords; // will reconst the coords
2048 if (nllVar->fFuncGlobs)
2049 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2050 std::unique_ptr<RooAbsCollection>(nllVar->fFuncVars->selectCommon(poi()))
2051 ->setAttribAll("Constant", false); // float the poi
2052 if (fGenFit) {
2053 // make initial guess same as pars we generated with
2054 nllVar->fFuncVars->assignValueOnly(fGenFit->constPars());
2055 nllVar->fFuncVars->assignValueOnly(fGenFit->floatParsFinal());
2056 // rename nll so if caching fit results will cache into subdir
2057 nllVar->get()->SetName(
2058 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2059 if (!isExpected)
2060 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2061
2062 } else if (!std::isnan(fAltVal())) {
2063 // guess data given is expected to align with alt value, unless initVal attribute specified
2064 for (auto _poiCoord : poi()) {
2065 auto _poi = dynamic_cast<RooRealVar *>(nllVar->fFuncVars->find(_poiCoord->GetName()));
2066 if (_poi) {
2067 _poi->setVal(_poi->getStringAttribute("initVal") ? TString(_poi->getStringAttribute("initVal")).Atof()
2068 : fAltVal());
2069 }
2070 }
2071 }
2072 return (fUfit = nllVar->minimize());
2073}
2074
2076{
2077 std::string out;
2078 for (auto &c : coll) {
2079 if (!out.empty())
2080 out += ",";
2081 out += c->GetName();
2082 if (auto v = dynamic_cast<RooAbsReal *>(c); v) {
2083 out += TString::Format("=%g", v->getVal());
2084 } else if (auto cc = dynamic_cast<RooAbsCategory *>(c); cc) {
2085 out += TString::Format("=%s", cc->getLabel());
2086 } else if (auto s = dynamic_cast<RooStringVar *>(c); v) {
2087 out += TString::Format("=%s", s->getVal());
2088 }
2089 }
2090 return out;
2091}
2092
2093std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::cfit_null(bool readOnly)
2094{
2095 if (fNull_cfit)
2096 return fNull_cfit;
2097 if (auto rfit = retrieveFit(1)) {
2098 return fNull_cfit = rfit;
2099 }
2100 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2101 return nullptr;
2102 if (!nllVar->fFuncVars)
2103 nllVar->reinitialize();
2104 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2105 if (!fData.first) {
2106 if (!readOnly && isExpected && fGenFit) {
2107 // can try to do a readOnly in case can load from cache
2108 bool tmp = nllVar->get()->getAttribute("readOnly");
2109 nllVar->get()->setAttribute("readOnly");
2110 auto out = cfit_null(true);
2111 nllVar->get()->setAttribute("readOnly", tmp);
2112 if (out) {
2113 // retrieve from cache worked, no need to generate dataset
2114 return out;
2115 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2116 nllVar->setData(data());
2117 }
2118 }
2119 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2120 nllVar->setData(fData);
2121 }
2122 if (fUfit) {
2123 // move to ufit coords before evaluating
2124 *nllVar->fFuncVars = fUfit->floatParsFinal();
2125 }
2126 nllVar->fFuncVars->setAttribAll("Constant", false);
2127 *nllVar->fFuncVars = *coords; // will reconst the coords
2128 if (nllVar->fFuncGlobs)
2129 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2130 if (fPOIName()) {
2131 nllVar->fFuncVars->find(fPOIName())
2132 ->setStringAttribute("altVal", (!std::isnan(fAltVal())) ? TString::Format("%g", fAltVal()) : nullptr);
2133 }
2134 if (fGenFit) {
2135 nllVar->get()->SetName(
2136 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2137 if (!isExpected)
2138 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2139 }
2140 nllVar->get()->setStringAttribute("fitresultTitle", collectionContents(poi()).c_str());
2141 return (fNull_cfit = nllVar->minimize());
2142}
2143
2144std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::cfit_lbound(bool readOnly)
2145{
2146 auto _first_poi = dynamic_cast<RooRealVar *>(poi().first());
2147 if (!_first_poi)
2148 return nullptr;
2149 if (_first_poi->getMin("physical") <= _first_poi->getMin())
2150 return nullptr;
2151 if (fLbound_cfit)
2152 return fLbound_cfit;
2153 if (auto rfit = retrieveFit(6)) {
2154 return fLbound_cfit = rfit;
2155 }
2156 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2157 return nullptr;
2158 if (!nllVar->fFuncVars)
2159 nllVar->reinitialize();
2160 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2161 if (!fData.first) {
2162 if (!readOnly && isExpected && fGenFit) {
2163 // can try to do a readOnly in case can load from cache
2164 bool tmp = nllVar->get()->getAttribute("readOnly");
2165 nllVar->get()->setAttribute("readOnly");
2166 auto out = cfit_lbound(true);
2167 nllVar->get()->setAttribute("readOnly", tmp);
2168 if (out) {
2169 // retrieve from cache worked, no need to generate dataset
2170 return out;
2171 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2172 nllVar->setData(data());
2173 }
2174 }
2175 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2176 nllVar->setData(fData);
2177 }
2178 if (fUfit) {
2179 // move to ufit coords before evaluating
2180 *nllVar->fFuncVars = fUfit->floatParsFinal();
2181 }
2182 nllVar->fFuncVars->setAttribAll("Constant", false);
2183 *nllVar->fFuncVars = *coords; // will reconst the coords
2184 nllVar->fFuncVars->setRealValue(_first_poi->GetName(), _first_poi->getMin("physical"));
2185 if (nllVar->fFuncGlobs)
2186 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2187 if (fPOIName()) {
2188 nllVar->fFuncVars->find(fPOIName())
2189 ->setStringAttribute("altVal", (!std::isnan(fAltVal())) ? TString::Format("%g", fAltVal()) : nullptr);
2190 }
2191 if (fGenFit) {
2192 nllVar->get()->SetName(
2193 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2194 if (!isExpected)
2195 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2196 }
2197 nllVar->get()->setStringAttribute(
2198 "fitresultTitle",
2199 collectionContents(*std::unique_ptr<RooAbsCollection>(nllVar->fFuncVars->selectCommon(poi()))).c_str());
2200 return (fLbound_cfit = nllVar->minimize());
2201}
2202
2203std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::cfit_alt(bool readOnly)
2204{
2205 if (std::isnan(fAltVal()))
2206 return nullptr;
2207 if (fAlt_cfit)
2208 return fAlt_cfit;
2209 if (auto rfit = retrieveFit(2)) {
2210 return fAlt_cfit = rfit;
2211 }
2212 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2213 return nullptr;
2214 if (!nllVar->fFuncVars)
2215 nllVar->reinitialize();
2216 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2217 if (!fData.first) {
2218 if (!readOnly && isExpected && fGenFit) {
2219 // can try to do a readOnly in case can load from cache
2220 bool tmp = nllVar->get()->getAttribute("readOnly");
2221 nllVar->get()->setAttribute("readOnly");
2222 auto out = cfit_alt(true);
2223 nllVar->get()->setAttribute("readOnly", tmp);
2224 if (out) {
2225 // retrieve from cache worked, no need to generate dataset
2226 return out;
2227 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2228 nllVar->setData(data());
2229 }
2230 }
2231 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2232 nllVar->setData(fData);
2233 }
2234 if (fUfit) {
2235 // move to ufit coords before evaluating
2236 *nllVar->fFuncVars = fUfit->floatParsFinal();
2237 }
2238 nllVar->fFuncVars->setAttribAll("Constant", false);
2239 *nllVar->fFuncVars = *coords; // will reconst the coords
2240 if (nllVar->fFuncGlobs)
2241 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2242 *nllVar->fFuncVars = alt_poi();
2243 if (fGenFit) {
2244 nllVar->get()->SetName(
2245 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2246 if (!isExpected)
2247 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2248 }
2249 nllVar->get()->setStringAttribute("fitresultTitle", collectionContents(alt_poi()).c_str());
2250 return (fAlt_cfit = nllVar->minimize());
2251}
2252
2254{
2255
2256 auto asi = asimov(readOnly);
2257
2258 if (!asi) {
2259 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
2260 }
2261
2262 auto out = asi->pll(readOnly);
2263 return std::pair<double, double>(std::abs(asi->fNullVal() - fAltVal()) / sqrt(out.first),
2264 out.second * 0.5 * std::abs(asi->fNullVal() - fAltVal()) /
2265 (out.first * sqrt(out.first)));
2266}
2267
2269{
2270 auto _ts = ts_toys(nSigma);
2271 if (std::isnan(_ts.first))
2272 return _ts;
2273 if (fPllType != xRooFit::Asymptotics::Uncapped && _ts.first == 0)
2274 return std::pair<double, double>(1, 0); // don't need toys to compute this point!
2275
2276 TEfficiency eff("", "", 1, 0, 1);
2277
2278 auto &_theToys = (alt) ? altToys : nullToys;
2279
2280 if (_theToys.empty()) {
2281 return std::pair(0.5, std::numeric_limits<double>::infinity());
2282 }
2283
2284 // loop over toys, count how many are > ts value
2285 // nans (mean bad ts evaluations) will count towards uncertainty
2286 int nans = 0;
2287 double result = 0;
2288 double result_err_up = 0;
2289 double result_err_down = 0;
2290 for (auto &toy : _theToys) {
2291 if (std::isnan(std::get<1>(toy))) {
2292 nans++;
2293 } else {
2294 bool res = std::get<1>(toy) >= _ts.first;
2295 if (std::get<2>(toy) != 1) {
2296 eff.FillWeighted(res, 0.5, std::get<2>(toy));
2297 } else {
2298 eff.Fill(res, 0.5);
2299 }
2300 if (res)
2301 result += std::get<2>(toy);
2302 if (std::get<1>(toy) >= _ts.first - _ts.second)
2303 result_err_up += std::get<2>(toy);
2304 if (std::get<1>(toy) >= _ts.first - _ts.second)
2305 result_err_down += std::get<2>(toy);
2306 }
2307 }
2308 // symmetrize the error
2311 double result_err = std::max(std::abs(result_err_up), std::abs(result_err_down));
2312 // assume the nans would "add" to the p-value, conservative scenario
2313 result_err += nans;
2314 result_err /= _theToys.size();
2315
2316 // don't include the nans for the central value though
2317 result /= (_theToys.size() - nans);
2318
2319 // add to the result_err (in quadrature) the uncert due to limited stats
2321 return std::pair<double, double>(result, result_err);
2322}
2323
2328
2330{
2331 if (!std::isnan(nSigma)) {
2332 return std::pair<double, double>(ROOT::Math::gaussian_cdf(nSigma), 0); // by construction
2333 }
2334 return pX_toys(true, nSigma);
2335}
2336
2338{
2339 xRooHypoPoint out;
2340 out.coords = coords;
2341 out.fPllType = fPllType; // out.fPOIName = fPOIName; out.fNullVal=fNullVal; out.fAltVal = fAltVal;
2342 out.nllVar = nllVar;
2343 if (!nllVar)
2344 return out;
2345 auto _cfit = cfit_null();
2346 if (!_cfit)
2347 return out;
2348 if (!nllVar->fFuncVars)
2349 nllVar->reinitialize();
2350 //*nllVar->fFuncVars = cfit_null()->floatParsFinal();
2351 //*nllVar->fFuncVars = cfit_null()->constPars();
2352 out.fData = xRooFit::generateFrom(*nllVar->fPdf, *_cfit, false, seed); // nllVar->generate(false,seed);
2353 out.fGenFit = _cfit;
2354 return out;
2355}
2356
2358{
2359 xRooHypoPoint out;
2360 out.coords = coords;
2361 out.fPllType = fPllType; // out.fPOIName = fPOIName; out.fNullVal=fNullVal; out.fAltVal = fAltVal;
2362 out.nllVar = nllVar;
2363 if (!nllVar)
2364 return out;
2365 if (!cfit_alt())
2366 return out;
2367 if (!nllVar->fFuncVars)
2368 nllVar->reinitialize();
2369 //*nllVar->fFuncVars = cfit_alt()->floatParsFinal();
2370 //*nllVar->fFuncVars = cfit_alt()->constPars();
2371 out.fData =
2372 xRooFit::generateFrom(*nllVar->fPdf, *cfit_alt(), false, seed); // out.data = nllVar->generate(false,seed);
2373 out.fGenFit = cfit_alt();
2374 return out;
2375}
2376
2378 bool targetCLs, double relErrThreshold, size_t maxToys)
2379{
2380 if ((alt && !cfit_alt()) || (!alt && !cfit_null())) {
2381 throw std::runtime_error("Cannot add toys, invalid conditional fit");
2382 }
2383
2384 auto condition = [&]() { // returns true if need more toys
2385 if (std::isnan(target))
2386 return false;
2387 auto obs = targetCLs ? pCLs_toys(target_nSigma) : (alt ? pAlt_toys(target_nSigma) : pNull_toys(target_nSigma));
2388 if (!std::isnan(obs.first)) {
2389 double diff = (target < 0) ? obs.first : std::abs(obs.first - target);
2390 double err = obs.second;
2391 if (err > 1e-4 && diff <= relErrThreshold * obs.second) {
2392 // return true; // more toys needed
2393 if (targetCLs) {
2394 // decide which type we'd want to generate and update alt flag
2395 auto pNull = pNull_toys(target_nSigma);
2396 auto pAlt = pAlt_toys(target_nSigma);
2397 // std::cout << obs.first << " +/- " << obs.second << ": " << pNull.first << " +/- " << pNull.second << "
2398 // , " << pAlt.first << " +/- " << pAlt.second << std::endl;
2399 alt = (pAlt.second * pNull.first > pNull.second * pAlt.first);
2400 if ((alt ? pAlt.second : pNull.second) < 1e-4)
2401 return false; // stop if error gets too small
2402 }
2403 return true;
2404 }
2405 }
2406 return false;
2407 };
2408
2409 if (!std::isnan(target) && std::isnan(ts_toys(target_nSigma).first)) {
2410 if (std::isnan(target_nSigma)) {
2411 throw std::runtime_error("Cannot target obs p-value because ts value unavailable");
2412 }
2413 if (targetCLs && pCLs_toys(target_nSigma).second == 0) {
2414 // this happens if the mu_test=mu_alt ... no toys needed
2415 return 0;
2416 }
2417
2418 // try generating 100 alt toys
2419 Info("addToys", "First generating 100 alt toys in order to determine expected ts value");
2420 addToys(true, 100, initialSeed);
2421 // if still null then exit
2422 if (std::isnan(ts_toys(target_nSigma).first)) {
2423 throw std::runtime_error("Unable to determine expected ts value");
2424 }
2425 }
2426
2427 size_t nans = 0;
2428 float lastTime = 0;
2429 int lasti = 0;
2430 auto g = gROOT->Get<TGraph>("toyTime");
2431 if (!g) {
2432 g = new TGraph;
2433 g->SetNameTitle("toyTime", "Time per toy;Toy;time [s]");
2434 gROOT->Add(g);
2435 }
2436 g->Set(0);
2437 TStopwatch s2;
2438 s2.Start();
2439 TStopwatch s;
2440 s.Start();
2441
2442 size_t toysAdded(0);
2443 size_t altToysAdded(0);
2444 if (initialSeed) {
2446 }
2447 do {
2448 auto &toys = (alt) ? altToys : nullToys;
2449 if (toys.size() >= maxToys) {
2450 // cannot generate more toys, reached limit already
2451 break;
2452 }
2453 // don't generate toys if reached target
2454 if (!std::isnan(target) && !condition()) {
2455 break;
2456 }
2457 auto currVal = std::isnan(target) ? std::pair(0., 0.)
2458 : (targetCLs ? pCLs_toys(target_nSigma)
2459 : (alt ? pAlt_toys(target_nSigma) : pNull_toys(target_nSigma)));
2460 size_t nnToys = std::min(size_t(nToys), (maxToys - toys.size()));
2461
2462 for (size_t i = 0; i < nnToys; i++) {
2463 int seed = RooRandom::randomGenerator()->Integer(std::numeric_limits<uint32_t>::max());
2464 auto toy = ((alt) ? generateAlt(seed) : generateNull(seed));
2465 {
2466 TDirectory::TContext ctx{nullptr}; // disables any saving of fit results for toys
2467 toys.push_back(std::make_tuple(seed, toy.pll().first, 1.));
2468 }
2469 (alt ? altToysAdded : toysAdded)++;
2470 if (std::isnan(std::get<1>(toys.back())))
2471 nans++;
2472 g->SetPoint(g->GetN(), g->GetN(), s.RealTime() - lastTime); // stops the clock
2473 lastTime = s.RealTime();
2474 if (s.RealTime() > 10) {
2475 std::cout << "\r"
2476 << TString::Format("Generated %d/%d %s hypothesis toys [%.2f toys/s]",
2477 int(alt ? altToysAdded : toysAdded), int(nnToys), alt ? "alt" : "null",
2479 if (!std::isnan(target)) {
2480 std::cout << " [current=" << currVal.first << "+/-" << currVal.second << " target=" << target
2481 << " nSigma=" << target_nSigma << "]";
2482 }
2483 std::cout << "..." << std::flush;
2485 s.Reset();
2486 if (!gROOT->IsBatch()) {
2487 Draw();
2488 if (gPad) {
2489 gPad->Update();
2490#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
2491 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
2492#endif
2494 }
2495 }
2496 s.Start();
2497 // std::cout << "Generated " << i << "/" << nToys << (alt ? " alt " : " null ") << " hypothesis toys " ..."
2498 // << std::endl;
2499 }
2500 s.Continue();
2501 }
2502 // sort the toys ... put nans first - do by setting all as negative inf (is that still necessary with the custom
2503 // sort below??)
2504 for (auto &t : toys) {
2505 if (std::isnan(std::get<1>(t)))
2506 std::get<1>(t) = -std::numeric_limits<double>::infinity();
2507 }
2508 std::sort(toys.begin(), toys.end(),
2509 [](const decltype(nullToys)::value_type &a, const decltype(nullToys)::value_type &b) -> bool {
2510 if (std::isnan(std::get<1>(a)))
2511 return true;
2512 if (std::isnan(std::get<1>(b)))
2513 return false;
2514 return std::get<1>(a) < std::get<1>(b);
2515 });
2516 for (auto &t : toys) {
2517 if (std::isinf(std::get<1>(t)))
2518 std::get<1>(t) = std::numeric_limits<double>::quiet_NaN();
2519 }
2520 if (std::isnan(target)) {
2521 break; // no more toys if not doing a target
2522 }
2523 // if(condition()) {
2524 // Info("addToys","Generating more toys to determine p-value ... currently: %f +/-
2525 // %f",pNull_toys(target_nSigma).first,pNull_toys(target_nSigma).second);
2526 // }
2527 } while (condition());
2528 if (lasti) {
2529 std::cout << "\r"
2530 << "Finished Generating ";
2531 if (toysAdded) {
2532 std::cout << toysAdded << " null ";
2533 }
2534 if (altToysAdded) {
2535 std::cout << altToysAdded << " alt ";
2536 }
2537 std::cout << "toys " << TString::Format("[%.2f toys/s overall]", double(toysAdded + altToysAdded) / s2.RealTime())
2538 << std::endl;
2539 if (!gROOT->IsBatch()) {
2540 Draw();
2541 if (gPad) {
2542 gPad->Update();
2543#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
2544 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
2545#endif
2547 }
2548 }
2549 }
2550
2551 if (nans > 0) {
2552 std::cout << "Warning: " << nans << " toys were bad" << std::endl;
2553 }
2554 return toysAdded;
2555}
2556
2558{
2559 addToys(false, nToys, seed, target, target_nSigma);
2560}
2562{
2563 addToys(true, nToys, seed, target, target_nSigma);
2564}
2565
2567{
2568 addToys(false, nToys, seed, target, target_nSigma, true);
2569 return;
2570 //
2571 // auto condition = [&](bool doingAlt=false) { // returns true if need more toys
2572 // if(std::isnan(target)) return false;
2573 // auto pval = pCLs_toys(target_nSigma);
2574 // if (!std::isnan(pval.first)) {
2575 // double diff = std::abs(pval.first - target);
2576 // double err = pval.second;
2577 // if (err > 1e-4 && diff <= 2 * pval.second) {
2578 // return true; // more toys needed
2579 // // decide which type we'd want to generate
2580 // // if it matches the type we are generating, then return true
2581 // auto pNull = pNull_toys(target_nSigma);
2582 // auto pAlt = pAlt_toys(target_nSigma);
2583 // if ((doingAlt ? pAlt.second : pNull.second) < 1e-4) return false; // stop if error gets too small
2584 // bool doAlt = (pAlt.second * pNull.first > pNull.second * pAlt.first);
2585 // return doAlt == doingAlt;
2586 // }
2587 // }
2588 // return false;
2589 // };
2590 // while(condition()) {
2591 // bool doAlt = false;
2592 // double relErrThreshold = 2;
2593 // if(nullToys.size()<size_t(nToys)) {
2594 // addToys(false,nToys);continue;
2595 // } else if(altToys.size()<size_t(nToys)) {
2596 // addToys(true,nToys);continue;
2597 // } else {
2598 // // see which have bigger errors ... generate more of that ...
2599 // auto pNull = pNull_toys(target_nSigma);
2600 // auto pAlt = pAlt_toys(target_nSigma);
2601 // doAlt = (pAlt.second*pNull.first > pNull.second*pAlt.first);
2602 // if( (doAlt ? pAlt.second : pNull.second) < 1e-4 ) break; // stop if error gets too small
2603 // auto pCLs = pCLs_toys(target_nSigma);
2604 // relErrThreshold = (doAlt) ? (pNull.second/pNull.first) : (pAlt.second/pAlt.first);
2605 // relErrThreshold = std::min(2.,std::abs(relErrThreshold));
2606 // std::cout << "Current pCLs = " << pCLs.first << " +/- " << pCLs.second
2607 // << " (pNull = " << pNull.first << " +/- " << pNull.second
2608 // << " , pAlt = " << pAlt.first << " +/- " << pAlt.second << ") ... generating more " << (doAlt ?
2609 // "alt" : "null") << " toys " << relErrThreshold << std::endl;
2610 //
2611 // }
2612 // if( addToys(doAlt, nToys/*, seed, -1, target_nSigma,relErrThreshold*/) == 0) {
2613 // break; // no toys got added, so stop looping
2614 // }
2615 // }
2616}
2617
2620{
2621 xRooHypoPoint out;
2622 // out.fPOIName = parName; out.fNullVal = value; out.fAltVal = alt_value;
2623
2624 if (!fFuncVars) {
2625 reinitialize();
2626 }
2628
2629 out.nllVar = std::make_shared<xRooNLLVar>(*this);
2630 // clear the underlying RooAbsReal, so that we don't accidentally alter it (e.g. setAttribute readOnly)
2631 // and therefore alter the xRooNLLVar object we are creating this hypoPoint from
2632 // basically ensure the hypoPoint has an independent version of the function
2633 out.nllVar->reset();
2634 out.fData = getData();
2635
2636 TStringToken pattern(poiValues, ",");
2638 while (pattern.NextToken()) {
2639 TString s = pattern.Data();
2640 TString cName = s;
2641 double val = std::numeric_limits<double>::quiet_NaN();
2642 auto i = s.Index("=");
2643 if (i != -1) {
2644 cName = s(0, i);
2645 TString cVal = s(i + 1, s.Length());
2646 if (!cVal.IsFloat())
2647 throw std::runtime_error("poiValues must contain value");
2648 val = cVal.Atof();
2649 }
2650 auto v = dynamic_cast<RooRealVar *>(fFuncVars->find(cName));
2651 if (!v)
2652 throw std::runtime_error("Cannot find poi");
2653 if (!std::isnan(val))
2654 v->setVal(val);
2655 v->setConstant(); // because will select constants as coords
2656 if (poiNames != "") {
2657 poiNames += ",";
2658 }
2659 poiNames += cName;
2660 }
2661 if (poiNames == "") {
2662 throw std::runtime_error("No poi");
2663 }
2664
2665 auto _snap = std::unique_ptr<RooAbsCollection>(fFuncVars->selectByAttrib("Constant", true))->snapshot();
2666 _snap->setAttribAll("poi", false);
2667 std::unique_ptr<RooAbsCollection> _poi(_snap->selectByName(poiNames));
2668 _poi->setAttribAll("poi", true);
2669 if (std::isnan(alt_value)) {
2670 for (auto a : *_poi)
2671 a->setStringAttribute("altVal", nullptr);
2672 } else {
2673 for (auto a : *_poi)
2674 a->setStringAttribute("altVal", TString::Format("%g", alt_value));
2675 }
2676 if (fGlobs)
2677 _snap->remove(*fGlobs, true, true);
2678 out.coords.reset(_snap);
2679
2680 auto _type = pllType;
2681 if (_type == xRooFit::Asymptotics::Unknown) {
2682 // decide based on values
2683 if (std::isnan(alt_value)) {
2685 } else if (dynamic_cast<RooRealVar *>(_poi->first())->getVal() >= alt_value) {
2687 } else {
2689 // for uncapped, should check min is not at physical boundary
2690 for (auto b : out.poi()) {
2691 if (auto r = dynamic_cast<RooRealVar *>(b)) {
2692 if (r->hasRange("physical") && r->getMin() >= r->getMin("physical")) {
2693 ::Info(
2694 "xRooNLLVar::hypoPoint",
2695 "fitting min of %s is >= physical limit (%g), but using uncapped test-statistic, so will set to "
2696 "-max = %g",
2697 r->GetName(), r->getMin("physical"), -r->getMax());
2698 r->setMin(-r->getMax());
2699 }
2700 }
2701 }
2702 }
2703 }
2704
2705 out.fPllType = _type;
2706
2707 // if doing onesidedpositive with an alt value, will assume we need a physical boundary
2708 if (!std::isnan(alt_value) && out.fPllType == xRooFit::Asymptotics::OneSidedPositive) {
2709 std::unique_ptr<RooAbsCollection> thePoi(fFuncVars->selectByName(poiNames));
2710 for (auto b : *thePoi) {
2711 if (!static_cast<RooRealVar *>(b)->hasRange("physical")) {
2712 static_cast<RooRealVar *>(b)->setRange("physical", 0, std::numeric_limits<double>::infinity());
2713 }
2714 }
2715 }
2716
2717 return out;
2718}
2719
2720xRooNLLVar::xRooHypoPoint
2722{
2723 if (!fFuncVars) {
2724 reinitialize();
2725 }
2726 std::unique_ptr<RooAbsCollection> _poi(fFuncVars->selectByAttrib("poi", true));
2727 if (_poi->empty()) {
2728 throw std::runtime_error("No POI specified in model");
2729 } else if (_poi->size() != 1) {
2730 throw std::runtime_error("Multiple POI specified in model");
2731 }
2732 return hypoPoint(_poi->first()->GetName(), value, alt_value, pllType);
2733}
2734
2740
2742{
2743
2744 if (!nllVar && !hypoTestResult)
2745 return;
2746
2747 TString sOpt(opt);
2748 sOpt.ToLower();
2749 bool hasSame = sOpt.Contains("same");
2750 sOpt.ReplaceAll("same", "");
2751
2752 TVirtualPad *pad = gPad;
2753
2754 TH1 *hAxis = nullptr;
2755
2756 auto clearPad = []() {
2757 gPad->Clear();
2758 if (gPad->GetNumber() == 0) {
2759 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
2760 gPad->SetTopMargin(gStyle->GetPadTopMargin());
2761 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
2762 gPad->SetRightMargin(gStyle->GetPadRightMargin());
2763 }
2764 };
2765
2766 if (!hasSame || !pad) {
2767 if (!pad) {
2769 pad = gPad;
2770 }
2771 clearPad();
2772 } else {
2773 // get the histogram representing the axes
2774 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive(".axis"));
2775 if (!hAxis) {
2776 for (auto o : *pad->GetListOfPrimitives()) {
2777 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
2778 break;
2779 }
2780 }
2781 }
2782
2783 // get min and max values
2784 double _min = std::numeric_limits<double>::quiet_NaN();
2785 double _max = -std::numeric_limits<double>::quiet_NaN();
2786
2787 for (auto &p : nullToys) {
2788 if (std::get<2>(p) == 0)
2789 continue;
2790 if (std::isnan(std::get<1>(p)))
2791 continue;
2792 _min = std::min(std::get<1>(p), _min);
2793 _max = std::max(std::get<1>(p), _max);
2794 }
2795 for (auto &p : altToys) {
2796 if (std::get<2>(p) == 0)
2797 continue;
2798 if (std::isnan(std::get<1>(p)))
2799 continue;
2800 _min = std::min(std::get<1>(p), _min);
2801 _max = std::max(std::get<1>(p), _max);
2802 }
2803
2804 auto obs = ts_asymp();
2805 if (!std::isnan(obs.first)) {
2806 _min = std::min(obs.first - std::abs(obs.first) * 0.1, _min);
2807 _max = std::max(obs.first + std::abs(obs.first) * 0.1, _max);
2808 }
2809 // these are used down below to add obs p-values to legend, but up here because can trigger fits that create asimov
2810 auto pNull = pNull_toys();
2811 auto pAlt = pAlt_toys();
2812 auto pNullA = (fPllType == xRooFit::Asymptotics::Unknown)
2813 ? std::pair(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN())
2814 : pNull_asymp();
2815 auto pAltA = (fPllType == xRooFit::Asymptotics::Unknown)
2816 ? std::pair(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN())
2817 : pAlt_asymp();
2818 sigma_mu(true);
2819 auto asi = (fPllType != xRooFit::Asymptotics::Unknown && fAsimov && fAsimov->fUfit && fAsimov->fNull_cfit)
2820 ? fAsimov->pll().first
2821 : std::numeric_limits<double>::quiet_NaN();
2822 if (!std::isnan(asi) && asi > 0) {
2823 // can calculate asymptotic distributions,
2824 _min = std::min(asi - std::abs(asi), _min);
2825 _max = std::max(asi + std::abs(asi), _max);
2826 }
2827 if (_min > 0)
2828 _min = 0;
2829
2830 auto _poi = dynamic_cast<RooRealVar *>(poi().first());
2831
2832 auto makeHist = [&](bool isAlt) {
2833 TString title;
2834 auto h = new TH1D((isAlt) ? "alt_toys" : "null_toys", "", 100, _min, _max + (_max - _min) * 0.01);
2835 h->SetDirectory(nullptr);
2836 size_t nBadOrZero = 0;
2837 for (auto &p : (isAlt) ? altToys : nullToys) {
2838 double w = std::isnan(std::get<1>(p)) ? 0 : std::get<2>(p);
2839 if (w == 0)
2840 nBadOrZero++;
2841 if (!std::isnan(std::get<1>(p)))
2842 h->Fill(std::get<1>(p), w);
2843 }
2844 if (h->GetEntries() > 0)
2845 h->Scale(1. / h->Integral(0, h->GetNbinsX() + 1));
2846
2847 // add POI values to identify hypos
2848 // for(auto p : *fPOI) {
2849 // if (auto v = dynamic_cast<RooRealVar*>(p)) {
2850 // if (auto v2 = dynamic_cast<RooRealVar*>(fAltPoint->fCoords->find(*v)); v2 &&
2851 // v2->getVal()!=v->getVal()) {
2852 // // found point that differs in poi and altpoint value, so print my coords value for this
2853 // title += TString::Format("%s' = %g,
2854 // ",v->GetTitle(),dynamic_cast<RooRealVar*>(fCoords->find(*v))->getVal());
2855 // }
2856 // }
2857 // }
2858 if (fPOIName())
2859 title += TString::Format("%s' = %g", fPOIName(), (isAlt) ? fAltVal() : fNullVal());
2860 title += TString::Format(" , N_{toys}=%d", int((isAlt) ? altToys.size() : nullToys.size()));
2861 if (nBadOrZero > 0)
2862 title += TString::Format(" (N_{bad/0}=%d)", int(nBadOrZero));
2863 title += ";";
2864 title += tsTitle();
2865 title += TString::Format(";Probability Mass");
2866 h->SetTitle(title);
2867 h->SetLineColor(isAlt ? kRed : kBlue);
2868 h->SetLineWidth(2);
2869 h->SetMarkerSize(0);
2870 h->SetBit(kCanDelete);
2871 return h;
2872 };
2873
2874 auto nullHist = makeHist(false);
2875 auto altHist = makeHist(true);
2876
2877 TLegend *l = nullptr;
2878 auto h = (nullHist->GetEntries()) ? nullHist : altHist;
2879 if (!hasSame) {
2880 gPad->SetLogy();
2881 auto axis = static_cast<TH1 *>(h->Clone(".axis"));
2882 axis->SetBit(kCanDelete);
2883 axis->SetStats(false);
2884 axis->Reset("ICES");
2885 axis->SetTitle(TString::Format("%s HypoPoint", collectionContents(poi()).c_str()));
2886 axis->SetLineWidth(0);
2887 axis->Draw(""); // h->Draw("axis"); cant use axis option if want title drawn
2888 axis->SetMinimum(1e-7);
2889 axis->GetYaxis()->SetRangeUser(1e-7, 10);
2890 axis->SetMaximum(h->GetMaximum());
2891 hAxis = axis;
2892 l = new TLegend(0.4, 0.7, 1. - gPad->GetRightMargin(), 1. - gPad->GetTopMargin());
2893 l->SetName("legend");
2894 l->SetFillStyle(0);
2895 l->SetBorderSize(0);
2897 l->Draw();
2898 l->ConvertNDCtoPad();
2899 } else {
2900 for (auto o : *gPad->GetListOfPrimitives()) {
2901 l = dynamic_cast<TLegend *>(o);
2902 if (l)
2903 break;
2904 }
2905 }
2906
2907 if (h->GetEntries() > 0) {
2908 h->Draw("esame");
2909 } else {
2910 h->Draw("axissame"); // for unknown reason if second histogram empty it still draws with two weird bars???
2911 }
2912 h = altHist;
2913 if (h->GetEntries() > 0) {
2914 h->Draw("esame");
2915 } else {
2916 h->Draw("axissame"); // for unknown reason if second histogram empty it still draws with two weird bars???
2917 }
2918
2919 if (l) {
2920 l->AddEntry(nullHist);
2921 l->AddEntry(altHist);
2922 }
2923
2924 if (fAsimov && fAsimov->fUfit && fAsimov->fNull_cfit && !std::isnan(sigma_mu().first) && !std::isnan(fAltVal())) {
2925 auto hh = static_cast<TH1 *>(nullHist->Clone("null_asymp"));
2926 hh->SetBit(kCanDelete);
2927 hh->SetStats(false);
2928 hh->SetLineStyle(2);
2929 hh->Reset();
2930 for (int i = 1; i <= hh->GetNbinsX(); i++) {
2931 hh->SetBinContent(
2932 i, xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i), fNullVal(), fNullVal(), sigma_mu().first,
2933 _poi->getMin("physical"), _poi->getMax("physical")) -
2934 xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i + 1), fNullVal(), fNullVal(),
2935 sigma_mu().first, _poi->getMin("physical"), _poi->getMax("physical")));
2936 }
2937 hh->Draw("lsame");
2938 hh = static_cast<TH1 *>(altHist->Clone("alt_asymp"));
2939 hh->SetBit(kCanDelete);
2940 hh->SetStats(false);
2941 hh->SetLineStyle(2);
2942 hh->Reset();
2943 for (int i = 1; i <= hh->GetNbinsX(); i++) {
2944 hh->SetBinContent(
2945 i, xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i), fNullVal(), fAltVal(), sigma_mu().first,
2946 _poi->getMin("physical"), _poi->getMax("physical")) -
2947 xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i + 1), fNullVal(), fAltVal(),
2948 sigma_mu().first, _poi->getMin("physical"), _poi->getMax("physical")));
2949 }
2950 hh->Draw("lsame");
2951 }
2952
2953 // draw observed points
2954 TLine ll;
2955 ll.SetLineStyle(1);
2956 ll.SetLineWidth(3);
2957 // for(auto p : fObs) {
2958 auto tl = ll.DrawLine(obs.first, hAxis->GetMinimum(), obs.first, 0.1);
2959 auto label = TString::Format("obs ts = %.4f", obs.first);
2960 if (obs.second)
2961 label += TString::Format(" #pm %.4f", obs.second);
2962
2963 l->AddEntry(tl, label, "l");
2964 label = "";
2965 if (nullHist->GetEntries() || altHist->GetEntries()) {
2966 auto pCLs = pCLs_toys();
2967 label += " p_{toy}=(";
2968 label += (std::isnan(pNull.first)) ? "-" : TString::Format("%.4f #pm %.4f", pNull.first, pNull.second);
2969 label += (std::isnan(pAlt.first)) ? ",-" : TString::Format(",%.4f #pm %.4f", pAlt.first, pAlt.second);
2970 label += (std::isnan(pCLs.first)) ? ",-)" : TString::Format(",%.4f #pm %.4f)", pCLs.first, pCLs.second);
2971 }
2972 if (label.Length() > 0)
2973 l->AddEntry("", label, "");
2974 label = "";
2975 if (!std::isnan(pNullA.first) || !std::isnan(pAltA.first)) {
2976 auto pCLs = pCLs_asymp();
2977 label += " p_{asymp}=(";
2978 label += (std::isnan(pNullA.first)) ? "-" : TString::Format("%.4f #pm %.4f", pNullA.first, pNullA.second);
2979 label += (std::isnan(pAltA.first)) ? ",-" : TString::Format(",%.4f #pm %.4f", pAltA.first, pAltA.second);
2980 label += (std::isnan(pCLs.first)) ? ",-)" : TString::Format(",%.4f #pm %.4f)", pCLs.first, pCLs.second);
2981 }
2982 if (label.Length() > 0)
2983 l->AddEntry("", label, "");
2984
2985 if (auto ax = dynamic_cast<TH1 *>(gPad->GetPrimitive(".axis")))
2986 ax->GetYaxis()->SetRangeUser(1e-7, 1);
2987}
2988
2990{
2991 auto v = dynamic_cast<RooRealVar *>(poi().empty() ? nullptr : poi().first());
2993 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
2994 return (inWords) ? TString::Format("Lower-Bound One-Sided Limit PLR")
2995 : TString::Format("#tilde{q}_{%s=%g}", v->GetTitle(), v->getVal());
2996 } else if (v) {
2997 return (inWords) ? TString::Format("One-Sided Limit PLR")
2998 : TString::Format("q_{%s=%g}", v->GetTitle(), v->getVal());
2999 } else {
3000 return "q";
3001 }
3002 } else if (fPllType == xRooFit::Asymptotics::TwoSided) {
3003 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
3004 return (inWords) ? TString::Format("Lower-Bound PLR")
3005 : TString::Format("#tilde{t}_{%s=%g}", v->GetTitle(), v->getVal());
3006 } else if (v) {
3007 return (inWords) ? TString::Format("-2log[L(%s,#hat{#hat{#theta}})/L(#hat{%s},#hat{#theta})]", v->GetTitle(),
3008 v->GetTitle())
3009 : TString::Format("t_{%s=%g}", v->GetTitle(), v->getVal());
3010 } else
3011 return "t";
3012 } else if (fPllType == xRooFit::Asymptotics::OneSidedNegative) {
3013 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
3014 return (inWords) ? TString::Format("Lower-Bound One-Sided Discovery PLR")
3015 : TString::Format("#tilde{r}_{%s=%g}", v->GetTitle(), v->getVal());
3016 } else if (v) {
3017 return (inWords) ? TString::Format("One-Sided Discovery PLR")
3018 : TString::Format("r_{%s=%g}", v->GetTitle(), v->getVal());
3019 } else {
3020 return "r";
3021 }
3022 } else if (fPllType == xRooFit::Asymptotics::Uncapped) {
3023 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
3024 return (inWords) ? TString::Format("Lower-Bound Uncapped PLR")
3025 : TString::Format("#tilde{u}_{%s=%g}", v->GetTitle(), v->getVal());
3026 } else if (v) {
3027 return (inWords) ? TString::Format("Uncapped PLR") : TString::Format("u_{%s=%g}", v->GetTitle(), v->getVal());
3028 } else {
3029 return "u";
3030 }
3031 } else {
3032 return "Test Statistic";
3033 }
3034}
3035
3037{
3038 return (poi().empty()) ? nullptr : (poi().first())->GetName();
3039}
3041{
3042 auto first_poi = dynamic_cast<RooAbsReal *>(poi().first());
3043 return (first_poi == nullptr) ? std::numeric_limits<double>::quiet_NaN() : first_poi->getVal();
3044}
3046{
3047 auto _alt_poi = alt_poi(); // need to keep alive as alt_poi owns its contents
3048 auto first_poi = dynamic_cast<RooAbsReal *>(_alt_poi.first());
3049 return (first_poi == nullptr) ? std::numeric_limits<double>::quiet_NaN() : first_poi->getVal();
3050}
3051
3053{
3054 auto first_poi = dynamic_cast<RooAbsRealLValue *>(poi().first());
3055 if (!first_poi) {
3056 throw std::runtime_error("HypoPoint has no POI, cannot set null value");
3057 }
3058 first_poi->setVal(val);
3059}
3061{
3062 auto first_poi = dynamic_cast<RooAbsArg *>(poi().first());
3063 if (!first_poi) {
3064 throw std::runtime_error("HypoPoint has no POI, cannot set alt value");
3065 }
3066 first_poi->setStringAttribute("altVal", TString::Format("%g", val).Data());
3067}
3068
3069xRooNLLVar::xRooHypoSpace xRooNLLVar::hypoSpace(const char *parName, int nPoints, double low, double high,
3071 int tsType)
3072{
3073 if (nPoints < 0 || tsType < 0) {
3074 // nPoints<0 catches case where pyROOT has converted TestStatistic enum to int
3075 if (nPoints < 0) {
3076 tsType = nPoints;
3077 nPoints = int(low + 0.5);
3078 low = high;
3079 high = alt_value;
3080 }
3081 if (alt_value == std::numeric_limits<double>::quiet_NaN()) {
3083 alt_value = 0;
3085 alt_value = 1;
3086 }
3087 }
3088
3089 auto out = hypoSpace(parName, pllType, alt_value);
3090
3091 // TODO: things like the physical range and alt value can't be stored on the poi
3092 // because if they change they will change for all hypoSpaces at once, so cannot have
3093 // two hypoSpace with e.g. different physical ranges.
3094 // the hypoSpace should make a copy of them at some point
3095 for (auto p : out.poi()) {
3097 dynamic_cast<RooRealVar *>(p)->setRange("physical", 0, std::numeric_limits<double>::infinity());
3098 Info("xRooNLLVar::hypoSpace", "Setting physical range of %s to [0,inf]", p->GetName());
3099 } else if (auto v = dynamic_cast<RooRealVar *>(p); v->hasRange("physical")) {
3100 v->removeMin("physical");
3101 v->removeMax("physical"); // v->removeRange("physical");
3102 Info("xRooNLLVar::hypoSpace", "Removing physical range of %s", p->GetName());
3103 }
3104 }
3105
3106 // ensure pll type is set explicitly if known at this point
3108 out.fTestStatType = xRooFit::Asymptotics::OneSidedPositive;
3110 out.fTestStatType = xRooFit::Asymptotics::Uncapped;
3111 } else if (tsType == xRooFit::TestStatistic::q0) {
3112 out.fTestStatType = xRooFit::Asymptotics::OneSidedNegative;
3113 }
3114
3115 if (nPoints > 0) {
3116 out.AddPoints(parName, nPoints, low, high);
3117 } else {
3118 // if (!std::isnan(low) && !std::isnan(high) && !(std::isinf(low) && std::isinf(high))) {
3119 for (auto p : out.poi()) {
3120 if (auto r = dynamic_cast<RooRealVar *>(p)) {
3121 r->setRange("scan", std::isnan(low) ? r->getMin() : low, std::isnan(high) ? r->getMax() : high);
3122 }
3123 }
3124 //}
3125 }
3126 return out;
3127 }
3128
3130 if (nPoints > 0)
3131 hs.AddPoints(parName, nPoints, low, high);
3132 else {
3133 // if (!std::isnan(low) && !std::isnan(high) && !(std::isinf(low) && std::isinf(high))) {
3134 for (auto p : hs.poi()) {
3135 if (auto r = dynamic_cast<RooRealVar *>(p)) {
3136 r->setRange("scan", std::isnan(low) ? r->getMin() : low, std::isnan(high) ? r->getMax() : high);
3137 }
3138 }
3139 //}
3140 }
3141 return hs;
3142}
3143
3146{
3147 auto _poi = std::unique_ptr<RooAbsCollection>(
3148 std::unique_ptr<RooAbsCollection>(pdf()->getVariables())->selectByAttrib("poi", true));
3149 if (_poi->empty())
3150 throw std::runtime_error("You must specify a POI for the hypoSpace");
3151 return hypoSpace(_poi->first()->GetName(), nPoints, low, high, alt_value, pllType);
3152}
3153
3156{
3158
3159 s.AddModel(pdf());
3160 if (strlen(parName)) {
3161 std::unique_ptr<RooAbsCollection> axes(s.pars()->selectByName(parName));
3162 if (axes->empty())
3163 throw std::runtime_error("parameter not found");
3164 axes->setAttribAll("axis", true);
3165 }
3166 /*if (std::unique_ptr<RooAbsCollection>(s.pars()->selectByAttrib("poi", true))->empty()) {
3167 throw std::runtime_error("You must specify at least one POI for the hypoSpace");
3168 }*/
3169 s.fNlls[s.fPdfs.begin()->second] = std::make_shared<xRooNLLVar>(*this);
3170 // clear the underlying RooAbsReal, so that we don't accidentally alter it (e.g. setAttribute readOnly)
3171 // and therefore alter the xRooNLLVar object we are creating this hypoPoint from
3172 // basically ensure the hypoPoint has an independent version of the function
3173 s.fNlls[s.fPdfs.begin()->second]->reset();
3175
3176 for (auto poi : s.poi()) {
3177 poi->setStringAttribute("altVal", std::isnan(alt_value) ? nullptr : TString::Format("%f", alt_value));
3178 // default scan range to range of poi
3179 if (auto r = dynamic_cast<RooRealVar *>(poi)) {
3180 r->setRange("scan", r->getMin(), r->getMax());
3181 }
3182 }
3183
3184 return s;
3185}
3186
3188{
3189 if (hypoTestResult) {
3190 return *hypoTestResult;
3191 }
3193 out.SetBackgroundAsAlt(true);
3194 out.SetName(TUUID().AsString());
3195 out.SetTitle(TString::Format("%s HypoPoint", collectionContents(poi()).c_str()));
3196
3197 bool setReadonly = false;
3198 if (nllVar && !nllVar->get()->getAttribute("readOnly")) {
3199 setReadonly = true;
3200 nllVar->get()->setAttribute("readOnly");
3201 }
3202
3203 out.SetTestStatisticData(ts_asymp().first);
3204
3205 // build a ds to hold all fits ... store coords in the globs list of the nullDist
3206 // also need to store at least mu_hat value(s)
3209 fitMeta.addClone(RooCategory("pllType", "test statistic type",
3210 {{"TwoSided", 0},
3211 {"OneSidedPositive", 1},
3212 {"OneSidedNegative", 2},
3213 {"OneSidedAbsolute", 3},
3214 {"Uncapped", 4},
3215 {"Unknown", 5}}));
3216 if (ufit()) {
3217 fitMeta.addClone(ufit()->floatParsFinal());
3218 }
3219 fitMeta.setCatIndex("pllType", int(fPllType));
3220 fitMeta.addClone(RooRealVar("isExpected", "isExpected", int(isExpected)));
3221 fitMeta.addClone(RooRealVar("obs_ts_err", "obs_ts_err", obs_ts_err));
3222 fitDetails.addClone(RooCategory("type", "fit type",
3223 {{"ufit", 0},
3224 {"cfit_null", 1},
3225 {"cfit_alt", 2},
3226 {"asimov_ufit", 3},
3227 {"asimov_cfit_null", 4},
3228 {"gen", 5},
3229 {"cfit_lbound", 6}}));
3230 // fitDetails.addClone(RooStringVar("name", "Fit Name", "")); -- not supported properly in ROOT yet
3231 fitDetails.addClone(RooRealVar("status", "status", 0));
3232 fitDetails.addClone(RooRealVar("covQual", "covQual", 0));
3233 fitDetails.addClone(RooRealVar("minNll", "minNll", 0));
3234 fitDetails.addClone(RooRealVar("edm", "edm", 0));
3235 auto fitDS = new RooDataSet("fits", "fit summary data", fitDetails);
3236 // fitDS->convertToTreeStore(); // strings not stored properly in vector store, so do convert! - not needed since
3237 // string var storage not properly supported - storing in globs list instead
3238
3239 for (int i = 0; i < 7; i++) {
3240 std::shared_ptr<const RooFitResult> fit;
3241 switch (i) {
3242 case 0: fit = ufit(); break;
3243 case 1: fit = cfit_null(); break;
3244 case 2: fit = cfit_alt(); break;
3245 case 3: fit = asimov() ? asimov()->ufit(true) : nullptr; break;
3246 case 4: fit = asimov() ? asimov()->cfit_null(true) : nullptr; break;
3247 case 5: fit = fGenFit; break;
3248 case 6: fit = cfit_lbound(); break;
3249 }
3250 if (fit) {
3251 fitDetails.setCatIndex("type", i);
3252 fitMeta.addClone(RooStringVar(TString::Format("%s.name", fitDetails.getCatLabel("type")),
3253 fitDetails.getCatLabel("type"), fit->GetName()));
3254 // fitDetails.setStringValue("name",fit->GetName());
3255 fitDetails.setRealValue("status", fit->status());
3256 fitDetails.setRealValue("minNll", fit->minNll());
3257 fitDetails.setRealValue("edm", fit->edm());
3258 fitDetails.setRealValue("covQual", fit->covQual());
3259 fitDS->add(fitDetails);
3260 }
3261 }
3262 fitDS->setGlobalObservables(fitMeta);
3263
3264 out.SetFitInfo(fitDS);
3265
3268 nullMeta.addClone(*coords);
3269 nullDetails.addClone(RooRealVar("seed", "Toy Seed", 0));
3270 nullDetails.addClone(RooRealVar("ts", "test statistic value", 0));
3271 nullDetails.addClone(RooRealVar("weight", "weight", 1));
3272 auto nullToyDS = new RooDataSet("nullToys", "nullToys", nullDetails, RooFit::WeightVar("weight"));
3273 nullToyDS->setGlobalObservables(nullMeta);
3274 if (!nullToys.empty()) {
3275
3276 std::vector<double> values;
3277 std::vector<double> weights;
3278 values.reserve(nullToys.size());
3279 weights.reserve(nullToys.size());
3280
3281 for (auto &t : nullToys) {
3282 values.push_back(std::get<1>(t));
3283 weights.push_back(std::get<2>(t));
3284 nullDetails.setRealValue("seed", std::get<0>(t));
3285 nullDetails.setRealValue("ts", std::get<1>(t));
3286 nullToyDS->add(nullDetails, std::get<2>(t));
3287 }
3288 out.SetNullDistribution(new RooStats::SamplingDistribution("null", "Null dist", values, weights, tsTitle()));
3289#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3290 out.fNullPValue = pNull_toys().first; // technically set above
3291 out.fNullPValueError =
3292 pNull_toys().second; // overrides binomial error used in SamplingDistribution::IntegralAndError
3293#else
3294 out.SetNullPValue(pNull_toys().first); // technically set above
3296 pNull_toys().second); // overrides binomial error used in SamplingDistribution::IntegralAndError
3297#endif
3298 } else {
3299#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3300 out.fNullPValue = pNull_asymp().first;
3301 out.fNullPValueError = pNull_asymp().second;
3302#else
3303 out.SetNullPValue(pNull_asymp().first);
3304 out.SetNullPValueError(pNull_asymp().second);
3305#endif
3306 }
3308
3309 if (!altToys.empty()) {
3310 std::vector<double> values;
3311 std::vector<double> weights;
3312 values.reserve(altToys.size());
3313 weights.reserve(altToys.size());
3316 altDetails.addClone(RooRealVar("seed", "Toy Seed", 0));
3317 altDetails.addClone(RooRealVar("ts", "test statistic value", 0));
3318 altDetails.addClone(RooRealVar("weight", "weight", 1));
3319 auto altToyDS = new RooDataSet("altToys", "altToys", altDetails, RooFit::WeightVar("weight"));
3320 altToyDS->setGlobalObservables(altMeta);
3321 for (auto &t : altToys) {
3322 values.push_back(std::get<1>(t));
3323 weights.push_back(std::get<2>(t));
3324 altDetails.setRealValue("seed", std::get<0>(t));
3325 altDetails.setRealValue("ts", std::get<1>(t));
3326 altToyDS->add(altDetails, std::get<2>(t));
3327 }
3328 out.SetAltDistribution(new RooStats::SamplingDistribution("alt", "Alt dist", values, weights, tsTitle()));
3330#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3331 out.fAlternatePValue = pAlt_toys().first; // technically set above
3333 pAlt_toys().second; // overrides binomial error used in SamplingDistribution::IntegralAndError
3334#else
3335 out.SetAltPValue(pAlt_toys().first); // technically set above
3337 pAlt_toys().second); // overrides binomial error used in SamplingDistribution::IntegralAndError
3338#endif
3339
3340 } else if (fPllType != xRooFit::Asymptotics::Unknown) {
3341#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3342 out.fAlternatePValue = pAlt_asymp().first;
3343 out.fAlternatePValueError = pAlt_asymp().second;
3344#else
3345 out.SetAltPValue(pAlt_asymp().first);
3346 out.SetAltPValueError(pAlt_asymp().second);
3347#endif
3348 }
3349
3350 if (setReadonly) {
3351 nllVar->get()->setAttribute("readOnly", false);
3352 }
3353
3354 return out;
3355}
3356
3358
3359std::string cling::printValue(const XROOFIT_NAMESPACE_NAME::xRooNLLVar::xValueWithError *v)
3360{
3361 if (!v)
3362 return "xValueWithError: nullptr\n";
3363 return v->__repr__();
3364}
3365std::string cling::printValue(const std::map<std::string, XROOFIT_NAMESPACE_NAME::xRooNLLVar::xValueWithError> *m)
3366{
3367 if (!m)
3368 return "nullptr\n";
3369 std::string out = "{\n";
3370 for (auto [k, v] : *m) {
3371 out += "\"" + k + "\" => " + printValue(&v) + "\n";
3372 }
3373 out += "}\n";
3374 return out;
3375}
#define SafeDelete(p)
Definition RConfig.hxx:531
#define b(i)
Definition RSha256.hxx:100
#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 h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Option string (const char)
Definition RtypesCore.h:80
@ kRed
Definition Rtypes.h:67
@ kBlue
Definition Rtypes.h:67
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define gDirectory
Definition TDirectory.h:385
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
winID h TVirtualViewer3D TVirtualGLPainter p
winID h TVirtualViewer3D vv
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t 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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t np
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t 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 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
@ kCanDelete
Definition TObject.h:375
#define gROOT
Definition TROOT.h:426
static char * Format(const char *format, va_list ap)
Format a string in a circular formatting buffer (using a printf style format descriptor).
Definition TString.cxx:2449
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#define gPad
AutoRestorer(const RooAbsCollection &s, xRooNLLVar *nll=nullptr)
RooArgSet fPars
TString fOldTitle
TString fOldName
std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > fOldData
xRooNLLVar * fNll
std::unique_ptr< RooAbsCollection > fSnap
static double k(const IncompatFunc &compatRegions, double pValue, double poiVal, double poiPrimeVal, double sigma_mu=0, double mu_low=-std::numeric_limits< double >::infinity(), double mu_high=std::numeric_limits< double >::infinity())
static int CompatFactor(const IncompatFunc &func, double mu_hat)
static double PValue(const IncompatFunc &compatRegions, double k, double mu, double mu_prime, double sigma_mu=0, double mu_low=-std::numeric_limits< double >::infinity(), double mu_high=std::numeric_limits< double >::infinity())
static std::shared_ptr< const RooFitResult > minimize(RooAbsReal &nll, const std::shared_ptr< ROOT::Fit::FitConfig > &fitConfig=nullptr, const std::shared_ptr< RooLinkedList > &nllOpts=nullptr)
Definition xRooFit.cxx:752
static std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generateFrom(RooAbsPdf &pdf, const RooFitResult &fr, bool expected=false, int seed=0)
Definition xRooFit.cxx:154
static std::shared_ptr< ROOT::Fit::FitConfig > createFitConfig()
Definition xRooFit.cxx:490
double impact(const char *poi, const char *np, bool up=true, bool prefit=false, bool approx=false)
xRooFitResult ifit(const char *np, bool up, bool prefit=false)
double conditionalError(const char *poi, const char *nps, bool up=true, bool approx=false)
RooArgList ranknp(const char *poi, bool up=true, bool prefit=false, double approxThreshold=std::numeric_limits< double >::infinity())
xRooFitResult cfit(const char *poiValues, const char *alias=nullptr)
std::shared_ptr< RooStats::HypoTestResult > hypoTestResult
Definition xRooNLLVar.h:281
std::shared_ptr< const RooFitResult > retrieveFit(int type)
std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > fData
Definition xRooNLLVar.h:181
std::vector< std::tuple< int, double, double > > altToys
Definition xRooNLLVar.h:278
std::shared_ptr< const RooAbsCollection > coords
Definition xRooNLLVar.h:264
std::shared_ptr< const RooFitResult > cfit_lbound(bool readOnly=false)
void Draw(Option_t *opt="") override
Default Draw method for all objects.
xValueWithError ts_toys(double nSigma=std::numeric_limits< double >::quiet_NaN())
std::shared_ptr< const RooFitResult > fUfit
Definition xRooNLLVar.h:266
xRooHypoPoint(std::shared_ptr< RooStats::HypoTestResult > htr=nullptr, const RooAbsCollection *_coords=nullptr)
xValueWithError sigma_mu(bool readOnly=false)
xValueWithError pAlt_asymp(double nSigma=std::numeric_limits< double >::quiet_NaN())
std::vector< std::tuple< int, double, double > > nullToys
Definition xRooNLLVar.h:276
std::shared_ptr< xRooHypoPoint > asimov(bool readOnly=false)
xValueWithError pAlt_toys(double nSigma=std::numeric_limits< double >::quiet_NaN())
std::shared_ptr< const RooFitResult > ufit(bool readOnly=false)
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< const RooFitResult > cfit_null(bool readOnly=false)
std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > data()
xValueWithError pCLs_asymp(double nSigma=std::numeric_limits< double >::quiet_NaN())
std::shared_ptr< const RooFitResult > cfit_alt(bool readOnly=false)
size_t addToys(bool alt, int nToys, int initialSeed=0, double target=std::numeric_limits< double >::quiet_NaN(), double target_nSigma=std::numeric_limits< double >::quiet_NaN(), bool targetCLs=false, double relErrThreshold=2., size_t maxToys=10000)
void addAltToys(int nToys=1, int seed=0, double target=std::numeric_limits< double >::quiet_NaN(), double target_nSigma=std::numeric_limits< double >::quiet_NaN())
void addCLsToys(int nToys=1, int seed=0, double target=std::numeric_limits< double >::quiet_NaN(), double target_nSigma=std::numeric_limits< double >::quiet_NaN())
xValueWithError pX_toys(bool alt, double nSigma=std::numeric_limits< double >::quiet_NaN())
std::shared_ptr< const RooFitResult > fGenFit
Definition xRooNLLVar.h:267
void addNullToys(int nToys=1, int seed=0, double target=std::numeric_limits< double >::quiet_NaN(), double target_nSigma=std::numeric_limits< double >::quiet_NaN())
xValueWithError ts_asymp(double nSigma=std::numeric_limits< double >::quiet_NaN())
xValueWithError pNull_asymp(double nSigma=std::numeric_limits< double >::quiet_NaN())
xValueWithError pNull_toys(double nSigma=std::numeric_limits< double >::quiet_NaN())
std::shared_ptr< RooArgSet > pars() const
Definition xRooNLLVar.h:326
bool AddModel(const xRooNode &pdf, const char *validity="")
std::map< std::shared_ptr< xRooNode >, std::shared_ptr< xRooNLLVar > > fNlls
Definition xRooNLLVar.h:391
std::set< std::pair< std::shared_ptr< RooArgList >, std::shared_ptr< xRooNode > > > fPdfs
Definition xRooNLLVar.h:393
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
std::shared_ptr< RooAbsCollection > fFuncGlobs
Definition xRooNLLVar.h:509
std::shared_ptr< const RooAbsCollection > fGlobs
Definition xRooNLLVar.h:502
std::shared_ptr< RooLinkedList > fOpts
Definition xRooNLLVar.h:504
std::shared_ptr< RooAbsReal > func() const
std::set< std::string > binnedChannels() const
ROOT::Math::IOptions * fitConfigOptions()
RooConstraintSum * constraintTerm() const
std::shared_ptr< ROOT::Fit::FitConfig > fFitConfig
Definition xRooNLLVar.h:505
xRooHypoSpace hypoSpace(const char *parName, int nPoints, double low, double high, double alt_value=std::numeric_limits< double >::quiet_NaN(), const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown, int tsType=0)
TObject * Scan(const RooArgList &scanPars, const std::vector< std::vector< double > > &coords, const RooArgList &profilePars=RooArgList())
std::shared_ptr< RooAbsCollection > fConstVars
Definition xRooNLLVar.h:508
xRooNLLVar(RooAbsPdf &pdf, const std::pair< RooAbsData *, const RooAbsCollection * > &data, const RooLinkedList &nllOpts=RooLinkedList())
std::shared_ptr< RooAbsPdf > pdf() const
Definition xRooNLLVar.h:453
std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generate(bool expected=false, int seed=0)
std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > getData() const
double getEntryVal(size_t entry) const
std::shared_ptr< RooAbsCollection > fFuncVars
Definition xRooNLLVar.h:507
double getEntryBinWidth(size_t entry) const
std::shared_ptr< ROOT::Fit::FitConfig > fitConfig()
std::shared_ptr< RooArgSet > pars(bool stripGlobalObs=true) const
void SetOption(const RooCmdArg &opt)
std::shared_ptr< RooAbsData > fData
Definition xRooNLLVar.h:501
std::shared_ptr< RooAbsPdf > fPdf
Definition xRooNLLVar.h:500
bool setData(const std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > &_data)
xRooHypoPoint hypoPoint(const char *parName, double value, double alt_value=std::numeric_limits< double >::quiet_NaN(), const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown)
xRooFitResult minimize(const std::shared_ptr< ROOT::Fit::FitConfig > &=nullptr)
The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacti...
Definition xRooNode.h:52
Generic interface for defining configuration options of a numerical algorithm.
Definition IOptions.h:28
void SetValue(const char *name, double val)
generic methods for retrieving options
Definition IOptions.h:42
virtual void SetNamedValue(const char *, const char *)
Definition IOptions.cxx:50
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
Abstract base class for objects that represent a discrete value that can be set from the outside,...
A space to attach TBranches.
Abstract container object that can hold multiple RooAbsArg objects.
Storage_t const & get() const
Const access to the underlying stl container.
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...
Int_t index(const RooAbsArg *arg) const
Returns index of given arg, or -1 if arg is not in the collection.
Storage_t::size_type size() const
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
void setName(const char *name)
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:56
virtual const RooArgSet * get() const
Definition RooAbsData.h:100
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
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 ...
virtual void setVal(double value)=0
Set the current value of the object. Needs to be overridden by implementations.
bool hasRange(const char *name) const override
Check if variable has a binning with given name.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
virtual bool setData(RooAbsData &, bool=true)
Definition RooAbsReal.h:391
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Object to represent discrete states.
Definition RooCategory.h:28
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
double getDouble(Int_t idx) const
Return double stored in slot idx.
Definition RooCmdArg.h:92
Int_t getInt(Int_t idx) const
Definition RooCmdArg.h:87
TObject * Clone(const char *newName=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooCmdArg.h:58
const char * getString(Int_t idx) const
Return string stored in slot idx.
Definition RooCmdArg.h:96
Calculates the sum of the -(log) likelihoods of a set of RooAbsPfs that represent constraint function...
Container class to hold unbinned data.
Definition RooDataSet.h:32
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
Plain Gaussian p.d.f.
Definition RooGaussian.h:24
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
static RooMsgService & instance()
Return reference to singleton instance.
Poisson pdf.
Definition RooPoisson.h:19
static TRandom * randomGenerator()
Return a pointer to a singleton random-number generator implementation.
Definition RooRandom.cxx:47
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
HypoTestResult is a base class for results from hypothesis tests.
void SetAltDetailedOutput(RooDataSet *d)
double fNullPValue
p-value for the null hypothesis (small number means disfavoured)
double fAlternatePValueError
error of p-value for the alternate hypothesis (small number means disfavoured)
void SetNullDetailedOutput(RooDataSet *d)
void SetBackgroundAsAlt(bool l=true)
void SetAltPValue(double pvalue)
void SetNullDistribution(SamplingDistribution *null)
void SetTestStatisticData(const double tsd)
void SetAltPValueError(double err)
void SetFitInfo(RooDataSet *d)
double fNullPValueError
error of p-value for the null hypothesis (small number means disfavoured)
void SetAltDistribution(SamplingDistribution *alt)
double fAlternatePValue
p-value for the alternate hypothesis (small number means disfavoured)
void SetNullPValue(double pvalue)
void SetNullPValueError(double err)
This class simply holds a sampling distribution of some test statistic.
A RooAbsArg implementing string values.
Draw all kinds of Arrows.
Definition TArrow.h:29
virtual TArrow * DrawArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Float_t arrowsize=0, Option_t *option="")
Draw this arrow with new coordinates.
Definition TArrow.cxx:130
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1509
const char * AsString() const
Return the date & time as a string (ctime() format).
Definition TDatime.cxx:101
TDirectory::TContext keeps track and restore the current directory.
Definition TDirectory.h:89
Describe directory structure in memory.
Definition TDirectory.h:45
virtual TKey * FindKeyAny(const char *) const
Definition TDirectory.h:199
Class to handle efficiency histograms.
Definition TEfficiency.h:29
void FillWeighted(Bool_t bPassed, Double_t weight, Double_t x, Double_t y=0, Double_t z=0)
This function is used for filling the two histograms with a weight.
Double_t GetEfficiencyErrorUp(Int_t bin) const
Returns the upper error on the efficiency in the given global bin.
void Fill(Bool_t bPassed, Double_t x, Double_t y=0, Double_t z=0)
This function is used for filling the two histograms.
Graphics object made of three arrays X, Y and Z with the same number of points each.
Definition TGraph2D.h:41
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2387
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:2531
virtual void Add(TF1 *f, Double_t c1=1)
Performs the operation: y = y + c1*f(x,y) Errors are not recalculated.
Definition TGraph.cxx:650
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2426
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:1585
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2442
void SetNameTitle(const char *name="", const char *title="") override
Set graph name and title.
Definition TGraph.cxx:2462
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:926
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
void Draw(Option_t *option="") override
Draw this legend with its current attributes.
Definition TLegend.cxx:424
Use the TLine constructor to create a simple line.
Definition TLine.h:22
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
Mother of all ROOT objects.
Definition TObject.h:42
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:265
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:885
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:290
Regular expression class.
Definition TRegexp.h:31
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:138
Ssiz_t Length() const
Definition TString.h:425
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2060
const char * Data() const
Definition TString.h:384
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:632
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:2385
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:641
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:660
Float_t GetPadRightMargin() const
Definition TStyle.h:216
Float_t GetPadLeftMargin() const
Definition TStyle.h:215
Float_t GetPadBottomMargin() const
Definition TStyle.h:213
Float_t GetPadTopMargin() const
Definition TStyle.h:214
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:418
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
TDatime GetTime() const
Get time from UUID.
Definition TUUID.cxx:701
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg GlobalObservables(Args_t &&... argsOrArgSet)
RooCmdArg GlobalObservablesSource(const char *sourceName)
double gaussian_pdf(double x, double sigma=1, double x0=0)
Probability density function of the normal (Gaussian) distribution with mean x0 and standard deviatio...
const Int_t n
Definition legend1.C:16
TGraphErrors * gr
Definition legend1.C:25
double gaussian_cdf(double x, double sigma=1, double x0=0)
Alternative name for same function.
double gaussian_cdf_c(double x, double sigma=1, double x0=0)
Alternative name for same function.
static constexpr auto NumIntegration
Alias of MsgLevel::NumericIntegration for backwards compatibility.
@ NumericIntegration
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t Poisson(Double_t x, Double_t par)
Computes the Poisson distribution function for (x,par).
Definition TMath.cxx:587
Double_t LnGamma(Double_t z)
Computation of ln[gamma(z)] for all z.
Definition TMath.cxx:509
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
th1 Draw()
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
std::string collectionContents(const RooAbsCollection &coll)
#define GETWS(a)
#define GETWSSETS(w)