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