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>>
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();
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 }
716
717 return xRooFitResult(std::make_shared<xRooNode>(out, fPdf), std::make_shared<xRooNLLVar>(*this));
718}
719
720std::shared_ptr<ROOT::Fit::FitConfig> xRooNLLVar::fitConfig()
721{
722 if (!fFitConfig)
724 return fFitConfig;
725}
726
728{
729 if (auto conf = fitConfig(); conf)
730 return const_cast<ROOT::Math::IOptions *>(conf->MinimizerOptions().ExtraOptions());
731 return nullptr;
732}
733
734double xRooNLLVar::getEntryVal(size_t entry) const
735{
736 auto _data = data();
737 if (!_data)
738 return 0;
739 if (size_t(_data->numEntries()) <= entry)
740 return 0;
741 auto _pdf = pdf();
742 *std::unique_ptr<RooAbsCollection>(_pdf->getObservables(_data)) = *_data->get(entry);
743 // if (auto s = dynamic_cast<RooSimultaneous*>(_pdf.get());s) return
744 // -_data->weight()*s->getPdf(s->indexCat().getLabel())->getLogVal(_data->get());
745 return -_data->weight() * _pdf->getLogVal(_data->get());
746}
747
748std::set<std::string> xRooNLLVar::binnedChannels() const
749{
750 std::set<std::string> out;
751
752 auto binnedOpt = dynamic_cast<RooCmdArg *>(fOpts->find("Binned")); // the binned option, if explicitly specified
753
754 if (auto s = dynamic_cast<RooSimultaneous *>(pdf().get())) {
755 xRooNode simPdf(*s);
756 bool allChannels = true;
757 for (auto c : simPdf.bins()) {
758 // see if there's a RooRealSumPdf in the channel - if there is, if it has BinnedLikelihood set
759 // then assume is a BinnedLikelihood channel
760 RooArgSet nodes;
761 c->get<RooAbsArg>()->treeNodeServerList(&nodes, nullptr, true, false);
762 bool isBinned = false;
763 for (auto a : nodes) {
764 if (a->InheritsFrom("RooRealSumPdf") &&
765 ((binnedOpt && binnedOpt->getInt(0)) || (!binnedOpt && a->getAttribute("BinnedLikelihood")))) {
766 TString chanName(c->GetName());
767 out.insert(chanName(chanName.Index("=") + 1, chanName.Length()).Data());
768 isBinned = true;
769 break;
770 }
771 }
772 if (!isBinned) {
773 allChannels = false;
774 }
775 }
776 if (allChannels) {
777 out.clear();
778 out.insert("*");
779 }
780 } else {
781 RooArgSet nodes;
782 pdf()->treeNodeServerList(&nodes, nullptr, true, false);
783 for (auto a : nodes) {
784 if (a->InheritsFrom("RooRealSumPdf") &&
785 ((binnedOpt && binnedOpt->getInt(0)) || (!binnedOpt && a->getAttribute("BinnedLikelihood")))) {
786 out.insert("*");
787 break;
788 }
789 }
790 }
791 return out;
792}
793
795{
796
797 auto _data = data();
798 if (!_data)
799 return 0;
800 if (size_t(_data->numEntries()) <= entry)
801 return 0;
802 auto _pdf = pdf().get();
803 std::unique_ptr<RooAbsCollection> _robs(_pdf->getObservables(_data->get()));
804 *_robs = *_data->get(entry); // only set robs
805 if (auto s = dynamic_cast<RooSimultaneous *>(_pdf); s) {
806 _pdf = s->getPdf(s->indexCat().getCurrentLabel());
807 }
808 double volume = 1.;
809 for (auto o : *_robs) {
810
811 if (auto a = dynamic_cast<RooAbsRealLValue *>(o);
812 a && _pdf->dependsOn(*a)) { // dependsOn check needed until ParamHistFunc binBoundaries method fixed
813 std::unique_ptr<std::list<double>> bins(
814 _pdf->binBoundaries(*a, -std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()));
815 if (bins) {
816 double lowEdge = -std::numeric_limits<double>::infinity();
817 for (auto b : *bins) {
818 if (b > a->getVal()) {
819 volume *= (b - lowEdge);
820 break;
821 }
822 lowEdge = b;
823 }
824 }
825 }
826 }
827
828 return volume;
829}
830
832{
833 // for each global observable in the dataset, determine which constraint term is associated to it
834 // and given its type, add the necessary saturated term...
835
836 double out = 0;
837
838 if (!fGlobs)
839 return 0;
840
841 auto cTerm = constraintTerm();
842 if (!cTerm)
843 return 0;
844
845 for (auto c : cTerm->list()) {
846 if (std::string(c->ClassName()) == "RooAbsPdf" ||
847 std::string(c->ClassName()).find("RooNormalizedPdf") != std::string::npos) {
848 // in ROOT 6.32 the constraintTerm is full of RooNormalizedPdfs which aren't public
849 // became public in 6.34, hence now also check for RooNormalizedPdf explicitly
850 // in this case use the first server
851 c = c->servers()[0];
852 }
853 if (auto gaus = dynamic_cast<RooGaussian *>(c)) {
854 auto v = dynamic_cast<RooAbsReal *>(fGlobs->find(gaus->getX().GetName()));
855 if (!v) {
856 v = dynamic_cast<RooAbsReal *>(fGlobs->find(
857 gaus->getMean().GetName())); // shouldn't really happen but does for at least ws made by pyhf
858 }
859 if (!v)
860 continue;
861 out -= std::log(ROOT::Math::gaussian_pdf(v->getVal(), gaus->getSigma().getVal(), v->getVal()));
862 } else if (auto pois = dynamic_cast<RooPoisson *>(c)) {
863 auto v = dynamic_cast<RooAbsReal *>(fGlobs->find(pois->getX().GetName()));
864 if (!v)
865 continue;
866 out -= std::log(TMath::Poisson(v->getVal(), v->getVal()));
867 }
868 }
869
870 return out;
871}
872
873double xRooNLLVar::ndof() const
874{
875 return data()->numEntries() + (fFuncGlobs ? fFuncGlobs->size() : 0) -
876 std::unique_ptr<RooAbsCollection>(pars()->selectByAttrib("Constant", false))->size();
877}
878
879double xRooNLLVar::pgof() const
880{
881 // note that if evaluating this for a single channel, until 6.30 is available if you are using Binned mode the pdf
882 // will need to be part of a Simultaneous
883 return TMath::Prob(2. * (get()->getVal() - saturatedVal()), ndof());
884}
885
887{
888 // need to count number of floating unconstrained parameters
889 // which are floating parameters not featured in the constraintTerm
890 std::unique_ptr<RooAbsCollection> _floats(pars()->selectByAttrib("Constant", false));
891 if (auto _constraintTerm = constraintTerm()) {
892 _floats->remove(*std::unique_ptr<RooAbsCollection>(_constraintTerm->getVariables()));
893 }
894 return data()->numEntries() - _floats->size();
895}
896
898{
899 // using totVal - constraintTerm while new evalbackend causes mainTerm() to return nullptr
900 return get()->getVal() - constraintTermVal();
901}
902
904{
905 if (auto _constraintTerm = constraintTerm()) {
906 return _constraintTerm->getVal();
907 }
908 return 0;
909}
910
912{
914}
915
920
922{
923
924 // Use this term to create a goodness-of-fit metric, which is approx chi2 distributed with numEntries (data) d.o.f:
925 // prob = TMath::Prob( 2.*(nll.mainTerm()->getVal() - nll.saturatedNllTerm()), nll.data()->numEntries() )
926
927 // note that need to construct nll with explicit Binned(1 or 0) option otherwise will pick up nll eval
928 // from attributes in model already, so many get binned mainTerm eval when thinking not binned because didnt specify
929 // Binned(1)
930
931 auto _data = data();
932 if (!_data)
933 return std::numeric_limits<double>::quiet_NaN();
934
935 std::set<std::string> _binnedChannels = binnedChannels();
936
937 // for binned case each entry is: -(-N + Nlog(N) - TMath::LnGamma(N+1))
938 // for unbinned case each entry is: -(N*log(N/(sumN*binW))) = -N*logN + N*log(sumN) + N*log(binW)
939 // but unbinned gets extendedTerm = sumN - sumN*log(sumN)
940 // so resulting sum is just sumN - sum[ N*logN - N*log(binW) ]
941 // which is the same as the binned case without the LnGamma part and with the extra sum[N*log(binW)] part
942
943 const RooAbsCategoryLValue *cat = (dynamic_cast<RooSimultaneous *>(pdf().get()))
944 ? &dynamic_cast<RooSimultaneous *>(pdf().get())->indexCat()
945 : nullptr;
946
947 double out = _data->sumEntries();
948 for (int i = 0; i < _data->numEntries(); i++) {
949 _data->get(i);
950 double w = _data->weight();
951 if (w == 0)
952 continue;
953 out -= w * std::log(w);
954 if (_binnedChannels.count("*")) {
955 out += TMath::LnGamma(w + 1);
956 } else if (_binnedChannels.empty()) {
957 out += w * std::log(getEntryBinWidth(i));
958 } else if (cat) {
959 // need to determine which channel we are in for this entry to decide if binned or unbinned active
960 if (_binnedChannels.count(_data->get()->getCatLabel(cat->GetName()))) {
961 out += TMath::LnGamma(w + 1);
962 } else {
963 out += w * std::log(getEntryBinWidth(i));
964 }
965 } else {
966 throw std::runtime_error("Cannot determine category of RooSimultaneous pdf");
967 }
968 }
969
970 out += simTermVal();
971
972 return out;
973}
974
975std::shared_ptr<RooArgSet> xRooNLLVar::pars(bool stripGlobalObs) const
976{
977 auto out = std::shared_ptr<RooArgSet>(get()->getVariables());
978 if (stripGlobalObs && fGlobs) {
979 out->remove(*fGlobs, true, true);
980 }
981 return out;
982}
983
984TObject *
985xRooNLLVar::Scan(const char *scanPars, const std::vector<std::vector<double>> &coords, const RooArgList &profilePars)
986{
987 return Scan(*std::unique_ptr<RooAbsCollection>(get()->getVariables()->selectByName(scanPars)), coords, profilePars);
988}
989
990TObject *xRooNLLVar::Scan(const RooArgList &scanPars, const std::vector<std::vector<double>> &coords,
991 const RooArgList &profilePars)
992{
993
994 if (scanPars.size() > 2 || scanPars.empty())
995 return nullptr;
996
997 TGraph2D *out2d = (scanPars.size() == 2) ? new TGraph2D() : nullptr;
998 TGraph *out1d = (out2d) ? nullptr : new TGraph();
999 TNamed *out = (out2d) ? static_cast<TNamed *>(out2d) : static_cast<TNamed *>(out1d);
1000 out->SetName(get()->GetName());
1001 out->SetTitle(TString::Format("%s;%s%s%s", get()->GetTitle(), scanPars.first()->GetTitle(), out2d ? ";" : "",
1002 out2d ? scanPars.at(1)->GetTitle() : ""));
1003
1004 std::unique_ptr<RooAbsCollection> funcVars(get()->getVariables());
1006
1007 for (auto &coord : coords) {
1008 if (coord.size() != scanPars.size()) {
1009 throw std::runtime_error("Invalid coordinate");
1010 }
1011 for (size_t i = 0; i < coord.size(); i++) {
1012 static_cast<RooAbsRealLValue &>(scanPars[i]).setVal(coord[i]);
1013 }
1014
1015 if (profilePars.empty()) {
1016 // just evaluate
1017 if (out2d) {
1018 out2d->SetPoint(out2d->GetN(), coord[0], coord[1], get()->getVal());
1019 } else {
1020 out1d->SetPoint(out1d->GetN(), coord[0], get()->getVal());
1021 }
1022 }
1023 }
1024
1025 return out;
1026}
1027
1029{
1030 TString sOpt(opt);
1031
1032 auto _pars = pars();
1033
1034 if (sOpt == "sensitivity") {
1035
1036 // will make a plot of DeltaNLL
1037 }
1038
1039 if (sOpt == "floating") {
1040 // start scanning floating pars
1041 auto floats = std::unique_ptr<RooAbsCollection>(_pars->selectByAttrib("Constant", false));
1042 TVirtualPad *pad = gPad;
1043 if (!pad) {
1045 pad = gPad;
1046 }
1047 TMultiGraph *gr = new TMultiGraph;
1048 gr->SetName("multigraph");
1049 gr->SetTitle(TString::Format("%s;Normalized Parameter Value;#Delta NLL", get()->GetTitle()));
1050 /*((TPad*)pad)->DivideSquare(floats->size());
1051 int i=0;
1052 for(auto a : *floats) {
1053 i++;
1054 pad->cd(i);
1055 Draw(a->GetName());
1056 }*/
1057 return;
1058 }
1059
1060 RooArgList vars;
1061 TStringToken pattern(sOpt, ":");
1062 while (pattern.NextToken()) {
1063 TString s(pattern);
1064 if (auto a = _pars->find(s); a)
1065 vars.add(*a);
1066 }
1067
1068 if (vars.size() == 1) {
1069 TGraph *out = new TGraph;
1070 out->SetBit(kCanDelete);
1071 TGraph *bad = new TGraph;
1072 bad->SetBit(kCanDelete);
1073 bad->SetMarkerColor(kRed);
1074 bad->SetMarkerStyle(5);
1075 TMultiGraph *gr = (gPad) ? dynamic_cast<TMultiGraph *>(gPad->GetPrimitive("multigraph")) : nullptr;
1076 bool normRange = false;
1077 if (!gr) {
1078 gr = new TMultiGraph;
1079 gr->Add(out, "LP");
1081 } else {
1082 normRange = true;
1083 }
1084 out->SetName(get()->GetName());
1085 gr->SetTitle(TString::Format("%s;%s;#Delta NLL", get()->GetTitle(), vars.at(0)->GetTitle()));
1086 // scan outwards from current value towards limits
1087 auto v = dynamic_cast<RooRealVar *>(vars.at(0));
1088 double low = v->getVal();
1089 double high = low;
1090 double step = (v->getMax() - v->getMin()) / 100;
1091 double init = v->getVal();
1092 double initVal = func()->getVal();
1093 // double xscale = (normRange) ? (2.*(v->getMax() - v->getMin())) : 1.;
1094 auto currTime = std::chrono::steady_clock::now();
1095 while (out->GetN() < 100 && (low > v->getMin() || high < v->getMax())) {
1096 if (out->GetN() == 0) {
1097 out->SetPoint(out->GetN(), low, 0);
1098 low -= step;
1099 high += step;
1100 if (!normRange) {
1101 gr->Draw("A");
1102 gPad->SetGrid();
1103 }
1104 continue;
1105 }
1106 if (low > v->getMin()) {
1107 v->setVal(low);
1108 auto _v = func()->getVal();
1109 if (std::isnan(_v) || std::isinf(_v)) {
1110 if (bad->GetN() == 0)
1111 gr->Add(bad, "P");
1112 bad->SetPoint(bad->GetN(), low, out->GetPointY(0));
1113 } else {
1114 out->SetPoint(out->GetN(), low, _v - initVal);
1115 }
1116 low -= step;
1117 }
1118 if (high < v->getMax()) {
1119 v->setVal(high);
1120 auto _v = func()->getVal();
1121 if (std::isnan(_v) || std::isinf(_v)) {
1122 if (bad->GetN() == 0)
1123 gr->Add(bad, "P");
1124 bad->SetPoint(bad->GetN(), high, out->GetPointY(0));
1125 } else {
1126 out->SetPoint(out->GetN(), high, _v - initVal);
1127 }
1128 high += step;
1129 }
1130 out->Sort();
1131 // should only do processEvents once every second in case using x11 (which is slow)
1132 gPad->Modified();
1133 if (std::chrono::steady_clock::now() - currTime > std::chrono::seconds(1)) {
1134 currTime = std::chrono::steady_clock::now();
1135 gPad->Update();
1137 }
1138 }
1139 // add arrow to show current par value
1140 TArrow a;
1141 a.DrawArrow(init, 0, init, -0.1);
1142 gPad->Update();
1143#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
1144 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
1145#endif
1147 v->setVal(init);
1148 } else {
1149 Error("Draw", "Name a parameter to scan over: Draw(<name>) , choose from: %s",
1150 _pars->empty() ? "" : _pars->contentsString().c_str());
1151 }
1152}
1153
1154std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> xRooNLLVar::getData() const
1155{
1156 return std::make_pair(fData, fGlobs);
1157}
1158
1160{
1161 if (data.fComp && !data.get<RooAbsData>()) {
1162 return false;
1163 }
1164 return setData(std::dynamic_pointer_cast<RooAbsData>(data.fComp),
1165 std::shared_ptr<const RooAbsCollection>(data.globs().argList().snapshot()));
1166}
1167
1168bool xRooNLLVar::setData(const std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> &_data)
1169{
1170
1171 if (fData == _data.first && fGlobs == _data.second)
1172 return true;
1173
1174 auto _globs = fGlobs; // done to keep globs alive while NLL might still be alive.
1175
1176 auto _dglobs = (_data.second) ? _data.second
1177 : std::shared_ptr<const RooAbsCollection>(_data.first->getGlobalObservables(),
1178 [](const RooAbsCollection *) {});
1179
1180 if (fGlobs && !(fGlobs->empty() && !_dglobs) && _data.first &&
1181 fGlobs != _dglobs) { // second condition allows for no globs being a nullptr, third allow globs to remain if
1182 // nullifying data
1183 if (!_dglobs)
1184 throw std::runtime_error("Missing globs");
1185 // ignore 'extra' globs
1186 RooArgSet s;
1187 s.add(*fGlobs);
1188 std::unique_ptr<RooAbsCollection> _actualGlobs(fPdf->getObservables(s));
1189 RooArgSet s2;
1190 s2.add(*_dglobs);
1191 std::unique_ptr<RooAbsCollection> _actualGlobs2(fPdf->getObservables(s2));
1192 if (!_actualGlobs->equals(*_actualGlobs2)) {
1193 RooArgSet rC;
1194 rC.add(*_actualGlobs2);
1195 rC.remove(*std::unique_ptr<RooAbsCollection>(rC.selectCommon(*_actualGlobs)));
1196 TString r = (!rC.empty()) ? rC.contentsString() : "";
1197 RooArgSet lC;
1198 lC.add(*_actualGlobs);
1199 lC.remove(*std::unique_ptr<RooAbsCollection>(lC.selectCommon(*_actualGlobs2)));
1200 TString l = (!lC.empty()) ? lC.contentsString() : "";
1201 throw std::runtime_error(TString::Format("globs mismatch: adding %s removing %s", r.Data(), l.Data()));
1202 }
1203 fGlobs = _dglobs;
1204 }
1205
1206 if (!std::shared_ptr<RooAbsReal>::get()) {
1207 fData = _data.first;
1208 return true; // not loaded yet so nothing to do
1209 }
1210
1211 try {
1212 if (!kReuseNLL /*|| !mainTerm()*/
1213 /*|| mainTerm()->operMode() == RooAbsTestStatistic::MPMaster*/) { // lost access to RooAbsTestStatistic
1214 // in 6.34, but MP-mode will still throw
1215 // exception, so we will still catch it
1216 // happens when using MP need to rebuild the nll instead
1217 // also happens if there's no mainTerm(), which is the case in 6.32 where RooNLLVar is partially deprecated
1219 // ensure the const state is back where it was at nll construction time;
1220 fFuncVars->setAttribAll("Constant", false);
1221 fConstVars->setAttribAll("Constant", true);
1222 std::shared_ptr<RooAbsData> __data = fData; // do this just to keep fData alive while killing previous NLLVar
1223 // (can't kill data while NLL constructed with it)
1224 fData = _data.first;
1225 reinitialize();
1226 return true;
1227 }
1228 bool out = false;
1229 if (_data.first) {
1230 // replace in all terms
1231 out = get()->setData(*_data.first, false /* clone data */);
1232 // get()->setValueDirty();
1233 // if (_data.first->getGlobalObservables()) {
1234 // // replace in all terms
1235 // out = get()->setData(*_data.first, false);
1236 // get()->setValueDirty();
1237 // } else {
1238 // // replace just in mainTerm ... note to self: why not just replace in all like above? should
1239 // test! auto _mainTerm = mainTerm(); out = _mainTerm->setData(*_data.first, false /* clone data?
1240 // */); _mainTerm->setValueDirty();
1241 // }
1242 } else {
1243 reset();
1244 }
1245 fData = _data.first;
1246 return out;
1247 } catch (std::runtime_error &) {
1248 // happens when using MP need to rebuild the nll instead
1249 // also happens if there's no mainTerm(), which is the case in 6.32 where RooNLLVar is partially deprecated
1251 // ensure the const state is back where it was at nll construction time;
1252 fFuncVars->setAttribAll("Constant", false);
1253 fConstVars->setAttribAll("Constant", true);
1254 std::shared_ptr<RooAbsData> __data = fData; // do this just to keep fData alive while killing previous NLLVar
1255 // (can't kill data while NLL constructed with it)
1256 fData = _data.first;
1257 reinitialize();
1258 return true;
1259 }
1260 throw std::runtime_error("Unable to setData");
1261}
1262
1263std::shared_ptr<RooAbsReal> xRooNLLVar::func() const
1264{
1265 if (!(*this)) {
1266 const_cast<xRooNLLVar *>(this)->reinitialize();
1267 } else if (auto f = std::unique_ptr<RooAbsCollection>(fConstVars->selectByAttrib("Constant", false)); !f->empty()) {
1268 // have to reinitialize if const par values have changed - const optimization forces this
1269 // TODO: currently changes to globs also triggers this since the vars includes globs (vars are the non-obs pars)
1270 // std::cout << "Reinitializing because of change of const parameters:" << f->contentsString() << std::endl;
1271 const_cast<xRooNLLVar *>(this)->reinitialize();
1272
1273 // note ... it may be sufficient here to do:
1274 // nll.constOptimizeTestStatistic(RooAbsArg::ConfigChange, constOptimize>1 /* do tracking too if >1 */); //
1275 // trigger a re-evaluate of which nodes to cache-and-track nll.constOptimizeTestStatistic(RooAbsArg::ValueChange,
1276 // constOptimize>1); // update the cache values -- is this needed??
1277 // this forces the optimization to be redone
1278 // for now leave as a reinitialize though, until had a chance to test this properly
1279 }
1280 if (fGlobs && fFuncGlobs) {
1281 *fFuncGlobs = *fGlobs;
1282 fFuncGlobs->setAttribAll("Constant", true);
1283 }
1284 return *this;
1285}
1286
1288{
1289
1290 if (strcmp(opt.GetName(), "Hesse") == 0) {
1291 fitConfig()->SetParabErrors(opt.getInt(0)); // controls hesse
1292 } else if (strcmp(opt.GetName(), "Minos") == 0) {
1293 fitConfig()->SetMinosErrors(opt.getInt(0)); // controls minos
1294 } else if (strcmp(opt.GetName(), "Strategy") == 0) {
1295 fitConfig()->MinimizerOptions().SetStrategy(opt.getInt(0));
1296 } else if (strcmp(opt.GetName(), "StrategySequence") == 0) {
1297 fitConfigOptions()->SetNamedValue("StrategySequence", opt.getString(0));
1298 } else if (strcmp(opt.GetName(), "Tolerance") == 0) {
1299 fitConfig()->MinimizerOptions().SetTolerance(opt.getDouble(0));
1300 } else if (strcmp(opt.GetName(), "MaxCalls") == 0) {
1301 fitConfig()->MinimizerOptions().SetMaxFunctionCalls(opt.getInt(0));
1302 } else if (strcmp(opt.GetName(), "MaxIterations") == 0) {
1303 fitConfig()->MinimizerOptions().SetMaxIterations(opt.getInt(0));
1304 } else if (strcmp(opt.GetName(), "PrintLevel") == 0) {
1305 fitConfig()->MinimizerOptions().SetPrintLevel(opt.getInt(0));
1306 } else {
1307 if (strcmp(opt.GetName(), "Optimize") == 0) {
1308 // this flag will trigger constOptimizeTestStatistic to be called on the nll in createNLL method
1309 // we should ensure that the fitconfig setting is consistent with it ...
1310 fitConfigOptions()->SetValue("OptimizeConst", opt.getInt(0));
1311 }
1312 if (auto prevObject = fOpts->FindObject(opt.GetName()); prevObject) {
1313 // replace previous option
1314 fOpts->Replace(prevObject, opt.Clone(nullptr));
1315 delete prevObject;
1316 } else {
1317 fOpts->Add(opt.Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
1318 // "" instead, so doesnt copy names
1319 }
1320 if (std::shared_ptr<RooAbsReal>::get()) {
1321 reinitialize(); // do this way to keep name of nll if user set
1322 } else {
1323 reset(); // will trigger reinitialize
1324 }
1325 }
1326}
1327
1329{
1330 return fData.get();
1331 /*
1332#if ROOT_VERSION_CODE < ROOT_VERSION(6, 33, 00)
1333 auto _nll = mainTerm();
1334 if (!_nll)
1335 return fData.get();
1336 RooAbsData *out = &static_cast<RooAbsOptTestStatistic*>(_nll)->data();
1337#else
1338 RooAbsData* out = nullptr; // new backends not conducive to having a reference to a RooAbsData in them (they use
1339buffers instead) #endif if (!out) return fData.get(); return out;
1340 */
1341}
1342
1343/*
1344RooAbsReal *xRooNLLVar::mainTerm() const
1345{
1346 return nullptr;
1347 // the main term is the "other term" in a RooAddition alongside a ConstraintSum
1348 // if can't find the ConstraintSum, just return the function
1349
1350 RooAbsArg* _func = func().get();
1351 if(!_func->InheritsFrom("RooAddition")) {
1352 _func = nullptr;
1353 // happens with new 6.32 backend, where the top-level function is an EvaluatorWrapper
1354 for (auto s : func()->servers()) {
1355 if(s->InheritsFrom("RooAddition")) {
1356 _func = s; break;
1357 }
1358 }
1359 if(!_func) {
1360 return func().get();
1361 }
1362 }
1363 std::set<RooAbsArg*> others,constraints;
1364 for (auto s : _func->servers()) {
1365 if(s->InheritsFrom("RooConstraintSum")) {
1366 constraints.insert(s);
1367 } else {
1368 others.insert(s);
1369 }
1370 }
1371 if(constraints.size()==1 && others.size()==1) {
1372 return static_cast<RooAbsReal*>(*others.begin());
1373 }
1374 return nullptr; // failed to find the right term?
1375
1376
1377}
1378 */
1379
1381{
1382 // returns Nexp - Nobs*log(Nexp)
1383 return fPdf->extendedTerm(fData->sumEntries(), fData->get());
1384}
1385
1387{
1388 // comes from the _simCount code inside RooNLLVar
1389 // is this actually only appropriate if the roosimultaneous is not extended?
1390 // i.e. then this term represents the probability the entry belongs to a given state, and given
1391 // all the states are normalized to 1, this probability is assumed to just be 1/N_states
1392 if (auto s = dynamic_cast<RooSimultaneous *>(fPdf.get()); s) {
1393 return fData->sumEntries() * log(1.0 * (s->servers().size() - 1)); // one of the servers is the cat
1394 }
1395 return 0;
1396}
1397
1399{
1400 // this is only relevant if BinnedLikelihood active
1401 // = sum[ N_i! ] since LnGamma(N_i+1) ~= N_i!
1402 // need to also subtract off sum[ N_i*log(width_i) ] in order to have formula: binnedLL = unbinnedLL + binnedDataTerm
1403 // note this is 0 if all the bin widths are 1
1404 double out = 0;
1405 for (int i = 0; i < fData->numEntries(); i++) {
1406 fData->get(i);
1407 out += TMath::LnGamma(fData->weight() + 1) - fData->weight() * std::log(getEntryBinWidth(i));
1408 }
1409
1410 return out;
1411}
1412
1414{
1415 auto _func = func();
1416 if (auto a = dynamic_cast<RooConstraintSum *>(_func.get()); a)
1417 return a;
1418 for (auto s : _func->servers()) {
1419 if (auto a = dynamic_cast<RooConstraintSum *>(s); a)
1420 return a;
1421 // allow one more depth to support 6.32 (where sum is hidden inside the first server)
1422 for (auto s2 : s->servers()) {
1423 if (auto a2 = dynamic_cast<RooConstraintSum *>(s2); a2)
1424 return a2;
1425 }
1426 }
1427 return nullptr;
1428}
1429
1430/*xRooNLLVar::operator RooAbsReal &() const {
1431 // this works in c++ but not in python
1432 std::cout << "implicit conversion" << std::endl;
1433 return *fFunc;
1434}*/
1435
1437{
1439 sWhat.ToLower();
1440 sWhat.ReplaceAll(" =", "=").ReplaceAll("= ", "="); // remove spaces around equals signs
1441 bool doTS = sWhat.Contains("ts");
1442 bool doCLs = sWhat.Contains("pcls");
1443 bool doNull = sWhat.Contains("pnull");
1444 bool doAlt = sWhat.Contains("palt");
1445 double nSigma = (sWhat.Contains("exp"))
1446 ? (TString(sWhat(sWhat.Index("exp") + 3, sWhat.Index(" ", sWhat.Index("exp")) == -1
1447 ? sWhat.Length()
1448 : sWhat.Index(" ", sWhat.Index("exp"))))
1449 .Atof())
1450 : std::numeric_limits<double>::quiet_NaN();
1451
1452 bool toys = sWhat.Contains("toys");
1453
1454 // bool asymp = sWhat.Contains("asymp");
1455
1456 bool readOnly = sWhat.Contains("readonly");
1457
1458 if (!readOnly) {
1459 if (toys) {
1460 sigma_mu(); // means we will be able to evaluate the asymptotic values too
1461 }
1462 // only add toys if actually required
1463 if (getVal(sWhat + " readonly").second != 0) {
1464 if (sWhat.Contains("toys=")) {
1465 // extract number of toys required ... format is "nullToys.altToysFraction" if altToysFraction=0 then use
1466 // same for both, unless explicitly set (i.e. N.0) then means we want no alt toys
1467 // e.g. if doing just pnull significance
1468 TString toyNum = sWhat(sWhat.Index("toys=") + 5, sWhat.Length());
1469 size_t nToys = toyNum.Atoi();
1470 size_t nToysAlt = (toyNum.Atof() - nToys) * nToys;
1471 if (nToysAlt == 0 && !toyNum.Contains('.'))
1472 nToysAlt = nToys;
1473 if (nullToys.size() < nToys) {
1474 addNullToys(nToys - nullToys.size());
1475 }
1476 if (altToys.size() < nToysAlt) {
1477 addAltToys(nToysAlt - altToys.size());
1478 }
1479 } else if (doCLs && toys) {
1480 // auto toy-generating for limits .. do in blocks of 100
1481 addCLsToys(100, 0, 0.05, nSigma);
1482 } else if (toys) {
1483 throw std::runtime_error("Auto-generating toys for anything other than CLs not yet supported, please "
1484 "specify number of toys with 'toys=N' ");
1485 }
1486 }
1487 }
1488
1489 struct RestoreNll {
1490 RestoreNll(std::shared_ptr<xRooNLLVar> &v, bool r) : rr(r), var(v)
1491 {
1492 if (rr && var && var->get()) {
1493 _readOnly = var->get()->getAttribute("readOnly");
1494 var->get()->setAttribute("readOnly", rr);
1495 } else {
1496 rr = false;
1497 }
1498 };
1499 ~RestoreNll()
1500 {
1501 if (rr)
1502 var->get()->setAttribute("readOnly", _readOnly);
1503 };
1504
1505 bool rr = false;
1506 bool _readOnly = false;
1507
1508 std::shared_ptr<xRooNLLVar> &var;
1509 };
1510
1511 RestoreNll rest(nllVar, readOnly);
1512
1513 if (doTS)
1514 return (toys) ? ts_toys(nSigma) : ts_asymp(nSigma);
1515 if (doNull)
1516 return (toys) ? pNull_toys(nSigma) : pNull_asymp(nSigma);
1517 if (doAlt)
1518 return (toys) ? pAlt_toys(nSigma) : pAlt_asymp(nSigma);
1519 if (doCLs)
1520 return (toys) ? pCLs_toys(nSigma) : pCLs_asymp(nSigma);
1521
1522 throw std::runtime_error(std::string("Unknown: ") + what);
1523}
1524
1526{
1527 RooArgList out;
1528 out.setName("poi");
1529 out.add(*std::unique_ptr<RooAbsCollection>(coords->selectByAttrib("poi", true)));
1530 return out;
1531}
1532
1534{
1535 RooArgList out;
1536 out.setName("alt_poi");
1537 out.addClone(*std::unique_ptr<RooAbsCollection>(coords->selectByAttrib("poi", true)));
1538 for (auto a : out) {
1539 auto v = dynamic_cast<RooAbsRealLValue *>(a);
1540 if (!v)
1541 continue;
1542 if (auto s = a->getStringAttribute("altVal"); s && strlen(s)) {
1543 v->setVal(TString(s).Atof());
1544 } else {
1545 v->setVal(std::numeric_limits<double>::quiet_NaN());
1546 }
1547 }
1548 return out;
1549}
1550
1552{
1553 auto &me = const_cast<xRooHypoPoint &>(*this);
1554 int out = 0;
1555 if (me.ufit(true) && !allowedStatusCodes.count(me.ufit(true)->status()))
1556 out += 1;
1557 if (me.cfit_null(true) && !allowedStatusCodes.count(me.cfit_null(true)->status()))
1558 out += 1 << 1;
1559 if (me.cfit_alt(true) && !allowedStatusCodes.count(me.cfit_alt(true)->status()))
1560 out += 1 << 2;
1561 if (me.asimov(true))
1562 out += me.asimov(true)->status() << 3;
1563 return out;
1564}
1565
1567{
1568 auto _poi = const_cast<xRooHypoPoint *>(this)->poi();
1569 auto _alt_poi = const_cast<xRooHypoPoint *>(this)->alt_poi();
1570 std::cout << "POI: " << _poi.contentsString() << " , null: ";
1571 bool first = true;
1572 for (auto a : _poi) {
1573 auto v = dynamic_cast<RooAbsReal *>(a);
1574 if (!a)
1575 continue;
1576 if (!first)
1577 std::cout << ",";
1578 std::cout << v->getVal();
1579 first = false;
1580 }
1581 std::cout << " , alt: ";
1582 first = true;
1583 bool any_alt = false;
1584 for (auto a : _alt_poi) {
1585 auto v = dynamic_cast<RooAbsReal *>(a);
1586 if (!a)
1587 continue;
1588 if (!first)
1589 std::cout << ",";
1590 std::cout << v->getVal();
1591 first = false;
1592 if (!std::isnan(v->getVal()))
1593 any_alt = true;
1594 }
1595 std::cout << " , pllType: " << fPllType << std::endl;
1596
1597 if (fPllType == xRooFit::Asymptotics::Unknown) {
1598 std::cout << " obs ts: " << obs_ts << " +/- " << obs_ts_err << std::endl;
1599 }
1600
1601 std::cout << " - ufit: ";
1602 if (fUfit) {
1603 std::cout << fUfit->GetName() << " " << fUfit->minNll() << " (status=" << fUfit->status() << ") (";
1604 first = true;
1605 for (auto a : _poi) {
1606 auto v = dynamic_cast<RooRealVar *>(fUfit->floatParsFinal().find(a->GetName()));
1607 if (!v)
1608 continue;
1609 if (!first)
1610 std::cout << ",";
1611 std::cout << v->GetName() << "_hat: " << v->getVal() << " +/- " << v->getError();
1612 first = false;
1613 }
1614 std::cout << ")" << std::endl;
1615 } else {
1616 std::cout << "Not calculated" << std::endl;
1617 }
1618 std::cout << " - cfit_null: ";
1619 if (fNull_cfit) {
1620 std::cout << fNull_cfit->GetName() << " " << fNull_cfit->minNll() << " (status=" << fNull_cfit->status() << ")";
1621 } else {
1622 std::cout << "Not calculated";
1623 }
1624 if (any_alt) {
1625 std::cout << std::endl << " - cfit_alt: ";
1626 if (fAlt_cfit) {
1627 std::cout << fAlt_cfit->GetName() << " " << fAlt_cfit->minNll() << " (status=" << fAlt_cfit->status() << ")"
1628 << std::endl;
1629 } else {
1630 std::cout << "Not calculated" << std::endl;
1631 }
1632 std::cout << " sigma_mu: ";
1633 const_cast<xRooHypoPoint *>(this)->asimov(true); // will trigger construction of fAsimov hypoPoint if possible
1634 if (!fAsimov || !fAsimov->fUfit || !fAsimov->fNull_cfit) {
1635 std::cout << "Not calculated";
1636 } else {
1637 std::cout << const_cast<xRooHypoPoint *>(this)->sigma_mu().first << " +/- "
1638 << const_cast<xRooHypoPoint *>(this)->sigma_mu().second;
1639 }
1640 if (fAsimov) {
1641 std::cout << std::endl;
1642 std::cout << " - asimov ufit: ";
1643 if (fAsimov->fUfit) {
1644 std::cout << fAsimov->fUfit->GetName() << " " << fAsimov->fUfit->minNll()
1645 << " (status=" << fAsimov->fUfit->status() << ")";
1646 } else {
1647 std::cout << "Not calculated";
1648 }
1649 std::cout << std::endl << " - asimov cfit_null: ";
1650 if (fAsimov->fNull_cfit) {
1651 std::cout << fAsimov->fNull_cfit->GetName() << " " << fAsimov->fNull_cfit->minNll()
1652 << " (status=" << fAsimov->fNull_cfit->status() << ")";
1653 } else {
1654 std::cout << "Not calculated";
1655 }
1656 }
1657 std::cout << std::endl;
1658 } else {
1659 std::cout << std::endl;
1660 }
1661 if (fLbound_cfit) {
1662 std::cout << " - cfit_lbound: " << fLbound_cfit->GetName() << " " << fLbound_cfit->minNll()
1663 << " (status=" << fLbound_cfit->status() << ")" << std::endl;
1664 }
1665 if (fGenFit)
1666 std::cout << " - gfit: " << fGenFit->GetName() << std::endl;
1667 if (!nullToys.empty() || !altToys.empty()) {
1668 std::cout << " * null toys: " << nullToys.size();
1669 size_t firstToy = 0;
1670 while (firstToy < nullToys.size() && std::isnan(std::get<1>(nullToys[firstToy])))
1671 firstToy++;
1672 if (firstToy > 0)
1673 std::cout << " [ of which " << firstToy << " are bad]";
1674 std::cout << " , alt toys: " << altToys.size();
1675 firstToy = 0;
1676 while (firstToy < altToys.size() && std::isnan(std::get<1>(altToys[firstToy])))
1677 firstToy++;
1678 if (firstToy > 0)
1679 std::cout << " [ of which " << firstToy << " are bad]";
1680 std::cout << std::endl;
1681 }
1682 // std::cout << " nllVar: " << nllVar << std::endl;
1683}
1684
1686{
1687 if (ufit()) {
1688 auto var = dynamic_cast<RooRealVar *>(ufit()->floatParsFinal().find(fPOIName()));
1689 if (var) {
1690 return *var;
1691 } else {
1692 throw std::runtime_error(TString::Format("Cannot find POI: %s", fPOIName()));
1693 }
1694 }
1695 throw std::runtime_error("Unconditional fit unavailable");
1696}
1697
1698std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> xRooNLLVar::xRooHypoPoint::data()
1699{
1700 if (fData.first)
1701 return fData;
1702 if (fGenFit && isExpected) {
1703 // std::cout << "Generating asimov" << std::endl;poi().Print("v");
1704 fData = xRooFit::generateFrom(*nllVar->fPdf, *fGenFit, true);
1705 }
1706 return fData;
1707}
1708
1709xRooNLLVar::xRooHypoPoint::xRooHypoPoint(std::shared_ptr<RooStats::HypoTestResult> htr, const RooAbsCollection *_coords)
1710 : hypoTestResult(htr)
1711{
1712 if (hypoTestResult) {
1713 // load the pllType
1714 fPllType =
1715 xRooFit::Asymptotics::PLLType(hypoTestResult->GetFitInfo()->getGlobalObservables()->getCatIndex("pllType"));
1716 isExpected = hypoTestResult->GetFitInfo()->getGlobalObservables()->getRealValue("isExpected");
1717
1718 // load obsTS value
1719 obs_ts = hypoTestResult->GetTestStatisticData();
1720 obs_ts_err = hypoTestResult->GetFitInfo()->getGlobalObservables()->getRealValue("obs_ts_err");
1721
1722 // load the toys
1723 auto toys = hypoTestResult->GetNullDetailedOutput();
1724 if (toys) {
1725 // load coords from the nullDist globs list
1726 if (toys->getGlobalObservables()) {
1727 coords = std::shared_ptr<RooAbsCollection>(toys->getGlobalObservables()->snapshot());
1728 }
1729 for (int i = 0; i < toys->numEntries(); i++) {
1730 auto toy = toys->get(i);
1731 nullToys.emplace_back(
1732 std::make_tuple(int(toy->getRealValue("seed")), toy->getRealValue("ts"), toys->weight()));
1733 }
1734 }
1735 toys = hypoTestResult->GetAltDetailedOutput();
1736 if (toys) {
1737 for (int i = 0; i < toys->numEntries(); i++) {
1738 auto toy = toys->get(i);
1739 altToys.emplace_back(
1740 std::make_tuple(int(toy->getRealValue("seed")), toy->getRealValue("ts"), toys->weight()));
1741 }
1742 }
1743 }
1744 if (!coords && _coords)
1745 coords.reset(_coords->snapshot());
1746}
1747
1748std::shared_ptr<xRooNLLVar::xRooHypoPoint> xRooNLLVar::xRooHypoPoint::asimov(bool readOnly)
1749{
1750
1751 if (!fAsimov && (nllVar || hypoTestResult)) {
1752 auto theFit = (!fData.first && fGenFit && !isExpected)
1753 ? fGenFit
1754 : cfit_alt(readOnly); // first condition allows genFit to be used as the altFit *if* the data is
1755 // entirely absent, provided not expected data because we postpone data
1756 // creation til later in that case (see below)
1757 if (!theFit || allowedStatusCodes.find(theFit->status()) == allowedStatusCodes.end())
1758 return fAsimov;
1759 fAsimov = std::make_shared<xRooHypoPoint>(*this);
1760 fAsimov->coords.reset(fAsimov->coords->snapshot()); // create a copy so can remove the physical range below
1761 fAsimov->hypoTestResult.reset();
1762 fAsimov->fPllType = xRooFit::Asymptotics::TwoSided;
1763 for (auto p : fAsimov->poi()) {
1764 // dynamic_cast<RooRealVar *>(p)->removeRange("physical"); -- can't use this as will modify shared property
1765 if (auto v = dynamic_cast<RooRealVar *>(p)) {
1766 v->deleteSharedProperties(); // effectively removes all custom ranges
1767 }
1768 }
1769
1770 fAsimov->nullToys.clear();
1771 fAsimov->altToys.clear();
1772 fAsimov->fUfit = retrieveFit(3);
1773 fAsimov->fNull_cfit = retrieveFit(4);
1774 fAsimov->fAlt_cfit.reset();
1775 fAsimov->fData =
1776 std::make_pair(nullptr, nullptr); // postpone generating expected data until we definitely need it
1777 fAsimov->fGenFit = theFit;
1778 fAsimov->isExpected = true;
1779 }
1780
1781 return fAsimov;
1782}
1783
1785{
1786 if (fPllType != xRooFit::Asymptotics::Uncapped && ts_asymp(nSigma).first == 0)
1787 return std::pair<double, double>(1, 0);
1788 auto first_poi = dynamic_cast<RooRealVar *>(poi().first());
1789 if (!first_poi)
1790 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1791 auto _sigma_mu = sigma_mu();
1792 double nom = xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first, fNullVal(), fNullVal(), _sigma_mu.first,
1793 first_poi->getMin("physical"), first_poi->getMax("physical"));
1794 double up =
1795 xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first + ts_asymp(nSigma).second, fNullVal(), fNullVal(),
1796 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1797 double down =
1798 xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first - ts_asymp(nSigma).second, fNullVal(), fNullVal(),
1799 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1800 return std::pair(nom, std::max(std::abs(up - nom), std::abs(down - nom)));
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 auto _sigma_mu = sigma_mu();
1811 double nom = xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first, fNullVal(), fAltVal(), _sigma_mu.first,
1812 first_poi->getMin("physical"), first_poi->getMax("physical"));
1813 double up =
1814 xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first + ts_asymp(nSigma).second, fNullVal(), fAltVal(),
1815 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1816 double down =
1817 xRooFit::Asymptotics::PValue(fPllType, ts_asymp(nSigma).first - ts_asymp(nSigma).second, fNullVal(), fAltVal(),
1818 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1819
1820 return std::pair(nom, std::max(std::abs(up - nom), std::abs(down - nom)));
1821}
1822
1824{
1825 if (fNullVal() == fAltVal())
1826 return std::pair<double, double>(1, 0); // by construction
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
1834 auto _ts_asymp = ts_asymp(nSigma);
1835 auto _sigma_mu = sigma_mu();
1836 double nom1 = xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first, fNullVal(), fNullVal(), _sigma_mu.first,
1837 first_poi->getMin("physical"), first_poi->getMax("physical"));
1838 double up1 =
1839 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first + _ts_asymp.second, fNullVal(), fNullVal(),
1840 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1841 double down1 =
1842 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first - _ts_asymp.second, fNullVal(), fNullVal(),
1843 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1844 double nom2 = xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first, fNullVal(), fAltVal(), _sigma_mu.first,
1845 first_poi->getMin("physical"), first_poi->getMax("physical"));
1846 double up2 =
1847 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first + _ts_asymp.second, fNullVal(), fAltVal(), _sigma_mu.first,
1848 first_poi->getMin("physical"), first_poi->getMax("physical"));
1849 double down2 =
1850 xRooFit::Asymptotics::PValue(fPllType, _ts_asymp.first - _ts_asymp.second, fNullVal(), fAltVal(), _sigma_mu.first,
1851 first_poi->getMin("physical"), first_poi->getMax("physical"));
1852
1853 auto nom = (nom1 == 0) ? 0 : nom1 / nom2;
1854 auto up = (up1 == 0) ? 0 : up1 / up2;
1855 auto down = (down1 == 0) ? 0 : down1 / down2;
1856
1857 return std::pair(nom, std::max(std::abs(up - nom), std::abs(down - nom)));
1858}
1859
1861{
1862 if (std::isnan(nSigma)) {
1863 return (fPllType == xRooFit::Asymptotics::Unknown) ? std::make_pair(obs_ts, obs_ts_err) : pll();
1864 }
1865
1866 auto first_poi = dynamic_cast<RooRealVar *>(poi().first());
1867 auto _sigma_mu = sigma_mu();
1868 if (!first_poi || (!std::isnan(nSigma) && std::isnan(_sigma_mu.first)))
1869 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1870 double nom = xRooFit::Asymptotics::k(fPllType, ROOT::Math::gaussian_cdf(nSigma), fNullVal(), fAltVal(),
1871 _sigma_mu.first, first_poi->getMin("physical"), first_poi->getMax("physical"));
1872 double up = xRooFit::Asymptotics::k(fPllType, ROOT::Math::gaussian_cdf(nSigma), fNullVal(), fAltVal(),
1873 _sigma_mu.first + _sigma_mu.second, first_poi->getMin("physical"),
1874 first_poi->getMax("physical"));
1875 double down = xRooFit::Asymptotics::k(fPllType, ROOT::Math::gaussian_cdf(nSigma), fNullVal(), fAltVal(),
1876 _sigma_mu.first - _sigma_mu.second, first_poi->getMin("physical"),
1877 first_poi->getMax("physical"));
1878 return std::pair<double, double>(nom, std::max(std::abs(nom - up), std::abs(nom - down)));
1879}
1880
1882{
1883 if (std::isnan(nSigma)) {
1884 return ts_asymp();
1885 }
1886 // nans should appear in the alt toys first ... so loop until past nans
1887 size_t firstToy = 0;
1888 while (firstToy < altToys.size() && std::isnan(std::get<1>(altToys[firstToy])))
1889 firstToy++;
1890 if (firstToy >= altToys.size())
1891 return std::pair(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
1892 int targetIdx =
1893 (altToys.size() - firstToy) * ROOT::Math::gaussian_cdf_c(nSigma) + firstToy; // TODO: Account for weights
1894 return std::pair(std::get<1>(altToys[targetIdx]), (std::get<1>(altToys[std::min(int(altToys.size()), targetIdx)]) -
1895 std::get<1>(altToys[std::max(0, targetIdx)])) /
1896 2.);
1897}
1898
1900{
1901 auto _ufit = ufit(readOnly);
1902 if (!_ufit) {
1903 if (hypoTestResult)
1904 return std::pair<double, double>(hypoTestResult->GetTestStatisticData(), 0);
1905 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1906 }
1907 if (allowedStatusCodes.find(_ufit->status()) == allowedStatusCodes.end()) {
1908 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1909 }
1910 if (auto _first_poi = dynamic_cast<RooRealVar *>(poi().first());
1911 _first_poi && _first_poi->getMin("physical") > _first_poi->getMin() &&
1912 mu_hat().getVal() < _first_poi->getMin("physical")) {
1913 // replace _ufit with fit "boundary" conditional fit
1914 _ufit = cfit_lbound(readOnly);
1915 if (!_ufit) {
1916 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1917 }
1918 }
1919 auto cFactor = (fPllType == xRooFit::Asymptotics::TwoSided)
1920 ? 1.
1921 : xRooFit::Asymptotics::CompatFactor(fPllType, fNullVal(), mu_hat().getVal());
1922 if (cFactor == 0)
1923 return std::pair<double, double>(0, 0);
1924 auto _cfit_null = cfit_null(readOnly);
1925 if (!_cfit_null || allowedStatusCodes.find(_cfit_null->status()) == allowedStatusCodes.end())
1926 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
1927 // std::cout << cfit->minNll() << ":" << cfit->edm() << " " << ufit->minNll() << ":" << ufit->edm() << std::endl;
1928 double diff = _cfit_null->minNll() - _ufit->minNll();
1929 if (diff < 0) {
1930 // use edm to attempt a small correction to the diff, i.e. assume edm is an additional correction required to
1931 // minNll
1932 diff += (_ufit->edm() - _cfit_null->edm());
1933 }
1934 return std::pair<double, double>(2. * cFactor * diff,
1935 2. * cFactor * sqrt(pow(cfit_null(readOnly)->edm(), 2) + pow(_ufit->edm(), 2)));
1936 // return 2.*cFactor*(cfit->minNll()+cfit->edm() - ufit->minNll()+ufit->edm());
1937}
1938
1939std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::retrieveFit(int type)
1940{
1941 if (!hypoTestResult)
1942 return nullptr;
1943 // see if can retrieve from that ....
1944 if (auto fits = hypoTestResult->GetFitInfo()) {
1945 for (int i = 0; i < fits->numEntries(); i++) {
1946 auto fit = fits->get(i);
1947 if (fit->getCatIndex("type") != type)
1948 continue;
1949 // found ufit ... construct
1950 std::string _name =
1951 fits->getGlobalObservables()->getStringValue(TString::Format("%s.name", fit->getCatLabel("type")));
1952 // see if can retrieve from any open file ....
1953 TDirectory *tmp = gDirectory;
1954 for (auto file : *gROOT->GetListOfFiles()) {
1955 if (auto k = static_cast<TDirectory *>(file)->FindKeyAny(_name.c_str())) {
1956 // use pre-retrieved fits if available
1958 k->GetMotherDir()->GetList()
1959 ? dynamic_cast<xRooFit::StoredFitResult *>(k->GetMotherDir()->GetList()->FindObject(k->GetName()))
1960 : nullptr;
1961 if (auto cachedFit = (storedFr) ? storedFr->fr.get() : k->ReadObject<RooFitResult>(); cachedFit) {
1962 if (!storedFr) {
1964 k->GetMotherDir()->Add(storedFr);
1965 }
1966 gDirectory = tmp; // one of the above calls moves to key's directory ... i didn't check which
1967 return storedFr->fr;
1968 }
1969 }
1970 }
1971 auto rfit = std::make_shared<RooFitResult>(_name.c_str(), TUUID(_name.c_str()).GetTime().AsString());
1972 rfit->setStatus(fit->getRealValue("status"));
1973 rfit->setMinNLL(fit->getRealValue("minNll"));
1974 rfit->setEDM(fit->getRealValue("edm"));
1975 if (type == 0) {
1976 std::unique_ptr<RooAbsCollection> par_hats(
1977 hypoTestResult->GetFitInfo()->getGlobalObservables()->selectByName(coords->contentsString().c_str()));
1978 par_hats->setName("floatParsFinal");
1979 rfit->setFinalParList(*par_hats);
1980 } else {
1981 rfit->setFinalParList(RooArgList());
1982 }
1983 rfit->setConstParList(RooArgList());
1984 rfit->setInitParList(RooArgList());
1985 TMatrixDSym cov(0);
1986 rfit->setCovarianceMatrix(cov);
1987 rfit->setCovQual(fit->getRealValue("covQual"));
1988 return rfit;
1989 }
1990 }
1991 return nullptr;
1992}
1993
1994std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::ufit(bool readOnly)
1995{
1996 if (fUfit)
1997 return fUfit;
1998 if (auto rfit = retrieveFit(0)) {
1999 return fUfit = rfit;
2000 }
2001 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2002 return nullptr;
2003 if (!nllVar->fFuncVars)
2004 nllVar->reinitialize();
2005 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2006 if (!fData.first) {
2007 if (!readOnly && isExpected && fGenFit) {
2008 // can try to do a readOnly in case can load from cache
2009 bool tmp = nllVar->get()->getAttribute("readOnly");
2010 nllVar->get()->setAttribute("readOnly");
2011 auto out = ufit(true);
2012 nllVar->get()->setAttribute("readOnly", tmp);
2013 if (out) {
2014 // retrieve from cache worked, no need to generate dataset
2015 return out;
2016 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2017 nllVar->setData(data());
2018 }
2019 }
2020 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2021 nllVar->setData(fData);
2022 }
2023 nllVar->fFuncVars->setAttribAll("Constant", false);
2024 *nllVar->fFuncVars = *coords; // will reconst the coords
2025 if (nllVar->fFuncGlobs)
2026 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2027 std::unique_ptr<RooAbsCollection>(nllVar->fFuncVars->selectCommon(poi()))
2028 ->setAttribAll("Constant", false); // float the poi
2029 if (fGenFit) {
2030 // make initial guess same as pars we generated with
2031 nllVar->fFuncVars->assignValueOnly(fGenFit->constPars());
2032 nllVar->fFuncVars->assignValueOnly(fGenFit->floatParsFinal());
2033 // rename nll so if caching fit results will cache into subdir
2034 nllVar->get()->SetName(
2035 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2036 if (!isExpected)
2037 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2038
2039 } else if (!std::isnan(fAltVal())) {
2040 // guess data given is expected to align with alt value, unless initVal attribute specified
2041 for (auto _poiCoord : poi()) {
2042 auto _poi = dynamic_cast<RooRealVar *>(nllVar->fFuncVars->find(_poiCoord->GetName()));
2043 if (_poi) {
2044 _poi->setVal(_poi->getStringAttribute("initVal") ? TString(_poi->getStringAttribute("initVal")).Atof()
2045 : fAltVal());
2046 }
2047 }
2048 }
2049 return (fUfit = nllVar->minimize());
2050}
2051
2053{
2054 std::string out;
2055 for (auto &c : coll) {
2056 if (!out.empty())
2057 out += ",";
2058 out += c->GetName();
2059 if (auto v = dynamic_cast<RooAbsReal *>(c); v) {
2060 out += TString::Format("=%g", v->getVal());
2061 } else if (auto cc = dynamic_cast<RooAbsCategory *>(c); cc) {
2062 out += TString::Format("=%s", cc->getLabel());
2063 } else if (auto s = dynamic_cast<RooStringVar *>(c); v) {
2064 out += TString::Format("=%s", s->getVal());
2065 }
2066 }
2067 return out;
2068}
2069
2070std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::cfit_null(bool readOnly)
2071{
2072 if (fNull_cfit)
2073 return fNull_cfit;
2074 if (auto rfit = retrieveFit(1)) {
2075 return fNull_cfit = rfit;
2076 }
2077 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2078 return nullptr;
2079 if (!nllVar->fFuncVars)
2080 nllVar->reinitialize();
2081 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2082 if (!fData.first) {
2083 if (!readOnly && isExpected && fGenFit) {
2084 // can try to do a readOnly in case can load from cache
2085 bool tmp = nllVar->get()->getAttribute("readOnly");
2086 nllVar->get()->setAttribute("readOnly");
2087 auto out = cfit_null(true);
2088 nllVar->get()->setAttribute("readOnly", tmp);
2089 if (out) {
2090 // retrieve from cache worked, no need to generate dataset
2091 return out;
2092 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2093 nllVar->setData(data());
2094 }
2095 }
2096 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2097 nllVar->setData(fData);
2098 }
2099 if (fUfit) {
2100 // move to ufit coords before evaluating
2101 *nllVar->fFuncVars = fUfit->floatParsFinal();
2102 }
2103 nllVar->fFuncVars->setAttribAll("Constant", false);
2104 *nllVar->fFuncVars = *coords; // will reconst the coords
2105 if (nllVar->fFuncGlobs)
2106 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2107 if (fPOIName()) {
2108 nllVar->fFuncVars->find(fPOIName())
2109 ->setStringAttribute("altVal", (!std::isnan(fAltVal())) ? TString::Format("%g", fAltVal()) : nullptr);
2110 }
2111 if (fGenFit) {
2112 nllVar->get()->SetName(
2113 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2114 if (!isExpected)
2115 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2116 }
2117 nllVar->get()->setStringAttribute("fitresultTitle", collectionContents(poi()).c_str());
2118 return (fNull_cfit = nllVar->minimize());
2119}
2120
2121std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::cfit_lbound(bool readOnly)
2122{
2123 auto _first_poi = dynamic_cast<RooRealVar *>(poi().first());
2124 if (!_first_poi)
2125 return nullptr;
2126 if (_first_poi->getMin("physical") <= _first_poi->getMin())
2127 return nullptr;
2128 if (fLbound_cfit)
2129 return fLbound_cfit;
2130 if (auto rfit = retrieveFit(6)) {
2131 return fLbound_cfit = rfit;
2132 }
2133 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2134 return nullptr;
2135 if (!nllVar->fFuncVars)
2136 nllVar->reinitialize();
2137 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2138 if (!fData.first) {
2139 if (!readOnly && isExpected && fGenFit) {
2140 // can try to do a readOnly in case can load from cache
2141 bool tmp = nllVar->get()->getAttribute("readOnly");
2142 nllVar->get()->setAttribute("readOnly");
2143 auto out = cfit_lbound(true);
2144 nllVar->get()->setAttribute("readOnly", tmp);
2145 if (out) {
2146 // retrieve from cache worked, no need to generate dataset
2147 return out;
2148 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2149 nllVar->setData(data());
2150 }
2151 }
2152 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2153 nllVar->setData(fData);
2154 }
2155 if (fUfit) {
2156 // move to ufit coords before evaluating
2157 *nllVar->fFuncVars = fUfit->floatParsFinal();
2158 }
2159 nllVar->fFuncVars->setAttribAll("Constant", false);
2160 *nllVar->fFuncVars = *coords; // will reconst the coords
2161 nllVar->fFuncVars->setRealValue(_first_poi->GetName(), _first_poi->getMin("physical"));
2162 if (nllVar->fFuncGlobs)
2163 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2164 if (fPOIName()) {
2165 nllVar->fFuncVars->find(fPOIName())
2166 ->setStringAttribute("altVal", (!std::isnan(fAltVal())) ? TString::Format("%g", fAltVal()) : nullptr);
2167 }
2168 if (fGenFit) {
2169 nllVar->get()->SetName(
2170 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2171 if (!isExpected)
2172 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2173 }
2174 nllVar->get()->setStringAttribute(
2175 "fitresultTitle",
2176 collectionContents(*std::unique_ptr<RooAbsCollection>(nllVar->fFuncVars->selectCommon(poi()))).c_str());
2177 return (fLbound_cfit = nllVar->minimize());
2178}
2179
2180std::shared_ptr<const RooFitResult> xRooNLLVar::xRooHypoPoint::cfit_alt(bool readOnly)
2181{
2182 if (std::isnan(fAltVal()))
2183 return nullptr;
2184 if (fAlt_cfit)
2185 return fAlt_cfit;
2186 if (auto rfit = retrieveFit(2)) {
2187 return fAlt_cfit = rfit;
2188 }
2189 if (!nllVar || (readOnly && nllVar->get() && !nllVar->get()->getAttribute("readOnly")))
2190 return nullptr;
2191 if (!nllVar->fFuncVars)
2192 nllVar->reinitialize();
2193 AutoRestorer snap(*nllVar->fFuncVars, nllVar.get());
2194 if (!fData.first) {
2195 if (!readOnly && isExpected && fGenFit) {
2196 // can try to do a readOnly in case can load from cache
2197 bool tmp = nllVar->get()->getAttribute("readOnly");
2198 nllVar->get()->setAttribute("readOnly");
2199 auto out = cfit_alt(true);
2200 nllVar->get()->setAttribute("readOnly", tmp);
2201 if (out) {
2202 // retrieve from cache worked, no need to generate dataset
2203 return out;
2204 } else if (!tmp) { // don't need to setData if doing a readOnly fit
2205 nllVar->setData(data());
2206 }
2207 }
2208 } else if (!nllVar->get()->getAttribute("readOnly")) { // don't need to setData if doing a readOnly fit
2209 nllVar->setData(fData);
2210 }
2211 if (fUfit) {
2212 // move to ufit coords before evaluating
2213 *nllVar->fFuncVars = fUfit->floatParsFinal();
2214 }
2215 nllVar->fFuncVars->setAttribAll("Constant", false);
2216 *nllVar->fFuncVars = *coords; // will reconst the coords
2217 if (nllVar->fFuncGlobs)
2218 nllVar->fFuncGlobs->setAttribAll("Constant", true);
2219 *nllVar->fFuncVars = alt_poi();
2220 if (fGenFit) {
2221 nllVar->get()->SetName(
2222 TString::Format("%s/%s_%s", nllVar->get()->GetName(), fGenFit->GetName(), (isExpected) ? "asimov" : "toys"));
2223 if (!isExpected)
2224 nllVar->get()->SetName(TString::Format("%s/%s", nllVar->get()->GetName(), fData.first->GetName()));
2225 }
2226 nllVar->get()->setStringAttribute("fitresultTitle", collectionContents(alt_poi()).c_str());
2227 return (fAlt_cfit = nllVar->minimize());
2228}
2229
2231{
2232
2233 auto asi = asimov(readOnly);
2234
2235 if (!asi) {
2236 return std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), 0);
2237 }
2238
2239 auto out = asi->pll(readOnly);
2240 return std::pair<double, double>(std::abs(fNullVal() - fAltVal()) / sqrt(out.first),
2241 out.second * 0.5 * std::abs(fNullVal() - fAltVal()) /
2242 (out.first * sqrt(out.first)));
2243}
2244
2246{
2247 auto _ts = ts_toys(nSigma);
2248 if (std::isnan(_ts.first))
2249 return _ts;
2250 if (fPllType != xRooFit::Asymptotics::Uncapped && _ts.first == 0)
2251 return std::pair<double, double>(1, 0); // don't need toys to compute this point!
2252
2253 TEfficiency eff("", "", 1, 0, 1);
2254
2255 auto &_theToys = (alt) ? altToys : nullToys;
2256
2257 if (_theToys.empty()) {
2258 return std::pair(0.5, std::numeric_limits<double>::infinity());
2259 }
2260
2261 // loop over toys, count how many are > ts value
2262 // nans (mean bad ts evaluations) will count towards uncertainty
2263 int nans = 0;
2264 double result = 0;
2265 double result_err_up = 0;
2266 double result_err_down = 0;
2267 for (auto &toy : _theToys) {
2268 if (std::isnan(std::get<1>(toy))) {
2269 nans++;
2270 } else {
2271 bool res = std::get<1>(toy) >= _ts.first;
2272 if (std::get<2>(toy) != 1) {
2273 eff.FillWeighted(res, 0.5, std::get<2>(toy));
2274 } else {
2275 eff.Fill(res, 0.5);
2276 }
2277 if (res)
2278 result += std::get<2>(toy);
2279 if (std::get<1>(toy) >= _ts.first - _ts.second)
2280 result_err_up += std::get<2>(toy);
2281 if (std::get<1>(toy) >= _ts.first - _ts.second)
2282 result_err_down += std::get<2>(toy);
2283 }
2284 }
2285 // symmetrize the error
2288 double result_err = std::max(std::abs(result_err_up), std::abs(result_err_down));
2289 // assume the nans would "add" to the p-value, conservative scenario
2290 result_err += nans;
2291 result_err /= _theToys.size();
2292
2293 // don't include the nans for the central value though
2294 result /= (_theToys.size() - nans);
2295
2296 // add to the result_err (in quadrature) the uncert due to limited stats
2298 return std::pair<double, double>(result, result_err);
2299}
2300
2305
2307{
2308 if (!std::isnan(nSigma)) {
2309 return std::pair<double, double>(ROOT::Math::gaussian_cdf(nSigma), 0); // by construction
2310 }
2311 return pX_toys(true, nSigma);
2312}
2313
2315{
2316 xRooHypoPoint out;
2317 out.coords = coords;
2318 out.fPllType = fPllType; // out.fPOIName = fPOIName; out.fNullVal=fNullVal; out.fAltVal = fAltVal;
2319 out.nllVar = nllVar;
2320 if (!nllVar)
2321 return out;
2322 auto _cfit = cfit_null();
2323 if (!_cfit)
2324 return out;
2325 if (!nllVar->fFuncVars)
2326 nllVar->reinitialize();
2327 //*nllVar->fFuncVars = cfit_null()->floatParsFinal();
2328 //*nllVar->fFuncVars = cfit_null()->constPars();
2329 out.fData = xRooFit::generateFrom(*nllVar->fPdf, *_cfit, false, seed); // nllVar->generate(false,seed);
2330 out.fGenFit = _cfit;
2331 return out;
2332}
2333
2335{
2336 xRooHypoPoint out;
2337 out.coords = coords;
2338 out.fPllType = fPllType; // out.fPOIName = fPOIName; out.fNullVal=fNullVal; out.fAltVal = fAltVal;
2339 out.nllVar = nllVar;
2340 if (!nllVar)
2341 return out;
2342 if (!cfit_alt())
2343 return out;
2344 if (!nllVar->fFuncVars)
2345 nllVar->reinitialize();
2346 //*nllVar->fFuncVars = cfit_alt()->floatParsFinal();
2347 //*nllVar->fFuncVars = cfit_alt()->constPars();
2348 out.fData =
2349 xRooFit::generateFrom(*nllVar->fPdf, *cfit_alt(), false, seed); // out.data = nllVar->generate(false,seed);
2350 out.fGenFit = cfit_alt();
2351 return out;
2352}
2353
2355 bool targetCLs, double relErrThreshold, size_t maxToys)
2356{
2357 if ((alt && !cfit_alt()) || (!alt && !cfit_null())) {
2358 throw std::runtime_error("Cannot add toys, invalid conditional fit");
2359 }
2360
2361 auto condition = [&]() { // returns true if need more toys
2362 if (std::isnan(target))
2363 return false;
2364 auto obs = targetCLs ? pCLs_toys(target_nSigma) : (alt ? pAlt_toys(target_nSigma) : pNull_toys(target_nSigma));
2365 if (!std::isnan(obs.first)) {
2366 double diff = (target < 0) ? obs.first : std::abs(obs.first - target);
2367 double err = obs.second;
2368 if (err > 1e-4 && diff <= relErrThreshold * obs.second) {
2369 // return true; // more toys needed
2370 if (targetCLs) {
2371 // decide which type we'd want to generate and update alt flag
2372 auto pNull = pNull_toys(target_nSigma);
2373 auto pAlt = pAlt_toys(target_nSigma);
2374 // std::cout << obs.first << " +/- " << obs.second << ": " << pNull.first << " +/- " << pNull.second << "
2375 // , " << pAlt.first << " +/- " << pAlt.second << std::endl;
2376 alt = (pAlt.second * pNull.first > pNull.second * pAlt.first);
2377 if ((alt ? pAlt.second : pNull.second) < 1e-4)
2378 return false; // stop if error gets too small
2379 }
2380 return true;
2381 }
2382 }
2383 return false;
2384 };
2385
2386 if (!std::isnan(target) && std::isnan(ts_toys(target_nSigma).first)) {
2387 if (std::isnan(target_nSigma)) {
2388 throw std::runtime_error("Cannot target obs p-value because ts value unavailable");
2389 }
2390 if (targetCLs && pCLs_toys(target_nSigma).second == 0) {
2391 // this happens if the mu_test=mu_alt ... no toys needed
2392 return 0;
2393 }
2394
2395 // try generating 100 alt toys
2396 Info("addToys", "First generating 100 alt toys in order to determine expected ts value");
2397 addToys(true, 100, initialSeed);
2398 // if still null then exit
2399 if (std::isnan(ts_toys(target_nSigma).first)) {
2400 throw std::runtime_error("Unable to determine expected ts value");
2401 }
2402 }
2403
2404 size_t nans = 0;
2405 float lastTime = 0;
2406 int lasti = 0;
2407 auto g = gROOT->Get<TGraph>("toyTime");
2408 if (!g) {
2409 g = new TGraph;
2410 g->SetNameTitle("toyTime", "Time per toy;Toy;time [s]");
2411 gROOT->Add(g);
2412 }
2413 g->Set(0);
2414 TStopwatch s2;
2415 s2.Start();
2416 TStopwatch s;
2417 s.Start();
2418
2419 size_t toysAdded(0);
2420 size_t altToysAdded(0);
2421 if (initialSeed) {
2423 }
2424 do {
2425 auto &toys = (alt) ? altToys : nullToys;
2426 if (toys.size() >= maxToys) {
2427 // cannot generate more toys, reached limit already
2428 break;
2429 }
2430 // don't generate toys if reached target
2431 if (!std::isnan(target) && !condition()) {
2432 break;
2433 }
2434 auto currVal = std::isnan(target) ? std::pair(0., 0.)
2435 : (targetCLs ? pCLs_toys(target_nSigma)
2436 : (alt ? pAlt_toys(target_nSigma) : pNull_toys(target_nSigma)));
2437 size_t nnToys = std::min(size_t(nToys), (maxToys - toys.size()));
2438
2439 for (size_t i = 0; i < nnToys; i++) {
2440 int seed = RooRandom::randomGenerator()->Integer(std::numeric_limits<uint32_t>::max());
2441 auto toy = ((alt) ? generateAlt(seed) : generateNull(seed));
2442 {
2443 TDirectory::TContext ctx{nullptr}; // disables any saving of fit results for toys
2444 toys.push_back(std::make_tuple(seed, toy.pll().first, 1.));
2445 }
2446 (alt ? altToysAdded : toysAdded)++;
2447 if (std::isnan(std::get<1>(toys.back())))
2448 nans++;
2449 g->SetPoint(g->GetN(), g->GetN(), s.RealTime() - lastTime); // stops the clock
2450 lastTime = s.RealTime();
2451 if (s.RealTime() > 10) {
2452 std::cout << "\r"
2453 << TString::Format("Generated %d/%d %s hypothesis toys [%.2f toys/s]",
2454 int(alt ? altToysAdded : toysAdded), int(nnToys), alt ? "alt" : "null",
2456 if (!std::isnan(target)) {
2457 std::cout << " [current=" << currVal.first << "+/-" << currVal.second << " target=" << target
2458 << " nSigma=" << target_nSigma << "]";
2459 }
2460 std::cout << "..." << std::flush;
2462 s.Reset();
2463 if (!gROOT->IsBatch()) {
2464 Draw();
2465 if (gPad) {
2466 gPad->Update();
2467#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
2468 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
2469#endif
2471 }
2472 }
2473 s.Start();
2474 // std::cout << "Generated " << i << "/" << nToys << (alt ? " alt " : " null ") << " hypothesis toys " ..."
2475 // << std::endl;
2476 }
2477 s.Continue();
2478 }
2479 // sort the toys ... put nans first - do by setting all as negative inf (is that still necessary with the custom
2480 // sort below??)
2481 for (auto &t : toys) {
2482 if (std::isnan(std::get<1>(t)))
2483 std::get<1>(t) = -std::numeric_limits<double>::infinity();
2484 }
2485 std::sort(toys.begin(), toys.end(),
2486 [](const decltype(nullToys)::value_type &a, const decltype(nullToys)::value_type &b) -> bool {
2487 if (std::isnan(std::get<1>(a)))
2488 return true;
2489 if (std::isnan(std::get<1>(b)))
2490 return false;
2491 return std::get<1>(a) < std::get<1>(b);
2492 });
2493 for (auto &t : toys) {
2494 if (std::isinf(std::get<1>(t)))
2495 std::get<1>(t) = std::numeric_limits<double>::quiet_NaN();
2496 }
2497 if (std::isnan(target)) {
2498 break; // no more toys if not doing a target
2499 }
2500 // if(condition()) {
2501 // Info("addToys","Generating more toys to determine p-value ... currently: %f +/-
2502 // %f",pNull_toys(target_nSigma).first,pNull_toys(target_nSigma).second);
2503 // }
2504 } while (condition());
2505 if (lasti) {
2506 std::cout << "\r"
2507 << "Finished Generating ";
2508 if (toysAdded) {
2509 std::cout << toysAdded << " null ";
2510 }
2511 if (altToysAdded) {
2512 std::cout << altToysAdded << " alt ";
2513 }
2514 std::cout << "toys " << TString::Format("[%.2f toys/s overall]", double(toysAdded + altToysAdded) / s2.RealTime())
2515 << std::endl;
2516 if (!gROOT->IsBatch()) {
2517 Draw();
2518 if (gPad) {
2519 gPad->Update();
2520#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
2521 gPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
2522#endif
2524 }
2525 }
2526 }
2527
2528 if (nans > 0) {
2529 std::cout << "Warning: " << nans << " toys were bad" << std::endl;
2530 }
2531 return toysAdded;
2532}
2533
2535{
2536 addToys(false, nToys, seed, target, target_nSigma);
2537}
2539{
2540 addToys(true, nToys, seed, target, target_nSigma);
2541}
2542
2544{
2545 addToys(false, nToys, seed, target, target_nSigma, true);
2546 return;
2547 //
2548 // auto condition = [&](bool doingAlt=false) { // returns true if need more toys
2549 // if(std::isnan(target)) return false;
2550 // auto pval = pCLs_toys(target_nSigma);
2551 // if (!std::isnan(pval.first)) {
2552 // double diff = std::abs(pval.first - target);
2553 // double err = pval.second;
2554 // if (err > 1e-4 && diff <= 2 * pval.second) {
2555 // return true; // more toys needed
2556 // // decide which type we'd want to generate
2557 // // if it matches the type we are generating, then return true
2558 // auto pNull = pNull_toys(target_nSigma);
2559 // auto pAlt = pAlt_toys(target_nSigma);
2560 // if ((doingAlt ? pAlt.second : pNull.second) < 1e-4) return false; // stop if error gets too small
2561 // bool doAlt = (pAlt.second * pNull.first > pNull.second * pAlt.first);
2562 // return doAlt == doingAlt;
2563 // }
2564 // }
2565 // return false;
2566 // };
2567 // while(condition()) {
2568 // bool doAlt = false;
2569 // double relErrThreshold = 2;
2570 // if(nullToys.size()<size_t(nToys)) {
2571 // addToys(false,nToys);continue;
2572 // } else if(altToys.size()<size_t(nToys)) {
2573 // addToys(true,nToys);continue;
2574 // } else {
2575 // // see which have bigger errors ... generate more of that ...
2576 // auto pNull = pNull_toys(target_nSigma);
2577 // auto pAlt = pAlt_toys(target_nSigma);
2578 // doAlt = (pAlt.second*pNull.first > pNull.second*pAlt.first);
2579 // if( (doAlt ? pAlt.second : pNull.second) < 1e-4 ) break; // stop if error gets too small
2580 // auto pCLs = pCLs_toys(target_nSigma);
2581 // relErrThreshold = (doAlt) ? (pNull.second/pNull.first) : (pAlt.second/pAlt.first);
2582 // relErrThreshold = std::min(2.,std::abs(relErrThreshold));
2583 // std::cout << "Current pCLs = " << pCLs.first << " +/- " << pCLs.second
2584 // << " (pNull = " << pNull.first << " +/- " << pNull.second
2585 // << " , pAlt = " << pAlt.first << " +/- " << pAlt.second << ") ... generating more " << (doAlt ?
2586 // "alt" : "null") << " toys " << relErrThreshold << std::endl;
2587 //
2588 // }
2589 // if( addToys(doAlt, nToys/*, seed, -1, target_nSigma,relErrThreshold*/) == 0) {
2590 // break; // no toys got added, so stop looping
2591 // }
2592 // }
2593}
2594
2597{
2598 xRooHypoPoint out;
2599 // out.fPOIName = parName; out.fNullVal = value; out.fAltVal = alt_value;
2600
2601 if (!fFuncVars) {
2602 reinitialize();
2603 }
2605
2606 out.nllVar = std::make_shared<xRooNLLVar>(*this);
2607 out.fData = getData();
2608
2609 TStringToken pattern(poiValues, ",");
2611 while (pattern.NextToken()) {
2612 TString s = pattern.Data();
2613 TString cName = s;
2614 double val = std::numeric_limits<double>::quiet_NaN();
2615 auto i = s.Index("=");
2616 if (i != -1) {
2617 cName = s(0, i);
2618 TString cVal = s(i + 1, s.Length());
2619 if (!cVal.IsFloat())
2620 throw std::runtime_error("poiValues must contain value");
2621 val = cVal.Atof();
2622 }
2623 auto v = dynamic_cast<RooRealVar *>(fFuncVars->find(cName));
2624 if (!v)
2625 throw std::runtime_error("Cannot find poi");
2626 if (!std::isnan(val))
2627 v->setVal(val);
2628 v->setConstant(); // because will select constants as coords
2629 if (poiNames != "") {
2630 poiNames += ",";
2631 }
2632 poiNames += cName;
2633 }
2634 if (poiNames == "") {
2635 throw std::runtime_error("No poi");
2636 }
2637 if (!std::isnan(alt_value)) {
2638 std::unique_ptr<RooAbsCollection> thePoi(fFuncVars->selectByName(poiNames));
2639 for (auto b : *thePoi) {
2640 if (!static_cast<RooRealVar *>(b)->hasRange("physical")) {
2641 static_cast<RooRealVar *>(b)->setRange("physical", 0, std::numeric_limits<double>::infinity());
2642 }
2643 }
2644 }
2645 auto _snap = std::unique_ptr<RooAbsCollection>(fFuncVars->selectByAttrib("Constant", true))->snapshot();
2646 _snap->setAttribAll("poi", false);
2647 std::unique_ptr<RooAbsCollection> _poi(_snap->selectByName(poiNames));
2648 _poi->setAttribAll("poi", true);
2649 if (std::isnan(alt_value)) {
2650 for (auto a : *_poi)
2651 a->setStringAttribute("altVal", nullptr);
2652 } else {
2653 for (auto a : *_poi)
2654 a->setStringAttribute("altVal", TString::Format("%g", alt_value));
2655 }
2656 if (fGlobs)
2657 _snap->remove(*fGlobs, true, true);
2658 out.coords.reset(_snap);
2659
2660 auto _type = pllType;
2661 if (_type == xRooFit::Asymptotics::Unknown) {
2662 // decide based on values
2663 if (std::isnan(alt_value)) {
2665 } else if (dynamic_cast<RooRealVar *>(_poi->first())->getVal() >= alt_value) {
2667 } else {
2669 }
2670 }
2671
2672 out.fPllType = _type;
2673
2674 return out;
2675}
2676
2677xRooNLLVar::xRooHypoPoint
2679{
2680 if (!fFuncVars) {
2681 reinitialize();
2682 }
2683 std::unique_ptr<RooAbsCollection> _poi(fFuncVars->selectByAttrib("poi", true));
2684 if (_poi->empty()) {
2685 throw std::runtime_error("No POI specified in model");
2686 } else if (_poi->size() != 1) {
2687 throw std::runtime_error("Multiple POI specified in model");
2688 }
2689 return hypoPoint(_poi->first()->GetName(), value, alt_value, pllType);
2690}
2691
2697
2699{
2700
2701 if (!nllVar && !hypoTestResult)
2702 return;
2703
2704 TString sOpt(opt);
2705 sOpt.ToLower();
2706 bool hasSame = sOpt.Contains("same");
2707 sOpt.ReplaceAll("same", "");
2708
2709 TVirtualPad *pad = gPad;
2710
2711 TH1 *hAxis = nullptr;
2712
2713 auto clearPad = []() {
2714 gPad->Clear();
2715 if (gPad->GetNumber() == 0) {
2716 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
2717 gPad->SetTopMargin(gStyle->GetPadTopMargin());
2718 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
2719 gPad->SetRightMargin(gStyle->GetPadRightMargin());
2720 }
2721 };
2722
2723 if (!hasSame || !pad) {
2724 if (!pad) {
2726 pad = gPad;
2727 }
2728 clearPad();
2729 } else {
2730 // get the histogram representing the axes
2731 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive(".axis"));
2732 if (!hAxis) {
2733 for (auto o : *pad->GetListOfPrimitives()) {
2734 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
2735 break;
2736 }
2737 }
2738 }
2739
2740 // get min and max values
2741 double _min = std::numeric_limits<double>::quiet_NaN();
2742 double _max = -std::numeric_limits<double>::quiet_NaN();
2743
2744 for (auto &p : nullToys) {
2745 if (std::get<2>(p) == 0)
2746 continue;
2747 if (std::isnan(std::get<1>(p)))
2748 continue;
2749 _min = std::min(std::get<1>(p), _min);
2750 _max = std::max(std::get<1>(p), _max);
2751 }
2752 for (auto &p : altToys) {
2753 if (std::get<2>(p) == 0)
2754 continue;
2755 if (std::isnan(std::get<1>(p)))
2756 continue;
2757 _min = std::min(std::get<1>(p), _min);
2758 _max = std::max(std::get<1>(p), _max);
2759 }
2760
2761 auto obs = ts_asymp();
2762 if (!std::isnan(obs.first)) {
2763 _min = std::min(obs.first - std::abs(obs.first) * 0.1, _min);
2764 _max = std::max(obs.first + std::abs(obs.first) * 0.1, _max);
2765 }
2766 // these are used down below to add obs p-values to legend, but up here because can trigger fits that create asimov
2767 auto pNull = pNull_toys();
2768 auto pAlt = pAlt_toys();
2769 auto pNullA = (fPllType == xRooFit::Asymptotics::Unknown)
2770 ? std::pair(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN())
2771 : pNull_asymp();
2772 auto pAltA = (fPllType == xRooFit::Asymptotics::Unknown)
2773 ? std::pair(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN())
2774 : pAlt_asymp();
2775 sigma_mu(true);
2776 auto asi = (fPllType != xRooFit::Asymptotics::Unknown && fAsimov && fAsimov->fUfit && fAsimov->fNull_cfit)
2777 ? fAsimov->pll().first
2778 : std::numeric_limits<double>::quiet_NaN();
2779 if (!std::isnan(asi) && asi > 0) {
2780 // can calculate asymptotic distributions,
2781 _min = std::min(asi - std::abs(asi), _min);
2782 _max = std::max(asi + std::abs(asi), _max);
2783 }
2784 if (_min > 0)
2785 _min = 0;
2786
2787 auto _poi = dynamic_cast<RooRealVar *>(poi().first());
2788
2789 auto makeHist = [&](bool isAlt) {
2790 TString title;
2791 auto h = new TH1D((isAlt) ? "alt_toys" : "null_toys", "", 100, _min, _max + (_max - _min) * 0.01);
2792 h->SetDirectory(nullptr);
2793 size_t nBadOrZero = 0;
2794 for (auto &p : (isAlt) ? altToys : nullToys) {
2795 double w = std::isnan(std::get<1>(p)) ? 0 : std::get<2>(p);
2796 if (w == 0)
2797 nBadOrZero++;
2798 if (!std::isnan(std::get<1>(p)))
2799 h->Fill(std::get<1>(p), w);
2800 }
2801 if (h->GetEntries() > 0)
2802 h->Scale(1. / h->Integral(0, h->GetNbinsX() + 1));
2803
2804 // add POI values to identify hypos
2805 // for(auto p : *fPOI) {
2806 // if (auto v = dynamic_cast<RooRealVar*>(p)) {
2807 // if (auto v2 = dynamic_cast<RooRealVar*>(fAltPoint->fCoords->find(*v)); v2 &&
2808 // v2->getVal()!=v->getVal()) {
2809 // // found point that differs in poi and altpoint value, so print my coords value for this
2810 // title += TString::Format("%s' = %g,
2811 // ",v->GetTitle(),dynamic_cast<RooRealVar*>(fCoords->find(*v))->getVal());
2812 // }
2813 // }
2814 // }
2815 if (fPOIName())
2816 title += TString::Format("%s' = %g", fPOIName(), (isAlt) ? fAltVal() : fNullVal());
2817 title += TString::Format(" , N_{toys}=%d", int((isAlt) ? altToys.size() : nullToys.size()));
2818 if (nBadOrZero > 0)
2819 title += TString::Format(" (N_{bad/0}=%d)", int(nBadOrZero));
2820 title += ";";
2821 title += tsTitle();
2822 title += TString::Format(";Probability Mass");
2823 h->SetTitle(title);
2824 h->SetLineColor(isAlt ? kRed : kBlue);
2825 h->SetLineWidth(2);
2826 h->SetMarkerSize(0);
2827 h->SetBit(kCanDelete);
2828 return h;
2829 };
2830
2831 auto nullHist = makeHist(false);
2832 auto altHist = makeHist(true);
2833
2834 TLegend *l = nullptr;
2835 auto h = (nullHist->GetEntries()) ? nullHist : altHist;
2836 if (!hasSame) {
2837 gPad->SetLogy();
2838 auto axis = static_cast<TH1 *>(h->Clone(".axis"));
2839 axis->SetBit(kCanDelete);
2840 axis->SetStats(false);
2841 axis->Reset("ICES");
2842 axis->SetTitle(TString::Format("%s HypoPoint", collectionContents(poi()).c_str()));
2843 axis->SetLineWidth(0);
2844 axis->Draw(""); // h->Draw("axis"); cant use axis option if want title drawn
2845 axis->SetMinimum(1e-7);
2846 axis->GetYaxis()->SetRangeUser(1e-7, 10);
2847 axis->SetMaximum(h->GetMaximum());
2848 hAxis = axis;
2849 l = new TLegend(0.4, 0.7, 1. - gPad->GetRightMargin(), 1. - gPad->GetTopMargin());
2850 l->SetName("legend");
2851 l->SetFillStyle(0);
2852 l->SetBorderSize(0);
2854 l->Draw();
2855 l->ConvertNDCtoPad();
2856 } else {
2857 for (auto o : *gPad->GetListOfPrimitives()) {
2858 l = dynamic_cast<TLegend *>(o);
2859 if (l)
2860 break;
2861 }
2862 }
2863
2864 if (h->GetEntries() > 0) {
2865 h->Draw("esame");
2866 } else {
2867 h->Draw("axissame"); // for unknown reason if second histogram empty it still draws with two weird bars???
2868 }
2869 h = altHist;
2870 if (h->GetEntries() > 0) {
2871 h->Draw("esame");
2872 } else {
2873 h->Draw("axissame"); // for unknown reason if second histogram empty it still draws with two weird bars???
2874 }
2875
2876 if (l) {
2877 l->AddEntry(nullHist);
2878 l->AddEntry(altHist);
2879 }
2880
2881 if (fAsimov && fAsimov->fUfit && fAsimov->fNull_cfit && !std::isnan(sigma_mu().first) && !std::isnan(fAltVal())) {
2882 auto hh = static_cast<TH1 *>(nullHist->Clone("null_asymp"));
2883 hh->SetBit(kCanDelete);
2884 hh->SetStats(false);
2885 hh->SetLineStyle(2);
2886 hh->Reset();
2887 for (int i = 1; i <= hh->GetNbinsX(); i++) {
2888 hh->SetBinContent(
2889 i, xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i), fNullVal(), fNullVal(), sigma_mu().first,
2890 _poi->getMin("physical"), _poi->getMax("physical")) -
2891 xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i + 1), fNullVal(), fNullVal(),
2892 sigma_mu().first, _poi->getMin("physical"), _poi->getMax("physical")));
2893 }
2894 hh->Draw("lsame");
2895 hh = static_cast<TH1 *>(altHist->Clone("alt_asymp"));
2896 hh->SetBit(kCanDelete);
2897 hh->SetStats(false);
2898 hh->SetLineStyle(2);
2899 hh->Reset();
2900 for (int i = 1; i <= hh->GetNbinsX(); i++) {
2901 hh->SetBinContent(
2902 i, xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i), fNullVal(), fAltVal(), sigma_mu().first,
2903 _poi->getMin("physical"), _poi->getMax("physical")) -
2904 xRooFit::Asymptotics::PValue(fPllType, hh->GetBinLowEdge(i + 1), fNullVal(), fAltVal(),
2905 sigma_mu().first, _poi->getMin("physical"), _poi->getMax("physical")));
2906 }
2907 hh->Draw("lsame");
2908 }
2909
2910 // draw observed points
2911 TLine ll;
2912 ll.SetLineStyle(1);
2913 ll.SetLineWidth(3);
2914 // for(auto p : fObs) {
2915 auto tl = ll.DrawLine(obs.first, hAxis->GetMinimum(), obs.first, 0.1);
2916 auto label = TString::Format("obs ts = %.4f", obs.first);
2917 if (obs.second)
2918 label += TString::Format(" #pm %.4f", obs.second);
2919
2920 l->AddEntry(tl, label, "l");
2921 label = "";
2922 if (nullHist->GetEntries() || altHist->GetEntries()) {
2923 auto pCLs = pCLs_toys();
2924 label += " p_{toy}=(";
2925 label += (std::isnan(pNull.first)) ? "-" : TString::Format("%.4f #pm %.4f", pNull.first, pNull.second);
2926 label += (std::isnan(pAlt.first)) ? ",-" : TString::Format(",%.4f #pm %.4f", pAlt.first, pAlt.second);
2927 label += (std::isnan(pCLs.first)) ? ",-)" : TString::Format(",%.4f #pm %.4f)", pCLs.first, pCLs.second);
2928 }
2929 if (label.Length() > 0)
2930 l->AddEntry("", label, "");
2931 label = "";
2932 if (!std::isnan(pNullA.first) || !std::isnan(pAltA.first)) {
2933 auto pCLs = pCLs_asymp();
2934 label += " p_{asymp}=(";
2935 label += (std::isnan(pNullA.first)) ? "-" : TString::Format("%.4f #pm %.4f", pNullA.first, pNullA.second);
2936 label += (std::isnan(pAltA.first)) ? ",-" : TString::Format(",%.4f #pm %.4f", pAltA.first, pAltA.second);
2937 label += (std::isnan(pCLs.first)) ? ",-)" : TString::Format(",%.4f #pm %.4f)", pCLs.first, pCLs.second);
2938 }
2939 if (label.Length() > 0)
2940 l->AddEntry("", label, "");
2941
2942 if (auto ax = dynamic_cast<TH1 *>(gPad->GetPrimitive(".axis")))
2943 ax->GetYaxis()->SetRangeUser(1e-7, 1);
2944}
2945
2947{
2948 auto v = dynamic_cast<RooRealVar *>(poi().empty() ? nullptr : poi().first());
2950 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
2951 return (inWords) ? TString::Format("Lower-Bound One-Sided Limit PLR")
2952 : TString::Format("#tilde{q}_{%s=%g}", v->GetTitle(), v->getVal());
2953 } else if (v) {
2954 return (inWords) ? TString::Format("One-Sided Limit PLR")
2955 : TString::Format("q_{%s=%g}", v->GetTitle(), v->getVal());
2956 } else {
2957 return "q";
2958 }
2959 } else if (fPllType == xRooFit::Asymptotics::TwoSided) {
2960 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
2961 return (inWords) ? TString::Format("Lower-Bound PLR")
2962 : TString::Format("#tilde{t}_{%s=%g}", v->GetTitle(), v->getVal());
2963 } else if (v) {
2964 return (inWords) ? TString::Format("-2log[L(%s,#hat{#hat{#theta}})/L(#hat{%s},#hat{#theta})]", v->GetTitle(),
2965 v->GetTitle())
2966 : TString::Format("t_{%s=%g}", v->GetTitle(), v->getVal());
2967 } else
2968 return "t";
2969 } else if (fPllType == xRooFit::Asymptotics::OneSidedNegative) {
2970 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
2971 return (inWords) ? TString::Format("Lower-Bound One-Sided Discovery PLR")
2972 : TString::Format("#tilde{r}_{%s=%g}", v->GetTitle(), v->getVal());
2973 } else if (v) {
2974 return (inWords) ? TString::Format("One-Sided Discovery PLR")
2975 : TString::Format("r_{%s=%g}", v->GetTitle(), v->getVal());
2976 } else {
2977 return "r";
2978 }
2979 } else if (fPllType == xRooFit::Asymptotics::Uncapped) {
2980 if (v && v->hasRange("physical") && v->getMin("physical") != -std::numeric_limits<double>::infinity()) {
2981 return (inWords) ? TString::Format("Lower-Bound Uncapped PLR")
2982 : TString::Format("#tilde{u}_{%s=%g}", v->GetTitle(), v->getVal());
2983 } else if (v) {
2984 return (inWords) ? TString::Format("Uncapped PLR") : TString::Format("u_{%s=%g}", v->GetTitle(), v->getVal());
2985 } else {
2986 return "u";
2987 }
2988 } else {
2989 return "Test Statistic";
2990 }
2991}
2992
2994{
2995 return (poi().empty()) ? nullptr : (poi().first())->GetName();
2996}
2998{
2999 auto first_poi = dynamic_cast<RooAbsReal *>(poi().first());
3000 return (first_poi == nullptr) ? std::numeric_limits<double>::quiet_NaN() : first_poi->getVal();
3001}
3003{
3004 auto _alt_poi = alt_poi(); // need to keep alive as alt_poi owns its contents
3005 auto first_poi = dynamic_cast<RooAbsReal *>(_alt_poi.first());
3006 return (first_poi == nullptr) ? std::numeric_limits<double>::quiet_NaN() : first_poi->getVal();
3007}
3008
3010{
3011 auto first_poi = dynamic_cast<RooAbsRealLValue *>(poi().first());
3012 if (!first_poi) {
3013 throw std::runtime_error("HypoPoint has no POI, cannot set null value");
3014 }
3015 first_poi->setVal(val);
3016}
3018{
3019 auto first_poi = dynamic_cast<RooAbsArg *>(poi().first());
3020 if (!first_poi) {
3021 throw std::runtime_error("HypoPoint has no POI, cannot set alt value");
3022 }
3023 first_poi->setStringAttribute("altVal", TString::Format("%g", val).Data());
3024}
3025
3026xRooNLLVar::xRooHypoSpace xRooNLLVar::hypoSpace(const char *parName, int nPoints, double low, double high,
3028 int tsType)
3029{
3030 if (nPoints < 0 || tsType < 0) {
3031 // nPoints<0 catches case where pyROOT has converted TestStatistic enum to int
3032 if (nPoints < 0) {
3033 tsType = nPoints;
3034 nPoints = int(low + 0.5);
3035 low = high;
3036 high = alt_value;
3037 }
3038 if (alt_value == std::numeric_limits<double>::quiet_NaN()) {
3040 alt_value = 0;
3042 alt_value = 1;
3043 }
3044 }
3045
3046 auto out = hypoSpace(parName, pllType, alt_value);
3047
3048 // TODO: things like the physical range and alt value can't be stored on the poi
3049 // because if they change they will change for all hypoSpaces at once, so cannot have
3050 // two hypoSpace with e.g. different physical ranges.
3051 // the hypoSpace should make a copy of them at some point
3052 for (auto p : out.poi()) {
3054 dynamic_cast<RooRealVar *>(p)->setRange("physical", 0, std::numeric_limits<double>::infinity());
3055 Info("xRooNLLVar::hypoSpace", "Setting physical range of %s to [0,inf]", p->GetName());
3056 } else if (dynamic_cast<RooRealVar *>(p) && static_cast<RooRealVar *>(p)->hasRange("physical")) {
3057 auto *var = static_cast<RooRealVar *>(p);
3058 var->removeMin("physical");
3059 var->removeMax("physical");
3060 Info("xRooNLLVar::hypoSpace", "Removing physical range of %s", p->GetName());
3061 }
3062 }
3063
3064 // ensure pll type is set explicitly if known at this point
3066 out.fTestStatType = xRooFit::Asymptotics::OneSidedPositive;
3068 out.fTestStatType = xRooFit::Asymptotics::Uncapped;
3069 } else if (tsType == xRooFit::TestStatistic::q0) {
3070 out.fTestStatType = xRooFit::Asymptotics::OneSidedNegative;
3071 }
3072
3073 if (nPoints > 0) {
3074 out.AddPoints(parName, nPoints, low, high);
3075 } else {
3076 if (!std::isnan(low) && !std::isnan(high) && !(std::isinf(low) && std::isinf(high))) {
3077 for (auto p : out.poi()) {
3078 dynamic_cast<RooRealVar *>(p)->setRange("scan", low, high);
3079 }
3080 }
3081 }
3082 return out;
3083 }
3084
3086 if (nPoints > 0)
3087 hs.AddPoints(parName, nPoints, low, high);
3088 else {
3089 if (!std::isnan(low) && !std::isnan(high) && !(std::isinf(low) && std::isinf(high))) {
3090 for (auto p : hs.poi()) {
3091 dynamic_cast<RooRealVar *>(p)->setRange("scan", low, high);
3092 }
3093 }
3094 }
3095 return hs;
3096}
3097
3100{
3101 auto _poi = std::unique_ptr<RooAbsCollection>(
3102 std::unique_ptr<RooAbsCollection>(pdf()->getVariables())->selectByAttrib("poi", true));
3103 if (_poi->empty())
3104 throw std::runtime_error("You must specify a POI for the hypoSpace");
3105 return hypoSpace(_poi->first()->GetName(), nPoints, low, high, alt_value, pllType);
3106}
3107
3110{
3112
3113 s.AddModel(pdf());
3114 if (strlen(parName)) {
3115 std::unique_ptr<RooAbsCollection> axes(s.pars()->selectByName(parName));
3116 if (axes->empty())
3117 throw std::runtime_error("parameter not found");
3118 axes->setAttribAll("axis", true);
3119 }
3120 /*if (std::unique_ptr<RooAbsCollection>(s.pars()->selectByAttrib("poi", true))->empty()) {
3121 throw std::runtime_error("You must specify at least one POI for the hypoSpace");
3122 }*/
3123 s.fNlls[s.fPdfs.begin()->second] = std::make_shared<xRooNLLVar>(*this);
3125
3126 for (auto poi : s.poi()) {
3127 poi->setStringAttribute("altVal", std::isnan(alt_value) ? nullptr : TString::Format("%f", alt_value));
3128 }
3129
3130 return s;
3131}
3132
3134{
3135 if (hypoTestResult) {
3136 return *hypoTestResult;
3137 }
3139 out.SetBackgroundAsAlt(true);
3140 out.SetName(TUUID().AsString());
3141 out.SetTitle(TString::Format("%s HypoPoint", collectionContents(poi()).c_str()));
3142
3143 bool setReadonly = false;
3144 if (nllVar && !nllVar->get()->getAttribute("readOnly")) {
3145 setReadonly = true;
3146 nllVar->get()->setAttribute("readOnly");
3147 }
3148
3149 out.SetTestStatisticData(ts_asymp().first);
3150
3151 // build a ds to hold all fits ... store coords in the globs list of the nullDist
3152 // also need to store at least mu_hat value(s)
3155 fitMeta.addClone(RooCategory("pllType", "test statistic type",
3156 {{"TwoSided", 0},
3157 {"OneSidedPositive", 1},
3158 {"OneSidedNegative", 2},
3159 {"OneSidedAbsolute", 3},
3160 {"Uncapped", 4},
3161 {"Unknown", 5}}));
3162 if (ufit()) {
3163 fitMeta.addClone(ufit()->floatParsFinal());
3164 }
3165 fitMeta.setCatIndex("pllType", int(fPllType));
3166 fitMeta.addClone(RooRealVar("isExpected", "isExpected", int(isExpected)));
3167 fitMeta.addClone(RooRealVar("obs_ts_err", "obs_ts_err", obs_ts_err));
3168 fitDetails.addClone(RooCategory("type", "fit type",
3169 {{"ufit", 0},
3170 {"cfit_null", 1},
3171 {"cfit_alt", 2},
3172 {"asimov_ufit", 3},
3173 {"asimov_cfit_null", 4},
3174 {"gen", 5},
3175 {"cfit_lbound", 6}}));
3176 // fitDetails.addClone(RooStringVar("name", "Fit Name", "")); -- not supported properly in ROOT yet
3177 fitDetails.addClone(RooRealVar("status", "status", 0));
3178 fitDetails.addClone(RooRealVar("covQual", "covQual", 0));
3179 fitDetails.addClone(RooRealVar("minNll", "minNll", 0));
3180 fitDetails.addClone(RooRealVar("edm", "edm", 0));
3181 auto fitDS = new RooDataSet("fits", "fit summary data", fitDetails);
3182 // fitDS->convertToTreeStore(); // strings not stored properly in vector store, so do convert! - not needed since
3183 // string var storage not properly supported - storing in globs list instead
3184
3185 for (int i = 0; i < 7; i++) {
3186 std::shared_ptr<const RooFitResult> fit;
3187 switch (i) {
3188 case 0: fit = ufit(); break;
3189 case 1: fit = cfit_null(); break;
3190 case 2: fit = cfit_alt(); break;
3191 case 3: fit = asimov() ? asimov()->ufit(true) : nullptr; break;
3192 case 4: fit = asimov() ? asimov()->cfit_null(true) : nullptr; break;
3193 case 5: fit = fGenFit; break;
3194 case 6: fit = cfit_lbound(); break;
3195 }
3196 if (fit) {
3197 fitDetails.setCatIndex("type", i);
3198 fitMeta.addClone(RooStringVar(TString::Format("%s.name", fitDetails.getCatLabel("type")),
3199 fitDetails.getCatLabel("type"), fit->GetName()));
3200 // fitDetails.setStringValue("name",fit->GetName());
3201 fitDetails.setRealValue("status", fit->status());
3202 fitDetails.setRealValue("minNll", fit->minNll());
3203 fitDetails.setRealValue("edm", fit->edm());
3204 fitDetails.setRealValue("covQual", fit->covQual());
3205 fitDS->add(fitDetails);
3206 }
3207 }
3208 fitDS->setGlobalObservables(fitMeta);
3209
3210 out.SetFitInfo(fitDS);
3211
3214 nullMeta.addClone(*coords);
3215 nullDetails.addClone(RooRealVar("seed", "Toy Seed", 0));
3216 nullDetails.addClone(RooRealVar("ts", "test statistic value", 0));
3217 nullDetails.addClone(RooRealVar("weight", "weight", 1));
3218 auto nullToyDS = new RooDataSet("nullToys", "nullToys", nullDetails, RooFit::WeightVar("weight"));
3219 nullToyDS->setGlobalObservables(nullMeta);
3220 if (!nullToys.empty()) {
3221
3222 std::vector<double> values;
3223 std::vector<double> weights;
3224 values.reserve(nullToys.size());
3225 weights.reserve(nullToys.size());
3226
3227 for (auto &t : nullToys) {
3228 values.push_back(std::get<1>(t));
3229 weights.push_back(std::get<2>(t));
3230 nullDetails.setRealValue("seed", std::get<0>(t));
3231 nullDetails.setRealValue("ts", std::get<1>(t));
3232 nullToyDS->add(nullDetails, std::get<2>(t));
3233 }
3234 out.SetNullDistribution(new RooStats::SamplingDistribution("null", "Null dist", values, weights, tsTitle()));
3235#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3236 out.fNullPValue = pNull_toys().first; // technically set above
3237 out.fNullPValueError =
3238 pNull_toys().second; // overrides binomial error used in SamplingDistribution::IntegralAndError
3239#else
3240 out.SetNullPValue(pNull_toys().first); // technically set above
3241 out.SetNullPValueError(
3242 pNull_toys().second); // overrides binomial error used in SamplingDistribution::IntegralAndError
3243#endif
3244 } else {
3245#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3246 out.fNullPValue = pNull_asymp().first;
3247 out.fNullPValueError = pNull_asymp().second;
3248#else
3249 out.SetNullPValue(pNull_asymp().first);
3250 out.SetNullPValueError(pNull_asymp().second);
3251#endif
3252 }
3253 out.SetNullDetailedOutput(nullToyDS);
3254
3255 if (!altToys.empty()) {
3256 std::vector<double> values;
3257 std::vector<double> weights;
3258 values.reserve(altToys.size());
3259 weights.reserve(altToys.size());
3262 altDetails.addClone(RooRealVar("seed", "Toy Seed", 0));
3263 altDetails.addClone(RooRealVar("ts", "test statistic value", 0));
3264 altDetails.addClone(RooRealVar("weight", "weight", 1));
3265 auto altToyDS = new RooDataSet("altToys", "altToys", altDetails, RooFit::WeightVar("weight"));
3266 altToyDS->setGlobalObservables(altMeta);
3267 for (auto &t : altToys) {
3268 values.push_back(std::get<1>(t));
3269 weights.push_back(std::get<2>(t));
3270 altDetails.setRealValue("seed", std::get<0>(t));
3271 altDetails.setRealValue("ts", std::get<1>(t));
3272 altToyDS->add(altDetails, std::get<2>(t));
3273 }
3274 out.SetAltDistribution(new RooStats::SamplingDistribution("alt", "Alt dist", values, weights, tsTitle()));
3275 out.SetAltDetailedOutput(altToyDS);
3276#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3277 out.fAlternatePValue = pAlt_toys().first; // technically set above
3278 out.fAlternatePValueError =
3279 pAlt_toys().second; // overrides binomial error used in SamplingDistribution::IntegralAndError
3280#else
3281 out.SetAltPValue(pAlt_toys().first); // technically set above
3282 out.SetAltPValueError(
3283 pAlt_toys().second); // overrides binomial error used in SamplingDistribution::IntegralAndError
3284#endif
3285
3286 } else if (fPllType != xRooFit::Asymptotics::Unknown) {
3287#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3288 out.fAlternatePValue = pAlt_asymp().first;
3289 out.fAlternatePValueError = pAlt_asymp().second;
3290#else
3291 out.SetAltPValue(pAlt_asymp().first);
3292 out.SetAltPValueError(pAlt_asymp().second);
3293#endif
3294 }
3295
3296 if (setReadonly) {
3297 nllVar->get()->setAttribute("readOnly", false);
3298 }
3299
3300 return out;
3301}
3302
3303std::string cling::printValue(const xRooNLLVar::xValueWithError *v)
3304{
3305 if (!v)
3306 return "xValueWithError: nullptr\n";
3307 return Form("%f +/- %f", v->first, v->second);
3308}
3309std::string cling::printValue(const std::map<std::string, xRooNLLVar::xValueWithError> *m)
3310{
3311 if (!m)
3312 return "nullptr\n";
3313 std::string out = "{\n";
3314 for (auto [k, v] : *m) {
3315 out += "\"" + k + "\" => " + printValue(&v) + "\n";
3316 }
3317 out += "}\n";
3318 return out;
3319}
3320
#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:372
#define gROOT
Definition TROOT.h:414
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:2448
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2495
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:722
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:150
static std::shared_ptr< ROOT::Fit::FitConfig > createFitConfig()
Definition xRooFit.cxx:487
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:280
std::shared_ptr< const RooFitResult > retrieveFit(int type)
std::vector< std::tuple< int, double, double > > altToys
Definition xRooNLLVar.h:277
std::shared_ptr< const RooAbsCollection > coords
Definition xRooNLLVar.h:263
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:265
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:275
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())
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:325
bool AddModel(const xRooNode &pdf, const char *validity="")
std::map< std::shared_ptr< xRooNode >, std::shared_ptr< xRooNLLVar > > fNlls
Definition xRooNLLVar.h:390
std::set< std::pair< std::shared_ptr< RooArgList >, std::shared_ptr< xRooNode > > > fPdfs
Definition xRooNLLVar.h:392
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
std::shared_ptr< RooAbsCollection > fFuncGlobs
Definition xRooNLLVar.h:508
std::shared_ptr< const RooAbsCollection > fGlobs
Definition xRooNLLVar.h:501
std::shared_ptr< RooLinkedList > fOpts
Definition xRooNLLVar.h:503
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:504
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:507
xRooNLLVar(RooAbsPdf &pdf, const std::pair< RooAbsData *, const RooAbsCollection * > &data, const RooLinkedList &nllOpts=RooLinkedList())
std::shared_ptr< RooAbsPdf > pdf() const
Definition xRooNLLVar.h:452
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:506
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:500
std::shared_ptr< RooAbsPdf > fPdf
Definition xRooNLLVar.h:499
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
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.
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:373
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 removeMin(const char *name=nullptr)
Remove lower range limit for binning with given name. Empty name means default range.
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.
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:133
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1514
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 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
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
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
Mother of all ROOT objects.
Definition TObject.h:42
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:267
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:882
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:293
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:2384
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:414
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:669
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)