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