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 // disable const-optimization at the construction step ... can happen in the minimization though
486 sDefaultNLLOptions->Add(RooFit::Optimize(0).Clone());
487 return sDefaultNLLOptions;
488}
489
490std::shared_ptr<ROOT::Fit::FitConfig> xRooFit::createFitConfig()
491{
492 return std::make_shared<ROOT::Fit::FitConfig>(*defaultFitConfig());
493}
494
495std::shared_ptr<ROOT::Fit::FitConfig> xRooFit::defaultFitConfig()
496{
497 if (sDefaultFitConfig)
498 return sDefaultFitConfig;
499 sDefaultFitConfig = std::make_shared<ROOT::Fit::FitConfig>();
500 auto &fitConfig = *sDefaultFitConfig;
501 fitConfig.SetParabErrors(true); // will use to run hesse after fit
502 fitConfig.MinimizerOptions().SetMinimizerType("Minuit2");
503 fitConfig.MinimizerOptions().SetErrorDef(0.5); // ensures errors are +/- 1 sigma ..IMPORTANT
504 fitConfig.SetParabErrors(true); // runs HESSE
505 fitConfig.SetMinosErrors(true); // computes asymmetric errors on any parameter with the "minos" attribute set
506 fitConfig.MinimizerOptions().SetMaxFunctionCalls(
507 -1); // calls per iteration. if left as 0 will set automatically to 500*nPars below
508 fitConfig.MinimizerOptions().SetMaxIterations(-1); // if left as 0 will set automatically to 500*nPars
509 fitConfig.MinimizerOptions().SetStrategy(-1); // will start at front of StrategySequence (given below)
510 // fitConfig.MinimizerOptions().SetTolerance(
511 // 1); // default is 0.01 (i think) but roominimizer uses 1 as default - use specify with
512 // ROOT::Math::MinimizerOptions::SetDefaultTolerance(..)
513 fitConfig.MinimizerOptions().SetPrintLevel(-2);
514 fitConfig.MinimizerOptions().SetExtraOptions(ROOT::Math::GenAlgoOptions());
515 // have to const cast to set extra options
516 auto extraOpts = const_cast<ROOT::Math::IOptions *>(fitConfig.MinimizerOptions().ExtraOptions());
517 extraOpts->SetValue("OptimizeConst", 2); // if 0 will disable constant term optimization and cache-and-track of the
518 // NLL. 1 = just caching, 2 = cache and track
519#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 29, 00)
520 extraOpts->SetValue(
521 "StrategySequence",
522 "0s01s12s2r2s23"); // 'm' would indicate use migradImproved from minuit v1. Dropped from default for 6.40
523 extraOpts->SetValue("HesseStrategySequence", "23");
524#else
525 extraOpts->SetValue("StrategySequence", "0s01s12s2m");
526 extraOpts->SetValue("HesseStrategySequence", "2");
527#endif
528 extraOpts->SetValue(
529 "HesseStrategy",
530 -1); // when hesse is run after minimization, will use this strategy. -1 means start at begin of strat sequence
531 extraOpts->SetValue("LogSize", 0); // length of log to capture and save
532 extraOpts->SetValue("BoundaryCheck",
533 0.); // if non-zero, warn if any post-fit value is close to boundary (e.g. 0.01 = within 1%)
534 extraOpts->SetValue("TrackProgress", 30); // seconds between output to log of evaluation progress
535 extraOpts->SetValue("xRooFitVersion", GIT_COMMIT_HASH); // not really options but here for logging purposes
536 // extraOpts->SetValue("ROOTVersion",ROOT_VERSION_CODE); - not needed as should by part of the ROOT TFile definition
537
538 // extraOpts->SetValue("HessianStepTolerance",0.);
539 // extraOpts->SetValue("HessianG2Tolerance",0.);
540
541 return sDefaultFitConfig;
542}
543
544ROOT::Math::IOptions *xRooFit::defaultFitConfigOptions()
545{
546 return const_cast<ROOT::Math::IOptions *>(defaultFitConfig()->MinimizerOptions().ExtraOptions());
547}
548
549void printCerr(const char *msg)
550{
553 // if (PyObject *sys_stdout = PySys_GetObject("stderr"); sys_stdout != nullptr) {
554 // Py_XDECREF(PyObject_CallMethod(sys_stdout, "flush", nullptr));
555 // }
556 } else {
557 std::cerr << msg << std::endl;
558 }
559}
560void printCout(const char *msg)
561{
564 // if (PyObject *sys_stdout = PySys_GetObject("stdout"); sys_stdout != nullptr) {
565 // Py_XDECREF(PyObject_CallMethod(sys_stdout, "flush", nullptr));
566 // }
567 } else {
568 std::cout << msg << std::endl;
569 }
570}
571
573public:
574 void (*oldHandlerr)(int) = nullptr;
576 static bool fInterrupt;
577 static void interruptHandler(int signum)
578 {
579 if (signum == SIGINT) {
580 printCout("Minimization interrupted ... will exit as soon as possible");
581 // TODO: create a global mutex for this
582 fInterrupt = true;
583 } else {
584 if (me)
585 me->oldHandlerr(signum);
586 }
587 };
589 : RooAbsReal(Form("progress_%s", f.GetName()), ""),
591 fFunc("func", "func", this, f),
593 {
594 s.Start();
595
596 me = this;
597 vars.reset(std::unique_ptr<RooAbsCollection>(f.getVariables())->selectByAttrib("Constant", false));
598 }
600 {
601 if (oldHandlerr) {
603 }
604 if (me == this)
605 me = nullptr;
606 };
607 ProgressMonitor(const ProgressMonitor &other, const char *name = nullptr)
609 {
610 }
611 TObject *clone(const char *newname) const override { return new ProgressMonitor(*this, newname); }
612
613 // required forwarding methods for RooEvaluatorWrapper in 6.32 onwards
614 double defaultErrorLevel() const override { return fFunc->defaultErrorLevel(); }
615 bool getParameters(const RooArgSet *observables, RooArgSet &outputSet, bool stripDisconnected) const override
616 {
617 return fFunc->getParameters(observables, outputSet, stripDisconnected);
618 }
619 bool setData(RooAbsData &data, bool cloneData) override { return fFunc->setData(data, cloneData); }
620 double getValV(const RooArgSet *) const override { return evaluate(); }
622 void printMultiline(std::ostream &os, Int_t contents, bool verbose = false, TString indent = "") const override
623 {
624 fFunc->printMultiline(os, contents, verbose, indent);
625 }
630
631 double evaluate() const override
632 {
633 if (fInterrupt) {
634 throw std::runtime_error("Keyboard interrupt");
635 return std::numeric_limits<double>::quiet_NaN();
636 }
637 double out = fFunc;
638 if (prevMin == std::numeric_limits<double>::infinity()) {
639 prevMin = out;
641 }
642 if (!std::isnan(out)) {
643 if (out < minVal) {
644 if (minPars.empty())
646 minPars = *vars;
647 }
648 minVal = std::min(minVal, out);
649 }
650 counter++;
651 if (s.RealTime() > fInterval) {
652 double evalRate = (counter - prevCounter) / s.RealTime();
653 s.Reset();
654 std::stringstream sout;
655
656 sout << TDatime().AsString() << ":(" << (counter) << "|" << evalRate << "Hz)";
657 if (!fState.empty())
658 sout << " : " << fState;
659 if (counter2) {
660 // doing a hesse step, estimate progress based on evaluations
661 int nRequired = prevPars.size();
662 if (nRequired > 1) {
663 nRequired *= (nRequired - 1);
664 nRequired /= 2; // since only need to do the a 'triangle' of the hessian matrix
665 if (fState == "Hesse3") {
666 nRequired *= 4;
667 }
668 sout << " (~" << int(100.0 * (counter - counter2) / nRequired) << "%)";
669 }
670 }
671 sout << " : " << minVal << " Delta=" << (minVal - prevMin);
672 if (minVal < prevMin) {
673 sout << " : ";
674 // compare minPars and prevPars, print biggest deltas
675 std::vector<std::pair<double, std::string>> parDeltas;
676 parDeltas.reserve(minPars.size());
677 for (auto p : minPars) {
678 parDeltas.emplace_back(std::pair<double, std::string>(
679 dynamic_cast<RooRealVar *>(p)->getVal() - prevPars.getRealValue(p->GetName()), p->GetName()));
680 }
681 std::sort(parDeltas.begin(), parDeltas.end(),
682 [](auto &left, auto &right) { return std::abs(left.first) > std::abs(right.first); });
683 int i;
684 for (i = 0; i < std::min(3, int(parDeltas.size())); i++) {
685 if (parDeltas.at(i).first == 0)
686 break;
687 if (i != 0)
688 sout << ",";
689 sout << parDeltas.at(i).second << (parDeltas.at(i).first >= 0 ? "+" : "-") << "="
690 << std::abs(parDeltas.at(i).first) << "(" << minPars.getRealValue(parDeltas.at(i).second.c_str())
691 << ")";
692 }
693 if (i < int(parDeltas.size()) && parDeltas.at(i).first != 0)
694 sout << " ...";
696 }
697
698 if (gROOT->FromPopUp() && gROOT->GetListOfBrowsers()->At(0)) {
699 auto browser = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0));
700 std::string status = sout.str();
701 int col = 0;
702 while (col < 4) {
703 std::string status_part;
704 if (status.find(" : ") != std::string::npos) {
705 status_part = status.substr(0, status.find(" : "));
706 status = status.substr(status.find(" : ") + 3);
707 } else {
708 status_part = status;
709 status = "";
710 }
711 browser->SetStatusText(status_part.c_str(), col);
712 col++;
713 }
715 }
716 printCerr(sout.str().c_str()); // std::cerr << sout.str() << std::endl;
717
718 prevMin = minVal;
720 } else {
721 s.Continue();
722 }
723 return out;
724 }
725
726 std::string fState;
727 mutable int counter = 0;
728 int counter2 = 0; // used to estimate progress of a Hesse calculation
729
730 mutable double minVal = std::numeric_limits<double>::infinity();
731 mutable double prevMin = std::numeric_limits<double>::infinity();
732
733private:
737 mutable int prevCounter = 0;
738 mutable int fInterval = 0; // time in seconds before next report
739 mutable TStopwatch s;
740 std::shared_ptr<RooAbsCollection> vars;
741};
742bool ProgressMonitor::fInterrupt = false;
744
745xRooFit::StoredFitResult::StoredFitResult(RooFitResult *_fr) : TNamed(*_fr)
746{
747 fr.reset(_fr);
748}
749
750xRooFit::StoredFitResult::StoredFitResult(const std::shared_ptr<RooFitResult> &_fr) : TNamed(*_fr), fr(_fr) {}
751
752std::shared_ptr<const RooFitResult> xRooFit::minimize(RooAbsReal &nll,
753 const std::shared_ptr<ROOT::Fit::FitConfig> &_fitConfig,
754 const std::shared_ptr<RooLinkedList> &nllOpts)
755{
756
758 auto &fitConfig = *myFitConfig;
759
760 auto _nll = &nll;
761
762 TString resultTitle = nll.getStringAttribute("fitresultTitle");
764 if (resultTitle == "")
766
767 // extract any user pars from the nll too
769 if (nll.getStringAttribute("userPars")) {
770 TStringToken st(nll.getStringAttribute("userPars"), ",");
771 while (st.NextToken()) {
773 TString parVal = nll.getStringAttribute(parName);
774 if (parVal.IsFloat()) {
775 fUserPars.addClone(RooRealVar(parName, parName, parVal.Atof()));
776 } else {
778 }
779 }
780 }
781
782 auto _nllVars = std::unique_ptr<RooAbsCollection>(_nll->getVariables());
783
784 std::unique_ptr<RooAbsCollection> constPars(_nllVars->selectByAttrib("Constant", true));
785 constPars->add(fUserPars, true); // add here so checked for when loading from cache
786 std::unique_ptr<RooAbsCollection> floatPars(_nllVars->selectByAttrib("Constant", false));
787
788 int _progress = 0;
789 double boundaryCheck = 0;
790 std::string s;
791 std::string hs;
792 int logSize = 0;
793#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 29, 00)
794 int hesseStrategy = 3; // uses most precise hesse settings (step sizes and g2 tolerances)
795#else
796 int hesseStrategy = 2; // uses most precise hesse settings (step sizes and g2 tolerances)
797#endif
798 if (fitConfig.MinimizerOptions().ExtraOptions()) {
799 fitConfig.MinimizerOptions().ExtraOptions()->GetNamedValue("StrategySequence", s);
800 fitConfig.MinimizerOptions().ExtraOptions()->GetIntValue("TrackProgress", _progress);
801 fitConfig.MinimizerOptions().ExtraOptions()->GetRealValue("BoundaryCheck", boundaryCheck);
802 fitConfig.MinimizerOptions().ExtraOptions()->GetIntValue("LogSize", logSize);
803 fitConfig.MinimizerOptions().ExtraOptions()->GetIntValue("HesseStrategy", hesseStrategy);
804 fitConfig.MinimizerOptions().ExtraOptions()->GetNamedValue("HesseStrategySequence", hs);
805 }
808
809 // if fit caching enabled, try to locate a valid fitResult
810 // must have matching constPars
812
813 if (cacheDir) {
814 if (auto nllDir = cacheDir->GetDirectory(nll.GetName()); nllDir) {
815 if (auto keys = nllDir->GetListOfKeys(); keys) {
816 for (auto &&k : *keys) {
817 auto cl = TClass::GetClass((static_cast<TKey *>(k))->GetClassName());
818 if (cl->InheritsFrom("RooFitResult")) {
820 nllDir->GetList() ? dynamic_cast<StoredFitResult *>(nllDir->GetList()->FindObject(k->GetName()))
821 : nullptr;
822 if (auto cachedFit =
823 (storedFr) ? storedFr->fr.get() : dynamic_cast<TKey *>(k)->ReadObject<RooFitResult>();
824 cachedFit) {
825 if (!storedFr) {
827 nllDir->Add(storedFr);
828 // std::cout << "Loaded " << nllDir->GetPath() << "/" << k->GetName() << " : " << k->GetTitle()
829 // << std::endl;
830 }
831 bool match = true;
832 if (!cachedFit->floatParsFinal().equals(*floatPars)) {
833 match = false;
834 } else {
835 for (auto &p : *constPars) {
836 auto v = dynamic_cast<RooAbsReal *>(p);
837 if (!v) {
838 if (auto c = dynamic_cast<RooAbsCategory *>(p)) {
839 if (auto _p =
840 dynamic_cast<RooAbsCategory *>(cachedFit->constPars().find(p->GetName()));
841 _p && !_p->getAttribute("global") &&
842 _p->getCurrentIndex() != c->getCurrentIndex()) {
843 match = false;
844 break;
845 }
846 } else {
847 match = false;
848 break;
849 }
850 };
851 if (auto _p = dynamic_cast<RooAbsReal *>(cachedFit->constPars().find(p->GetName())); _p) {
852 // note: do not need global observable values to match (globals currently added to
853 // constPars list)
854 if (!_p->getAttribute("global") && std::abs(_p->getVal() - v->getVal()) > 1e-12) {
855 match = false;
856 break;
857 }
858 }
859 }
860 }
861 if (match) {
862 return storedFr->fr;
863 // return std::shared_ptr<RooFitResult>(cachedFit,[](RooFitResult*){}); // dir owns the
864 // fitResult - this means dir needs to stay open for fits to be valid return
865 // std::make_shared<RooFitResult>(*cachedFit); // return a copy ... dir doesn't need to stay
866 // open, but fit result isn't shared
867 } else {
868 // delete cachedFit;
869 }
870 }
871 }
872 }
873 }
874 }
875 }
876
877 if (nll.getAttribute("readOnly"))
878 return nullptr;
879
880 int printLevel = fitConfig.MinimizerOptions().PrintLevel();
882 if (printLevel < 0)
883 RooMsgService::instance().setGlobalKillBelow(RooFit::FATAL);
884
885 // check how many parameters we have ... if 0 parameters then we wont run a fit, we just evaluate nll and return ...
886 if (floatPars->empty() || fitConfig.MinimizerOptions().MaxFunctionCalls() == 1) {
887 std::shared_ptr<RooFitResult> result;
889 parsList.add(*floatPars);
890 // construct an empty fit result ...
891 result = std::make_shared<RooFitResult>(); // if put name here fitresult gets added to dir, we don't want that
892 result->SetName(TUUID().AsString());
893 result->SetTitle(resultTitle);
894 result->setFinalParList(parsList);
895 result->setInitParList(parsList);
896 result->setConstParList(dynamic_cast<RooArgSet &>(*constPars)); /* RooFitResult takes a snapshot */
898 d.ResizeTo(parsList.size(), parsList.size());
899 result->setCovarianceMatrix(d);
900 result->setCovQual(floatPars->empty() ? 3 : -1);
901 result->setMinNLL(_nll->getVal());
902 result->setEDM(0);
903 result->setStatus(floatPars->empty() ? 0 : 1);
904
905 std::vector<std::pair<std::string, int>> statusHistory;
906 statusHistory.emplace_back(std::make_pair("EVAL", result->status()));
907 result->setStatusHistory(statusHistory);
908
909 if (cacheDir && cacheDir->IsWritable()) {
910 // save a copy of fit result to relevant dir
911 if (!cacheDir->GetDirectory(nll.GetName()))
912 cacheDir->mkdir(nll.GetName());
913 if (auto dir = cacheDir->GetDirectory(nll.GetName()); dir) {
914 // save NLL opts if was given one, unless already present
915 if (nllOpts) {
916 if (strlen(nllOpts->GetName()) == 0) {
917 nllOpts->SetName(TUUID().AsString());
918 }
919 if (!dir->FindKey(nllOpts->GetName())) {
920 dir->WriteObject(nllOpts.get(), nllOpts->GetName());
921 }
922 }
923 dir->WriteObject(result.get(), result->GetName());
924 }
925 }
926
927 if (printLevel < 0)
928 RooMsgService::instance().setGlobalKillBelow(msglevel);
929 return result;
930 }
931
932 std::shared_ptr<RooFitResult> out;
933
934 // check if any floatPars are categorical .. if so, need to a "discrete minimization" over the permutations
936 for (auto p : *floatPars) {
937 if (p->isCategory()) {
938 floatCats.add(*p);
939 }
940 }
941 if (!floatCats.empty()) {
942 RooSuperCategory allCats("floatCats", "Floating categorical parameters", floatCats);
943 std::unique_ptr<RooAbsCollection> _snap(floatCats.snapshot());
944 floatCats.setAttribAll("Constant");
945
946 std::shared_ptr<const RooFitResult> bestFr;
947 for (auto c : allCats) {
948 allCats.setIndex(c.second);
949 Info("minimize", "Minimizing with discrete %s", c.first.c_str());
950 auto fr = minimize(nll, _fitConfig, nllOpts);
951 if (!fr) {
952 Warning("minimize", "Minimization with discrete %s failed", c.first.c_str());
953 continue;
954 }
955 if (!bestFr || fr->minNll() < bestFr->minNll()) {
956 bestFr = fr;
957 }
958 }
959
960 floatCats.setAttribAll("Constant", false);
961
962 if (!bestFr)
963 return out;
964
965 // create a copy of the fit result, give it a new uuid, and move the const categories into the float area
966 out = std::make_shared<RooFitResult>(*bestFr);
967 const_cast<RooArgList &>(out->floatParsFinal())
968 .addClone(*std::unique_ptr<RooAbsCollection>(out->constPars().selectCommon(floatCats)));
969 const_cast<RooArgList &>(out->floatParsInit()).addClone(*_snap);
970 const_cast<RooArgList &>(out->constPars()).remove(floatCats);
971 out->SetName(TUUID().AsString());
972 }
973
974 bool restore = !fitConfig.UpdateAfterFit();
975 bool minos = fitConfig.MinosErrors();
976 std::string logs;
977 if (!out) {
978 int strategy = fitConfig.MinimizerOptions().Strategy();
979 // Note: AsymptoticCalculator enforces not less than 1 on tolerance - should we do so too?
980 if (_progress && printLevel >= -2) {
981 _nll = new ProgressMonitor(*_nll, _progress);
983 }
984 auto logger = (logSize > 0) ? std::make_unique<cout_redirect>(logs, logSize) : nullptr;
985 std::unique_ptr<RooMinimizer> _minimizerPtr = std::make_unique<RooMinimizer>(*_nll);
986 RooMinimizer *_minimizer = _minimizerPtr.get();
987 _minimizer->fitter()->Config() = fitConfig;
988 // if(fitConfig.MinimizerOptions().ExtraOptions()) {
989 // //for loading hesse options
990 // double a;
991 // if(fitConfig.MinimizerOptions().ExtraOptions()->GetValue("HessianStepTolerance",a)) {
992 // ROOT::Math::MinimizerOptions::Default("Minuit2").SetValue("HessianStepTolerance",a);
993 // }
994 // if(fitConfig.MinimizerOptions().ExtraOptions()->GetValue("HessianG2Tolerance",a)) {
995 // ROOT::Math::MinimizerOptions::Default("Minuit2").SetValue("HessianG2Tolerance",a);
996 // }
997 // }
998
999 bool autoMaxCalls = (_minimizer->fitter()->Config().MinimizerOptions().MaxFunctionCalls() == 0);
1000 if (autoMaxCalls) {
1001 _minimizer->fitter()->Config().MinimizerOptions().SetMaxFunctionCalls(
1002 500 * floatPars->size() * floatPars->size()); // hesse requires O(N^2) function calls
1003 }
1004 if (_minimizer->fitter()->Config().MinimizerOptions().MaxIterations() == 0) {
1005 _minimizer->fitter()->Config().MinimizerOptions().SetMaxIterations(500 * floatPars->size());
1006 }
1007
1008 std::unique_ptr<RooAbsCollection> floatInitVals(floatPars->snapshot());
1009 bool hesse = _minimizer->fitter()->Config().ParabErrors();
1010 _minimizer->fitter()->Config().SetParabErrors(
1011 false); // turn "off" so can run hesse as a separate step, appearing in status
1012 _minimizer->fitter()->Config().SetMinosErrors(false);
1013 _minimizer->fitter()->Config().SetUpdateAfterFit(true); // note: seems to always take effect
1014
1015 std::vector<std::pair<std::string, int>> statusHistory;
1016
1017 // gCurrentSampler = this;
1018 // gOldHandlerr = signal(SIGINT,toyInterruptHandlerr);
1019
1020 TString actualFirstMinimizer = _minimizer->fitter()->Config().MinimizerType();
1021
1022 int status = 0;
1023
1024 int constOptimize = 2;
1025 _minimizer->fitter()->Config().MinimizerOptions().ExtraOptions()->GetValue("OptimizeConst", constOptimize);
1026 if (constOptimize) {
1027 _minimizer->optimizeConst(constOptimize);
1028 // for safety force a refresh of the cache (and tracking) in the nll
1029 // DO NOT do a ConfigChange ... this is just a deactivate-reactivate of caching
1030 // but it seems like doing this breaks the const optimization and function is badly behaved
1031 // so once its turned on never turn it off.
1032 // nll.constOptimizeTestStatistic(RooAbsArg::ConfigChange, constOptimize>1 /* do tracking too if >1 */); //
1033 // trigger a re-evaluate of which nodes to cache-and-track
1034 // the next line seems safe to do but wont bother doing it because not bothering with above
1035 // need to understand why turning the cache off and on again breaks it??
1036 // nll.constOptimizeTestStatistic(RooAbsArg::ValueChange, constOptimize>1); // update the cache values -- is
1037 // this needed??
1038 } else {
1039 // disable const optimization
1040 // warning - if the nll was previously activated then it seems like deactivating may break it.
1041 nll.constOptimizeTestStatistic(RooAbsArg::DeActivate);
1042 }
1043
1044 int sIdx = -1;
1045 TString minim = _minimizer->fitter()->Config().MinimizerType();
1046 TString algo = _minimizer->fitter()->Config().MinimizerAlgoType();
1047 if (minim == "Minuit2") {
1048 if (strategy == -1) {
1049 sIdx = 0;
1050 } else {
1051 sIdx = m_strategy.Index('0' + strategy);
1052 }
1053 if (sIdx == -1) {
1054 Warning("minimize", "Strategy %d not specified in StrategySequence %s ... defaulting to start of sequence",
1055 strategy, m_strategy.Data());
1056 sIdx = 0;
1057 }
1058 } else if (minim == "Minuit")
1059 sIdx = m_strategy.Index('m');
1060
1061 int tries = 0;
1062 int maxtries = 4;
1063 bool first = true;
1064 while (tries < maxtries && sIdx != -1) {
1065 if (m_strategy(sIdx) == 'm') {
1066 minim = "Minuit";
1067 algo = "migradImproved";
1068 } else if (m_strategy(sIdx) == 's') {
1069 algo = "Scan";
1070 } else if (m_strategy(sIdx) == 'h') {
1071 break; // jumping straight to a hesse evaluation
1072 } else if (m_strategy(sIdx) == 'r') {
1073 // reset minimizer
1075 tries = 0;
1076 *floatPars = *floatInitVals; // resets floats
1077 std::unique_ptr<RooMinimizer> _minimizerPtr2 = std::make_unique<RooMinimizer>(*_nll);
1078 auto initPars = _minimizerPtr2->fitter()->Config().ParamsSettings();
1079 auto initOpts = _minimizerPtr2->fitter()->Config().MinimizerOptions();
1080 _minimizerPtr2->fitter()->Config() = _minimizer->fitter()->Config();
1081 _minimizerPtr2->fitter()->Config().SetParamsSettings({}); // clears param settings
1082 _minimizerPtr = std::move(_minimizerPtr2);
1083 _minimizer = _minimizerPtr.get();
1084 sIdx++;
1085 statusHistory.emplace_back("Reset", 0);
1086 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1087 fff->counter2 = 0; // may have become non-zero if progressed to hesse and then resumed
1088 fff->prevMin = fff->minVal; // reset minimum
1089 }
1090 continue;
1091 } else {
1092 strategy = int(m_strategy(sIdx) - '0');
1093 _minimizer->setStrategy(strategy);
1094 minim = "Minuit2";
1095 algo = "Migrad";
1096 }
1097 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1098 fff->fState = minim + algo + std::to_string(_minimizer->fitter()->Config().MinimizerOptions().Strategy());
1099 }
1100 try {
1101 status = _minimizer->minimize(minim, algo);
1102 } catch (const std::exception &e) {
1103 std::cerr << "Exception while minimizing: " << e.what() << std::endl;
1104 }
1105 if (first && actualFirstMinimizer != _minimizer->fitter()->Config().MinimizerType())
1106 actualFirstMinimizer = _minimizer->fitter()->Config().MinimizerType();
1107 first = false;
1108 tries++;
1109
1110 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff && fff->fInterrupt) {
1111 delete _nll;
1112 throw std::runtime_error("Keyboard interrupt while minimizing");
1113 }
1114
1115 // RooMinimizer loses the useful status code, so here we will override it
1116 status = _minimizer->fitter()
1117 ->Result()
1118 .Status(); // note: Minuit failure is status code 4, minuit2 that is edm above max
1119 minim = _minimizer->fitter()->Config().MinimizerType(); // may have changed value
1120 statusHistory.emplace_back(_minimizer->fitter()->Config().MinimizerType() +
1121 _minimizer->fitter()->Config().MinimizerAlgoType() +
1122 std::to_string(_minimizer->fitter()->Config().MinimizerOptions().Strategy()),
1123 status);
1124 if (status % 1000 == 0)
1125 break; // fit was good
1126
1127 if (status == 4 && minim != "Minuit") {
1128 if (printLevel >= -1) {
1129 Warning("fitTo", "%s Hit max function calls of %d", fitName.Data(),
1130 _minimizer->fitter()->Config().MinimizerOptions().MaxFunctionCalls());
1131 }
1132 if (autoMaxCalls) {
1133 if (printLevel >= -1)
1134 Warning("fitTo", "will try doubling this");
1135 _minimizer->fitter()->Config().MinimizerOptions().SetMaxFunctionCalls(
1136 _minimizer->fitter()->Config().MinimizerOptions().MaxFunctionCalls() * 2);
1137 _minimizer->fitter()->Config().MinimizerOptions().SetMaxIterations(
1138 _minimizer->fitter()->Config().MinimizerOptions().MaxIterations() * 2);
1139 continue;
1140 }
1141 }
1142
1143 // NOTE: minuit2 seems to distort the tolerance in a weird way, so that tol becomes 1000 times smaller than
1144 // specified Also note that if fits are failing because of edm over max, it can be a good idea to activate the
1145 // Offset option when building nll
1146 if (printLevel >= -1) {
1147 printCerr(TString::Format("Warning: %s %s%s Status=%d (edm=%f, tol=%f, strat=%d), tries=#%d...",
1148 fitName.Data(), _minimizer->fitter()->Config().MinimizerType().c_str(),
1149 _minimizer->fitter()->Config().MinimizerAlgoType().c_str(), status,
1150 _minimizer->fitter()->Result().Edm(),
1151 _minimizer->fitter()->Config().MinimizerOptions().Tolerance(),
1152 _minimizer->fitter()->Config().MinimizerOptions().Strategy(), tries)
1153 .Data());
1154 }
1155
1156 // decide what to do next based on strategy sequence
1157 if (sIdx == m_strategy.Length() - 1) {
1158 break; // done
1159 }
1160
1161 tries--;
1162 sIdx++;
1163 }
1164 auto mini_sIdx = sIdx;
1165
1166 /* Minuit2 status codes:
1167 * status = 0 : OK
1168 status = 1 : Covariance was made pos defined
1169 status = 2 : Hesse is invalid
1170 status = 3 : Edm is above max
1171 status = 4 : Reached call limit
1172 status = 5 : Any other failure
1173
1174 For Minuit its basically 0 is OK, 4 is failure, I think?
1175 */
1176
1177 if (printLevel >= -1 && status != 0) {
1178 Warning("fitTo", "%s final status is %d", fitName.Data(), status);
1179 }
1180
1181 // currently dont have a way to access the covariance "dcovar" which is a metric from iterative
1182 // covariance method that is used by minuit2 to say if the covariance is accurate or not
1183 // See MinimumError.h: IsAccurate if Dcovar < 0.1
1184 // Note that if strategy>=2 or (strategy=1 and Dcovar>0.05) then hesse will be forced to be run (see
1185 // VariadicMetricBuilder) So only in Strategy=0 can you skip hesse (even if SetParabErrors false).
1186
1187 int miniStrat = _minimizer->fitter()->Config().MinimizerOptions().Strategy();
1188 double dCovar = std::numeric_limits<double>::quiet_NaN();
1189 // if(auto _minuit2 = dynamic_cast<ROOT::Minuit2::Minuit2Minimizer*>(_minimizer->fitter()->GetMinimizer());
1190 // _minuit2 && _minuit2->fMinimum) {
1191 // dCovar = _minuit2->fMinimum->Error().Dcovar();
1192 // }
1193
1194 // only do hesse if was a valid min and not full accurate cov matrix already (can happen if e.g. ran strat2)
1195 if (hesse && m_hessestrategy.Length() != 0 &&
1196 (m_strategy(sIdx) == 'h' || (_minimizer->fitter()->Result().IsValid()))) {
1197
1198 // Note: minima where the covariance was made posdef are deemed 'valid' ...
1199
1200 // remove limits on pars before calculation - CURRENTLY HAS NO EFFECT, minuit still holds the state as
1201 // transformed interesting note: error on pars before hesse can be significantly smaller than after hesse ...
1202 // what is the pre-hesse error corresponding to? - corresponds to approximation of covariance matrix calculated
1203 // with iterative method
1204 /*auto parSettings = _minimizer->fitter()->Config().ParamsSettings();
1205 for (auto &ss : _minimizer->fitter()->Config().ParamsSettings()) {
1206 ss.RemoveLimits();
1207 }
1208
1209 for(auto f : *floatPars) {
1210 auto v = dynamic_cast<RooRealVar*>(f);
1211 if(v->hasRange(nullptr)) v->setRange("backup",v->getMin(),v->getMax());
1212 v->removeRange();
1213 }*/
1214
1215 // std::cout << "nIterations = " << _minimizer->fitter()->GetMinimizer()->NIterations() << std::endl;
1216 // std::cout << "covQual before hesse = " << _minimizer->fitter()->GetMinimizer()->CovMatrixStatus() <<
1217 // std::endl;
1218 sIdx = -1;
1219 if (hesseStrategy == -1) {
1220 sIdx = 0;
1221 } else {
1222 sIdx = m_hessestrategy.Index('0' + hesseStrategy);
1223 }
1224 if (sIdx == -1) {
1225 Warning("minimize",
1226 "HesseStrategy %d not specified in HesseStrategySequence %s ... defaulting to start of sequence",
1228 sIdx = 0;
1229 }
1230 while (sIdx != -1) {
1232
1233 if (strategy == 2 && hesseStrategy == 2) {
1234 // don't repeat hesse if strategy=2 and hesseStrategy=2, and the matrix was valid
1235 if (_minimizer->fitter()->GetMinimizer()->CovMatrixStatus() == 3) {
1236 break;
1237 }
1238 if (sIdx >= m_hessestrategy.Length() - 1) {
1239 break; // run out of strategies to try, stop
1240 }
1241 sIdx++;
1242 continue;
1243 }
1244
1245 _minimizer->fitter()->Config().MinimizerOptions().SetStrategy(hesseStrategy);
1246 // const_cast<ROOT::Math::IOptions*>(_minimizer->fitter()->Config().MinimizerOptions().ExtraOptions())->SetValue("HessianStepTolerance",0.1);
1247 // const_cast<ROOT::Math::IOptions*>(_minimizer->fitter()->Config().MinimizerOptions().ExtraOptions())->SetValue("HessianG2Tolerance",0.02);
1248
1249 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1250 fff->fState = TString::Format("Hesse%d", _minimizer->fitter()->Config().MinimizerOptions().Strategy());
1251 fff->counter2 = fff->counter;
1252 fff->prevMin =
1253 fff->minVal; // reset minimum when change to hesse. Helps see if hesse eval gives new lower values
1254 }
1255
1256 //_nll->getVal(); // for reasons I dont understand, if nll evaluated before hesse call the edm is smaller? -
1257 // and also becomes WRONG :-S
1258
1259 // auto _status = (_minimizer->fitter()->CalculateHessErrors()) ? _minimizer->fitter()->Result().Status() :
1260 // -1;
1261 auto _status = _minimizer->hesse(); // note: I have seen that you can get 'full covariance quality' without
1262 // running hesse ... is that expected?
1263 // note: hesse status will be -1 if hesse failed (no covariance matrix)
1264 // otherwise the status appears to be whatever was the status before
1265 // note that hesse succeeds even if the cov matrix it calculates is forced pos def. Failure is only
1266 // if it cannot calculate a cov matrix at all.
1267 if (_status != -1)
1268 _status = 0; // mark as hesse succeeded, although need to look at covQual to see if was any good
1269
1270 /*for(auto f : *floatPars) {
1271 auto v = dynamic_cast<RooRealVar*>(f);
1272 if(v->hasRange("backup")) {
1273 v->setRange(v->getMin(),v->getMax());
1274 v->removeRange("backup");
1275 }
1276 }
1277 _minimizer->fitter()->Config().SetParamsSettings(parSettings);*/
1278
1279 /*for (auto &ss : _minimizer->fitter()->Config().ParamsSettings()) {
1280 if( ss.HasLowerLimit() || ss.HasUpperLimit() ) std::cout << ss.Name() << " limit restored " <<
1281 ss.LowerLimit() << " - " << ss.UpperLimit() << std::endl;
1282 }*/
1283
1284 statusHistory.push_back(std::pair<std::string, int>(
1285 TString::Format("Hesse%d", _minimizer->fitter()->Config().MinimizerOptions().Strategy()), _status));
1286
1287 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff && fff->fInterrupt) {
1288 delete _nll;
1289 throw std::runtime_error("Keyboard interrupt while hesse calculating");
1290 }
1291 if ((_status != 0 || _minimizer->fitter()->GetMinimizer()->CovMatrixStatus() != 3) && status == 0 &&
1292 printLevel >= -1) {
1293 printCerr(TString::Format("Warning: %s hesse status is %d, covQual=%d", fitName.Data(), _status,
1294 _minimizer->fitter()->GetMinimizer()->CovMatrixStatus())
1295 .Data());
1296 }
1297
1298 if (_status == 0 && _minimizer->fitter()->GetMinimizer()->CovMatrixStatus() == 3) {
1299 // covariance is valid!
1300 break;
1301 } else if (_status == 0) {
1302 // set the statusHistory to the cov status, since that's more informative
1303 statusHistory.back().second = _minimizer->fitter()->GetMinimizer()->CovMatrixStatus();
1304 }
1305
1306 if (sIdx >= m_hessestrategy.Length() - 1) {
1307 break; // run out of strategies to try, stop
1308 }
1309
1310 sIdx++;
1311 } // end of hesse attempt loop
1312 // experimental feature to resume fits invalidated by hesse
1313 if (gEnv->GetValue("XRooFit.ResumeInvalidFits", false) &&
1314 (statusHistory.back().second == 1 || statusHistory.back().second == 2) &&
1315 mini_sIdx < (m_strategy.Length() - 1)) {
1316 sIdx = mini_sIdx;
1317 goto resetMinimization;
1318 }
1319 }
1320
1321 // call minos if requested on any parameters
1322 if (status == 0 && minos) {
1323 if (std::unique_ptr<RooAbsCollection> mpars(floatPars->selectByAttrib("minos", true)); !mpars->empty()) {
1324 if (auto fff = dynamic_cast<ProgressMonitor *>(_nll); fff) {
1325 fff->fState = "Minos";
1326 fff->counter2 = 0;
1327 fff->prevMin = fff->minVal;
1328 }
1329 auto _status = _minimizer->minos(*mpars);
1330 statusHistory.push_back(std::pair("Minos", _status));
1331 }
1332 }
1333
1334 // DO NOT DO THIS - seems to mess with the NLL function in a way that breaks the cache - reactivating wont fix
1335 // if(constOptimize) { _minimizer->optimizeConst(0); } // doing this because saw happens in RooAbsPdf::minimizeNLL
1336 // method
1337
1338 // signal(SIGINT,gOldHandlerr);
1339 out = std::unique_ptr<RooFitResult>{_minimizer->save(fitName, resultTitle)};
1340
1341 // if the final result is not valid, RooFit will mark the status as -1. But we should override that with the
1342 // more informative status of the last fit ...
1343 // for safety, will only override if status of last result is not 0 (don't want to report success when actually
1344 // bad)
1345 if (out->status() == -1 && !_minimizer->fitter()->Result().IsValid() && _minimizer->fitter()->Result().Status()) {
1346 out->setStatus(_minimizer->fitter()->Result().Status());
1347 }
1348
1349 // if status is 0 (min succeeded) but the covQual isn't fully accurate but requested hesse, reflect that in the
1350 // status
1351 if (out->status() == 0 && out->covQual() != 3 && hesse) {
1352 if (out->covQual() == 2) { // was made posdef
1353 out->setStatus(1); // indicates covariance made pos-def
1354 } else { // anything else indicates either hessian is approximate or something else wrong (e.g. not pos-def
1355 // return from strat3)
1356 out->setStatus(2); // hesse invalid
1357 }
1358 }
1359
1360 if (printLevel >= -2 && miniStrat < _minimizer->fitter()->Config().MinimizerOptions().Strategy() && hesse &&
1361 out->edm() > _minimizer->fitter()->Config().MinimizerOptions().Tolerance() * 1e-2 && out->status() != 3) {
1362 // hesse may have updated edm by using a better strategy than used in the minimization
1363 // so print a warning about this
1364 std::stringstream ss;
1365 ss << "Warning: post-Hesse edm " << out->edm()
1366 << " > 10xMaxEDM (MaxEDM=" << _minimizer->fitter()->Config().MinimizerOptions().Tolerance() * 1e-3
1367 << "). Consider increasing your minimization strategy";
1368 printCerr(ss.str().c_str());
1369 // Dec24: As this is a new warning, will not update status code for now, so edm will be large
1370 // but in the future we should probably update the code to 3 so that users don't miss this warning.
1371 // out->setStatus(3); // edm above max
1372 }
1373
1374 out->setStatusHistory(statusHistory);
1375
1376 // userPars wont have been added to the RooFitResult by RooMinimizer
1377 const_cast<RooArgList &>(out->constPars()).addClone(fUserPars, true);
1378
1379 if (!std::isnan(dCovar)) {
1380 const_cast<RooArgList &>(out->constPars())
1381 .addClone(RooRealVar(".dCovar", "dCovar from minimization", dCovar), true);
1382 }
1383
1384 if (boundaryCheck) {
1385 // check if any of the parameters are at their limits (potentially a problem with fit)
1386 // or their errors go over their limits (just a warning)
1387 int limit_status = 0;
1388 std::string listpars;
1390 if (!v)
1391 continue;
1392 double vRange = v->getMax() - v->getMin();
1393 if (v->getMin() > v->getVal() - vRange * boundaryCheck ||
1394 v->getMax() < v->getVal() + vRange * boundaryCheck) {
1395 // within 0.01% of edge
1396
1397 // check if nll actually lower 'at' the boundary, if it is, refine the best fit to the limit value
1398 auto tmp = v->getVal();
1399 v->setVal(v->getMin());
1400 double boundary_nll = _nll->getVal();
1401 if (boundary_nll <= out->minNll()) {
1402 static_cast<RooRealVar *>(out->floatParsFinal().find(v->GetName()))->setVal(v->getMin());
1403 out->setMinNLL(boundary_nll);
1404 // Info("fit","Corrected %s onto minimum @ %g",v->GetName(),v->getMin());
1405 } else {
1406 // not better, so restore value
1407 v->setVal(tmp);
1408 }
1409
1410 // if has a 'physical' range specified, don't warn if near the limit
1411 if (v->hasRange("physical"))
1412 limit_status = 900;
1413 listpars += v->GetName();
1414 listpars += ",";
1415 } else if (hesse &&
1416 (v->getMin() > v->getVal() - v->getError() || v->getMax() < v->getVal() + v->getError())) {
1417 if (printLevel >= 0) {
1418 Info("minimize", "PARLIM: %s (%f +/- %f) range (%f - %f)", v->GetName(), v->getVal(), v->getError(),
1419 v->getMin(), v->getMax());
1420 }
1421 limit_status = 9000;
1422 }
1423 }
1424 if (limit_status == 900) {
1425 if (printLevel >= 0) {
1426 Warning("minimize", "BOUNDCHK: Parameters within %g%% limit in fit result: %s", boundaryCheck * 100,
1427 listpars.c_str());
1428 }
1429 } else if (limit_status > 0) {
1430 if (printLevel >= 0)
1431 Warning("minimize", "BOUNDCHK: Parameters near limit in fit result");
1432 }
1433
1434 // store the limit check result
1435 statusHistory.emplace_back("BOUNDCHK", limit_status);
1436 out->setStatusHistory(statusHistory);
1437 out->setStatus(out->status() + limit_status);
1438 }
1439
1440 // // automatic parameter range adjustment based on errors
1441 // for(auto a : *floatPars) {
1442 // RooRealVar *v = dynamic_cast<RooRealVar *>(a);
1443 // if(v->getMin() > v->getVal() - 3.*v->getError()) {
1444 // v->setMin(v->getVal() - 3.1*v->getError());
1445 // }
1446 // if(v->getMax() < v->getVal() + 3.*v->getError()) {
1447 // v->setMax(v->getVal() + 3.1*v->getError());
1448 // }
1449 // // also make sure the range isn't too big (fits can struggle)
1450 // if(v->getMin() < v->getVal() - 10.*v->getError()) {
1451 // v->setMin(v->getVal() - 9.9*v->getError());
1452 // }
1453 // if(v->getMax() > v->getVal() + 10.*v->getError()) {
1454 // v->setMax(v->getVal() + 9.9*v->getError());
1455 // }
1456 // }
1457
1458 if (printLevel < 0)
1459 RooMsgService::instance().setGlobalKillBelow(msglevel);
1460
1461 // before returning we will override _minLL with the actual NLL value ... offsetting could have messed up the
1462 // value
1463 out->setMinNLL(_nll->getVal());
1464
1465 // ensure no asymm errors on any pars unless had minuitMinos
1466 for (auto o : out->floatParsFinal()) {
1467 if (auto v = dynamic_cast<RooRealVar *>(o);
1468 v && !v->getAttribute("minos") && !v->getAttribute("xminos") && !v->getAttribute("xMinos"))
1469 v->removeAsymError();
1470 }
1471
1472 // minimizer may have slightly altered the fitConfig (e.g. unavailable minimizer etc) so update for that ...
1473 if (fitConfig.MinimizerOptions().MinimizerType() != actualFirstMinimizer) {
1474 fitConfig.MinimizerOptions().SetMinimizerType(actualFirstMinimizer);
1475 }
1476
1477 if (_progress && printLevel >= -2) {
1478 delete _nll;
1479 }
1480 }
1481
1482 if (out && out->status() == 0 && minos) {
1483 // call minos if requested on any parameters
1484 for (auto label : {"xminos", "xMinos"}) {
1485 std::unique_ptr<RooAbsCollection> pars(floatPars->selectByAttrib(label, true));
1486 for (auto p : *pars) {
1487 Info("minimize", "Computing xminos error for %s", p->GetName());
1488 xRooFit::minos(nll, *out, p->GetName(), myFitConfig);
1489 }
1490 if (!pars->empty())
1491 *floatPars = out->floatParsFinal(); // put values back to best fit
1492 }
1493 }
1494
1495 if (restore) {
1496 *floatPars = out->floatParsInit();
1497 }
1498
1499 if (out && !logs.empty()) {
1500 // save logs to StringVar in constPars list
1501#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
1502 const_cast<RooArgList &>(out->constPars()).addOwned(std::make_unique<RooStringVar>(".log", "log", logs.c_str()));
1503#else
1504 const_cast<RooArgList &>(out->constPars()).addOwned(*new RooStringVar(".log", "log", logs.c_str()));
1505#endif
1506 }
1507
1508 if (out && cacheDir && cacheDir->IsWritable()) {
1509 // std::cout << "Saving " << out->GetName() << " " << out->GetTitle() << " to " << nll.GetName() << std::endl;
1510 // save a copy of fit result to relevant dir
1511 if (!cacheDir->GetDirectory(nll.GetName()))
1512 cacheDir->mkdir(nll.GetName());
1513 if (auto dir = cacheDir->GetDirectory(nll.GetName()); dir) {
1514 // save NLL opts if was given one, unless already present
1515 if (nllOpts) {
1516 if (strlen(nllOpts->GetName()) == 0) {
1517 nllOpts->SetName(TUUID().AsString());
1518 }
1519 if (!dir->FindKey(nllOpts->GetName())) {
1520 dir->WriteObject(nllOpts.get(), nllOpts->GetName());
1521 }
1522 }
1523
1524 // also save the fitConfig ... unless one with same name already present
1525 std::string configName;
1526 if (!fitConfig.MinimizerOptions().ExtraOptions()->GetValue("Name", configName)) {
1527 auto extraOpts = const_cast<ROOT::Math::IOptions *>(fitConfig.MinimizerOptions().ExtraOptions());
1529 extraOpts->SetValue("Name", configName.data());
1530 }
1531 if (!dir->GetKey(configName.data())) {
1532 dir->WriteObject(&fitConfig, configName.data());
1533 }
1534 // add the fitConfig name into the fit result before writing, so can retrieve in future
1535#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
1536 const_cast<RooArgList &>(out->constPars())
1537 .addOwned(std::make_unique<RooStringVar>(".fitConfigName", "fitConfigName", configName.c_str()));
1538#else
1539 const_cast<RooArgList &>(out->constPars())
1540 .addOwned(*new RooStringVar(".fitConfigName", "fitConfigName", configName.c_str()));
1541#endif
1542 dir->WriteObject(out.get(), out->GetName());
1543 auto sfr = new StoredFitResult(out);
1544 dir->Add(sfr);
1545 return sfr->fr;
1546 // return std::shared_ptr<const RooFitResult>(out, [](const RooFitResult*){}); // disowned shared_ptr
1547 }
1548 }
1549
1550 return out;
1551}
1552
1553// calculate asymmetric errors, if required, on the named parameter that was floating in the fit
1554// returns status code. 0 = all good, 1 = failure, ...
1555int xRooFit::minos(RooAbsReal &nll, const RooFitResult &ufit, const char *parName,
1556 const std::shared_ptr<ROOT::Fit::FitConfig> &_fitConfig)
1557{
1558
1559 auto par = dynamic_cast<RooRealVar *>(std::unique_ptr<RooArgSet>(nll.getVariables())->find(parName));
1560 if (!par)
1561 return 1;
1562
1563 auto par_hat = dynamic_cast<RooRealVar *>(ufit.floatParsFinal().find(parName));
1564 if (!par_hat)
1565 return 1;
1566
1568 auto &fitConfig = *myFitConfig;
1569
1570 bool pErrs = fitConfig.ParabErrors();
1571 fitConfig.SetParabErrors(false);
1572 double mErrs = fitConfig.MinosErrors();
1573 fitConfig.SetMinosErrors(false);
1574
1575 double val_best = par_hat->getVal();
1576 double val_err = (par_hat->hasError() ? par_hat->getError() : -1);
1577 double orig_err = val_err;
1578 double nll_min = ufit.minNll();
1579
1580 int status = 0;
1581
1582 bool isConst = par->isConstant();
1583 par->setConstant(true);
1584
1585 auto findValue = [&](double val_guess, double N_sigma = 1, double precision = 0.002, int printLevel = 0) {
1586 double tmu;
1587 int nrItr = 0;
1588 double sigma_guess = std::abs((val_guess - val_best) / N_sigma);
1589 double val_pre =
1590 val_guess -
1591 10 * precision * sigma_guess; // this is just to set value st. guarantees will do at least one iteration
1592 bool lastOverflow = false;
1593 bool lastUnderflow = false;
1594 while (std::abs(val_pre - val_guess) > precision * sigma_guess) {
1596 if (val_guess > 0 && par->getMax() < val_guess)
1597 par->setMax(2 * val_guess);
1598 if (val_guess < 0 && par->getMin() > val_guess)
1599 par->setMin(2 * val_guess);
1600 par->setVal(val_guess);
1601 // std::cout << "Guessing " << val_guess << std::endl;
1603 if (!result) {
1604 status = 1;
1605 return std::numeric_limits<double>::quiet_NaN();
1606 }
1607 double nll_val = result->minNll();
1608 status += result->status() * 10;
1609 tmu = 2 * (nll_val - nll_min);
1610 sigma_guess = std::abs(val_guess - val_best) / sqrt(tmu);
1611
1612 if (tmu <= 0) {
1613 // found an alternative or improved minima
1614 std::cout << "Warning: Alternative best-fit of " << par->GetName() << " @ " << val_guess << " vs "
1615 << val_best << " (delta=" << tmu / 2. << ")" << std::endl;
1616 double new_guess = val_guess + (val_guess - val_best);
1619 sigma_guess = std::abs((val_guess - val_best) / N_sigma);
1620 val_pre = val_guess - 10 * precision * sigma_guess;
1621 status = (status / 10) * 10 + 1;
1622 continue;
1623 }
1624
1625 double corr = /*damping_factor**/ (val_pre - val_best - N_sigma * sigma_guess);
1626
1627 // subtract off the difference in the new and damped correction
1628 val_guess -= corr;
1629
1630 if (printLevel > 1) {
1631 // cout << "nPars: " << nPars << std::endl;
1632 // cout << "NLL: " << nll->GetName() << " = " << nll->getVal() << endl;
1633 // cout << "delta(NLL): " << nll->getVal()-nll_min << endl;
1634 std::cout << "NLL min: " << nll_min << std::endl;
1635 std::cout << "N_sigma*sigma(pre): " << std::abs(val_pre - val_best) << std::endl;
1636 std::cout << "sigma(guess): " << sigma_guess << std::endl;
1637 std::cout << "par(guess): " << val_guess + corr << std::endl;
1638 std::cout << "true val: " << val_best << std::endl;
1639 std::cout << "tmu: " << tmu << std::endl;
1640 std::cout << "Precision: " << sigma_guess * precision << std::endl;
1641 std::cout << "Correction: " << (-corr < 0 ? " " : "") << -corr << std::endl;
1642 std::cout << "N_sigma*sigma(guess): " << std::abs(val_guess - val_best) << std::endl;
1643 std::cout << std::endl;
1644 }
1645 if (val_guess > par->getMax()) {
1646 if (lastOverflow) {
1647 val_guess = par->getMin();
1648 break;
1649 }
1650 lastOverflow = true;
1651 lastUnderflow = false;
1652 val_guess = par->getMax() - 1e-12;
1653 } else if (val_guess < par->getMin()) {
1654 if (lastUnderflow) {
1655 val_guess = par->getMin();
1656 break;
1657 }
1658 lastOverflow = false;
1659 lastUnderflow = true;
1660 val_guess = par->getMin() + 1e-12;
1661 } else {
1662 lastUnderflow = false;
1663 lastOverflow = false;
1664 }
1665
1666 nrItr++;
1667 if (nrItr > 25) {
1668 status = (status / 10) * 10 + 3;
1669 break;
1670 }
1671 }
1672
1673 if (lastOverflow) {
1674 // msg().Error("findSigma","%s at upper limit of %g .. error may be underestimated
1675 // (t=%g)",par->GetName(),par->getMax(),tmu);
1676 status = (status / 10) * 10 + 2;
1677 } else if (lastUnderflow) {
1678 // msg().Error("findSigma","%s at lower limit of %g .. error may be underestimated
1679 // (t=%g)",par->GetName(),par->getMin(),tmu);
1680 status = (status / 10) * 10 + 2;
1681 }
1682
1683 if (printLevel > 1)
1684 std::cout << "Found sigma for nll " << nll.GetName() << ": " << (val_guess - val_best) / N_sigma << std::endl;
1685 if (printLevel > 1)
1686 std::cout << "Finished in " << nrItr << " iterations." << std::endl;
1687 if (printLevel > 1)
1688 std::cout << std::endl;
1689 return (val_guess - val_best) / N_sigma;
1690 };
1691
1692 // determine if asym error defined by temporarily setting error to nan ... will then return non-nan if defined
1693
1694 par_hat->setError(std::numeric_limits<double>::quiet_NaN());
1695 double lo = par_hat->getErrorLo();
1696 double hi = par_hat->getErrorHi();
1697 if (std::isnan(hi)) {
1699 par_hat->getVal(); // put error wrt par_hat value, even if found better min
1700 if (hi > val_err)
1701 val_err = hi; // in case val_err was severe underestimate, don't want to waste time being too 'near' min
1702 }
1703 if (std::isnan(lo)) {
1704 lo = -findValue(val_best - val_err, -1) + val_best -
1705 par_hat->getVal(); // put error wrt par_hat value, even if found better min
1706 }
1707 dynamic_cast<RooRealVar *>(ufit.floatParsFinal().find(parName))->setAsymError(lo, hi);
1708 par_hat->setError(orig_err);
1709
1710 fitConfig.SetParabErrors(pErrs);
1711 fitConfig.SetMinosErrors(mErrs);
1712 par->setConstant(isConst);
1713
1714 std::vector<std::pair<std::string, int>> statusHistory;
1715 for (unsigned int i = 0; i < ufit.numStatusHistory(); i++) {
1716 statusHistory.emplace_back(ufit.statusLabelHistory(i), ufit.statusCodeHistory(i));
1717 }
1718 statusHistory.emplace_back(TString::Format("xMinos:%s", parName), status);
1719 const_cast<RooFitResult &>(ufit).setStatusHistory(statusHistory);
1720 const_cast<RooFitResult &>(ufit).setStatus(ufit.status() + status);
1721
1722 return status;
1723}
1724
1725TCanvas *
1727{
1728 TCanvas *out = nullptr;
1729
1730 // 1. Determine pdf: use top-level, if more than 1 then exit and tell user they need to flag
1731 RooAbsPdf *model = nullptr;
1732 std::deque<RooAbsArg *> topPdfs;
1733 int flagCount = 0;
1734 for (auto p : w.allPdfs()) {
1735 if (p->hasClients())
1736 continue;
1737 flagCount += p->getAttribute("hypoTest");
1738 if (p->getAttribute("hypoTest")) {
1739 topPdfs.push_front(p);
1740 } else {
1741 topPdfs.push_back(p);
1742 }
1743 }
1744 if (topPdfs.empty()) {
1745 Error("hypoTest", "Cannot find top-level pdf in workspace");
1746 return nullptr;
1747 } else if (topPdfs.size() > 1) {
1748 // should be one flagged
1749 if (flagCount == 0) {
1750 Error("hypoTest", "Multiple top-level pdfs. Flag which one to test with "
1751 "w->pdf(\"pdfName\")->setAttribute(\"hypoTest\",true)");
1752 return out;
1753 } else if (flagCount != 1) {
1754 Error("hypoTest", "Multiple top-level pdfs flagged for hypoTest -- pick one.");
1755 return out;
1756 }
1757 }
1758 model = dynamic_cast<RooAbsPdf *>(topPdfs.front());
1759
1760 Info("hypoTest", "Using PDF: %s", model->GetName());
1761
1762 double CL = 0.95; // TODO: make configurable
1763
1764 // 2. Determine the data (including globs). if more than 1 then exit and tell user they need to flag
1765 RooAbsData *obsData = nullptr;
1766 std::shared_ptr<RooArgSet> obsGlobs = nullptr;
1767
1768 for (auto p : w.allData()) {
1769 if (obsData) {
1770 Error("hypoTest", "Multiple datasets in workspace. Flag which one to test with "
1771 "w->data(\"dataName\")->setAttribute(\"hypoTest\",true)");
1772 return out;
1773 }
1774 obsData = p;
1775 }
1776
1777 if (!obsData) {
1778 Error("hypoTest", "No data -- cannot determine observables");
1779 return nullptr;
1780 }
1781
1782 Info("hypoTest", "Using Dataset: %s", obsData->GetName());
1783
1784 {
1785 auto _globs = xRooNode(w).datasets()[obsData->GetName()]->globs(); // keep alive because may own the globs
1786 obsGlobs = std::make_shared<RooArgSet>();
1787 obsGlobs->addClone(_globs.argList());
1788 Info("hypoTest", "Using Globs: %s", (obsGlobs->empty()) ? " <NONE>" : obsGlobs->contentsString().c_str());
1789 }
1790
1791 // 3. Determine the POI and args - look for model pars with "hypoPoints" binning, if none then cannot scan
1792 // args are const, poi are floating - exception is if only one then assume it is the POI
1793 auto _vars = std::unique_ptr<RooArgSet>(model->getVariables());
1794 RooArgSet poi;
1795 RooArgSet args;
1796 for (auto _v : *_vars) {
1797 if (auto v = dynamic_cast<RooRealVar *>(_v); v && v->hasBinning("hypoPoints")) {
1798 poi.add(*v);
1799 }
1800 }
1801 if (poi.size() > 1) {
1802 auto _const = std::unique_ptr<RooAbsCollection>(poi.selectByAttrib("Constant", true));
1803 args.add(*_const);
1804 poi.remove(*_const);
1805 }
1806 if (!args.empty()) {
1807 Info("hypoTest", "Using Arguments: %s", args.contentsString().c_str());
1808 }
1809 if (poi.empty()) {
1810 Error("hypoTest", "No POI detected: add the hypoPoints binning to at least one non-const model parameter e.g.:\n "
1811 "w->var(\"mu\")->setBinning(RooUniformBinning(0.5,10.5,10),\"hypoPoints\"))");
1812 return nullptr;
1813 }
1814
1815 Info("hypoTest", "Using Parameters of Interest: %s", poi.contentsString().c_str());
1816
1817 out = TCanvas::MakeDefCanvas();
1818
1819 // should check if exist in workspace
1820 auto nllOpts = createNLLOptions();
1821 auto fitConfig = createFitConfig();
1822
1823 xRooNLLVar nll(*model, std::make_pair(obsData, obsGlobs.get()), *nllOpts);
1824 nll.SetFitConfig(fitConfig);
1825
1826 if (poi.size() == 1) {
1827 auto mu = dynamic_cast<RooRealVar *>(poi.first());
1828
1829 double altVal = (mu->getStringAttribute("altVal")) ? TString(mu->getStringAttribute("altVal")).Atof()
1830 : std::numeric_limits<double>::quiet_NaN();
1831
1832 if (std::isnan(altVal) && mu->hasRange("physical")) {
1833 // use the smallest absolute value for the altValue
1834 altVal = mu->getMin("physical");
1835 Info("hypoTest", "No altVal specified - using min of given physical range = %g", altVal);
1836 } else {
1837 if (!std::isnan(altVal)) {
1838 Info("hypoTest", "alt hypo: %g - CLs activated", altVal);
1839 } else {
1840 Info("hypoTest", "No altVal found - to specify setStringAttribute(\"altVal\",\"<value>\") on POI or set "
1841 "the physical range");
1842 }
1843 }
1844 bool doCLs = !std::isnan(altVal) && std::abs(mu->getMin("hypoPoints")) > altVal &&
1845 std::abs(mu->getMax("hypoPoints")) > altVal;
1846
1847 const char *sCL = (doCLs) ? "CLs" : "null";
1848 Info("hypoTest", "%s testing active", sCL);
1849
1850 auto obs_ts = new TGraphErrors;
1851 obs_ts->SetNameTitle("obs_ts", TString::Format("Observed TestStat;%s", mu->GetTitle()));
1852 auto obs_pcls = new TGraphErrors;
1853 obs_pcls->SetNameTitle(TString::Format("obs_p%s", sCL),
1854 TString::Format("Observed p_{%s};%s", sCL, mu->GetTitle()));
1855 auto obs_cls = new TGraphErrors;
1856 obs_cls->SetNameTitle(TString::Format("obs_%s", sCL), TString::Format("Observed %s;%s", sCL, mu->GetTitle()));
1857
1858 std::vector<int> expSig = {-2, -1, 0, 1, 2};
1859 if (std::isnan(altVal))
1860 expSig.clear();
1861 std::map<int, TGraphErrors> exp_pcls;
1862 std::map<int, TGraphErrors> exp_cls;
1863 for (auto &s : expSig) {
1864 exp_pcls[s].SetNameTitle(TString::Format("exp%d_p%s", s, sCL),
1865 TString::Format("Expected (%d#sigma) p_{%s};%s", s, sCL, mu->GetTitle()));
1866 exp_cls[s].SetNameTitle(TString::Format("exp%d_%s", s, sCL),
1867 TString::Format("Expected (%d#sigma) %s;%s", s, sCL, mu->GetTitle()));
1868 }
1869
1870 auto getLimit = [CL](TGraphErrors &pValues) {
1871 double _out = std::numeric_limits<double>::quiet_NaN();
1872 bool lastAbove = false;
1873 for (int i = 0; i < pValues.GetN(); i++) {
1874 bool thisAbove = pValues.GetPointY(i) >= (1. - CL);
1875 if (i != 0 && thisAbove != lastAbove) {
1876 // crossed over ... find limit by interpolation
1877 // using linear interpolation so far
1878 _out = pValues.GetPointX(i - 1) + (pValues.GetPointX(i) - pValues.GetPointX(i - 1)) *
1879 ((1. - CL) - pValues.GetPointY(i - 1)) /
1880 (pValues.GetPointY(i) - pValues.GetPointY(i - 1));
1881 }
1883 }
1884 return _out;
1885 };
1886
1887 auto testPoint = [&](double testVal) {
1888 auto hp = nll.hypoPoint(mu->GetName(), testVal, altVal, pllType);
1889 obs_ts->SetPoint(obs_ts->GetN(), testVal, hp.pll().first);
1890 obs_ts->SetPointError(obs_ts->GetN() - 1, 0, hp.pll().second);
1891
1892 if (nToysNull > 0) {
1893 }
1894
1895 obs_pcls->SetPoint(obs_pcls->GetN(), testVal, (doCLs) ? hp.pCLs_asymp().first : hp.pNull_asymp().first);
1896 obs_pcls->SetPointError(obs_pcls->GetN() - 1, 0, (doCLs) ? hp.pCLs_asymp().second : hp.pNull_asymp().second);
1897 for (auto &s : expSig) {
1898 exp_pcls[s].SetPoint(exp_pcls[s].GetN(), testVal,
1899 (doCLs) ? hp.pCLs_asymp(s).first : hp.pNull_asymp(s).first);
1900 }
1901 if (doCLs) {
1902 Info("hypoTest", "%s=%g: %s=%g sigma_mu=%g %s=%g", mu->GetName(), testVal, obs_ts->GetName(),
1903 obs_ts->GetPointY(obs_ts->GetN() - 1), hp.sigma_mu().first, obs_pcls->GetName(),
1904 obs_pcls->GetPointY(obs_pcls->GetN() - 1));
1905 } else {
1906 Info("hypoTest", "%s=%g: %s=%g %s=%g", mu->GetName(), testVal, obs_ts->GetName(),
1907 obs_ts->GetPointY(obs_ts->GetN() - 1), obs_pcls->GetName(), obs_pcls->GetPointY(obs_pcls->GetN() - 1));
1908 }
1909 };
1910
1911 if (mu->getBins("hypoPoints") <= 0) {
1912 // autoTesting
1913 // evaluate min and max points
1914 testPoint(mu->getMin("hypoPoints"));
1915 testPoint(mu->getMax("hypoPoints"));
1916 testPoint((mu->getMax("hypoPoints") + mu->getMin("hypoPoints")) / 2.);
1917
1918 while (std::abs(obs_pcls->GetPointY(obs_pcls->GetN() - 1) - (1. - CL)) > 0.01) {
1919 obs_pcls->Sort();
1920 double nextTest = getLimit(*obs_pcls);
1921 if (std::isnan(nextTest))
1922 break;
1924 }
1925 for (auto s : expSig) {
1926 while (std::abs(exp_pcls[s].GetPointY(exp_pcls[s].GetN() - 1) - (1. - CL)) > 0.01) {
1927 exp_pcls[s].Sort();
1928 double nextTest = getLimit(exp_pcls[s]);
1929 if (std::isnan(nextTest))
1930 break;
1932 }
1933 }
1934 obs_ts->Sort();
1935 obs_pcls->Sort();
1936 for (auto &s : expSig)
1937 exp_pcls[s].Sort();
1938
1939 } else {
1940 for (int i = 0; i <= mu->getBins("hypoPoints"); i++) {
1941 testPoint((i == mu->getBins("hypoPoints")) ? mu->getBinning("hypoPoints").binHigh(i - 1)
1942 : mu->getBinning("hypoPoints").binLow(i));
1943 }
1944 }
1945
1946 obs_cls->SetPoint(obs_cls->GetN(), getLimit(*obs_pcls), 0.05);
1947 for (auto &s : expSig) {
1948 exp_cls[s].SetPoint(exp_cls[s].GetN(), getLimit(exp_pcls[s]), 0.05);
1949 }
1950
1951 // if more than two hypoPoints, visualize as bands
1952 if (exp_pcls[2].GetN() > 1) {
1953 TGraph *band2 = new TGraph;
1954 band2->SetNameTitle(".pCLs_2sigma", "2 sigma band");
1955 TGraph *band2up = new TGraph;
1956 band2up->SetNameTitle(".pCLs_2sigma_upUncert", "");
1957 TGraph *band2down = new TGraph;
1958 band2down->SetNameTitle(".pCLs_2sigma_downUncert", "");
1959 band2->SetFillColor(kYellow);
1960 band2up->SetFillColor(kYellow);
1961 band2down->SetFillColor(kYellow);
1962 band2up->SetFillStyle(3005);
1963 band2down->SetFillStyle(3005);
1964 for (int i = 0; i < exp_pcls[2].GetN(); i++) {
1965 band2->SetPoint(band2->GetN(), exp_pcls[2].GetPointX(i),
1966 exp_pcls[2].GetPointY(i) - exp_pcls[2].GetErrorYlow(i));
1967 band2up->SetPoint(band2up->GetN(), exp_pcls[2].GetPointX(i),
1968 exp_pcls[2].GetPointY(i) + exp_pcls[2].GetErrorYhigh(i));
1969 }
1970 for (int i = exp_pcls[2].GetN() - 1; i >= 0; i--) {
1971 band2up->SetPoint(band2up->GetN(), exp_pcls[2].GetPointX(i),
1972 exp_pcls[2].GetPointY(i) - exp_pcls[2].GetErrorYlow(i));
1973 }
1974 for (int i = 0; i < exp_pcls[-2].GetN(); i++) {
1975 band2down->SetPoint(band2down->GetN(), exp_pcls[-2].GetPointX(i),
1976 exp_pcls[-2].GetPointY(i) + exp_pcls[-2].GetErrorYhigh(i));
1977 }
1978 for (int i = exp_pcls[-2].GetN() - 1; i >= 0; i--) {
1979 band2->SetPoint(band2->GetN(), exp_pcls[-2].GetPointX(i),
1980 exp_pcls[-2].GetPointY(i) + exp_pcls[-2].GetErrorYhigh(i));
1981 band2down->SetPoint(band2down->GetN(), exp_pcls[-2].GetPointX(i),
1982 exp_pcls[-2].GetPointY(i) - exp_pcls[-2].GetErrorYlow(i));
1983 }
1984 band2->SetBit(kCanDelete);
1985 band2up->SetBit(kCanDelete);
1986 band2down->SetBit(kCanDelete);
1987 auto ax = static_cast<TNamed *>(band2->Clone(".axis"));
1988 ax->SetTitle(TString::Format("Hypothesis Test;%s", mu->GetTitle()));
1989 ax->Draw("AF");
1990 band2->Draw("F");
1991 band2up->Draw("F");
1992 band2down->Draw("F");
1993 }
1994
1995 if (exp_pcls[1].GetN() > 1) {
1996 TGraph *band2 = new TGraph;
1997 band2->SetNameTitle(".pCLs_1sigma", "1 sigma band");
1998 TGraph *band2up = new TGraph;
1999 band2up->SetNameTitle(".pCLs_1sigma_upUncert", "");
2000 TGraph *band2down = new TGraph;
2001 band2down->SetNameTitle(".pCLs_1sigma_downUncert", "");
2002 band2->SetFillColor(kGreen);
2003 band2up->SetFillColor(kGreen);
2004 band2down->SetFillColor(kGreen);
2005 band2up->SetFillStyle(3005);
2006 band2down->SetFillStyle(3005);
2007 for (int i = 0; i < exp_pcls[1].GetN(); i++) {
2008 band2->SetPoint(band2->GetN(), exp_pcls[1].GetPointX(i),
2009 exp_pcls[1].GetPointY(i) - exp_pcls[1].GetErrorYlow(i));
2010 band2up->SetPoint(band2up->GetN(), exp_pcls[1].GetPointX(i),
2011 exp_pcls[1].GetPointY(i) + exp_pcls[1].GetErrorYhigh(i));
2012 }
2013 for (int i = exp_pcls[1].GetN() - 1; i >= 0; i--) {
2014 band2up->SetPoint(band2up->GetN(), exp_pcls[1].GetPointX(i),
2015 exp_pcls[1].GetPointY(i) - exp_pcls[1].GetErrorYlow(i));
2016 }
2017 for (int i = 0; i < exp_pcls[-1].GetN(); i++) {
2018 band2down->SetPoint(band2down->GetN(), exp_pcls[-1].GetPointX(i),
2019 exp_pcls[-1].GetPointY(i) + exp_pcls[-1].GetErrorYhigh(i));
2020 }
2021 for (int i = exp_pcls[-1].GetN() - 1; i >= 0; i--) {
2022 band2->SetPoint(band2->GetN(), exp_pcls[-1].GetPointX(i),
2023 exp_pcls[-1].GetPointY(i) + exp_pcls[-1].GetErrorYhigh(i));
2024 band2down->SetPoint(band2down->GetN(), exp_pcls[-1].GetPointX(i),
2025 exp_pcls[-1].GetPointY(i) - exp_pcls[-1].GetErrorYlow(i));
2026 }
2027 band2->SetBit(kCanDelete);
2028 band2up->SetBit(kCanDelete);
2029 band2down->SetBit(kCanDelete);
2030 band2->Draw("F");
2031 band2up->Draw("F");
2032 band2down->Draw("F");
2033 }
2034
2035 TObject *expPlot = nullptr;
2036 if (exp_cls[0].GetN() > 0) {
2037 exp_pcls[0].SetLineStyle(2);
2038 exp_pcls[0].SetFillColor(kGreen);
2039 exp_pcls[0].SetMarkerStyle(0);
2040 expPlot = exp_pcls[0].DrawClone("L");
2041 }
2042 obs_pcls->SetBit(kCanDelete);
2043 obs_pcls->Draw(gPad->GetListOfPrimitives()->IsEmpty() ? "ALP" : "LP");
2044
2045 obs_ts->SetLineColor(kRed);
2046 obs_ts->SetMarkerColor(kRed);
2047 obs_ts->SetBit(kCanDelete);
2048 obs_ts->Draw("LP");
2049
2050 auto l = new TLegend(0.5, 0.6, 1. - gPad->GetRightMargin(), 1. - gPad->GetTopMargin());
2051 l->SetName("legend");
2052 l->AddEntry(obs_ts, obs_ts->GetTitle(), "LPE");
2053 l->AddEntry(obs_pcls, obs_pcls->GetTitle(), "LPE");
2054 if (expPlot)
2055 l->AddEntry(expPlot, "Expected", "LFE");
2057 l->Draw();
2058
2059 obs_cls->SetMarkerStyle(29);
2060 obs_cls->SetEditable(false);
2061 obs_cls->Draw("LP");
2062 for (auto s : expSig) {
2063 exp_cls[s].SetMarkerStyle(29);
2064 exp_cls[s].SetEditable(false);
2065 exp_cls[s].DrawClone("LP");
2066 }
2067 }
2068
2069 if (out)
2070 out->RedrawAxis();
2071
2072 return out;
2073}
2074
2075double round_to_digits(double value, int digits)
2076{
2077 if (value == 0.0)
2078 return 0.0;
2079 double factor = pow(10.0, digits - ceil(log10(std::abs(value))));
2080 return std::round(value * factor) / factor;
2081}
2083{
2084 const double multiplier = std::pow(10.0, decimal_places);
2085 return std::round(value * multiplier) / multiplier;
2086}
2087
2088// rounds error to 1 or 2 sig fig and round value to match that precision
2089std::pair<double, double> xRooFit::matchPrecision(const std::pair<double, double> &in)
2090{
2091 auto out = in;
2092 if (!std::isinf(out.second)) {
2093 auto tmp = out.second;
2094 out.second = round_to_digits(out.second, 2);
2095 int expo = (out.second == 0) ? 0 : (int)std::floor(std::log10(std::abs(out.second)));
2096 if (TString::Format("%e", out.second)(0) != '1') {
2097 out.second = round_to_digits(tmp, 1);
2098 out.first = (expo >= 0) ? round(out.first) : round_to_decimal(out.first, -expo);
2099 } else if (out.second != 0) {
2100 out.first = (expo >= 0) ? round(out.first) : round_to_decimal(out.first, -expo + 1);
2101 }
2102 }
2103 return out;
2104}
2105
#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:145
#define hi
@ kCanDelete
Definition TObject.h:375
#define gROOT
Definition TROOT.h:426
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2496
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#define gPad
double getValV(const RooArgSet *) const override
Return value of object.
Definition xRooFit.cxx:620
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
Definition xRooFit.cxx:631
std::string fState
Definition xRooFit.cxx:726
static bool fInterrupt
Definition xRooFit.cxx:576
bool setData(RooAbsData &data, bool cloneData) override
Definition xRooFit.cxx:619
ProgressMonitor(const ProgressMonitor &other, const char *name=nullptr)
Definition xRooFit.cxx:607
~ProgressMonitor() override
Definition xRooFit.cxx:599
TStopwatch s
Definition xRooFit.cxx:739
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Interface for detailed printing of object.
Definition xRooFit.cxx:622
TObject * clone(const char *newname) const override
Definition xRooFit.cxx:611
std::shared_ptr< RooAbsCollection > vars
Definition xRooFit.cxx:740
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:615
RooArgList minPars
Definition xRooFit.cxx:735
RooRealProxy fFunc
Definition xRooFit.cxx:734
void applyWeightSquared(bool flag) override
Disables or enables the usage of squared weights.
Definition xRooFit.cxx:621
void constOptimizeTestStatistic(ConstOpCode opcode, bool doAlsoTrackingOpt) override
Interface function signaling a request to perform constant term optimization.
Definition xRooFit.cxx:626
ProgressMonitor(RooAbsReal &f, int interval=30)
Definition xRooFit.cxx:588
static ProgressMonitor * me
Definition xRooFit.cxx:575
double defaultErrorLevel() const override
Definition xRooFit.cxx:614
RooArgList prevPars
Definition xRooFit.cxx:736
static void interruptHandler(int signum)
Definition xRooFit.cxx:577
void(* oldHandlerr)(int)
Definition xRooFit.cxx:574
static int minos(RooAbsReal &nll, const RooFitResult &ufit, const char *parName="", const std::shared_ptr< ROOT::Fit::FitConfig > &_fitConfig=nullptr)
Definition xRooFit.cxx:1555
static std::shared_ptr< const RooFitResult > minimize(RooAbsReal &nll, const std::shared_ptr< ROOT::Fit::FitConfig > &fitConfig=nullptr, const std::shared_ptr< RooLinkedList > &nllOpts=nullptr)
Definition xRooFit.cxx:752
static std::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:490
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2089
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:659
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:491
virtual void constOptimizeTestStatistic(ConstOpCode opcode, bool doAlsoTrackingOpt=true)
Interface function signaling a request to perform constant term optimization.
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:56
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:272
virtual bool setData(RooAbsData &, bool=true)
Definition RooAbsReal.h:391
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.
void optimizeConst(int flag) R__DEPRECATED(6
If flag is true, perform constant term optimization on function being minimized.
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:19
static TClass * Class()
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:36
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:1509
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:2994
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:2462
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:265
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:885
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:290
void RedrawAxis(Option_t *option="") override
Redraw the frame axis.
Definition TPad.cxx:5430
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:2385
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:641
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 Optimize(Int_t flag=2) R__DEPRECATED(6
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:452
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:2082
void printCout(const char *msg)
Definition xRooFit.cxx:560
void printCerr(const char *msg)
Definition xRooFit.cxx:549
double round_to_digits(double value, int digits)
Definition xRooFit.cxx:2075