Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
xRooFit.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#include "RVersion.h"
14
15// #define private public
16// #include "Minuit2/Minuit2Minimizer.h"
17// #undef private
18// #include "Minuit2/FunctionMinimum.h"
19
20#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
21#define protected public
22#endif
23#include "RooFitResult.h"
24#ifdef protected
25#undef protected
26#endif
27
28#include "xRooFit/xRooFit.h"
29
30#include "RooDataSet.h"
31#include "RooSimultaneous.h"
32#include "RooArgSet.h"
33#include "RooRandom.h"
34#include "RooAbsPdf.h"
35#include "TUUID.h"
36#include "RooProdPdf.h"
37#include "RooGamma.h"
38#include "RooPoisson.h"
39#include "RooGaussian.h"
40#include "RooBifurGauss.h"
41#include "RooLognormal.h"
42#include "RooBinning.h"
43#include "RooUniformBinning.h"
44
46#include "Math/GenAlgoOptions.h"
47#include "Math/Minimizer.h"
48#include "RooMinimizer.h"
49#include "coutCapture.h"
50
51#include "TCanvas.h"
52#include "TGraphErrors.h"
53#include "TLegend.h"
54#include "TKey.h"
55#include "TPRegexp.h"
56#include "RooStringVar.h"
57
58#include "RooRealProxy.h"
59#include "RooSuperCategory.h"
60
61#include "xRooFitVersion.h"
62
63#include <csignal>
64#include "TROOT.h"
65#include "TBrowser.h"
66
67#include "TEnv.h"
68
69#include "./PythonInterface.h"
70
72
73std::shared_ptr<RooLinkedList> xRooFit::sDefaultNLLOptions = nullptr;
74std::shared_ptr<ROOT::Fit::FitConfig> xRooFit::sDefaultFitConfig = nullptr;
75
76const char *xRooFit::GetVersion()
77{
78 return GIT_COMMIT_HASH;
79}
80const char *xRooFit::GetVersionDate()
81{
82 return GIT_COMMIT_DATE;
83}
84
85RooCmdArg xRooFit::ReuseNLL(bool flag)
86{
87 return RooCmdArg("ReuseNLL", flag, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
88}
89
90RooCmdArg xRooFit::Tolerance(double val)
91{
92 return RooCmdArg("Tolerance", 0, 0, val);
93}
94
95RooCmdArg xRooFit::StrategySequence(const char *val)
96{
97 return RooCmdArg("StrategySequence", 0, 0, 0, 0, val);
98}
99
100RooCmdArg xRooFit::MaxIterations(int val)
101{
102 return RooCmdArg("MaxIterations", val);
103}
104
105xRooNLLVar xRooFit::createNLL(const std::shared_ptr<RooAbsPdf> pdf, const std::shared_ptr<RooAbsData> data,
106 const RooLinkedList &nllOpts)
107{
108 return xRooNLLVar(pdf, data, nllOpts);
109}
110
111xRooNLLVar xRooFit::createNLL(RooAbsPdf &pdf, RooAbsData *data, const RooLinkedList &nllOpts)
112{
113 return createNLL(std::shared_ptr<RooAbsPdf>(&pdf, [](RooAbsPdf *) {}),
114 std::shared_ptr<RooAbsData>(data, [](RooAbsData *) {}), nllOpts);
115}
116
117xRooNLLVar xRooFit::createNLL(RooAbsPdf &pdf, RooAbsData *data, const RooCmdArg &arg1, const RooCmdArg &arg2,
118 const RooCmdArg &arg3, const RooCmdArg &arg4, const RooCmdArg &arg5,
119 const RooCmdArg &arg6, const RooCmdArg &arg7, const RooCmdArg &arg8)
120{
121
123 l.Add((TObject *)&arg1);
124 l.Add((TObject *)&arg2);
125 l.Add((TObject *)&arg3);
126 l.Add((TObject *)&arg4);
127 l.Add((TObject *)&arg5);
128 l.Add((TObject *)&arg6);
129 l.Add((TObject *)&arg7);
130 l.Add((TObject *)&arg8);
131 return createNLL(pdf, data, l);
132}
133
134std::shared_ptr<const RooFitResult>
135xRooFit::fitTo(RooAbsPdf &pdf,
136 const std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> &data,
138{
139 return xRooNLLVar(std::shared_ptr<RooAbsPdf>(&pdf, [](RooAbsPdf *) {}), data, nllOpts)
140 .minimize(std::shared_ptr<ROOT::Fit::FitConfig>(const_cast<ROOT::Fit::FitConfig *>(&fitConf),
141 [](ROOT::Fit::FitConfig *) {}));
142}
143
144std::shared_ptr<const RooFitResult> xRooFit::fitTo(RooAbsPdf &pdf,
145 const std::pair<RooAbsData *, const RooAbsCollection *> &data,
147{
148 return xRooNLLVar(pdf, data, nllOpts)
149 .minimize(std::shared_ptr<ROOT::Fit::FitConfig>(const_cast<ROOT::Fit::FitConfig *>(&fitConf),
150 [](ROOT::Fit::FitConfig *) {}));
151}
152
153std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>>
154xRooFit::generateFrom(RooAbsPdf &pdf, const RooFitResult &_fr, bool expected, int seed)
155{
156
157 std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooAbsCollection>> out;
158
159 auto fr = &_fr;
160 if (!fr)
161 return out;
162
163 auto _allVars = std::unique_ptr<RooAbsCollection>(pdf.getVariables());
164 auto _snap = std::unique_ptr<RooAbsCollection>(_allVars->snapshot());
165 *_allVars = fr->constPars();
166 *_allVars = fr->floatParsFinal();
167
168 // determine globs from fr constPars
169 auto _globs = std::unique_ptr<RooAbsCollection>(fr->constPars().selectByAttrib("global", true));
170
171 // bool doBinned = false;
172 // RooAbsPdf::GenSpec** gs = nullptr;
173
174 if (seed == 0)
175 seed = RooRandom::randomGenerator()->Integer(std::numeric_limits<uint32_t>::max());
176 RooRandom::randomGenerator()->SetSeed(seed);
177
178 TString uuid = TUUID().AsString();
179
180 std::function<std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooArgSet>>(RooAbsPdf *)> genSubPdf;
181
182 genSubPdf = [&](RooAbsPdf *_pdf) {
183 std::pair<std::shared_ptr<RooAbsData>, std::shared_ptr<const RooArgSet>> _out;
184 // std::unique_ptr<RooArgSet> _obs(_pdf->getParameters(*pars)); // using this "trick" to get observables can
185 // produce 'error' msg because of RooProdPdf trying to setup partial integrals
186 std::unique_ptr<RooArgSet> _obs(_pdf->getVariables());
187 _obs->remove(fr->constPars(), true, true);
188 _obs->remove(fr->floatParsFinal(), true, true); // use this instead
189
190 if (!_globs->empty()) {
191 RooArgSet *toy_gobs = new RooArgSet(uuid + "_globs");
192 // ensure we use the gobs from the model ...
193 RooArgSet t;
194 t.add(*_globs);
195 std::unique_ptr<RooArgSet> globs(_pdf->getObservables(t));
196 globs->snapshot(*toy_gobs);
197 if (!toy_gobs->empty() &&
198 !dynamic_cast<RooSimultaneous *>(
199 _pdf)) { // if was simPdf will call genSubPdf on each subpdf so no need to generate here
200 if (!expected) {
201 *toy_gobs = *std::unique_ptr<RooDataSet>(_pdf->generate(*globs, 1))->get();
202 } else {
203 // loop over pdfs in top-level prod-pdf,
204 auto pp = dynamic_cast<RooProdPdf *>(_pdf);
205 if (pp) {
206 for (auto thePdf : pp->pdfList()) {
207 auto gob = std::unique_ptr<RooArgSet>(thePdf->getObservables(*globs));
208 if (gob->empty())
209 continue;
210 if (gob->size() > 1) {
211 Warning("generate", "%s contains multiple global obs: %s", thePdf->GetName(),
212 gob->contentsString().c_str());
213 continue;
214 }
215 RooRealVar &rrv = dynamic_cast<RooRealVar &>(*gob->first());
216 std::unique_ptr<RooArgSet> cpars(thePdf->getParameters(*globs));
217
218 bool foundServer = false;
219 // note : this will work only for this type of constraints
220 // expressed as RooPoisson, RooGaussian, RooLognormal, RooGamma
221 // SimpleGaussianConstraint is CMS's own version of a RooGaussian, which also works.
222 TClass *cClass = thePdf->IsA();
226 !(cClass && strcmp(cClass->GetName(), "SimpleGaussianConstraint") == 0)) {
227 TString className = (cClass) ? cClass->GetName() : "undefined";
228 oocoutW((TObject *)nullptr, Generation)
229 << "xRooFit::generateFrom : constraint term " << thePdf->GetName() << " of type "
230 << className << " is a non-supported type - result might be not correct " << std::endl;
231 }
232
233 // in case of a Poisson constraint make sure the rounding is not set
234 if (cClass == RooPoisson::Class()) {
235 RooPoisson *pois = dynamic_cast<RooPoisson *>(thePdf);
236 assert(pois);
237 pois->setNoRounding(true);
238 }
239
240 // look at server of the constraint term and check if the global observable is part of the server
241 RooAbsArg *arg = thePdf->findServer(rrv);
242 if (!arg) {
243 // special case is for the Gamma where one might define the global observable n and you have a
244 // Gamma(b, n+1, ...._ in this case n+1 is the server and we don;t have a direct dependency, but
245 // we want to set n to the b value so in case of the Gamma ignore this test
246 if (cClass != RooGamma::Class()) {
247 oocoutE((TObject *)nullptr, Generation)
248 << "xRooFit::generateFrom : constraint term " << thePdf->GetName()
249 << " has no direct dependence on global observable- cannot generate it " << std::endl;
250 continue;
251 }
252 }
253
254 // loop on the server of the constraint term
255 // need to treat the Gamma as a special case
256 // the mode of the Gamma is (k-1)*theta where theta is the inverse of the rate parameter.
257 // we assume that the global observable is defined as ngobs = k-1 and the theta parameter has the
258 // name theta otherwise we use other procedure which might be wrong
259 RooAbsReal *thetaGamma = nullptr;
260 if (cClass == RooGamma::Class()) {
261 for (RooAbsArg *a2 : thePdf->servers()) {
262 if (TString(a2->GetName()).Contains("theta")) {
263 thetaGamma = dynamic_cast<RooAbsReal *>(a2);
264 break;
265 }
266 }
267 if (thetaGamma == nullptr) {
268 oocoutI((TObject *)nullptr, Generation)
269 << "xRooFit::generateFrom : constraint term " << thePdf->GetName()
270 << " is a Gamma distribution and no server named theta is found. Assume that the Gamma "
271 "scale is 1 "
272 << std::endl;
273 }
274 }
275 for (RooAbsArg *a2 : thePdf->servers()) {
276 RooAbsReal *rrv2 = dynamic_cast<RooAbsReal *>(a2);
277 if (rrv2 && !rrv2->dependsOn(*gob) &&
278 (!rrv2->isConstant() || !rrv2->InheritsFrom("RooConstVar"))) {
279
280 // found server not depending on the gob
281 if (foundServer) {
282 oocoutE((TObject *)nullptr, Generation)
283 << "xRooFit::generateFrom : constraint term " << thePdf->GetName()
284 << " constraint term has more server depending on nuisance- cannot generate it "
285 << std::endl;
286 foundServer = false;
287 break;
288 }
289 if (thetaGamma && thetaGamma->getVal() > 0) {
290 rrv.setVal(rrv2->getVal() / thetaGamma->getVal());
291 } else {
292 rrv.setVal(rrv2->getVal());
293 }
294 foundServer = true;
295 }
296 }
297
298 if (!foundServer) {
299 oocoutE((TObject *)nullptr, Generation)
300 << "xRooFit::generateFrom : can't find nuisance for constraint term - global "
301 "observables will not be set to Asimov value "
302 << thePdf->GetName() << " glob = " << rrv.GetName() << std::endl;
303 std::cerr << "Parameters: " << std::endl;
304 cpars->Print("V");
305 std::cerr << "Observables: " << std::endl;
306 gob->Print("V");
307 }
308 }
309 } else {
310 Error("generate", "Cannot generate global observables, pdf is: %s::%s", _pdf->ClassName(),
311 _pdf->GetName());
312 }
313 *toy_gobs = *globs;
314 }
315 }
316 _out.second.reset(toy_gobs);
317 } // end of globs generation
318
319 RooRealVar w("weightVar", "weightVar", 1);
320 if (auto s = dynamic_cast<RooSimultaneous *>(_pdf)) {
321 // do subpdf's individually
322 _obs->add(w);
323 _out.first = std::make_unique<RooDataSet>(
324 uuid, TString::Format("%s %s", _pdf->GetTitle(), (expected) ? "Expected" : "Toy"), *_obs,
325 RooFit::WeightVar("weightVar"));
326
327 for (auto &c : s->indexCat()) {
328#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 22, 00)
329 std::string cLabel = c.first;
330#else
331 std::string cLabel = c->GetName();
332#endif
333 auto p = s->getPdf(cLabel.c_str());
334 if (!p)
335 continue;
336 auto toy = genSubPdf(p);
337 if (toy.second && _out.second)
338 *const_cast<RooArgSet *>(_out.second.get()) = *toy.second;
339 _obs->setCatLabel(s->indexCat().GetName(), cLabel.c_str());
340 for (int i = 0; i < toy.first->numEntries(); i++) {
341 *_obs = *toy.first->get(i);
342 _out.first->add(*_obs, toy.first->weight());
343 }
344 }
345 return _out;
346 }
347
348 std::map<RooRealVar *, std::shared_ptr<RooAbsBinning>> binnings;
349
350 for (auto &o : *_obs) {
351 auto r = dynamic_cast<RooRealVar *>(o);
352 if (!r)
353 continue;
354 if (auto res = _pdf->binBoundaries(*r, -std::numeric_limits<double>::infinity(),
355 std::numeric_limits<double>::infinity())) {
356 binnings[r] = std::shared_ptr<RooAbsBinning>(r->getBinning().clone(r->getBinning().GetName()));
357
358 std::vector<double> boundaries;
359 boundaries.reserve(res->size());
360 for (auto &rr : *res) {
361 if (boundaries.empty() || std::abs(boundaries.back() - rr) > 1e-3 ||
362 std::abs(boundaries.back() - rr) > 1e-5 * boundaries.back())
363 boundaries.push_back(rr);
364 } // sometimes get virtual duplicates of boundaries
365 r->setBinning(RooBinning(boundaries.size() - 1, &boundaries[0]));
366 delete res;
367 } else if (r->numBins(r->getBinning().GetName()) == 0 && expected) {
368 // no bins ... in order to generate expected we need to have some bins
369 binnings[r] = std::shared_ptr<RooAbsBinning>(r->getBinning().clone(r->getBinning().GetName()));
370 r->setBinning(RooUniformBinning(r->getMin(), r->getMax(), 100));
371 }
372 }
373
374 // now can generate
375 if (_obs->empty()) {
376 // no observables, create a single dataset with 1 entry ... why 1 entry??
377 _obs->add(w);
379 _tmp.add(w);
380 _out.first = std::make_unique<RooDataSet>("", "Toy", _tmp, RooFit::WeightVar("weightVar"));
381 _out.first->add(_tmp);
382 } else {
383 if (_pdf->canBeExtended()) {
384 _out.first =
385 std::unique_ptr<RooDataSet>{_pdf->generate(*_obs, RooFit::Extended(), RooFit::ExpectedData(expected))};
386 } else {
387 if (expected) {
388 // use AsymptoticCalculator because generate expected not working correctly on unextended pdf?
389 // TODO: Can the above code for expected globs be used instead, or what about replace above code with
390 // ObsToExpected?
392 } else {
393 _out.first = std::unique_ptr<RooDataSet>{_pdf->generate(*_obs, RooFit::ExpectedData(expected))};
394 }
395 }
396 }
397 _out.first->SetName(TUUID().AsString());
398
399 for (auto &b : binnings) {
400 auto v = b.first;
401 auto binning = b.second;
402 v->setBinning(*binning);
403 // range of variable in dataset may be less than in the workspace
404 // if e.g. generate for a specific channel. So need to expand ranges to match
405 auto x = dynamic_cast<RooRealVar *>(_out.first->get()->find(v->GetName()));
406 auto r = x->getRange();
407 if (r.first > binning->lowBound())
408 x->setMin(binning->lowBound());
409 if (r.second < binning->highBound())
410 x->setMax(binning->highBound());
411 }
412 return _out;
413 };
414
415 out = genSubPdf(&pdf);
416 out.first->SetName(expected ? (TString(fr->GetName()) + "_asimov") : uuid);
417
418 // from now on we store the globs in the dataset
419 if (out.second) {
420 out.first->setGlobalObservables(*out.second);
421 out.second.reset();
422 }
423
424#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
425 // store fitResult name on the weightVar
426 if (auto w = dynamic_cast<RooDataSet *>(out.first.get())->weightVar()) {
427 w->setStringAttribute("fitResult", fr->GetName());
428 w->setAttribute("expected", expected);
429 }
430#endif
431
432 *_allVars = *_snap;
433
434 // June2023: Added this because found that generation was otherwise getting progressively slower
435 // the RooAbsPdf::generate does a clone, and it seems that the RooCacheManager of the original pdf
436 // is getting polluted on each generate call, causing it to grow larger and therefore the clone of it
437 // to take longer and longer. So sterilize to clear the caches of all components
438#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
439 auto _ws = pdf._myws;
440#else
441 auto _ws = pdf.workspace();
442#endif
443 if (_ws) {
444 // do explicitly rather than via xRooNode sterilize method because don't want to invoke the constructor
445 // workspace tweaking features (which sets poi etc etc)
446 for (auto obj : _ws->components()) {
447 for (int i = 0; i < obj->numCaches(); i++) {
448 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
449 cache->reset();
450 }
451 }
452 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
453 p->setNormRange(p->normRange());
454 }
455 obj->setValueDirty();
456 }
457 // xRooNode(pdf.workspace()).sterilize();
458 }
459
460 return out;
461}
462
463std::shared_ptr<RooLinkedList> xRooFit::createNLLOptions()
464{
465 auto out = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
466 l->Delete();
467 delete l;
468 });
469 for (auto opt : *defaultNLLOptions()) {
470 out->Add(opt->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
471 // "" instead, so doesnt copy names
472 }
473 return out;
474}
475
476std::shared_ptr<RooLinkedList> xRooFit::defaultNLLOptions()
477{
478 if (sDefaultNLLOptions)
479 return sDefaultNLLOptions;
480 sDefaultNLLOptions = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
481 l->Delete();
482 delete l;
483 });
484 sDefaultNLLOptions->Add(RooFit::Offset().Clone());
485 return sDefaultNLLOptions;
486}
487
488std::shared_ptr<ROOT::Fit::FitConfig> xRooFit::createFitConfig()
489{
490 return std::make_shared<ROOT::Fit::FitConfig>(*defaultFitConfig());
491}
492
493std::shared_ptr<ROOT::Fit::FitConfig> xRooFit::defaultFitConfig()
494{
495 if (sDefaultFitConfig)
496 return sDefaultFitConfig;
497 sDefaultFitConfig = std::make_shared<ROOT::Fit::FitConfig>();
498 auto &fitConfig = *sDefaultFitConfig;
499 fitConfig.SetParabErrors(true); // will use to run hesse after fit
500 fitConfig.MinimizerOptions().SetMinimizerType("Minuit2");
501 fitConfig.MinimizerOptions().SetErrorDef(0.5); // ensures errors are +/- 1 sigma ..IMPORTANT
502 fitConfig.SetParabErrors(true); // runs HESSE
503 fitConfig.SetMinosErrors(true); // computes asymmetric errors on any parameter with the "minos" attribute set
504 fitConfig.MinimizerOptions().SetMaxFunctionCalls(
505 -1); // calls per iteration. if left as 0 will set automatically to 500*nPars below
506 fitConfig.MinimizerOptions().SetMaxIterations(-1); // if left as 0 will set automatically to 500*nPars
507 fitConfig.MinimizerOptions().SetStrategy(-1); // will start at front of StrategySequence (given below)
508 // fitConfig.MinimizerOptions().SetTolerance(
509 // 1); // default is 0.01 (i think) but roominimizer uses 1 as default - use specify with
510 // ROOT::Math::MinimizerOptions::SetDefaultTolerance(..)
511 fitConfig.MinimizerOptions().SetPrintLevel(-2);
512 fitConfig.MinimizerOptions().SetExtraOptions(ROOT::Math::GenAlgoOptions());
513 // have to const cast to set extra options
514 auto extraOpts = const_cast<ROOT::Math::IOptions *>(fitConfig.MinimizerOptions().ExtraOptions());
515#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 29, 00)
516 extraOpts->SetValue(
517 "StrategySequence",
518 "0s01s12s2r2s23"); // 'm' would indicate use migradImproved from minuit v1. Dropped from default for 6.40
519 extraOpts->SetValue("HesseStrategySequence", "23");
520#else
521 extraOpts->SetValue("StrategySequence", "0s01s12s2m");
522 extraOpts->SetValue("HesseStrategySequence", "2");
523#endif
524 extraOpts->SetValue(
525 "HesseStrategy",
526 -1); // when hesse is run after minimization, will use this strategy. -1 means start at begin of strat sequence
527 extraOpts->SetValue("LogSize", 0); // length of log to capture and save
528 extraOpts->SetValue("BoundaryCheck",
529 0.); // if non-zero, warn if any post-fit value is close to boundary (e.g. 0.01 = within 1%)
530 extraOpts->SetValue("TrackProgress", 30); // seconds between output to log of evaluation progress
531 extraOpts->SetValue("xRooFitVersion", GIT_COMMIT_HASH); // not really options but here for logging purposes
532 // extraOpts->SetValue("ROOTVersion",ROOT_VERSION_CODE); - not needed as should by part of the ROOT TFile definition
533
534 // extraOpts->SetValue("HessianStepTolerance",0.);
535 // extraOpts->SetValue("HessianG2Tolerance",0.);
536
537 return sDefaultFitConfig;
538}
539
540ROOT::Math::IOptions *xRooFit::defaultFitConfigOptions()
541{
542 return const_cast<ROOT::Math::IOptions *>(defaultFitConfig()->MinimizerOptions().ExtraOptions());
543}
544
545void printCerr(const char *msg)
546{
549 // if (PyObject *sys_stdout = PySys_GetObject("stderr"); sys_stdout != nullptr) {
550 // Py_XDECREF(PyObject_CallMethod(sys_stdout, "flush", nullptr));
551 // }
552 } else {
553 std::cerr << msg << std::endl;
554 }
555}
556void printCout(const char *msg)
557{
560 // if (PyObject *sys_stdout = PySys_GetObject("stdout"); sys_stdout != nullptr) {
561 // Py_XDECREF(PyObject_CallMethod(sys_stdout, "flush", nullptr));
562 // }
563 } else {
564 std::cout << msg << std::endl;
565 }
566}
567
569public:
570 void (*oldHandlerr)(int) = nullptr;
572 static bool fInterrupt;
573 static void interruptHandler(int signum)
574 {
575 if (signum == SIGINT) {
576 printCout("Minimization interrupted ... will exit as soon as possible");
577 // TODO: create a global mutex for this
578 fInterrupt = true;
579 } else {
580 if (me)
581 me->oldHandlerr(signum);
582 }
583 };
585 : RooAbsReal(Form("progress_%s", f.GetName()), ""),
587 fFunc("func", "func", this, f),
589 {
590 s.Start();
591
592 me = this;
593 vars.reset(std::unique_ptr<RooAbsCollection>(f.getVariables())->selectByAttrib("Constant", false));
594 }
596 {
597 if (oldHandlerr) {
599 }
600 if (me == this)
601 me = nullptr;
602 };
603 ProgressMonitor(const ProgressMonitor &other, const char *name = nullptr)
605 {
606 }
607 TObject *clone(const char *newname) const override { return new ProgressMonitor(*this, newname); }
608
609 // required forwarding methods for RooEvaluatorWrapper in 6.32 onwards
610 double defaultErrorLevel() const override { return fFunc->defaultErrorLevel(); }
611 bool getParameters(const RooArgSet *observables, RooArgSet &outputSet, bool stripDisconnected) const override
612 {
613 return fFunc->getParameters(observables, outputSet, stripDisconnected);
614 }
615 bool setData(RooAbsData &data, bool cloneData) override { return fFunc->setData(data, cloneData); }
616 double getValV(const RooArgSet *) const override { return evaluate(); }
618 void printMultiline(std::ostream &os, Int_t contents, bool verbose = false, TString indent = "") const override
619 {
620 fFunc->printMultiline(os, contents, verbose, indent);
621 }
622
623 double evaluate() const override
624 {
625 if (fInterrupt) {
626 throw std::runtime_error("Keyboard interrupt");
627 return std::numeric_limits<double>::quiet_NaN();
628 }
629 double out = fFunc;
630 if (prevMin == std::numeric_limits<double>::infinity()) {
631 prevMin = out;
633 }
634 if (!std::isnan(out)) {
635 if (out < minVal) {
636 if (minPars.empty())
638 minPars = *vars;
639 }
640 minVal = std::min(minVal, out);
641 }
642 counter++;
643 if (s.RealTime() > fInterval) {
644 double evalRate = (counter - prevCounter) / s.RealTime();
645 s.Reset();
646 std::stringstream sout;
647
648 sout << TDatime().AsString() << ":(" << (counter) << "|" << evalRate << "Hz)";
649 if (!fState.empty())
650 sout << " : " << fState;
651 if (counter2) {
652 // doing a hesse step, estimate progress based on evaluations
653 int nRequired = prevPars.size();
654 if (nRequired > 1) {
655 nRequired *= (nRequired - 1);
656 nRequired /= 2; // since only need to do the a 'triangle' of the hessian matrix
657 if (fState == "Hesse3") {
658 nRequired *= 4;
659 }
660 sout << " (~" << int(100.0 * (counter - counter2) / nRequired) << "%)";
661 }
662 }
663 sout << " : " << minVal << " Delta=" << (minVal - prevMin);
664 if (minVal < prevMin) {
665 sout << " : ";
666 // compare minPars and prevPars, print biggest deltas
667 std::vector<std::pair<double, std::string>> parDeltas;
668 parDeltas.reserve(minPars.size());
669 for (auto p : minPars) {
670 parDeltas.emplace_back(std::pair<double, std::string>(
671 dynamic_cast<RooRealVar *>(p)->getVal() - prevPars.getRealValue(p->GetName()), p->GetName()));
672 }
673 std::sort(parDeltas.begin(), parDeltas.end(),
674 [](auto &left, auto &right) { return std::abs(left.first) > std::abs(right.first); });
675 int i;
676 for (i = 0; i < std::min(3, int(parDeltas.size())); i++) {
677 if (parDeltas.at(i).first == 0)
678 break;
679 if (i != 0)
680 sout << ",";
681 sout << parDeltas.at(i).second << (parDeltas.at(i).first >= 0 ? "+" : "-") << "="
682 << std::abs(parDeltas.at(i).first) << "(" << minPars.getRealValue(parDeltas.at(i).second.c_str())
683 << ")";
684 }
685 if (i < int(parDeltas.size()) && parDeltas.at(i).first != 0)
686 sout << " ...";
688 }
689
690 if (gROOT->FromPopUp() && gROOT->GetListOfBrowsers()->At(0)) {
691 auto browser = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0));
692 std::string status = sout.str();
693 int col = 0;
694 while (col < 4) {
695 std::string status_part;
696 if (status.find(" : ") != std::string::npos) {
697 status_part = status.substr(0, status.find(" : "));
698 status = status.substr(status.find(" : ") + 3);
699 } else {
700 status_part = status;
701 status = "";
702 }
703 browser->SetStatusText(status_part.c_str(), col);
704 col++;
705 }
707 }
708 printCerr(sout.str().c_str()); // std::cerr << sout.str() << std::endl;
709
710 prevMin = minVal;
712 } else {
713 s.Continue();
714 }
715 return out;
716 }
717
718 std::string fState;
719 mutable int counter = 0;
720 int counter2 = 0; // used to estimate progress of a Hesse calculation
721
722 mutable double minVal = std::numeric_limits<double>::infinity();
723 mutable double prevMin = std::numeric_limits<double>::infinity();
724
725private:
729 mutable int prevCounter = 0;
730 mutable int fInterval = 0; // time in seconds before next report
731 mutable TStopwatch s;
732 std::shared_ptr<RooAbsCollection> vars;
733};
734bool ProgressMonitor::fInterrupt = false;
736
737xRooFit::StoredFitResult::StoredFitResult(RooFitResult *_fr) : TNamed(*_fr)
738{
739 fr.reset(_fr);
740}
741
742xRooFit::StoredFitResult::StoredFitResult(const std::shared_ptr<RooFitResult> &_fr) : TNamed(*_fr), fr(_fr) {}
743
744std::shared_ptr<const RooFitResult> xRooFit::minimize(RooAbsReal &nll,
745 const std::shared_ptr<ROOT::Fit::FitConfig> &_fitConfig,
746 const std::shared_ptr<RooLinkedList> &nllOpts)
747{
748
750 auto &fitConfig = *myFitConfig;
751
752 auto _nll = &nll;
753
754 TString resultTitle = nll.getStringAttribute("fitresultTitle");
756 if (resultTitle == "")
758
759 // extract any user pars from the nll too
761 if (nll.getStringAttribute("userPars")) {
762 TStringToken st(nll.getStringAttribute("userPars"), ",");
763 while (st.NextToken()) {
765 TString parVal = nll.getStringAttribute(parName);
766 if (parVal.IsFloat()) {
767 fUserPars.addClone(RooRealVar(parName, parName, parVal.Atof()));
768 } else {
770 }
771 }
772 }
773
774 auto _nllVars = std::unique_ptr<RooAbsCollection>(_nll->getVariables());
775
776 std::unique_ptr<RooAbsCollection> constPars(_nllVars->selectByAttrib("Constant", true));
777 constPars->add(fUserPars, true); // add here so checked for when loading from cache
778 std::unique_ptr<RooAbsCollection> floatPars(_nllVars->selectByAttrib("Constant", false));
779
780 int _progress = 0;
781 double boundaryCheck = 0;
782 std::string s;
783 std::string hs;
784 int logSize = 0;
785#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 29, 00)
786 int hesseStrategy = 3; // uses most precise hesse settings (step sizes and g2 tolerances)
787#else
788 int hesseStrategy = 2; // uses most precise hesse settings (step sizes and g2 tolerances)
789#endif
790 if (fitConfig.MinimizerOptions().ExtraOptions()) {
791 fitConfig.MinimizerOptions().ExtraOptions()->GetNamedValue("StrategySequence", s);
792 fitConfig.MinimizerOptions().ExtraOptions()->GetIntValue("TrackProgress", _progress);
793 fitConfig.MinimizerOptions().ExtraOptions()->GetRealValue("BoundaryCheck", boundaryCheck);
794 fitConfig.MinimizerOptions().ExtraOptions()->GetIntValue("LogSize", logSize);
795 fitConfig.MinimizerOptions().ExtraOptions()->GetIntValue("HesseStrategy", hesseStrategy);
796 fitConfig.MinimizerOptions().ExtraOptions()->GetNamedValue("HesseStrategySequence", hs);
797 }
800
801 // if fit caching enabled, try to locate a valid fitResult
802 // must have matching constPars
804
805 if (cacheDir) {
806 if (auto nllDir = cacheDir->GetDirectory(nll.GetName()); nllDir) {
807 if (auto keys = nllDir->GetListOfKeys(); keys) {
808 for (auto &&k : *keys) {
809 auto cl = TClass::GetClass((static_cast<TKey *>(k))->GetClassName());
810 if (cl->InheritsFrom("RooFitResult")) {
812 nllDir->GetList() ? dynamic_cast<StoredFitResult *>(nllDir->GetList()->FindObject(k->GetName()))
813 : nullptr;
814 if (auto cachedFit =
815 (storedFr) ? storedFr->fr.get() : dynamic_cast<TKey *>(k)->ReadObject<RooFitResult>();
816 cachedFit) {
817 if (!storedFr) {
819 nllDir->Add(storedFr);
820 // std::cout << "Loaded " << nllDir->GetPath() << "/" << k->GetName() << " : " << k->GetTitle()
821 // << std::endl;
822 }
823 bool match = true;
824 if (!cachedFit->floatParsFinal().equals(*floatPars)) {
825 match = false;
826 } else {
827 for (auto &p : *constPars) {
828 auto v = dynamic_cast<RooAbsReal *>(p);
829 if (!v) {
830 if (auto c = dynamic_cast<RooAbsCategory *>(p)) {
831 if (auto _p =
832 dynamic_cast<RooAbsCategory *>(cachedFit->constPars().find(p->GetName()));
833 _p && !_p->getAttribute("global") &&
834 _p->getCurrentIndex() != c->getCurrentIndex()) {
835 match = false;
836 break;
837 }
838 } else {
839 match = false;
840 break;
841 }
842 };
843 if (auto _p = dynamic_cast<RooAbsReal *>(cachedFit->constPars().find(p->GetName())); _p) {
844 // note: do not need global observable values to match (globals currently added to
845 // constPars list)
846 if (!_p->getAttribute("global") && std::abs(_p->getVal() - v->getVal()) > 1e-12) {
847 match = false;
848 break;
849 }
850 }
851 }
852 }
853 if (match) {
854 return storedFr->fr;
855 // return std::shared_ptr<RooFitResult>(cachedFit,[](RooFitResult*){}); // dir owns the
856 // fitResult - this means dir needs to stay open for fits to be valid return
857 // std::make_shared<RooFitResult>(*cachedFit); // return a copy ... dir doesn't need to stay
858 // open, but fit result isn't shared
859 } else {
860 // delete cachedFit;
861 }
862 }
863 }
864 }
865 }
866 }
867 }
868
869 if (nll.getAttribute("readOnly"))
870 return nullptr;
871
872 int printLevel = fitConfig.MinimizerOptions().PrintLevel();
874 if (printLevel < 0)
875 RooMsgService::instance().setGlobalKillBelow(RooFit::FATAL);
876
877 // check how many parameters we have ... if 0 parameters then we wont run a fit, we just evaluate nll and return ...
878 if (floatPars->empty() || fitConfig.MinimizerOptions().MaxFunctionCalls() == 1) {
879 std::shared_ptr<RooFitResult> result;
881 parsList.add(*floatPars);
882 // construct an empty fit result ...
883 result = std::make_shared<RooFitResult>(); // if put name here fitresult gets added to dir, we don't want that
884 result->SetName(TUUID().AsString());
885 result->SetTitle(resultTitle);
886 result->setFinalParList(parsList);
887 result->setInitParList(parsList);
888 result->setConstParList(dynamic_cast<RooArgSet &>(*constPars)); /* RooFitResult takes a snapshot */
890 d.ResizeTo(parsList.size(), parsList.size());
891 result->setCovarianceMatrix(d);
892 result->setCovQual(floatPars->empty() ? 3 : -1);
893 result->setMinNLL(_nll->getVal());
894 result->setEDM(0);
895 result->setStatus(floatPars->empty() ? 0 : 1);
896
897 std::vector<std::pair<std::string, int>> statusHistory;
898 statusHistory.emplace_back(std::make_pair("EVAL", result->status()));
899 result->setStatusHistory(statusHistory);
900
901 if (cacheDir && cacheDir->IsWritable()) {
902 // save a copy of fit result to relevant dir
903 if (!cacheDir->GetDirectory(nll.GetName()))
904 cacheDir->mkdir(nll.GetName());
905 if (auto dir = cacheDir->GetDirectory(nll.GetName()); dir) {
906 // save NLL opts if was given one, unless already present
907 if (nllOpts) {
908 if (strlen(nllOpts->GetName()) == 0) {
909 nllOpts->SetName(TUUID().AsString());
910 }
911 if (!dir->FindKey(nllOpts->GetName())) {
912 dir->WriteObject(nllOpts.get(), nllOpts->GetName());
913 }
914 }
915 dir->WriteObject(result.get(), result->GetName());
916 }
917 }
918
919 if (printLevel < 0)
920 RooMsgService::instance().setGlobalKillBelow(msglevel);
921 return result;
922 }
923
924 std::shared_ptr<RooFitResult> out;
925
926 // check if any floatPars are categorical .. if so, need to a "discrete minimization" over the permutations
928 for (auto p : *floatPars) {
929 if (p->isCategory()) {
930 floatCats.add(*p);
931 }
932 }
933 if (!floatCats.empty()) {
934 RooSuperCategory allCats("floatCats", "Floating categorical parameters", floatCats);
935 std::unique_ptr<RooAbsCollection> _snap(floatCats.snapshot());
936 floatCats.setAttribAll("Constant");
937
938 std::shared_ptr<const RooFitResult> bestFr;
939 for (auto c : allCats) {
940 allCats.setIndex(c.second);
941 Info("minimize", "Minimizing with discrete %s", c.first.c_str());
942 auto fr = minimize(nll, _fitConfig, nllOpts);
943 if (!fr) {
944 Warning("minimize", "Minimization with discrete %s failed", c.first.c_str());
945 continue;
946 }
947 if (!bestFr || fr->minNll() < bestFr->minNll()) {
948 bestFr = fr;
949 }
950 }
951
952 floatCats.setAttribAll("Constant", false);
953
954 if (!bestFr)
955 return out;
956
957 // create a copy of the fit result, give it a new uuid, and move the const categories into the float area
958 out = std::make_shared<RooFitResult>(*bestFr);
959 const_cast<RooArgList &>(out->floatParsFinal())
960 .addClone(*std::unique_ptr<RooAbsCollection>(out->constPars().selectCommon(floatCats)));
961 const_cast<RooArgList &>(out->floatParsInit()).addClone(*_snap);
962 const_cast<RooArgList &>(out->constPars()).remove(floatCats);
963 out->SetName(TUUID().AsString());
964 }
965
966 bool restore = !fitConfig.UpdateAfterFit();
967 bool minos = fitConfig.MinosErrors();
968 std::string logs;
969 if (!out) {
970 int strategy = fitConfig.MinimizerOptions().Strategy();
971 // Note: AsymptoticCalculator enforces not less than 1 on tolerance - should we do so too?
972 if (_progress && printLevel >= -2) {
973 _nll = new ProgressMonitor(*_nll, _progress);
975 }
976 auto logger = (logSize > 0) ? std::make_unique<cout_redirect>(logs, logSize) : nullptr;
977 std::unique_ptr<RooMinimizer> _minimizerPtr = std::make_unique<RooMinimizer>(*_nll);
978 RooMinimizer *_minimizer = _minimizerPtr.get();
979 _minimizer->fitter()->Config() = fitConfig;
980 // if(fitConfig.MinimizerOptions().ExtraOptions()) {
981 // //for loading hesse options
982 // double a;
983 // if(fitConfig.MinimizerOptions().ExtraOptions()->GetValue("HessianStepTolerance",a)) {
984 // ROOT::Math::MinimizerOptions::Default("Minuit2").SetValue("HessianStepTolerance",a);
985 // }
986 // if(fitConfig.MinimizerOptions().ExtraOptions()->GetValue("HessianG2Tolerance",a)) {
987 // ROOT::Math::MinimizerOptions::Default("Minuit2").SetValue("HessianG2Tolerance",a);
988 // }
989 // }
990
991 bool autoMaxCalls = (_minimizer->fitter()->Config().MinimizerOptions().MaxFunctionCalls() == 0);
992 if (autoMaxCalls) {
993 _minimizer->fitter()->Config().MinimizerOptions().SetMaxFunctionCalls(
994 500 * floatPars->size() * floatPars->size()); // hesse requires O(N^2) function calls
995 }
996 if (_minimizer->fitter()->Config().MinimizerOptions().MaxIterations() == 0) {
997 _minimizer->fitter()->Config().MinimizerOptions().SetMaxIterations(500 * floatPars->size());
998 }
999
1000 std::unique_ptr<RooAbsCollection> floatInitVals(floatPars->snapshot());
1001 bool hesse = _minimizer->fitter()->Config().ParabErrors();
1002 _minimizer->fitter()->Config().SetParabErrors(
1003 false); // turn "off" so can run hesse as a separate step, appearing in status
1004 _minimizer->fitter()->Config().SetMinosErrors(false);
1005 _minimizer->fitter()->Config().SetUpdateAfterFit(true); // note: seems to always take effect
1006
1007 std::vector<std::pair<std::string, int>> statusHistory;
1008
1009 // gCurrentSampler = this;
1010 // gOldHandlerr = signal(SIGINT,toyInterruptHandlerr);
1011
1012 TString actualFirstMinimizer = _minimizer->fitter()->Config().MinimizerType();
1013
1014 int status = 0;
1015
1016 int sIdx = -1;
1017 TString minim = _minimizer->fitter()->Config().MinimizerType();
1018 TString algo = _minimizer->fitter()->Config().MinimizerAlgoType();
1019 if (minim == "Minuit2") {
1020 if (strategy == -1) {
1021 sIdx = 0;
1022 } else {
1023 sIdx = m_strategy.Index('0' + strategy);
1024 }
1025 if (sIdx == -1) {
1026 Warning("minimize", "Strategy %d not specified in StrategySequence %s ... defaulting to start of sequence",
1027 strategy, m_strategy.Data());
1028 sIdx = 0;
1029 }
1030 } else if (minim == "Minuit")
1031 sIdx = m_strategy.Index('m');
1032
1033 int tries = 0;
1034 int maxtries = 4;
1035 bool first = true;
1036 while (tries < maxtries && sIdx != -1) {
1037 if (m_strategy(sIdx) == 'm') {
1038 minim = "Minuit";
1039 algo = "migradImproved";
1040 } else if (m_strategy(sIdx) == 's') {
1041 algo = "Scan";
1042 } else if (m_strategy(sIdx) == 'h') {
1043 break; // jumping straight to a hesse evaluation
1044 } else if (m_strategy(sIdx) == 'r') {
1045 // reset minimizer
1047 tries = 0;
1048 *floatPars = *floatInitVals; // resets floats
1049 std::unique_ptr<RooMinimizer> _minimizerPtr2 = std::make_unique<RooMinimizer>(*_nll);
1050 auto initPars = _minimizerPtr2->fitter()->Config().ParamsSettings();
1051 auto initOpts = _minimizerPtr2->fitter()->Config().MinimizerOptions();
1052 _minimizerPtr2->fitter()->Config() = _minimizer->fitter()->Config();
1053 _minimizerPtr2->fitter()->Config().SetParamsSettings({}); // clears param settings
1054 _minimizerPtr = std::move(_minimizerPtr2);
1055 _minimizer = _minimizerPtr.get();
1056 sIdx++;
1057 statusHistory.emplace_back("Reset", 0);
1058 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1059 fff->counter2 = 0; // may have become non-zero if progressed to hesse and then resumed
1060 fff->prevMin = fff->minVal; // reset minimum
1061 }
1062 continue;
1063 } else {
1064 strategy = int(m_strategy(sIdx) - '0');
1065 _minimizer->setStrategy(strategy);
1066 minim = "Minuit2";
1067 algo = "Migrad";
1068 }
1069 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1070 fff->fState = minim + algo + std::to_string(_minimizer->fitter()->Config().MinimizerOptions().Strategy());
1071 }
1072 try {
1073 status = _minimizer->minimize(minim, algo);
1074 } catch (const std::exception &e) {
1075 std::cerr << "Exception while minimizing: " << e.what() << std::endl;
1076 }
1077 if (first && actualFirstMinimizer != _minimizer->fitter()->Config().MinimizerType())
1078 actualFirstMinimizer = _minimizer->fitter()->Config().MinimizerType();
1079 first = false;
1080 tries++;
1081
1082 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff && fff->fInterrupt) {
1083 delete _nll;
1084 throw std::runtime_error("Keyboard interrupt while minimizing");
1085 }
1086
1087 // RooMinimizer loses the useful status code, so here we will override it
1088 status = _minimizer->fitter()
1089 ->Result()
1090 .Status(); // note: Minuit failure is status code 4, minuit2 that is edm above max
1091 minim = _minimizer->fitter()->Config().MinimizerType(); // may have changed value
1092 statusHistory.emplace_back(_minimizer->fitter()->Config().MinimizerType() +
1093 _minimizer->fitter()->Config().MinimizerAlgoType() +
1094 std::to_string(_minimizer->fitter()->Config().MinimizerOptions().Strategy()),
1095 status);
1096 if (status % 1000 == 0)
1097 break; // fit was good
1098
1099 if (status == 4 && minim != "Minuit") {
1100 if (printLevel >= -1) {
1101 Warning("fitTo", "%s Hit max function calls of %d", fitName.Data(),
1102 _minimizer->fitter()->Config().MinimizerOptions().MaxFunctionCalls());
1103 }
1104 if (autoMaxCalls) {
1105 if (printLevel >= -1)
1106 Warning("fitTo", "will try doubling this");
1107 _minimizer->fitter()->Config().MinimizerOptions().SetMaxFunctionCalls(
1108 _minimizer->fitter()->Config().MinimizerOptions().MaxFunctionCalls() * 2);
1109 _minimizer->fitter()->Config().MinimizerOptions().SetMaxIterations(
1110 _minimizer->fitter()->Config().MinimizerOptions().MaxIterations() * 2);
1111 continue;
1112 }
1113 }
1114
1115 // NOTE: minuit2 seems to distort the tolerance in a weird way, so that tol becomes 1000 times smaller than
1116 // specified Also note that if fits are failing because of edm over max, it can be a good idea to activate the
1117 // Offset option when building nll
1118 if (printLevel >= -1) {
1119 printCerr(TString::Format("Warning: %s %s%s Status=%d (edm=%f, tol=%f, strat=%d), tries=#%d...",
1120 fitName.Data(), _minimizer->fitter()->Config().MinimizerType().c_str(),
1121 _minimizer->fitter()->Config().MinimizerAlgoType().c_str(), status,
1122 _minimizer->fitter()->Result().Edm(),
1123 _minimizer->fitter()->Config().MinimizerOptions().Tolerance(),
1124 _minimizer->fitter()->Config().MinimizerOptions().Strategy(), tries)
1125 .Data());
1126 }
1127
1128 // decide what to do next based on strategy sequence
1129 if (sIdx == m_strategy.Length() - 1) {
1130 break; // done
1131 }
1132
1133 tries--;
1134 sIdx++;
1135 }
1136 auto mini_sIdx = sIdx;
1137
1138 /* Minuit2 status codes:
1139 * status = 0 : OK
1140 status = 1 : Covariance was made pos defined
1141 status = 2 : Hesse is invalid
1142 status = 3 : Edm is above max
1143 status = 4 : Reached call limit
1144 status = 5 : Any other failure
1145
1146 For Minuit its basically 0 is OK, 4 is failure, I think?
1147 */
1148
1149 if (printLevel >= -1 && status != 0) {
1150 Warning("fitTo", "%s final status is %d", fitName.Data(), status);
1151 }
1152
1153 // currently dont have a way to access the covariance "dcovar" which is a metric from iterative
1154 // covariance method that is used by minuit2 to say if the covariance is accurate or not
1155 // See MinimumError.h: IsAccurate if Dcovar < 0.1
1156 // Note that if strategy>=2 or (strategy=1 and Dcovar>0.05) then hesse will be forced to be run (see
1157 // VariadicMetricBuilder) So only in Strategy=0 can you skip hesse (even if SetParabErrors false).
1158
1159 int miniStrat = _minimizer->fitter()->Config().MinimizerOptions().Strategy();
1160 double dCovar = std::numeric_limits<double>::quiet_NaN();
1161 // if(auto _minuit2 = dynamic_cast<ROOT::Minuit2::Minuit2Minimizer*>(_minimizer->fitter()->GetMinimizer());
1162 // _minuit2 && _minuit2->fMinimum) {
1163 // dCovar = _minuit2->fMinimum->Error().Dcovar();
1164 // }
1165
1166 // only do hesse if was a valid min and not full accurate cov matrix already (can happen if e.g. ran strat2)
1167 if (hesse && m_hessestrategy.Length() != 0 &&
1168 (m_strategy(sIdx) == 'h' || (_minimizer->fitter()->Result().IsValid()))) {
1169
1170 // Note: minima where the covariance was made posdef are deemed 'valid' ...
1171
1172 // remove limits on pars before calculation - CURRENTLY HAS NO EFFECT, minuit still holds the state as
1173 // transformed interesting note: error on pars before hesse can be significantly smaller than after hesse ...
1174 // what is the pre-hesse error corresponding to? - corresponds to approximation of covariance matrix calculated
1175 // with iterative method
1176 /*auto parSettings = _minimizer->fitter()->Config().ParamsSettings();
1177 for (auto &ss : _minimizer->fitter()->Config().ParamsSettings()) {
1178 ss.RemoveLimits();
1179 }
1180
1181 for(auto f : *floatPars) {
1182 auto v = dynamic_cast<RooRealVar*>(f);
1183 if(v->hasRange(nullptr)) v->setRange("backup",v->getMin(),v->getMax());
1184 v->removeRange();
1185 }*/
1186
1187 // std::cout << "nIterations = " << _minimizer->fitter()->GetMinimizer()->NIterations() << std::endl;
1188 // std::cout << "covQual before hesse = " << _minimizer->fitter()->GetMinimizer()->CovMatrixStatus() <<
1189 // std::endl;
1190 sIdx = -1;
1191 if (hesseStrategy == -1) {
1192 sIdx = 0;
1193 } else {
1194 sIdx = m_hessestrategy.Index('0' + hesseStrategy);
1195 }
1196 if (sIdx == -1) {
1197 Warning("minimize",
1198 "HesseStrategy %d not specified in HesseStrategySequence %s ... defaulting to start of sequence",
1200 sIdx = 0;
1201 }
1202 while (sIdx != -1) {
1204
1205 if (strategy == 2 && hesseStrategy == 2) {
1206 // don't repeat hesse if strategy=2 and hesseStrategy=2, and the matrix was valid
1207 if (_minimizer->fitter()->GetMinimizer()->CovMatrixStatus() == 3) {
1208 break;
1209 }
1210 if (sIdx >= m_hessestrategy.Length() - 1) {
1211 break; // run out of strategies to try, stop
1212 }
1213 sIdx++;
1214 continue;
1215 }
1216
1217 _minimizer->fitter()->Config().MinimizerOptions().SetStrategy(hesseStrategy);
1218 // const_cast<ROOT::Math::IOptions*>(_minimizer->fitter()->Config().MinimizerOptions().ExtraOptions())->SetValue("HessianStepTolerance",0.1);
1219 // const_cast<ROOT::Math::IOptions*>(_minimizer->fitter()->Config().MinimizerOptions().ExtraOptions())->SetValue("HessianG2Tolerance",0.02);
1220
1221 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1222 fff->fState = TString::Format("Hesse%d", _minimizer->fitter()->Config().MinimizerOptions().Strategy());
1223 fff->counter2 = fff->counter;
1224 fff->prevMin =
1225 fff->minVal; // reset minimum when change to hesse. Helps see if hesse eval gives new lower values
1226 }
1227
1228 //_nll->getVal(); // for reasons I dont understand, if nll evaluated before hesse call the edm is smaller? -
1229 // and also becomes WRONG :-S
1230
1231 // auto _status = (_minimizer->fitter()->CalculateHessErrors()) ? _minimizer->fitter()->Result().Status() :
1232 // -1;
1233 auto _status = _minimizer->hesse(); // note: I have seen that you can get 'full covariance quality' without
1234 // running hesse ... is that expected?
1235 // note: hesse status will be -1 if hesse failed (no covariance matrix)
1236 // otherwise the status appears to be whatever was the status before
1237 // note that hesse succeeds even if the cov matrix it calculates is forced pos def. Failure is only
1238 // if it cannot calculate a cov matrix at all.
1239 if (_status != -1)
1240 _status = 0; // mark as hesse succeeded, although need to look at covQual to see if was any good
1241
1242 /*for(auto f : *floatPars) {
1243 auto v = dynamic_cast<RooRealVar*>(f);
1244 if(v->hasRange("backup")) {
1245 v->setRange(v->getMin(),v->getMax());
1246 v->removeRange("backup");
1247 }
1248 }
1249 _minimizer->fitter()->Config().SetParamsSettings(parSettings);*/
1250
1251 /*for (auto &ss : _minimizer->fitter()->Config().ParamsSettings()) {
1252 if( ss.HasLowerLimit() || ss.HasUpperLimit() ) std::cout << ss.Name() << " limit restored " <<
1253 ss.LowerLimit() << " - " << ss.UpperLimit() << std::endl;
1254 }*/
1255
1256 statusHistory.push_back(std::pair<std::string, int>(
1257 TString::Format("Hesse%d", _minimizer->fitter()->Config().MinimizerOptions().Strategy()), _status));
1258
1259 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff && fff->fInterrupt) {
1260 delete _nll;
1261 throw std::runtime_error("Keyboard interrupt while hesse calculating");
1262 }
1263 if ((_status != 0 || _minimizer->fitter()->GetMinimizer()->CovMatrixStatus() != 3) && status == 0 &&
1264 printLevel >= -1) {
1265 printCerr(TString::Format("Warning: %s hesse status is %d, covQual=%d", fitName.Data(), _status,
1266 _minimizer->fitter()->GetMinimizer()->CovMatrixStatus())
1267 .Data());
1268 }
1269
1270 if (_status == 0 && _minimizer->fitter()->GetMinimizer()->CovMatrixStatus() == 3) {
1271 // covariance is valid!
1272 break;
1273 } else if (_status == 0) {
1274 // set the statusHistory to the cov status, since that's more informative
1275 statusHistory.back().second = _minimizer->fitter()->GetMinimizer()->CovMatrixStatus();
1276 }
1277
1278 if (sIdx >= m_hessestrategy.Length() - 1) {
1279 break; // run out of strategies to try, stop
1280 }
1281
1282 sIdx++;
1283 } // end of hesse attempt loop
1284 // experimental feature to resume fits invalidated by hesse
1285 if (gEnv->GetValue("XRooFit.ResumeInvalidFits", false) &&
1286 (statusHistory.back().second == 1 || statusHistory.back().second == 2) &&
1287 mini_sIdx < (m_strategy.Length() - 1)) {
1288 sIdx = mini_sIdx;
1289 goto resetMinimization;
1290 }
1291 }
1292
1293 // call minos if requested on any parameters
1294 if (status == 0 && minos) {
1295 if (std::unique_ptr<RooAbsCollection> mpars(floatPars->selectByAttrib("minos", true)); !mpars->empty()) {
1296 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1297 fff->fState = "Minos";
1298 fff->counter2 = 0;
1299 fff->prevMin = fff->minVal;
1300 }
1301 auto _status = _minimizer->minos(*mpars);
1302 statusHistory.push_back(std::pair("Minos", _status));
1303 }
1304 }
1305
1306 // signal(SIGINT,gOldHandlerr);
1307 out = std::unique_ptr<RooFitResult>{_minimizer->save(fitName, resultTitle)};
1308
1309 // if the final result is not valid, RooFit will mark the status as -1. But we should override that with the
1310 // more informative status of the last fit ...
1311 // for safety, will only override if status of last result is not 0 (don't want to report success when actually
1312 // bad)
1313 if (out->status() == -1 && !_minimizer->fitter()->Result().IsValid() && _minimizer->fitter()->Result().Status()) {
1314 out->setStatus(_minimizer->fitter()->Result().Status());
1315 }
1316
1317 // if status is 0 (min succeeded) but the covQual isn't fully accurate but requested hesse, reflect that in the
1318 // status
1319 if (out->status() == 0 && out->covQual() != 3 && hesse) {
1320 if (out->covQual() == 2) { // was made posdef
1321 out->setStatus(1); // indicates covariance made pos-def
1322 } else { // anything else indicates either hessian is approximate or something else wrong (e.g. not pos-def
1323 // return from strat3)
1324 out->setStatus(2); // hesse invalid
1325 }
1326 }
1327
1328 if (printLevel >= -2 && miniStrat < _minimizer->fitter()->Config().MinimizerOptions().Strategy() && hesse &&
1329 out->edm() > _minimizer->fitter()->Config().MinimizerOptions().Tolerance() * 1e-2 && out->status() != 3) {
1330 // hesse may have updated edm by using a better strategy than used in the minimization
1331 // so print a warning about this
1332 std::stringstream ss;
1333 ss << "Warning: post-Hesse edm " << out->edm()
1334 << " > 10xMaxEDM (MaxEDM=" << _minimizer->fitter()->Config().MinimizerOptions().Tolerance() * 1e-3
1335 << "). Consider increasing your minimization strategy";
1336 printCerr(ss.str().c_str());
1337 // Dec24: As this is a new warning, will not update status code for now, so edm will be large
1338 // but in the future we should probably update the code to 3 so that users don't miss this warning.
1339 // out->setStatus(3); // edm above max
1340 }
1341
1342 out->setStatusHistory(statusHistory);
1343
1344 // userPars wont have been added to the RooFitResult by RooMinimizer
1345 const_cast<RooArgList &>(out->constPars()).addClone(fUserPars, true);
1346
1347 if (!std::isnan(dCovar)) {
1348 const_cast<RooArgList &>(out->constPars())
1349 .addClone(RooRealVar(".dCovar", "dCovar from minimization", dCovar), true);
1350 }
1351
1352 if (boundaryCheck) {
1353 // check if any of the parameters are at their limits (potentially a problem with fit)
1354 // or their errors go over their limits (just a warning)
1355 int limit_status = 0;
1356 std::string listpars;
1358 if (!v)
1359 continue;
1360 double vRange = v->getMax() - v->getMin();
1361 if (v->getMin() > v->getVal() - vRange * boundaryCheck ||
1362 v->getMax() < v->getVal() + vRange * boundaryCheck) {
1363 // within 0.01% of edge
1364
1365 // check if nll actually lower 'at' the boundary, if it is, refine the best fit to the limit value
1366 auto tmp = v->getVal();
1367 v->setVal(v->getMin());
1368 double boundary_nll = _nll->getVal();
1369 if (boundary_nll <= out->minNll()) {
1370 static_cast<RooRealVar *>(out->floatParsFinal().find(v->GetName()))->setVal(v->getMin());
1371 out->setMinNLL(boundary_nll);
1372 // Info("fit","Corrected %s onto minimum @ %g",v->GetName(),v->getMin());
1373 } else {
1374 // not better, so restore value
1375 v->setVal(tmp);
1376 }
1377
1378 // if has a 'physical' range specified, don't warn if near the limit
1379 if (v->hasRange("physical"))
1380 limit_status = 900;
1381 listpars += v->GetName();
1382 listpars += ",";
1383 } else if (hesse &&
1384 (v->getMin() > v->getVal() - v->getError() || v->getMax() < v->getVal() + v->getError())) {
1385 if (printLevel >= 0) {
1386 Info("minimize", "PARLIM: %s (%f +/- %f) range (%f - %f)", v->GetName(), v->getVal(), v->getError(),
1387 v->getMin(), v->getMax());
1388 }
1389 limit_status = 9000;
1390 }
1391 }
1392 if (limit_status == 900) {
1393 if (printLevel >= 0) {
1394 Warning("minimize", "BOUNDCHK: Parameters within %g%% limit in fit result: %s", boundaryCheck * 100,
1395 listpars.c_str());
1396 }
1397 } else if (limit_status > 0) {
1398 if (printLevel >= 0)
1399 Warning("minimize", "BOUNDCHK: Parameters near limit in fit result");
1400 }
1401
1402 // store the limit check result
1403 statusHistory.emplace_back("BOUNDCHK", limit_status);
1404 out->setStatusHistory(statusHistory);
1405 out->setStatus(out->status() + limit_status);
1406 }
1407
1408 // // automatic parameter range adjustment based on errors
1409 // for(auto a : *floatPars) {
1410 // RooRealVar *v = dynamic_cast<RooRealVar *>(a);
1411 // if(v->getMin() > v->getVal() - 3.*v->getError()) {
1412 // v->setMin(v->getVal() - 3.1*v->getError());
1413 // }
1414 // if(v->getMax() < v->getVal() + 3.*v->getError()) {
1415 // v->setMax(v->getVal() + 3.1*v->getError());
1416 // }
1417 // // also make sure the range isn't too big (fits can struggle)
1418 // if(v->getMin() < v->getVal() - 10.*v->getError()) {
1419 // v->setMin(v->getVal() - 9.9*v->getError());
1420 // }
1421 // if(v->getMax() > v->getVal() + 10.*v->getError()) {
1422 // v->setMax(v->getVal() + 9.9*v->getError());
1423 // }
1424 // }
1425
1426 if (printLevel < 0)
1427 RooMsgService::instance().setGlobalKillBelow(msglevel);
1428
1429 // before returning we will override _minLL with the actual NLL value ... offsetting could have messed up the
1430 // value
1431 out->setMinNLL(_nll->getVal());
1432
1433 // ensure no asymm errors on any pars unless had minuitMinos
1434 for (auto o : out->floatParsFinal()) {
1435 if (auto v = dynamic_cast<RooRealVar *>(o);
1436 v && !v->getAttribute("minos") && !v->getAttribute("xminos") && !v->getAttribute("xMinos"))
1437 v->removeAsymError();
1438 }
1439
1440 // minimizer may have slightly altered the fitConfig (e.g. unavailable minimizer etc) so update for that ...
1441 if (fitConfig.MinimizerOptions().MinimizerType() != actualFirstMinimizer) {
1442 fitConfig.MinimizerOptions().SetMinimizerType(actualFirstMinimizer);
1443 }
1444
1445 if (_progress && printLevel >= -2) {
1446 delete _nll;
1447 }
1448 }
1449
1450 if (out && out->status() == 0 && minos) {
1451 // call minos if requested on any parameters
1452 for (auto label : {"xminos", "xMinos"}) {
1453 std::unique_ptr<RooAbsCollection> pars(floatPars->selectByAttrib(label, true));
1454 for (auto p : *pars) {
1455 Info("minimize", "Computing xminos error for %s", p->GetName());
1456 xRooFit::minos(nll, *out, p->GetName(), myFitConfig);
1457 }
1458 if (!pars->empty())
1459 *floatPars = out->floatParsFinal(); // put values back to best fit
1460 }
1461 }
1462
1463 if (restore) {
1464 *floatPars = out->floatParsInit();
1465 }
1466
1467 if (out && !logs.empty()) {
1468 // save logs to StringVar in constPars list
1469#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
1470 const_cast<RooArgList &>(out->constPars()).addOwned(std::make_unique<RooStringVar>(".log", "log", logs.c_str()));
1471#else
1472 const_cast<RooArgList &>(out->constPars()).addOwned(*new RooStringVar(".log", "log", logs.c_str()));
1473#endif
1474 }
1475
1476 if (out && cacheDir && cacheDir->IsWritable()) {
1477 // std::cout << "Saving " << out->GetName() << " " << out->GetTitle() << " to " << nll.GetName() << std::endl;
1478 // save a copy of fit result to relevant dir
1479 if (!cacheDir->GetDirectory(nll.GetName()))
1480 cacheDir->mkdir(nll.GetName());
1481 if (auto dir = cacheDir->GetDirectory(nll.GetName()); dir) {
1482 // save NLL opts if was given one, unless already present
1483 if (nllOpts) {
1484 if (strlen(nllOpts->GetName()) == 0) {
1485 nllOpts->SetName(TUUID().AsString());
1486 }
1487 if (!dir->FindKey(nllOpts->GetName())) {
1488 dir->WriteObject(nllOpts.get(), nllOpts->GetName());
1489 }
1490 }
1491
1492 // also save the fitConfig ... unless one with same name already present
1493 std::string configName;
1494 if (!fitConfig.MinimizerOptions().ExtraOptions()->GetValue("Name", configName)) {
1495 auto extraOpts = const_cast<ROOT::Math::IOptions *>(fitConfig.MinimizerOptions().ExtraOptions());
1497 extraOpts->SetValue("Name", configName.data());
1498 }
1499 if (!dir->GetKey(configName.data())) {
1500 dir->WriteObject(&fitConfig, configName.data());
1501 }
1502 // add the fitConfig name into the fit result before writing, so can retrieve in future
1503#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
1504 const_cast<RooArgList &>(out->constPars())
1505 .addOwned(std::make_unique<RooStringVar>(".fitConfigName", "fitConfigName", configName.c_str()));
1506#else
1507 const_cast<RooArgList &>(out->constPars())
1508 .addOwned(*new RooStringVar(".fitConfigName", "fitConfigName", configName.c_str()));
1509#endif
1510 dir->WriteObject(out.get(), out->GetName());
1511 auto sfr = new StoredFitResult(out);
1512 dir->Add(sfr);
1513 return sfr->fr;
1514 // return std::shared_ptr<const RooFitResult>(out, [](const RooFitResult*){}); // disowned shared_ptr
1515 }
1516 }
1517
1518 return out;
1519}
1520
1521// calculate asymmetric errors, if required, on the named parameter that was floating in the fit
1522// returns status code. 0 = all good, 1 = failure, ...
1523int xRooFit::minos(RooAbsReal &nll, const RooFitResult &ufit, const char *parName,
1524 const std::shared_ptr<ROOT::Fit::FitConfig> &_fitConfig)
1525{
1526
1527 auto par = dynamic_cast<RooRealVar *>(std::unique_ptr<RooArgSet>(nll.getVariables())->find(parName));
1528 if (!par)
1529 return 1;
1530
1531 auto par_hat = dynamic_cast<RooRealVar *>(ufit.floatParsFinal().find(parName));
1532 if (!par_hat)
1533 return 1;
1534
1536 auto &fitConfig = *myFitConfig;
1537
1538 bool pErrs = fitConfig.ParabErrors();
1539 fitConfig.SetParabErrors(false);
1540 double mErrs = fitConfig.MinosErrors();
1541 fitConfig.SetMinosErrors(false);
1542
1543 double val_best = par_hat->getVal();
1544 double val_err = (par_hat->hasError() ? par_hat->getError() : -1);
1545 double orig_err = val_err;
1546 double nll_min = ufit.minNll();
1547
1548 int status = 0;
1549
1550 bool isConst = par->isConstant();
1551 par->setConstant(true);
1552
1553 auto findValue = [&](double val_guess, double N_sigma = 1, double precision = 0.002, int printLevel = 0) {
1554 double tmu;
1555 int nrItr = 0;
1556 double sigma_guess = std::abs((val_guess - val_best) / N_sigma);
1557 double val_pre =
1558 val_guess -
1559 10 * precision * sigma_guess; // this is just to set value st. guarantees will do at least one iteration
1560 bool lastOverflow = false;
1561 bool lastUnderflow = false;
1562 while (std::abs(val_pre - val_guess) > precision * sigma_guess) {
1564 if (val_guess > 0 && par->getMax() < val_guess)
1565 par->setMax(2 * val_guess);
1566 if (val_guess < 0 && par->getMin() > val_guess)
1567 par->setMin(2 * val_guess);
1568 par->setVal(val_guess);
1569 // std::cout << "Guessing " << val_guess << std::endl;
1571 if (!result) {
1572 status = 1;
1573 return std::numeric_limits<double>::quiet_NaN();
1574 }
1575 double nll_val = result->minNll();
1576 status += result->status() * 10;
1577 tmu = 2 * (nll_val - nll_min);
1578 sigma_guess = std::abs(val_guess - val_best) / sqrt(tmu);
1579
1580 if (tmu <= 0) {
1581 // found an alternative or improved minima
1582 std::cout << "Warning: Alternative best-fit of " << par->GetName() << " @ " << val_guess << " vs "
1583 << val_best << " (delta=" << tmu / 2. << ")" << std::endl;
1584 double new_guess = val_guess + (val_guess - val_best);
1587 sigma_guess = std::abs((val_guess - val_best) / N_sigma);
1588 val_pre = val_guess - 10 * precision * sigma_guess;
1589 status = (status / 10) * 10 + 1;
1590 continue;
1591 }
1592
1593 double corr = /*damping_factor**/ (val_pre - val_best - N_sigma * sigma_guess);
1594
1595 // subtract off the difference in the new and damped correction
1596 val_guess -= corr;
1597
1598 if (printLevel > 1) {
1599 // cout << "nPars: " << nPars << std::endl;
1600 // cout << "NLL: " << nll->GetName() << " = " << nll->getVal() << endl;
1601 // cout << "delta(NLL): " << nll->getVal()-nll_min << endl;
1602 std::cout << "NLL min: " << nll_min << std::endl;
1603 std::cout << "N_sigma*sigma(pre): " << std::abs(val_pre - val_best) << std::endl;
1604 std::cout << "sigma(guess): " << sigma_guess << std::endl;
1605 std::cout << "par(guess): " << val_guess + corr << std::endl;
1606 std::cout << "true val: " << val_best << std::endl;
1607 std::cout << "tmu: " << tmu << std::endl;
1608 std::cout << "Precision: " << sigma_guess * precision << std::endl;
1609 std::cout << "Correction: " << (-corr < 0 ? " " : "") << -corr << std::endl;
1610 std::cout << "N_sigma*sigma(guess): " << std::abs(val_guess - val_best) << std::endl;
1611 std::cout << std::endl;
1612 }
1613 if (val_guess > par->getMax()) {
1614 if (lastOverflow) {
1615 val_guess = par->getMin();
1616 break;
1617 }
1618 lastOverflow = true;
1619 lastUnderflow = false;
1620 val_guess = par->getMax() - 1e-12;
1621 } else if (val_guess < par->getMin()) {
1622 if (lastUnderflow) {
1623 val_guess = par->getMin();
1624 break;
1625 }
1626 lastOverflow = false;
1627 lastUnderflow = true;
1628 val_guess = par->getMin() + 1e-12;
1629 } else {
1630 lastUnderflow = false;
1631 lastOverflow = false;
1632 }
1633
1634 nrItr++;
1635 if (nrItr > 25) {
1636 status = (status / 10) * 10 + 3;
1637 break;
1638 }
1639 }
1640
1641 if (lastOverflow) {
1642 // msg().Error("findSigma","%s at upper limit of %g .. error may be underestimated
1643 // (t=%g)",par->GetName(),par->getMax(),tmu);
1644 status = (status / 10) * 10 + 2;
1645 } else if (lastUnderflow) {
1646 // msg().Error("findSigma","%s at lower limit of %g .. error may be underestimated
1647 // (t=%g)",par->GetName(),par->getMin(),tmu);
1648 status = (status / 10) * 10 + 2;
1649 }
1650
1651 if (printLevel > 1)
1652 std::cout << "Found sigma for nll " << nll.GetName() << ": " << (val_guess - val_best) / N_sigma << std::endl;
1653 if (printLevel > 1)
1654 std::cout << "Finished in " << nrItr << " iterations." << std::endl;
1655 if (printLevel > 1)
1656 std::cout << std::endl;
1657 return (val_guess - val_best) / N_sigma;
1658 };
1659
1660 // determine if asym error defined by temporarily setting error to nan ... will then return non-nan if defined
1661
1662 par_hat->setError(std::numeric_limits<double>::quiet_NaN());
1663 double lo = par_hat->getErrorLo();
1664 double hi = par_hat->getErrorHi();
1665 if (std::isnan(hi)) {
1667 par_hat->getVal(); // put error wrt par_hat value, even if found better min
1668 if (hi > val_err)
1669 val_err = hi; // in case val_err was severe underestimate, don't want to waste time being too 'near' min
1670 }
1671 if (std::isnan(lo)) {
1672 lo = -findValue(val_best - val_err, -1) + val_best -
1673 par_hat->getVal(); // put error wrt par_hat value, even if found better min
1674 }
1675 dynamic_cast<RooRealVar *>(ufit.floatParsFinal().find(parName))->setAsymError(lo, hi);
1676 par_hat->setError(orig_err);
1677
1678 fitConfig.SetParabErrors(pErrs);
1679 fitConfig.SetMinosErrors(mErrs);
1680 par->setConstant(isConst);
1681
1682 std::vector<std::pair<std::string, int>> statusHistory;
1683 for (unsigned int i = 0; i < ufit.numStatusHistory(); i++) {
1684 statusHistory.emplace_back(ufit.statusLabelHistory(i), ufit.statusCodeHistory(i));
1685 }
1686 statusHistory.emplace_back(TString::Format("xMinos:%s", parName), status);
1687 const_cast<RooFitResult &>(ufit).setStatusHistory(statusHistory);
1688 const_cast<RooFitResult &>(ufit).setStatus(ufit.status() + status);
1689
1690 return status;
1691}
1692
1693TCanvas *
1695{
1696 TCanvas *out = nullptr;
1697
1698 // 1. Determine pdf: use top-level, if more than 1 then exit and tell user they need to flag
1699 RooAbsPdf *model = nullptr;
1700 std::deque<RooAbsArg *> topPdfs;
1701 int flagCount = 0;
1702 for (auto p : w.allPdfs()) {
1703 if (p->hasClients())
1704 continue;
1705 flagCount += p->getAttribute("hypoTest");
1706 if (p->getAttribute("hypoTest")) {
1707 topPdfs.push_front(p);
1708 } else {
1709 topPdfs.push_back(p);
1710 }
1711 }
1712 if (topPdfs.empty()) {
1713 Error("hypoTest", "Cannot find top-level pdf in workspace");
1714 return nullptr;
1715 } else if (topPdfs.size() > 1) {
1716 // should be one flagged
1717 if (flagCount == 0) {
1718 Error("hypoTest", "Multiple top-level pdfs. Flag which one to test with "
1719 "w->pdf(\"pdfName\")->setAttribute(\"hypoTest\",true)");
1720 return out;
1721 } else if (flagCount != 1) {
1722 Error("hypoTest", "Multiple top-level pdfs flagged for hypoTest -- pick one.");
1723 return out;
1724 }
1725 }
1726 model = dynamic_cast<RooAbsPdf *>(topPdfs.front());
1727
1728 Info("hypoTest", "Using PDF: %s", model->GetName());
1729
1730 double CL = 0.95; // TODO: make configurable
1731
1732 // 2. Determine the data (including globs). if more than 1 then exit and tell user they need to flag
1733 RooAbsData *obsData = nullptr;
1734 std::shared_ptr<RooArgSet> obsGlobs = nullptr;
1735
1736 for (auto p : w.allData()) {
1737 if (obsData) {
1738 Error("hypoTest", "Multiple datasets in workspace. Flag which one to test with "
1739 "w->data(\"dataName\")->setAttribute(\"hypoTest\",true)");
1740 return out;
1741 }
1742 obsData = p;
1743 }
1744
1745 if (!obsData) {
1746 Error("hypoTest", "No data -- cannot determine observables");
1747 return nullptr;
1748 }
1749
1750 Info("hypoTest", "Using Dataset: %s", obsData->GetName());
1751
1752 {
1753 auto _globs = xRooNode(w).datasets()[obsData->GetName()]->globs(); // keep alive because may own the globs
1754 obsGlobs = std::make_shared<RooArgSet>();
1755 obsGlobs->addClone(_globs.argList());
1756 Info("hypoTest", "Using Globs: %s", (obsGlobs->empty()) ? " <NONE>" : obsGlobs->contentsString().c_str());
1757 }
1758
1759 // 3. Determine the POI and args - look for model pars with "hypoPoints" binning, if none then cannot scan
1760 // args are const, poi are floating - exception is if only one then assume it is the POI
1761 auto _vars = std::unique_ptr<RooArgSet>(model->getVariables());
1762 RooArgSet poi;
1763 RooArgSet args;
1764 for (auto _v : *_vars) {
1765 if (auto v = dynamic_cast<RooRealVar *>(_v); v && v->hasBinning("hypoPoints")) {
1766 poi.add(*v);
1767 }
1768 }
1769 if (poi.size() > 1) {
1770 auto _const = std::unique_ptr<RooAbsCollection>(poi.selectByAttrib("Constant", true));
1771 args.add(*_const);
1772 poi.remove(*_const);
1773 }
1774 if (!args.empty()) {
1775 Info("hypoTest", "Using Arguments: %s", args.contentsString().c_str());
1776 }
1777 if (poi.empty()) {
1778 Error("hypoTest", "No POI detected: add the hypoPoints binning to at least one non-const model parameter e.g.:\n "
1779 "w->var(\"mu\")->setBinning(RooUniformBinning(0.5,10.5,10),\"hypoPoints\"))");
1780 return nullptr;
1781 }
1782
1783 Info("hypoTest", "Using Parameters of Interest: %s", poi.contentsString().c_str());
1784
1785 out = TCanvas::MakeDefCanvas();
1786
1787 // should check if exist in workspace
1788 auto nllOpts = createNLLOptions();
1789 auto fitConfig = createFitConfig();
1790
1791 xRooNLLVar nll(*model, std::make_pair(obsData, obsGlobs.get()), *nllOpts);
1792 nll.SetFitConfig(fitConfig);
1793
1794 if (poi.size() == 1) {
1795 auto mu = dynamic_cast<RooRealVar *>(poi.first());
1796
1797 double altVal = (mu->getStringAttribute("altVal")) ? TString(mu->getStringAttribute("altVal")).Atof()
1798 : std::numeric_limits<double>::quiet_NaN();
1799
1800 if (std::isnan(altVal) && mu->hasRange("physical")) {
1801 // use the smallest absolute value for the altValue
1802 altVal = mu->getMin("physical");
1803 Info("hypoTest", "No altVal specified - using min of given physical range = %g", altVal);
1804 } else {
1805 if (!std::isnan(altVal)) {
1806 Info("hypoTest", "alt hypo: %g - CLs activated", altVal);
1807 } else {
1808 Info("hypoTest", "No altVal found - to specify setStringAttribute(\"altVal\",\"<value>\") on POI or set "
1809 "the physical range");
1810 }
1811 }
1812 bool doCLs = !std::isnan(altVal) && std::abs(mu->getMin("hypoPoints")) > altVal &&
1813 std::abs(mu->getMax("hypoPoints")) > altVal;
1814
1815 const char *sCL = (doCLs) ? "CLs" : "null";
1816 Info("hypoTest", "%s testing active", sCL);
1817
1818 auto obs_ts = new TGraphErrors;
1819 obs_ts->SetNameTitle("obs_ts", TString::Format("Observed TestStat;%s", mu->GetTitle()));
1820 auto obs_pcls = new TGraphErrors;
1821 obs_pcls->SetNameTitle(TString::Format("obs_p%s", sCL),
1822 TString::Format("Observed p_{%s};%s", sCL, mu->GetTitle()));
1823 auto obs_cls = new TGraphErrors;
1824 obs_cls->SetNameTitle(TString::Format("obs_%s", sCL), TString::Format("Observed %s;%s", sCL, mu->GetTitle()));
1825
1826 std::vector<int> expSig = {-2, -1, 0, 1, 2};
1827 if (std::isnan(altVal))
1828 expSig.clear();
1829 std::map<int, TGraphErrors> exp_pcls;
1830 std::map<int, TGraphErrors> exp_cls;
1831 for (auto &s : expSig) {
1832 exp_pcls[s].SetNameTitle(TString::Format("exp%d_p%s", s, sCL),
1833 TString::Format("Expected (%d#sigma) p_{%s};%s", s, sCL, mu->GetTitle()));
1834 exp_cls[s].SetNameTitle(TString::Format("exp%d_%s", s, sCL),
1835 TString::Format("Expected (%d#sigma) %s;%s", s, sCL, mu->GetTitle()));
1836 }
1837
1838 auto getLimit = [CL](TGraphErrors &pValues) {
1839 double _out = std::numeric_limits<double>::quiet_NaN();
1840 bool lastAbove = false;
1841 for (int i = 0; i < pValues.GetN(); i++) {
1842 bool thisAbove = pValues.GetPointY(i) >= (1. - CL);
1843 if (i != 0 && thisAbove != lastAbove) {
1844 // crossed over ... find limit by interpolation
1845 // using linear interpolation so far
1846 _out = pValues.GetPointX(i - 1) + (pValues.GetPointX(i) - pValues.GetPointX(i - 1)) *
1847 ((1. - CL) - pValues.GetPointY(i - 1)) /
1848 (pValues.GetPointY(i) - pValues.GetPointY(i - 1));
1849 }
1851 }
1852 return _out;
1853 };
1854
1855 auto testPoint = [&](double testVal) {
1856 auto hp = nll.hypoPoint(mu->GetName(), testVal, altVal, pllType);
1857 obs_ts->SetPoint(obs_ts->GetN(), testVal, hp.pll().first);
1858 obs_ts->SetPointError(obs_ts->GetN() - 1, 0, hp.pll().second);
1859
1860 if (nToysNull > 0) {
1861 }
1862
1863 obs_pcls->SetPoint(obs_pcls->GetN(), testVal, (doCLs) ? hp.pCLs_asymp().first : hp.pNull_asymp().first);
1864 obs_pcls->SetPointError(obs_pcls->GetN() - 1, 0, (doCLs) ? hp.pCLs_asymp().second : hp.pNull_asymp().second);
1865 for (auto &s : expSig) {
1866 exp_pcls[s].SetPoint(exp_pcls[s].GetN(), testVal,
1867 (doCLs) ? hp.pCLs_asymp(s).first : hp.pNull_asymp(s).first);
1868 }
1869 if (doCLs) {
1870 Info("hypoTest", "%s=%g: %s=%g sigma_mu=%g %s=%g", mu->GetName(), testVal, obs_ts->GetName(),
1871 obs_ts->GetPointY(obs_ts->GetN() - 1), hp.sigma_mu().first, obs_pcls->GetName(),
1872 obs_pcls->GetPointY(obs_pcls->GetN() - 1));
1873 } else {
1874 Info("hypoTest", "%s=%g: %s=%g %s=%g", mu->GetName(), testVal, obs_ts->GetName(),
1875 obs_ts->GetPointY(obs_ts->GetN() - 1), obs_pcls->GetName(), obs_pcls->GetPointY(obs_pcls->GetN() - 1));
1876 }
1877 };
1878
1879 if (mu->getBins("hypoPoints") <= 0) {
1880 // autoTesting
1881 // evaluate min and max points
1882 testPoint(mu->getMin("hypoPoints"));
1883 testPoint(mu->getMax("hypoPoints"));
1884 testPoint((mu->getMax("hypoPoints") + mu->getMin("hypoPoints")) / 2.);
1885
1886 while (std::abs(obs_pcls->GetPointY(obs_pcls->GetN() - 1) - (1. - CL)) > 0.01) {
1887 obs_pcls->Sort();
1888 double nextTest = getLimit(*obs_pcls);
1889 if (std::isnan(nextTest))
1890 break;
1892 }
1893 for (auto s : expSig) {
1894 while (std::abs(exp_pcls[s].GetPointY(exp_pcls[s].GetN() - 1) - (1. - CL)) > 0.01) {
1895 exp_pcls[s].Sort();
1896 double nextTest = getLimit(exp_pcls[s]);
1897 if (std::isnan(nextTest))
1898 break;
1900 }
1901 }
1902 obs_ts->Sort();
1903 obs_pcls->Sort();
1904 for (auto &s : expSig)
1905 exp_pcls[s].Sort();
1906
1907 } else {
1908 for (int i = 0; i <= mu->getBins("hypoPoints"); i++) {
1909 testPoint((i == mu->getBins("hypoPoints")) ? mu->getBinning("hypoPoints").binHigh(i - 1)
1910 : mu->getBinning("hypoPoints").binLow(i));
1911 }
1912 }
1913
1914 obs_cls->SetPoint(obs_cls->GetN(), getLimit(*obs_pcls), 0.05);
1915 for (auto &s : expSig) {
1916 exp_cls[s].SetPoint(exp_cls[s].GetN(), getLimit(exp_pcls[s]), 0.05);
1917 }
1918
1919 // if more than two hypoPoints, visualize as bands
1920 if (exp_pcls[2].GetN() > 1) {
1921 TGraph *band2 = new TGraph;
1922 band2->SetNameTitle(".pCLs_2sigma", "2 sigma band");
1923 TGraph *band2up = new TGraph;
1924 band2up->SetNameTitle(".pCLs_2sigma_upUncert", "");
1925 TGraph *band2down = new TGraph;
1926 band2down->SetNameTitle(".pCLs_2sigma_downUncert", "");
1927 band2->SetFillColor(kYellow);
1928 band2up->SetFillColor(kYellow);
1929 band2down->SetFillColor(kYellow);
1930 band2up->SetFillStyle(3005);
1931 band2down->SetFillStyle(3005);
1932 for (int i = 0; i < exp_pcls[2].GetN(); i++) {
1933 band2->SetPoint(band2->GetN(), exp_pcls[2].GetPointX(i),
1934 exp_pcls[2].GetPointY(i) - exp_pcls[2].GetErrorYlow(i));
1935 band2up->SetPoint(band2up->GetN(), exp_pcls[2].GetPointX(i),
1936 exp_pcls[2].GetPointY(i) + exp_pcls[2].GetErrorYhigh(i));
1937 }
1938 for (int i = exp_pcls[2].GetN() - 1; i >= 0; i--) {
1939 band2up->SetPoint(band2up->GetN(), exp_pcls[2].GetPointX(i),
1940 exp_pcls[2].GetPointY(i) - exp_pcls[2].GetErrorYlow(i));
1941 }
1942 for (int i = 0; i < exp_pcls[-2].GetN(); i++) {
1943 band2down->SetPoint(band2down->GetN(), exp_pcls[-2].GetPointX(i),
1944 exp_pcls[-2].GetPointY(i) + exp_pcls[-2].GetErrorYhigh(i));
1945 }
1946 for (int i = exp_pcls[-2].GetN() - 1; i >= 0; i--) {
1947 band2->SetPoint(band2->GetN(), exp_pcls[-2].GetPointX(i),
1948 exp_pcls[-2].GetPointY(i) + exp_pcls[-2].GetErrorYhigh(i));
1949 band2down->SetPoint(band2down->GetN(), exp_pcls[-2].GetPointX(i),
1950 exp_pcls[-2].GetPointY(i) - exp_pcls[-2].GetErrorYlow(i));
1951 }
1952 band2->SetBit(kCanDelete);
1953 band2up->SetBit(kCanDelete);
1954 band2down->SetBit(kCanDelete);
1955 auto ax = static_cast<TNamed *>(band2->Clone(".axis"));
1956 ax->SetTitle(TString::Format("Hypothesis Test;%s", mu->GetTitle()));
1957 ax->Draw("AF");
1958 band2->Draw("F");
1959 band2up->Draw("F");
1960 band2down->Draw("F");
1961 }
1962
1963 if (exp_pcls[1].GetN() > 1) {
1964 TGraph *band2 = new TGraph;
1965 band2->SetNameTitle(".pCLs_1sigma", "1 sigma band");
1966 TGraph *band2up = new TGraph;
1967 band2up->SetNameTitle(".pCLs_1sigma_upUncert", "");
1968 TGraph *band2down = new TGraph;
1969 band2down->SetNameTitle(".pCLs_1sigma_downUncert", "");
1970 band2->SetFillColor(kGreen);
1971 band2up->SetFillColor(kGreen);
1972 band2down->SetFillColor(kGreen);
1973 band2up->SetFillStyle(3005);
1974 band2down->SetFillStyle(3005);
1975 for (int i = 0; i < exp_pcls[1].GetN(); i++) {
1976 band2->SetPoint(band2->GetN(), exp_pcls[1].GetPointX(i),
1977 exp_pcls[1].GetPointY(i) - exp_pcls[1].GetErrorYlow(i));
1978 band2up->SetPoint(band2up->GetN(), exp_pcls[1].GetPointX(i),
1979 exp_pcls[1].GetPointY(i) + exp_pcls[1].GetErrorYhigh(i));
1980 }
1981 for (int i = exp_pcls[1].GetN() - 1; i >= 0; i--) {
1982 band2up->SetPoint(band2up->GetN(), exp_pcls[1].GetPointX(i),
1983 exp_pcls[1].GetPointY(i) - exp_pcls[1].GetErrorYlow(i));
1984 }
1985 for (int i = 0; i < exp_pcls[-1].GetN(); i++) {
1986 band2down->SetPoint(band2down->GetN(), exp_pcls[-1].GetPointX(i),
1987 exp_pcls[-1].GetPointY(i) + exp_pcls[-1].GetErrorYhigh(i));
1988 }
1989 for (int i = exp_pcls[-1].GetN() - 1; i >= 0; i--) {
1990 band2->SetPoint(band2->GetN(), exp_pcls[-1].GetPointX(i),
1991 exp_pcls[-1].GetPointY(i) + exp_pcls[-1].GetErrorYhigh(i));
1992 band2down->SetPoint(band2down->GetN(), exp_pcls[-1].GetPointX(i),
1993 exp_pcls[-1].GetPointY(i) - exp_pcls[-1].GetErrorYlow(i));
1994 }
1995 band2->SetBit(kCanDelete);
1996 band2up->SetBit(kCanDelete);
1997 band2down->SetBit(kCanDelete);
1998 band2->Draw("F");
1999 band2up->Draw("F");
2000 band2down->Draw("F");
2001 }
2002
2003 TObject *expPlot = nullptr;
2004 if (exp_cls[0].GetN() > 0) {
2005 exp_pcls[0].SetLineStyle(2);
2006 exp_pcls[0].SetFillColor(kGreen);
2007 exp_pcls[0].SetMarkerStyle(0);
2008 expPlot = exp_pcls[0].DrawClone("L");
2009 }
2010 obs_pcls->SetBit(kCanDelete);
2011 obs_pcls->Draw(gPad->GetListOfPrimitives()->IsEmpty() ? "ALP" : "LP");
2012
2013 obs_ts->SetLineColor(kRed);
2014 obs_ts->SetMarkerColor(kRed);
2015 obs_ts->SetBit(kCanDelete);
2016 obs_ts->Draw("LP");
2017
2018 auto l = new TLegend(0.5, 0.6, 1. - gPad->GetRightMargin(), 1. - gPad->GetTopMargin());
2019 l->SetName("legend");
2020 l->AddEntry(obs_ts, obs_ts->GetTitle(), "LPE");
2021 l->AddEntry(obs_pcls, obs_pcls->GetTitle(), "LPE");
2022 if (expPlot)
2023 l->AddEntry(expPlot, "Expected", "LFE");
2025 l->Draw();
2026
2027 obs_cls->SetMarkerStyle(29);
2028 obs_cls->SetEditable(false);
2029 obs_cls->Draw("LP");
2030 for (auto s : expSig) {
2031 exp_cls[s].SetMarkerStyle(29);
2032 exp_cls[s].SetEditable(false);
2033 exp_cls[s].DrawClone("LP");
2034 }
2035 }
2036
2037 if (out)
2038 out->RedrawAxis();
2039
2040 return out;
2041}
2042
2043double round_to_digits(double value, int digits)
2044{
2045 if (value == 0.0)
2046 return 0.0;
2047 double factor = pow(10.0, digits - ceil(log10(std::abs(value))));
2048 return std::round(value * factor) / factor;
2049}
2051{
2052 const double multiplier = std::pow(10.0, decimal_places);
2053 return std::round(value * multiplier) / multiplier;
2054}
2055
2056// rounds error to 1 or 2 sig fig and round value to match that precision
2057std::pair<double, double> xRooFit::matchPrecision(const std::pair<double, double> &in)
2058{
2059 auto out = in;
2060 if (!std::isinf(out.second)) {
2061 auto tmp = out.second;
2062 out.second = round_to_digits(out.second, 2);
2063 int expo = (out.second == 0) ? 0 : (int)std::floor(std::log10(std::abs(out.second)));
2064 if (TString::Format("%e", out.second)(0) != '1') {
2065 out.second = round_to_digits(tmp, 1);
2066 out.first = (expo >= 0) ? round(out.first) : round_to_decimal(out.first, -expo);
2067 } else if (out.second != 0) {
2068 out.first = (expo >= 0) ? round(out.first) : round_to_decimal(out.first, -expo + 1);
2069 }
2070 }
2071 return out;
2072}
2073
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
#define oocoutW(o, a)
#define oocoutE(o, a)
#define oocoutI(o, a)
@ kRed
Definition Rtypes.h:67
@ kGreen
Definition Rtypes.h:67
@ kYellow
Definition Rtypes.h:67
static void indent(ostringstream &buf, int indent_level)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define gDirectory
Definition TDirectory.h:385
R__EXTERN TEnv * gEnv
Definition TEnv.h:126
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:241
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:252
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t 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 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
char name[80]
Definition TGX11.cxx:142
#define hi
@ kCanDelete
Definition TObject.h:375
#define gROOT
Definition TROOT.h:417
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2570
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#define gPad
double getValV(const RooArgSet *) const override
Return value of object.
Definition xRooFit.cxx:616
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
Definition xRooFit.cxx:623
std::string fState
Definition xRooFit.cxx:718
static bool fInterrupt
Definition xRooFit.cxx:572
bool setData(RooAbsData &data, bool cloneData) override
Definition xRooFit.cxx:615
ProgressMonitor(const ProgressMonitor &other, const char *name=nullptr)
Definition xRooFit.cxx:603
~ProgressMonitor() override
Definition xRooFit.cxx:595
TStopwatch s
Definition xRooFit.cxx:731
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Interface for detailed printing of object.
Definition xRooFit.cxx:618
TObject * clone(const char *newname) const override
Definition xRooFit.cxx:607
std::shared_ptr< RooAbsCollection > vars
Definition xRooFit.cxx:732
bool getParameters(const RooArgSet *observables, RooArgSet &outputSet, bool stripDisconnected) const override
Fills a list with leaf nodes in the arg tree starting with ourself as top node that don't match any o...
Definition xRooFit.cxx:611
RooArgList minPars
Definition xRooFit.cxx:727
RooRealProxy fFunc
Definition xRooFit.cxx:726
void applyWeightSquared(bool flag) override
Disables or enables the usage of squared weights.
Definition xRooFit.cxx:617
ProgressMonitor(RooAbsReal &f, int interval=30)
Definition xRooFit.cxx:584
static ProgressMonitor * me
Definition xRooFit.cxx:571
double defaultErrorLevel() const override
Definition xRooFit.cxx:610
RooArgList prevPars
Definition xRooFit.cxx:728
static void interruptHandler(int signum)
Definition xRooFit.cxx:573
void(* oldHandlerr)(int)
Definition xRooFit.cxx:570
static int minos(RooAbsReal &nll, const RooFitResult &ufit, const char *parName="", const std::shared_ptr< ROOT::Fit::FitConfig > &_fitConfig=nullptr)
Definition xRooFit.cxx:1523
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:744
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:463
static TCanvas * hypoTest(RooWorkspace &w, const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown)
Definition xRooFit.h:228
static std::shared_ptr< ROOT::Fit::FitConfig > createFitConfig()
Definition xRooFit.cxx:488
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2057
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
xRooFitResult minimize(const std::shared_ptr< ROOT::Fit::FitConfig > &=nullptr)
Class describing the configuration of the fit, options and parameter settings using the ROOT::Fit::Pa...
Definition FitConfig.h:49
class implementing generic options for a numerical algorithm Just store the options in a map of strin...
Generic interface for defining configuration options of a numerical algorithm.
Definition IOptions.h:28
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
RooWorkspace * _myws
! In which workspace do I live, if any
Definition RooAbsArg.h:637
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
RooWorkspace * workspace() const
Definition RooAbsArg.h:470
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
RooFit::OwningPtr< RooArgSet > getVariables(bool stripDisconnected=true) const
Return RooArgSet with all variables (tree leaf nodes of expression tree)
virtual void applyWeightSquared(bool flag)
Disables or enables the usage of squared weights.
A space to attach TBranches.
double getRealValue(const char *name, double defVal=0.0, bool verbose=false) const
Get value of a RooAbsReal stored in set with given name.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
void assignFast(const RooAbsCollection &other, bool setValDirty=true) const
Functional equivalent of assign() but assumes this and other collection have same layout.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
RooAbsArg * first() const
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
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:55
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
virtual double defaultErrorLevel() const
Definition RooAbsReal.h:245
virtual bool setData(RooAbsData &, bool=true)
Definition RooAbsReal.h:364
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Structure printing.
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
RooArgSet * selectByAttrib(const char *name, bool value) const
Use RooAbsCollection::selectByAttrib(), but return as RooArgSet.
Definition RooArgSet.h:144
static TClass * Class()
Implements a RooAbsBinning in terms of an array of boundary values, posing no constraints on the choi...
Definition RooBinning.h:27
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
Container class to hold unbinned data.
Definition RooDataSet.h:32
RooRealVar * weightVar() const
Returns a pointer to the weight variable (if set).
Definition RooDataSet.h:77
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
Int_t statusCodeHistory(UInt_t icycle) const
const char * statusLabelHistory(UInt_t icycle) const
const RooArgList & floatParsFinal() const
Return list of floating parameters after fit.
Int_t status() const
Return MINUIT status code.
UInt_t numStatusHistory() const
double minNll() const
Return minimized -log(L) value.
static TClass * Class()
static TClass * Class()
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
static TClass * Class()
Wrapper class around ROOT::Math::Minimizer that provides a seamless interface between the minimizer f...
auto fitter()
Return underlying ROOT fitter object.
RooFit::OwningPtr< RooFitResult > save(const char *name=nullptr, const char *title=nullptr)
Save and return a RooFitResult snapshot of current minimizer status.
int minos()
Execute MINOS.
int hesse()
Execute HESSE.
int minimize(const char *type, const char *alg=nullptr)
Minimise the function passed in the constructor.
void setStrategy(int istrat)
Change MINUIT strategy to istrat.
static RooMsgService & instance()
Return reference to singleton instance.
Implementation of a RooCacheManager<RooAbsCacheElement> that specializes in the storage of cache elem...
Poisson pdf.
Definition RooPoisson.h:18
static TClass * Class()
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:35
static TRandom * randomGenerator()
Return a pointer to a singleton random-number generator implementation.
Definition RooRandom.cxx:47
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
bool hasBinning(const char *name) const override
Returns true if variable has a binning named 'name'.
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
static RooAbsData * GenerateAsimovData(const RooAbsPdf &pdf, const RooArgSet &observables)
generate the asimov data for the observables (not the global ones) need to deal with the case of a si...
A RooAbsArg implementing string values.
Joins several RooAbsCategoryLValue objects into a single category.
bool setIndex(value_type index, bool printError=true) override
Set the value of the super category to the specified index.
Implementation of RooAbsBinning that provides a uniform binning in 'n' bins between the range end poi...
Persistable container for RooFit projects.
Using a TBrowser one can browse all ROOT objects.
Definition TBrowser.h:37
The Canvas class.
Definition TCanvas.h:23
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1521
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:3000
This class stores the date and time with a precision of one second in an unsigned 32 bit word (950130...
Definition TDatime.h:37
const char * AsString() const
Return the date & time as a string (ctime() format).
Definition TDatime.cxx:101
Describe directory structure in memory.
Definition TDirectory.h:45
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
A TGraphErrors is a TGraph with error bars.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
void SetNameTitle(const char *name="", const char *title="") override
Set graph name and title.
Definition TGraph.cxx:2464
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
TMatrixTBase< Element > & ResizeTo(Int_t nrows, Int_t ncols, Int_t=-1) override
Set size of the matrix to nrows x ncols New dynamic elements are created, the overlapping part of the...
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:267
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:886
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:292
void RedrawAxis(Option_t *option="") override
Redraw the frame axis.
Definition TPad.cxx:5472
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
Basic string class.
Definition TString.h:138
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:2459
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:643
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:418
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
TDatime GetTime() const
Get time from UUID.
Definition TUUID.cxx:701
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
Definition TUUID.cxx:602
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg Offset(std::string const &mode)
RooCmdArg Extended(bool flag=true)
RooCmdArg ExpectedData(bool flag=true)
Double_t x[n]
Definition legend1.C:17
double nll(double pdf, double weight, int binnedL, int doBinOffset)
Definition MathFuncs.h:449
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
void Sort(Index n, const Element *a, Index *index, Bool_t down=kTRUE)
Sort the n elements of the array a of generic templated type Element.
Definition TMathBase.h:413
void writeStdoutLine(const char *msg)
bool isPythonInitialized()
void writeStderrLine(const char *msg)
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
TLine l
Definition textangle.C:4
#define GIT_COMMIT_DATE
#define GIT_COMMIT_HASH
double round_to_decimal(double value, int decimal_places)
Definition xRooFit.cxx:2050
void printCout(const char *msg)
Definition xRooFit.cxx:556
void printCerr(const char *msg)
Definition xRooFit.cxx:545
double round_to_digits(double value, int digits)
Definition xRooFit.cxx:2043