Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
AsymptoticCalculator.cxx
Go to the documentation of this file.
1// @(#)root/roostats:$Id$
2// Author: Kyle Cranmer, Sven Kreiss 23/05/10
3/*************************************************************************
4 * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11/** \class RooStats::AsymptoticCalculator
12 \ingroup Roostats
13
14Hypothesis Test Calculator based on the asymptotic formulae for the profile
15likelihood ratio.
16
17It performs hypothesis tests using the asymptotic formula for the profile likelihood, and
18uses the Asimov data set to compute expected significances or limits.
19
20See G. Cowan, K. Cranmer, E. Gross and O. Vitells: Asymptotic formulae for
21likelihood- based tests of new physics. Eur. Phys. J., C71:1–19, 2011.
22It provides methods to perform hypothesis tests using the likelihood function,
23and computes the \f$p\f$-values for the null and the alternate hypothesis using the asymptotic
24formulae for the profile likelihood ratio described in the given paper.
25
26The calculator provides methods to produce the Asimov dataset, *i.e.* a dataset
27generated where the observed values are equal to the expected ones.
28The Asimov data set is then used to compute the observed asymptotic \f$p\f$-value for
29the alternate hypothesis and the asymptotic expected \f$p\f$-values.
30
31The asymptotic formulae are valid only for one POI (parameter of interest). So
32the calculator works only for one-dimensional (one POI) models.
33If more than one POI exists, only the first one is used.
34
35The calculator can generate Asimov datasets from two kinds of PDFs:
36- "Counting" distributions: RooPoisson, RooGaussian, or products of RooPoissons.
37- Extended, *i.e.* number of events can be read off from extended likelihood term.
38
39The fits performed by the calculator can be steered with the global default
40minimizer options, *e.g.* via ROOT::Math::MinimizerOptions::SetDefaultStrategy()
41and ROOT::Math::MinimizerOptions::SetDefaultTolerance() (the tolerance is
42clamped to a minimum value of 1). The RooFitResult objects of the fits are
43retrievable after calling GetHypoTest() via GetFitResultUncondObs(),
44GetFitResultCondObs(), GetFitResultUncondAsimov() and GetFitResultCondAsimov(),
45so quantities like the minimizer status or the EDM at the minimum can be
46inspected, for example to cross-check a fit that did not converge. Note that
47when the calculator is driven by the HypoTestInverter, the stored conditional
48fit results correspond to the last scanned point.
49*/
50
55
56#include "RooArgSet.h"
57#include "RooArgList.h"
58#include "RooProdPdf.h"
59#include "RooSimultaneous.h"
60#include "RooDataSet.h"
61#include "RooCategory.h"
62#include "RooRealVar.h"
63#include "RooMinimizer.h"
64#include "RooFitResult.h"
66#include "RooPoisson.h"
67#include "RooUniform.h"
68#include "RooGamma.h"
69#include "RooGaussian.h"
70#include "RooMultiVarGaussian.h"
71#include "RooBifurGauss.h"
72#include "RooLognormal.h"
73#include "RooDataHist.h"
74#include <cmath>
75#include <typeinfo>
76
79
80#include "TStopwatch.h"
81
82using namespace RooStats;
83using std::string, std::unique_ptr;
84
85
86namespace {
87
88/// Control print level (0 minimal, 1 normal, 2 debug).
89int &fgPrintLevel()
90{
91
92 static int val = 1;
93 return val;
94}
95
96// Forward declaration.
97double EvaluateNLL(RooStats::ModelConfig const &modelConfig, RooAbsData &data, const RooArgSet *poiSet = nullptr,
98 std::unique_ptr<RooFitResult> *fitResult = nullptr);
99
100} // namespace
101
102////////////////////////////////////////////////////////////////////////////////
103/// set print level (static function)
104///
105/// - 0 minimal,
106/// - 1 normal,
107/// - 2 debug
108
110 fgPrintLevel() = level;
111}
112
113////////////////////////////////////////////////////////////////////////////////
114/// constructor for asymptotic calculator from Data set and ModelConfig
115
118 const ModelConfig &altModel,
119 const ModelConfig &nullModel, bool nominalAsimov) :
120 HypoTestCalculatorGeneric(data, altModel, nullModel, nullptr),
121 fOneSided(false), fOneSidedDiscovery(false), fNominalAsimov(nominalAsimov),
122 fUseQTilde(-1),
123 fNLLObs(0), fNLLAsimov(0),
124 fAsimovData(nullptr)
125{
126 if (!Initialize()) return;
127
128 int verbose = fgPrintLevel();
129 // try to guess default configuration
130 // (this part should be only in constructor because the null snapshot might change during HypoTestInversion
131 const RooArgSet * nullSnapshot = GetNullModel()->GetSnapshot();
132 assert(nullSnapshot);
133 RooRealVar * muNull = dynamic_cast<RooRealVar*>(nullSnapshot->first() );
134 assert(muNull);
135 if (muNull->getVal() == muNull->getMin()) {
136 fOneSidedDiscovery = true;
137 if (verbose > 0)
138 oocoutI(nullptr,InputArguments) << "AsymptotiCalculator: Minimum of POI is " << muNull->getMin() << " corresponds to null snapshot - default configuration is one-sided discovery formulae " << std::endl;
139 }
140
141}
142
143////////////////////////////////////////////////////////////////////////////////
144/// Initialize the calculator
145/// The initialization will perform a global fit of the model to the data
146/// and build an Asimov data set.
147/// It will then also fit the model to the Asimov data set to find the likelihood value
148/// of the Asimov data set
149/// nominalAsimov is an option for using Asimov data set obtained using nominal nuisance parameter values
150/// By default the nuisance parameters are fitted to the data
151/// NOTE: If a fit has been done before, one for speeding up could set all the initial parameters
152/// to the fit value and in addition set the null snapshot to the best fit
153
155
156 int verbose = fgPrintLevel();
157 if (verbose >= 0)
158 oocoutP(nullptr,Eval) << "AsymptoticCalculator::Initialize...." << std::endl;
159
160
161 RooAbsPdf * nullPdf = GetNullModel()->GetPdf();
162 if (!nullPdf) {
163 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator::Initialize - ModelConfig has not a pdf defined" << std::endl;
164 return false;
165 }
166 RooAbsData * obsData = const_cast<RooAbsData *>(GetData() );
167 if (!obsData ) {
168 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator::Initialize - data set has not been defined" << std::endl;
169 return false;
170 }
171 RooAbsData & data = *obsData;
172
173
174
176 if (!poi || poi->empty()) {
177 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator::Initialize - ModelConfig has not POI defined." << std::endl;
178 return false;
179 }
180 if (poi->size() > 1) {
181 oocoutW(nullptr,InputArguments) << "AsymptoticCalculator::Initialize - ModelConfig has more than one POI defined \n\t"
182 << "The asymptotic calculator works for only one POI - consider as POI only the first parameter"
183 << std::endl;
184 }
185
186
187 // This will set the poi value to the null snapshot value in the ModelConfig
188 const RooArgSet * nullSnapshot = GetNullModel()->GetSnapshot();
189 if(nullSnapshot == nullptr || nullSnapshot->empty()) {
190 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator::Initialize - Null model needs a snapshot. Set using modelconfig->SetSnapshot(poi)." << std::endl;
191 return false;
192 }
193
194 // GetNullModel()->Print();
195 // std::cout << "ASymptotic calc: null snapshot\n";
196 // nullSnapshot->Print("v");
197 // std::cout << "PDF variables ";
198 // nullPdf->getVariables()->Print("v");
199
200 // keep snapshot for the initial parameter values (need for nominal Asimov)
201 RooArgSet nominalParams;
202 std::unique_ptr<RooArgSet> allParams{nullPdf->getParameters(data)};
203 RemoveConstantParameters(&*allParams);
204 if (fNominalAsimov) {
205 allParams->snapshot(nominalParams);
206 }
210 fFitResultUncondObs.reset();
211 fFitResultCondObs.reset();
213 fFitResultCondAsimov.reset();
214
215 // evaluate the unconditional nll for the full model on the observed data
216 if (verbose >= 0)
217 oocoutP(nullptr,Eval) << "AsymptoticCalculator::Initialize - Find best unconditional NLL on observed data" << std::endl;
218 fNLLObs = EvaluateNLL(*GetNullModel(), data, nullptr, &fFitResultUncondObs);
220 fFitResultUncondObs->SetName("fitResultUncondObs");
221 fFitResultUncondObs->SetTitle("Unconditional fit to observed data");
222 }
223 // fill also snapshot of best poi
224 poi->snapshot(fBestFitPoi);
225 RooRealVar * muBest = dynamic_cast<RooRealVar*>(fBestFitPoi.first());
226 assert(muBest);
227 if (verbose >= 0)
228 oocoutP(nullptr,Eval) << "Best fitted POI value = " << muBest->getVal() << " +/- " << muBest->getError() << std::endl;
229 // keep snapshot of all best fit parameters
230 allParams->snapshot(fBestFitParams);
231
232 // compute Asimov data set for the background (alt poi ) value
233 const RooArgSet * altSnapshot = GetAlternateModel()->GetSnapshot();
234 if(altSnapshot == nullptr || altSnapshot->empty()) {
235 oocoutE(nullptr,InputArguments) << "Alt (Background) model needs a snapshot. Set using modelconfig->SetSnapshot(poi)." << std::endl;
236 return false;
237 }
238
239 RooArgSet poiAlt(*altSnapshot); // this is the poi snapshot of B (i.e. for mu=0)
240
241 oocoutP(nullptr,Eval) << "AsymptoticCalculator: Building Asimov data Set" << std::endl;
242
243 // check that in case of binned models the n number of bins of the observables are consistent
244 // with the number of bins in the observed data
245 // This number will be used for making the Asimov data set so it will be more consistent with the
246 // observed data
247 int prevBins = 0;
248 RooRealVar * xobs = nullptr;
249 if (GetNullModel()->GetObservables() && GetNullModel()->GetObservables()->size() == 1 ) {
250 xobs = static_cast<RooRealVar*>((GetNullModel()->GetObservables())->first());
251 if (data.IsA() == RooDataHist::Class() ) {
252 if (data.numEntries() != xobs->getBins() ) {
253 prevBins = xobs->getBins();
254 oocoutW(nullptr,InputArguments) << "AsymptoticCalculator: number of bins in " << xobs->GetName() << " are different than data bins "
255 << " set the same data bins " << data.numEntries() << " in range "
256 << " [ " << xobs->getMin() << " , " << xobs->getMax() << " ]" << std::endl;
257 xobs->setBins(data.numEntries());
258 }
259 }
260 }
261
262 if (!fNominalAsimov) {
263 if (verbose >= 0)
264 oocoutI(nullptr,InputArguments) << "AsymptoticCalculator: Asimov data will be generated using fitted nuisance parameter values" << std::endl;
265 std::unique_ptr<RooArgSet> tmp{static_cast<RooArgSet *>(poiAlt.snapshot())};
267 }
268
269 else {
270 // assume use current value of nuisance as nominal ones
271 if (verbose >= 0)
272 oocoutI(nullptr,InputArguments) << "AsymptoticCalculator: Asimovdata set will be generated using nominal (current) nuisance parameter values" << std::endl;
273 nominalParams.assign(poiAlt); // set poi to alt value but keep nuisance at the nominal one
275 }
276
277 if (!fAsimovData) {
278 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator: Error : Asimov data set could not be generated " << std::endl;
279 return false;
280 }
281
282 // set global observables to their Asimov values
283 RooArgSet globObs;
284 RooArgSet globObsSnapshot;
285 if (GetNullModel()->GetGlobalObservables() ) {
286 globObs.add(*GetNullModel()->GetGlobalObservables());
287 assert(globObs.size() == fAsimovGlobObs.size() );
288 // store previous snapshot value
289 globObs.snapshot(globObsSnapshot);
290 globObs.assign(fAsimovGlobObs);
291 }
292
293
294 // evaluate the likelihood. Since we use on Asimov data , conditional and unconditional values should be the same
295 // do conditional fit since is faster
296
297 RooRealVar * muAlt = static_cast<RooRealVar*>(poiAlt.first());
298 assert(muAlt);
299 if (verbose >= 0) {
300 oocoutP(nullptr, Eval)
301 << "AsymptoticCalculator::Initialize Find best conditional NLL on ASIMOV data set for given alt POI ( "
302 << muAlt->GetName() << " ) = " << muAlt->getVal() << std::endl;
303 }
304
305 fNLLAsimov = EvaluateNLL(*GetNullModel(), *fAsimovData, &poiAlt, &fFitResultUncondAsimov);
307 fFitResultUncondAsimov->SetName("fitResultUncondAsimov");
308 fFitResultUncondAsimov->SetTitle("Fit to Asimov data with POI fixed to the alt-model snapshot");
309 }
310 // for unconditional fit
311 //fNLLAsimov = EvaluateNLL( *nullPdf, *fAsimovData);
312 //poi->Print("v");
313
314 // restore previous value
315 globObs.assign(globObsSnapshot);
316
317 // restore number of bins
318 if (prevBins > 0 && xobs) xobs->setBins(prevBins);
319
320 fIsInitialized = true;
321 return true;
322}
323
324namespace {
325
326double EvaluateNLL(RooStats::ModelConfig const &modelConfig, RooAbsData &data, const RooArgSet *poiSet,
327 std::unique_ptr<RooFitResult> *fitResult)
328{
329 int verbose = fgPrintLevel();
330
331 if (fitResult)
332 fitResult->reset();
333
334 RooAbsPdf &pdf = *modelConfig.GetPdf();
335
338
339
340 std::unique_ptr<RooArgSet> allParams{pdf.getParameters(data)};
342 // add constraint terms for all non-constant parameters
343
344 // need to call constrain for RooSimultaneous until stripDisconnected problem fixed
345 auto& config = GetGlobalRooStatsConfig();
346 std::unique_ptr<RooAbsReal> nll{modelConfig.createNLL(data, RooFit::Constrain(*allParams), RooFit::Offset(config.useLikelihoodOffset))};
347
348 std::unique_ptr<RooArgSet> attachedSet{nll->getVariables()};
349
350 // if poi are specified - do a conditional fit
351 RooArgSet paramsSetConstant;
352 // support now only one POI
353 if (poiSet && !poiSet->empty()) {
354 RooRealVar * muTest = static_cast<RooRealVar*> (poiSet->first());
355 RooRealVar * poiVar = dynamic_cast<RooRealVar*>(attachedSet->find( muTest->GetName() ) );
356 if (poiVar && !poiVar->isConstant() ) {
357 poiVar->setVal( muTest->getVal() );
358 poiVar->setConstant();
359 paramsSetConstant.add(*poiVar);
360 }
361 if (poiSet->size() > 1)
362 oocoutW(nullptr,InputArguments) << "Model with more than one POI are not supported - ignore extra parameters, consider only first one" << std::endl;
363
364
365
366 // This for more than one POI (not yet supported)
367 //
368 // RooLinkedListIter it = poiSet->iterator();
369 // RooRealVar* tmpPar = nullptr, *tmpParA=nullptr;
370 // while((tmpPar = (RooRealVar*)it.Next())){
371 // tmpParA = ((RooRealVar*)attachedSet->find(tmpPar->GetName()));
372 // tmpParA->setVal( tmpPar->getVal() );
373 // if (!tmpParA->isConstant() ) {
374 // tmpParA->setConstant();
375 // paramsSetConstant.add(*tmpParA);
376 // }
377 // }
378
379 // check if there are non-const parameters so it is worth to do the minimization
380
381 }
382
383 TStopwatch tw;
384 tw.Start();
385 double val = -1;
386
387 //check if needed to skip the fit
388 RooArgSet nllParams(*attachedSet);
390 bool skipFit = (nllParams.empty());
391
392 if (skipFit) {
393 val = nll->getVal(); // just evaluate nll in conditional fits with model without nuisance params
394 } else {
395
396 int minimPrintLevel = verbose;
397
398 RooMinimizer minim(*nll);
400 minim.setStrategy( strategy);
401 minim.setEvalErrorWall(config.useEvalErrorWall);
402 // use tolerance - but never smaller than 1 (default in RooMinimizer)
404 tol = std::max(tol,1.0); // 1.0 is the minimum value used in RooMinimizer
405 minim.setEps( tol );
406 //LM: RooMinimizer.setPrintLevel has +1 offset - so subtract here -1
407 minim.setPrintLevel(minimPrintLevel-1);
408 int status = -1;
409 TString minimizer = ""; // empty string to take RooMinimizer default initially
411
412 if (verbose > 0) {
413 oocoutP(nullptr,Eval) << "AsymptoticCalculator::EvaluateNLL ........ using " << minimizer << " / " << algorithm
414 << " with strategy " << strategy << " and tolerance " << tol << std::endl;
415 }
416
417 for (int tries = 1, maxtries = 4; tries <= maxtries; ++tries) {
418 // status = minim.minimize(fMinimizer, ROOT::Math::MinimizerOptions::DefaultMinimizerAlgo().c_str());
419 status = minim.minimize(minimizer, algorithm);
420 // RooMinimizer::minimize returns -1 when the fit fails
421 if (status >= 0) {
422 break;
423 } else {
424 if (tries == 1) {
425 oocoutW(nullptr,Minimization) << " ----> Doing a re-scan first" << std::endl;
426 minim.minimize(minimizer,"Scan");
427 }
428 if (tries == 2) {
430 oocoutW(nullptr,Minimization) << " ----> trying with strategy = 1" << std::endl;
431 minim.setStrategy(1);
432 } else {
433 tries++; // skip this trial if strategy is already 1
434 }
435 }
436 if (tries == 3) {
437 oocoutW(nullptr,Minimization) << " ----> trying with improve" << std::endl;
438 minimizer = "Minuit";
439 algorithm = "migradimproved";
440 }
441 }
442 }
443
444 // save the fit result also in case of failure, so that the status of a
445 // non-converged fit can be inspected by the user
446 std::unique_ptr<RooFitResult> result{minim.save()};
447
448 // ignore errors in Hesse or in Improve and also when matrix was made pos def (status returned = 1)
449 if (status >= 0 && result) {
450 if (RooStats::NLLOffsetMode() != "initial") {
451 val = result->minNll();
452 } else {
453 bool previous = RooAbsReal::hideOffset();
455 val = nll->getVal();
456 if (!previous) RooAbsReal::setHideOffset(false) ;
457 }
458
459 } else {
460 oocoutE(nullptr,Fitting) << "FIT FAILED !- return a NaN NLL " << std::endl;
461 val = TMath::QuietNaN();
462 }
463
464 if (fitResult)
465 *fitResult = std::move(result);
466 }
467
468 double muTest = 0;
469 if (verbose > 0) {
470 oocoutP(nullptr,Eval) << "AsymptoticCalculator::EvaluateNLL - value = " << val;
471 if (poiSet) {
472 muTest = ( static_cast<RooRealVar*>(poiSet->first()) )->getVal();
473 ooccoutP(nullptr,Eval) << " for poi fixed at = " << muTest;
474 }
475 if (!skipFit) {
476 tw.Stop();
477 ooccoutP(nullptr,Eval) << "\tfit time : " << tw.RealTime() << " s (real) " << tw.CpuTime() << " s (cpu)" << std::endl;
478 } else {
479 ooccoutP(nullptr,Eval) << std::endl;
480 }
481 }
482
483 // reset the parameter free which where set as constant
484 if (poiSet && !paramsSetConstant.empty()) SetAllConstant(paramsSetConstant,false);
485
486
487 if (verbose < 2) RooMsgService::instance().setGlobalKillBelow(msglevel);
488
489 return val;
490}
491
492} // namespace
493
494////////////////////////////////////////////////////////////////////////////////
495/// It performs an hypothesis tests using the likelihood function
496/// and computes the p values for the null and the alternate using the asymptotic
497/// formulae for the profile likelihood ratio.
498/// See G. Cowan, K. Cranmer, E. Gross and O. Vitells.
499/// Asymptotic formulae for likelihood- based tests of new physics. Eur. Phys. J., C71:1–19, 2011.
500/// The formulae are valid only for one POI. If more than one POI exists consider as POI only the
501/// first one
502
504 int verbose = fgPrintLevel();
505
506 // re-initialized the calculator in case it is needed (pdf or data modified)
507 if (!fIsInitialized) {
508 if (!Initialize() ) {
509 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator::GetHypoTest - Error initializing Asymptotic calculator - return nullptr result " << std::endl;
510 return nullptr;
511 }
512 }
513
514 if (!fAsimovData) {
515 oocoutE(nullptr,InputArguments) << "AsymptoticCalculator::GetHypoTest - Asimov data set has not been generated - return nullptr result " << std::endl;
516 return nullptr;
517 }
518
519 assert(GetNullModel() );
520 assert(GetData() );
521
522 RooAbsPdf * nullPdf = GetNullModel()->GetPdf();
523 assert(nullPdf);
524
525 // make conditional fit on null snapshot of poi
526
527 const RooArgSet * nullSnapshot = GetNullModel()->GetSnapshot();
528 assert(nullSnapshot && !nullSnapshot->empty());
529
530 // use as POI the nullSnapshot
531 // if more than one POI exists, consider only the first one
532 RooArgSet poiTest(*nullSnapshot);
533
534 if (poiTest.size() > 1) {
535 oocoutW(nullptr,InputArguments) << "AsymptoticCalculator::GetHypoTest: snapshot has more than one POI - assume as POI first parameter " << std::endl;
536 }
537
538 std::unique_ptr<RooArgSet> allParams{nullPdf->getParameters(*GetData() )};
539 allParams->assign(fBestFitParams);
540
541 // set the one-side condition
542 // (this works when we have only one params of interest
543 RooRealVar * muHat = dynamic_cast<RooRealVar*> ( fBestFitPoi.first() );
544 assert(muHat && "no best fit parameter defined");
545 RooRealVar * muTest = dynamic_cast<RooRealVar*> ( nullSnapshot->find(muHat->GetName() ) );
546 assert(muTest && "poi snapshot is not existing");
547
548
549
550 if (verbose> 0) {
551 oocoutI(nullptr,Eval) << "\nAsymptoticCalculator::GetHypoTest: - perform an hypothesis test for POI ( " << muTest->GetName() << " ) = " << muTest->getVal() << std::endl;
552 oocoutP(nullptr,Eval) << "AsymptoticCalculator::GetHypoTest - Find best conditional NLL on OBSERVED data set ..... " << std::endl;
553 }
554
555 // evaluate the conditional NLL on the observed data for the snapshot value
556 double condNLL = EvaluateNLL(*GetNullModel(), const_cast<RooAbsData &>(*GetData()), &poiTest, &fFitResultCondObs);
557 if (fFitResultCondObs) {
558 fFitResultCondObs->SetName("fitResultCondObs");
559 fFitResultCondObs->SetTitle(
560 TString::Format("Conditional fit to observed data for %s = %g", muTest->GetName(), muTest->getVal()));
561 }
562
563 double qmu = 2.*(condNLL - fNLLObs);
564
565
566
567 if (verbose > 0)
568 oocoutP(nullptr,Eval) << "\t OBSERVED DATA : qmu = " << qmu << " condNLL = " << condNLL << " uncond " << fNLLObs << std::endl;
569
570
571 // this tolerance is used to avoid having negative qmu due to numerical errors
572 double tol = 2.E-3 * std::max(1.,ROOT::Math::MinimizerOptions::DefaultTolerance());
573 if (qmu < -tol || TMath::IsNaN(fNLLObs) ) {
574
575 if (qmu < 0) {
576 oocoutW(nullptr,Minimization) << "AsymptoticCalculator: Found a negative value of the qmu - retry to do the unconditional fit "
577 << std::endl;
578 } else {
579 oocoutW(nullptr, Minimization)
580 << "AsymptoticCalculator: unconditional fit failed before - retry to do it now " << std::endl;
581 }
582
583 std::unique_ptr<RooFitResult> refitResult;
584 double nll = EvaluateNLL(*GetNullModel(), const_cast<RooAbsData &>(*GetData()), nullptr, &refitResult);
585
586 if (nll < fNLLObs || (TMath::IsNaN(fNLLObs) && !TMath::IsNaN(nll) ) ) {
587 oocoutW(nullptr,Minimization) << "AsymptoticCalculator: Found a better unconditional minimum "
588 << " old NLL = " << fNLLObs << " old muHat " << muHat->getVal() << std::endl;
589
590 // update values
591 fNLLObs = nll;
592 if (refitResult) {
593 fFitResultUncondObs = std::move(refitResult);
594 fFitResultUncondObs->SetName("fitResultUncondObs");
595 fFitResultUncondObs->SetTitle("Unconditional fit to observed data");
596 }
598 assert(poi);
600 poi->snapshot(fBestFitPoi);
601 // restore also muHad since previous pointer has been deleted
602 muHat = dynamic_cast<RooRealVar*> ( fBestFitPoi.first() );
603 assert(muHat);
604
605 oocoutW(nullptr,Minimization) << "AsymptoticCalculator: New minimum found for "
606 << " NLL = " << fNLLObs << " muHat " << muHat->getVal() << std::endl;
607
608
609 qmu = 2.*(condNLL - fNLLObs);
610
611 if (verbose > 0)
612 oocoutP(nullptr,Eval) << "After unconditional refit, new qmu value is " << qmu << std::endl;
613
614 }
615 }
616
617 if (qmu < -tol ) {
618 oocoutE(nullptr,Minimization) << "AsymptoticCalculator: qmu is still < 0 for mu = "
619 << muTest->getVal() << " return a dummy result "
620 << std::endl;
621 return new HypoTestResult();
622 }
623 if (TMath::IsNaN(qmu) ) {
624 oocoutE(nullptr,Minimization) << "AsymptoticCalculator: failure in fitting for qmu or qmuA "
625 << muTest->getVal() << " return a dummy result "
626 << std::endl;
627 return new HypoTestResult();
628 }
629
630
631
632
633
634 // compute conditional ML on Asimov data set
635 // (need to const cast because it uses fitTo which is a non const method
636 // RooArgSet asimovGlobObs;
637 // RooAbsData * asimovData = (const_cast<AsymptoticCalculator*>(this))->MakeAsimovData( poi, asimovGlobObs);
638 // set global observables to their Asimov values
639 RooArgSet globObs;
640 RooArgSet globObsSnapshot;
641 if (GetNullModel()->GetGlobalObservables() ) {
642 globObs.add(*GetNullModel()->GetGlobalObservables());
643 // store previous snapshot value
644 globObs.snapshot(globObsSnapshot);
645 globObs.assign(fAsimovGlobObs);
646 }
647
648
649 if (verbose > 0) oocoutP(nullptr,Eval) << "AsymptoticCalculator::GetHypoTest -- Find best conditional NLL on ASIMOV data set .... " << std::endl;
650
651 double condNLL_A = EvaluateNLL(*GetNullModel(), *fAsimovData, &poiTest, &fFitResultCondAsimov);
653 fFitResultCondAsimov->SetName("fitResultCondAsimov");
654 fFitResultCondAsimov->SetTitle(
655 TString::Format("Conditional fit to Asimov data for %s = %g", muTest->GetName(), muTest->getVal()));
656 }
657
658 double qmu_A = 2.*(condNLL_A - fNLLAsimov );
659
660 if (verbose > 0)
661 oocoutP(nullptr,Eval) << "\t ASIMOV data qmu_A = " << qmu_A << " condNLL = " << condNLL_A << " uncond " << fNLLAsimov << std::endl;
662
663 if (qmu_A < -tol || TMath::IsNaN(fNLLAsimov) ) {
664
665 if (qmu_A < 0) {
666 oocoutW(nullptr, Minimization)
667 << "AsymptoticCalculator: Found a negative value of the qmu Asimov- retry to do the unconditional fit "
668 << std::endl;
669 } else {
670 oocoutW(nullptr, Minimization)
671 << "AsymptoticCalculator: Fit failed for unconditional the qmu Asimov- retry unconditional fit "
672 << std::endl;
673 }
674
675 std::unique_ptr<RooFitResult> refitResult;
676 double nll = EvaluateNLL(*GetNullModel(), *fAsimovData, nullptr, &refitResult);
677
678 if (nll < fNLLAsimov || (TMath::IsNaN(fNLLAsimov) && !TMath::IsNaN(nll) )) {
679 oocoutW(nullptr,Minimization) << "AsymptoticCalculator: Found a better unconditional minimum for Asimov data set"
680 << " old NLL = " << fNLLAsimov << std::endl;
681
682 // update values
683 fNLLAsimov = nll;
684 if (refitResult) {
685 fFitResultUncondAsimov = std::move(refitResult);
686 fFitResultUncondAsimov->SetName("fitResultUncondAsimov");
687 fFitResultUncondAsimov->SetTitle("Unconditional fit to Asimov data");
688 }
689
690 oocoutW(nullptr,Minimization) << "AsymptoticCalculator: New minimum found for "
691 << " NLL = " << fNLLAsimov << std::endl;
692 qmu_A = 2.*(condNLL_A - fNLLAsimov);
693
694 if (verbose > 0)
695 oocoutP(nullptr,Eval) << "After unconditional Asimov refit, new qmu_A value is " << qmu_A << std::endl;
696
697 }
698 }
699
700 if (qmu_A < - tol) {
701 oocoutE(nullptr,Minimization) << "AsymptoticCalculator: qmu_A is still < 0 for mu = "
702 << muTest->getVal() << " return a dummy result "
703 << std::endl;
704 return new HypoTestResult();
705 }
706 if (TMath::IsNaN(qmu) ) {
707 oocoutE(nullptr,Minimization) << "AsymptoticCalculator: failure in fitting for qmu or qmuA "
708 << muTest->getVal() << " return a dummy result "
709 << std::endl;
710 return new HypoTestResult();
711 }
712
713
714 // restore previous value of global observables
715 globObs.assign(globObsSnapshot);
716
717 // now we compute p-values using the asymptotic formulae
718 // described in the paper
719 // Cowan et al, Eur.Phys.J. C (2011) 71:1554
720
721 // first try to guess automatically if needed to use qtilde (or ttilde in case of two sided)
722 // if explicitly fUseQTilde this was not set
723 // qtilde is in this case used if poi is bounded at the value of the alt hypothesis
724 // for Qtilde (need to distinguish case when qmu > qmuA = mu^2/ sigma^2)
725 // (see Cowan et al, Eur.Phys.J. C(2011) 71:1554 paper equations 64 and 65
726 // (remember qmu_A = mu^2/sigma^2 )
727 bool useQTilde = false;
728 // default case (check if poi is limited or not to a zero value)
729 if (!fOneSidedDiscovery) { // qtilde is not a discovery test
730 if (fUseQTilde == -1 && !fOneSidedDiscovery) {
731 // alternate snapshot is value for which background is zero (for limits)
732 RooRealVar * muAlt = dynamic_cast<RooRealVar*>(GetAlternateModel()->GetSnapshot()->first() );
733 // null snapshot is value for which background is zero (for discovery)
734 //RooRealVar * muNull = dynamic_cast<RooRealVar*>(GetNullModel()->GetSnapshot()->first() );
735 assert(muAlt != nullptr );
736 if (muTest->getMin() == muAlt->getVal() ) {
737 fUseQTilde = 1;
738 oocoutI(nullptr,InputArguments) << "Minimum of POI is " << muTest->getMin() << " corresponds to alt snapshot - using qtilde asymptotic formulae " << std::endl;
739 } else {
740 fUseQTilde = 0;
741 oocoutI(nullptr,InputArguments) << "Minimum of POI is " << muTest->getMin() << " is different to alt snapshot " << muAlt->getVal()
742 << " - using standard q asymptotic formulae " << std::endl;
743 }
744 }
745 useQTilde = fUseQTilde;
746 }
747
748 // check for one side condition (remember this is valid only for one poi)
749 // for a signed (uncapped) test statistic the sign of sqrt(qmu) is flipped instead of setting qmu to zero
750 bool flipSign = false;
751 if (fOneSided ) {
752 if ( muHat->getVal() > muTest->getVal() ) {
753 if (fSigned) {
754 oocoutI(nullptr, Eval) << "Using signed one-sided qmu - flipping the sign of the test statistic muHat = "
755 << muHat->getVal() << " muTest = " << muTest->getVal() << std::endl;
756 flipSign = true;
757 } else {
758 oocoutI(nullptr, Eval) << "Using one-sided qmu - setting qmu to zero muHat = " << muHat->getVal()
759 << " muTest = " << muTest->getVal() << std::endl;
760 qmu = 0;
761 }
762 }
763 }
764 if (fOneSidedDiscovery ) {
765 if ( muHat->getVal() < muTest->getVal() ) {
766 if (fSigned) {
767 oocoutI(nullptr, Eval)
768 << "Using signed one-sided discovery qmu - flipping the sign of the test statistic muHat = "
769 << muHat->getVal() << " muTest = " << muTest->getVal() << std::endl;
770 flipSign = true;
771 } else {
772 oocoutI(nullptr, Eval) << "Using one-sided discovery qmu - setting qmu to zero muHat = " << muHat->getVal()
773 << " muTest = " << muTest->getVal() << std::endl;
774 qmu = 0;
775 }
776 }
777 }
778
779 // fix for negative qmu values due to numerical errors
780 if (qmu < 0 && qmu > -tol) qmu = 0;
781 if (qmu_A < 0 && qmu_A > -tol) qmu_A = 0;
782
783 // asymptotic formula for pnull and from paper Eur.Phys.J C 2011 71:1554
784 // we have 4 different cases:
785 // t(mu), t_tilde(mu) for the 2-sided
786 // q(mu) and q_tilde(mu) for the one -sided test statistics
787
788 double pnull = -1;
789 double palt = -1;
790
791 // asymptotic formula for pnull (for only one POI)
792 // From fact that qmu is a chi2 with ndf=1
793
794 // for the signed test statistic, sqrtqmu becomes negative when the best fit
795 // value is beyond the tested value; the Gaussian asymptotic formulae below
796 // remain valid also in that case
797 double sqrtqmu = (qmu > 0) ? std::sqrt(qmu) : 0;
798 if (flipSign)
799 sqrtqmu = -sqrtqmu;
800 double sqrtqmu_A = (qmu_A > 0) ? std::sqrt(qmu_A) : 0;
801
802
804 // for one-sided PL (q_mu : equations 56,57)
805 if (verbose>2) {
806 if (fOneSided) {
807 oocoutI(nullptr,Eval) << "Using one-sided limit asymptotic formula (qmu)" << std::endl;
808 } else {
809 oocoutI(nullptr, Eval) << "Using one-sided discovery asymptotic formula (q0)" << std::endl;
810 }
811 }
812 pnull = ROOT::Math::normal_cdf_c( sqrtqmu, 1.);
813 palt = ROOT::Math::normal_cdf( sqrtqmu_A - sqrtqmu, 1.);
814 }
815 else {
816 // for 2-sided PL (t_mu : equations 35,36 in asymptotic paper)
817 if (verbose > 2) oocoutI(nullptr,Eval) << "Using two-sided asymptotic formula (tmu)" << std::endl;
818 pnull = 2.*ROOT::Math::normal_cdf_c( sqrtqmu, 1.);
819 palt = ROOT::Math::normal_cdf_c( sqrtqmu + sqrtqmu_A, 1.) +
820 ROOT::Math::normal_cdf_c( sqrtqmu - sqrtqmu_A, 1.);
821
822 }
823
824 // the qtilde corrections apply when the best fit value is at the boundary
825 // (qmu > qmu_A); they cannot apply when the sign was flipped, because then
826 // the best fit value is on the other side of the tested value
827 if (useQTilde && !flipSign) {
828 if (fOneSided) {
829 // for bounded one-sided (q_mu_tilde: equations 64,65)
830 if ( qmu > qmu_A && (qmu_A > 0 || qmu > tol) ) { // to avoid case 0/0
831 if (verbose > 2) oocoutI(nullptr,Eval) << "Using qmu_tilde (qmu is greater than qmu_A)" << std::endl;
832 pnull = ROOT::Math::normal_cdf_c( (qmu + qmu_A)/(2 * sqrtqmu_A), 1.);
833 palt = ROOT::Math::normal_cdf_c( (qmu - qmu_A)/(2 * sqrtqmu_A), 1.);
834 }
835 }
836 else {
837 // for 2 sided bounded test statistic (N.B there is no one sided discovery qtilde)
838 // t_mu_tilde: equations 43,44 in asymptotic paper
839 if ( qmu > qmu_A && (qmu_A > 0 || qmu > tol) ) {
840 if (verbose > 2) oocoutI(nullptr,Eval) << "Using tmu_tilde (qmu is greater than qmu_A)" << std::endl;
841 pnull = ROOT::Math::normal_cdf_c(sqrtqmu,1.) +
842 ROOT::Math::normal_cdf_c( (qmu + qmu_A)/(2 * sqrtqmu_A), 1.);
843 palt = ROOT::Math::normal_cdf_c( sqrtqmu_A + sqrtqmu, 1.) +
844 ROOT::Math::normal_cdf_c( (qmu - qmu_A)/(2 * sqrtqmu_A), 1.);
845 }
846 }
847 }
848
849 // create an HypoTest result but where the sampling distributions are set to zero
850 string resultname = "HypoTestAsymptotic_result";
851 HypoTestResult* res = new HypoTestResult(resultname.c_str(), pnull, palt);
852
853 if (verbose > 0) {
854 oocoutP(nullptr, Eval) << "poi = " << muTest->getVal() << " qmu = " << qmu << " qmu_A = " << qmu_A
855 << " sigma = " << muTest->getVal() / sqrtqmu_A << " CLsplusb = " << pnull
856 << " CLb = " << palt << " CLs = " << res->CLs() << std::endl;
857 }
858
859 return res;
860
861}
862
864 PaltFunction( double offset, double pval, int icase) :
865 fOffset(offset), fPval(pval), fCase(icase) {}
866 double operator() (double x) const {
867 return ROOT::Math::normal_cdf_c(x + fOffset) + ROOT::Math::normal_cdf_c(fCase*(x - fOffset)) - fPval;
868 }
869 double fOffset;
870 double fPval;
871 int fCase;
872};
873
874////////////////////////////////////////////////////////////////////////////////
875/// function given the null and the alt p value - return the expected one given the N - sigma value
876
877double AsymptoticCalculator::GetExpectedPValues(double pnull, double palt, double nsigma, bool useCls, bool oneSided ) {
878 if (oneSided) {
879 double sqrtqmu = ROOT::Math::normal_quantile_c( pnull,1.);
880 double sqrtqmu_A = ROOT::Math::normal_quantile( palt,1.) + sqrtqmu;
881 double clsplusb = ROOT::Math::normal_cdf_c( sqrtqmu_A - nsigma, 1.);
882 if (!useCls) return clsplusb;
883 double clb = ROOT::Math::normal_cdf( nsigma, 1.);
884 return (clb == 0) ? -1 : clsplusb / clb;
885 }
886
887 // case of 2 sided test statistic
888 // need to compute numerically
889 double sqrttmu = ROOT::Math::normal_quantile_c( 0.5*pnull,1.);
890 if (sqrttmu == 0) {
891 // here cannot invert the function - skip the point
892 return -1;
893 }
894 // invert formula for palt to get sqrttmu_A
895 PaltFunction f( sqrttmu, palt, -1);
898 brf.SetFunction( wf, 0, 20);
899 bool ret = brf.Solve();
900 if (!ret) {
901 oocoutE(nullptr,Eval) << "Error finding expected p-values - return -1" << std::endl;
902 return -1;
903 }
904 double sqrttmu_A = brf.Root();
905
906 // now invert for expected value
907 PaltFunction f2( sqrttmu_A, ROOT::Math::normal_cdf( nsigma, 1.), 1);
909 brf.SetFunction(wf2,0,20);
910 ret = brf.Solve();
911 if (!ret) {
912 oocoutE(nullptr,Eval) << "Error finding expected p-values - return -1" << std::endl;
913 return -1;
914 }
915 return 2*ROOT::Math::normal_cdf_c( brf.Root(),1.);
916}
917
918namespace {
919
920////////////////////////////////////////////////////////////////////////////////
921/// Fill bins by looping recursively on observables.
922
923void FillBins(const RooAbsPdf & pdf, const RooArgList &obs, RooAbsData & data, int &index, double &binVolume, int &ibin) {
924
925 bool debug = (fgPrintLevel() >= 2);
926
927 RooRealVar * v = dynamic_cast<RooRealVar*>(&(obs[index]) );
928 if (!v) return;
929
930 RooArgSet obstmp(obs);
931 double expectedEvents = pdf.expectedEvents(obstmp);
932
933 if (debug) oocoutI(nullptr,Generation) << "looping on observable " << v->GetName() << std::endl;
934 for (int i = 0; i < v->getBins(); ++i) {
935 v->setBin(i);
936 if (index < int(obs.size()) -1) {
937 index++; // increase index
938 double prevBinVolume = binVolume;
939 binVolume *= v->getBinWidth(i); // increase bin volume
940 FillBins(pdf, obs, data, index, binVolume, ibin);
941 index--; // decrease index
942 binVolume = prevBinVolume; // decrease also bin volume
943 }
944 else {
945
946 // this is now a new bin - compute the pdf in this bin
947 double totBinVolume = binVolume * v->getBinWidth(i);
948 double fval = pdf.getVal(&obstmp)*totBinVolume;
949
950 // Bins without expected events are kept with zero weight, such that
951 // the dataset has one entry per bin, like the binned likelihood
952 // optimization requires.
953 if (fval*expectedEvents <= 0)
954 {
955 if (fval*expectedEvents < 0) {
956 oocoutW(nullptr,InputArguments)
957 << "AsymptoticCalculator::" << __func__
958 << "(): Bin " << i << " of " << v->GetName() << " has negative expected events! Please check your inputs." << std::endl;
959 }
960 else {
961 oocoutW(nullptr,InputArguments)
962 << "AsymptoticCalculator::" << __func__
963 << "(): Bin " << i << " of " << v->GetName() << " has zero expected events" << std::endl;
964 }
965 data.add(obs, 0.0);
966 }
967 // have a cut off for overflows ??
968 else {
969 data.add(obs, fval*expectedEvents);
970 }
971
972 if (debug) {
973 oocoutI(nullptr,Generation) << "bin " << ibin << "\t";
974 for (std::size_t j=0; j < obs.size(); ++j) { ooccoutI(nullptr,Generation) << " " << (static_cast<RooRealVar&>( obs[j])).getVal(); }
975 ooccoutI(nullptr,Generation) << " w = " << fval*expectedEvents;
976 ooccoutI(nullptr,Generation) << std::endl;
977 }
978 ibin++;
979 }
980 }
981 //reset bin values
982 if (debug) {
983 oocoutI(nullptr,Generation) << "ending loop on .. " << v->GetName() << std::endl;
984 }
985
986 v->setBin(0);
987
988}
989
990bool setObsToExpected(RooAbsArg &x, RooAbsArg &mean, const RooArgSet &obs, std::string const &errPrefix)
991{
992 // Figure out which of the two arguments is the observable that should be
993 // set to the expected value given by the other one. Usually the observable
994 // is "x", but also the mean parameter can be the observable: this happens
995 // for example in constraint terms, where the global observable takes the
996 // role of the mean.
997 const bool xIsObs = obs.contains(x);
998 const bool meanIsObs = obs.contains(mean);
999 if (xIsObs && meanIsObs) {
1000 oocoutF(nullptr, Generation) << errPrefix << "Has two observables ?? " << std::endl;
1001 return false;
1002 }
1003 if (!xIsObs && !meanIsObs) {
1004 oocoutF(nullptr, Generation) << errPrefix << "No observable?" << std::endl;
1005 return false;
1006 }
1007 auto *myobs = dynamic_cast<RooRealVar *>(xIsObs ? &x : &mean);
1008 auto *myexp = dynamic_cast<RooAbsReal *>(xIsObs ? &mean : &x);
1009 if (myobs == nullptr) {
1010 oocoutF(nullptr, Generation) << errPrefix << "Observable is not a RooRealVar??" << std::endl;
1011 return false;
1012 }
1013 if (myexp == nullptr) {
1014 oocoutF(nullptr, Generation) << errPrefix << "Expected is not a RooAbsReal??" << std::endl;
1015 return false;
1016 }
1017
1018 myobs->setVal(myexp->getVal());
1019
1020 if (fgPrintLevel() > 2) {
1021 oocoutI(nullptr,Generation) << "SetObsToExpected : setting " << myobs->GetName() << " to expected value " << myexp->getVal() << " of " << myexp->GetName() << std::endl;
1022 }
1023
1024 return true;
1025}
1026
1027////////////////////////////////////////////////////////////////////////////////
1028/// set observed value to the expected one
1029/// works for Gaussian, Poisson or LogNormal
1030/// need to iterate on the components of the Poisson to get n and nu (nu can be a RooAbsReal)
1031/// (code from G. Petrucciani and extended by L.M.)
1032
1033bool SetObsToExpected(RooGaussian &pdf, const RooArgSet &obs)
1034{
1035 std::string const &errPrefix = "AsymptoticCalculator::SetObsExpected( " + std::string{pdf.ClassName()} + " ) : ";
1036 return setObsToExpected(const_cast<RooAbsReal &>(pdf.getX()), const_cast<RooAbsReal &>(pdf.getMean()), obs,
1037 errPrefix);
1038}
1039
1040bool SetObsToExpected(RooPoisson &pdf, const RooArgSet &obs)
1041{
1042 std::string const &errPrefix = "AsymptoticCalculator::SetObsExpected( " + std::string{pdf.ClassName()} + " ) : ";
1043 return setObsToExpected(const_cast<RooAbsReal &>(pdf.getX()), const_cast<RooAbsReal &>(pdf.getMean()), obs,
1044 errPrefix);
1045}
1046
1047bool setObsToExpectedMultiVarGauss(RooMultiVarGaussian &mvgauss, const RooArgSet &obs)
1048{
1049 // In the case of the multi-variate Gaussian, we need to iterate over the
1050 // dimensions and treat the observable and mean for each dimension
1051 // separately.
1052
1053 std::string const &errPrefix = "AsymptoticCalculator::SetObsExpected( " + std::string{mvgauss.ClassName()} + " ) : ";
1054 bool ret = true;
1055 for (std::size_t iDim = 0; iDim < mvgauss.xVec().size(); ++iDim) {
1056 ret &= setObsToExpected(mvgauss.xVec()[iDim], mvgauss.muVec()[iDim], obs,
1057 errPrefix + " : dim " + std::to_string(iDim) + " ");
1058 }
1059 return ret;
1060}
1061
1062////////////////////////////////////////////////////////////////////////////////
1063/// Inpspect a product pdf to find all the Poisson or Gaussian parts to set the observed
1064/// values to expected ones.
1065
1066bool setObsToExpectedProdPdf(RooProdPdf &prod, const RooArgSet &obs)
1067{
1068 bool ret = true;
1069 for (auto *a : prod.pdfList()) {
1070 if (!a->dependsOn(obs)) continue;
1071 RooPoisson *pois = nullptr;
1072 RooGaussian *gauss = nullptr;
1073 RooMultiVarGaussian *mvgauss = nullptr;
1074 // should try to add also lognormal case ?
1075 if ((pois = dynamic_cast<RooPoisson *>(a)) != nullptr) {
1076 ret &= SetObsToExpected(*pois, obs);
1077 pois->setNoRounding(true); //needed since expected value is not an integer
1078 } else if ((gauss = dynamic_cast<RooGaussian *>(a)) != nullptr) {
1079 ret &= SetObsToExpected(*gauss, obs);
1080 } else if ((mvgauss = dynamic_cast<RooMultiVarGaussian *>(a)) != nullptr) {
1081 ret &= setObsToExpectedMultiVarGauss(*mvgauss, obs);
1082 } else if (RooProdPdf *subprod = dynamic_cast<RooProdPdf *>(a)) {
1083 ret &= setObsToExpectedProdPdf(*subprod, obs);
1084 } else {
1085 oocoutE(nullptr, InputArguments)
1086 << "Illegal term in counting model: "
1087 << "the PDF " << a->GetName() << " depends on the observables, but is not a Poisson, Gaussian or Product"
1088 << std::endl;
1089 return false;
1090 }
1091 }
1092
1093 return ret;
1094}
1095
1096////////////////////////////////////////////////////////////////////////////////
1097/// Generate counting Asimov data for the case when the pdf cannot be extended.
1098/// This function assumes that the pdf is a RooPoisson or can be decomposed in a product of RooPoisson,
1099/// or is a RooGaussian. Otherwise, we cannot know how to make the Asimov data sets.
1100
1101RooAbsData *GenerateCountingAsimovData(RooAbsPdf & pdf, const RooArgSet & observables, const RooRealVar & , RooCategory * channelCat) {
1102 RooArgSet obs(observables);
1103 RooProdPdf *prod = dynamic_cast<RooProdPdf *>(&pdf);
1104 RooPoisson *pois = nullptr;
1105 RooGaussian *gauss = nullptr;
1106 RooMultiVarGaussian *mvgauss = nullptr;
1107
1108 if (fgPrintLevel() > 1)
1109 oocoutI(nullptr,Generation) << "generate counting Asimov data for pdf of type " << pdf.ClassName() << std::endl;
1110
1111 bool r = false;
1112 if (prod != nullptr) {
1113 r = setObsToExpectedProdPdf(*prod, observables);
1114 } else if ((pois = dynamic_cast<RooPoisson *>(&pdf)) != nullptr) {
1115 r = SetObsToExpected(*pois, observables);
1116 // we need in this case to set Poisson to real values
1117 pois->setNoRounding(true);
1118 } else if ((gauss = dynamic_cast<RooGaussian *>(&pdf)) != nullptr) {
1119 r = SetObsToExpected(*gauss, observables);
1120 } else if ((mvgauss = dynamic_cast<RooMultiVarGaussian *>(&pdf)) != nullptr) {
1121 r = setObsToExpectedMultiVarGauss(*mvgauss, observables);
1122 } else {
1123 oocoutE(nullptr,InputArguments) << "A counting model pdf must be either a RooProdPdf or a RooPoisson or a RooGaussian" << std::endl;
1124 }
1125 if (!r) return nullptr;
1126 int icat = 0;
1127 if (channelCat) {
1128 icat = channelCat->getCurrentIndex();
1129 }
1130
1131 RooDataSet *ret = new RooDataSet("CountingAsimovData" + std::to_string(icat),
1132 "CountingAsimovData" + std::to_string(icat), obs);
1133 ret->add(obs);
1134 return ret;
1135}
1136
1137////////////////////////////////////////////////////////////////////////////////
1138/// Compute the asimov data set for an observable of a pdf.
1139/// It generates binned data following the binning of the observables.
1140// TODO: (possibility to change number of bins)
1141// TODO: implement integration over bin content
1142
1143RooAbsData * GenerateAsimovDataSinglePdf(const RooAbsPdf & pdf, const RooArgSet & allobs, const RooRealVar & weightVar, RooCategory * channelCat) {
1144
1145 int printLevel = fgPrintLevel();
1146
1147 // Get observables defined by the pdf associated with this state
1148 std::unique_ptr<RooArgSet> obs(pdf.getObservables(allobs) );
1149
1150
1151 // if pdf cannot be extended assume is then a counting experiment
1152 if (!pdf.canBeExtended() ) return GenerateCountingAsimovData(const_cast<RooAbsPdf&>(pdf), *obs, weightVar, channelCat);
1153
1154 RooArgSet obsAndWeight(*obs);
1155 obsAndWeight.add(weightVar);
1156
1157 std::unique_ptr<RooDataSet> asimovData;
1158 if (channelCat) {
1159 int icat = channelCat->getCurrentIndex();
1160 asimovData = std::make_unique<RooDataSet>("AsimovData" + std::to_string(icat),
1161 "combAsimovData" + std::to_string(icat),
1162 RooArgSet(obsAndWeight,*channelCat),RooFit::WeightVar(weightVar));
1163 }
1164 else {
1165 asimovData = std::make_unique<RooDataSet>("AsimovData","AsimovData",RooArgSet(obsAndWeight),RooFit::WeightVar(weightVar));
1166 }
1167
1168 // This works only for 1D observables
1169 //RooRealVar* thisObs = ((RooRealVar*)obstmp->first());
1170
1171 RooArgList obsList(*obs);
1172
1173 // The Asimov data set is built by looping over the bins of the
1174 // observables. A freshly-constructed RooRealVar now defaults to zero bins,
1175 // so temporarily materialize the historical default binning for any
1176 // observable that has no explicit binning set. The change is undone right
1177 // after filling the bins so that the model observables are left untouched.
1178 std::vector<RooRealVar *> obsWithDefaultBinning;
1179 for (auto *arg : obsList) {
1180 auto *rrv = dynamic_cast<RooRealVar *>(arg);
1181 if (rrv && rrv->getBins() == 0) {
1183 obsWithDefaultBinning.push_back(rrv);
1184 }
1185 }
1186
1187 // loop on observables and on the bins
1188 if (printLevel >= 2) {
1189 oocoutI(nullptr,Generation) << "Generating Asimov data for pdf " << pdf.GetName() << std::endl;
1190 oocoutI(nullptr,Generation) << "list of observables " << std::endl;
1191 obsList.Print();
1192 }
1193
1194 int obsIndex = 0;
1195 double binVolume = 1;
1196 int nbins = 0;
1197 FillBins(pdf, obsList, *asimovData, obsIndex, binVolume, nbins);
1198
1199 // restore the zero-bins default on observables that had no explicit binning
1200 for (auto *rrv : obsWithDefaultBinning) {
1201 rrv->setBins(0);
1202 }
1203 if (printLevel >= 2)
1204 oocoutI(nullptr,Generation) << "filled from " << pdf.GetName() << " " << nbins << " nbins " << " volume is " << binVolume << std::endl;
1205
1206 // for (int iobs = 0; iobs < obsList.size(); ++iobs) {
1207 // RooRealVar * thisObs = dynamic_cast<RooRealVar*> &obsList[i];
1208 // if (thisObs == 0) continue;
1209 // // loop on the bin contents
1210 // for(int ibin=0; ibin<thisObs->numBins(); ++ibin){
1211 // thisObs->setBin(ibin);
1212
1213 // thisNorm=pdftmp->getVal(obstmp)*thisObs->getBinWidth(jj);
1214 // if (thisNorm*expectedEvents <= 0)
1215 // {
1216 // std::cout << "WARNING::Detected bin with zero expected events! Please check your inputs." << std::endl;
1217 // }
1218 // // have a cut off for overflows ??
1219 // obsDataUnbinned->add(*mc->GetObservables(), thisNorm*expectedEvents);
1220 // }
1221
1222 if (printLevel >= 1)
1223 {
1224 asimovData->Print();
1225 }
1226 if( TMath::IsNaN(asimovData->sumEntries()) ){
1227 oocoutE(nullptr,Generation) << "sum entries is nan"<< std::endl;
1228 assert(0);
1229 asimovData = nullptr;
1230 }
1231
1232 return asimovData.release();
1233
1234}
1235
1236} // namespace
1237
1238////////////////////////////////////////////////////////////////////////////////
1239/// generate the asimov data for the observables (not the global ones)
1240/// need to deal with the case of a sim pdf
1241/// \param pdf a RooAbsPdf. Can be also a RooSimultaneous, or a RooProPdf of a RooSimultaneous
1242
1244
1245 int printLevel = fgPrintLevel();
1246
1247 RooRealVar weightVar{"binWeightAsimov", "binWeightAsimov", 1, 0, 1.e30};
1248
1249 if (printLevel > 1) oocoutI(nullptr,Generation) <<" Generate Asimov data for observables"<< std::endl;
1250 // RooDataSet *simData = nullptr;
1251 const RooProdPdf *prodPdf = dynamic_cast<const RooProdPdf *>(&pdf);
1252 RooArgList strippedPdfSet("strippedPdfSet");
1253 if (prodPdf) {
1254 RooArgList list(prodPdf->pdfList());
1255 for (auto ele : list) {
1256 const RooAbsPdf *pdfi = dynamic_cast<const RooAbsPdf *>(ele);
1257 if (pdfi->dependsOn(observables))
1258 strippedPdfSet.add(*pdfi);
1259 }
1260 }
1261 RooProdPdf observableProdPdf("observableProdPdf", "observableProdPdf", strippedPdfSet);
1262 const RooAbsPdf *observablePdf;
1263 if (prodPdf) {
1264 if (strippedPdfSet.getSize() == 1) {
1265 observablePdf = dynamic_cast<const RooAbsPdf *>(strippedPdfSet.at(0));
1266 } else {
1267 observablePdf = &observableProdPdf;
1268 }
1269 } else {
1270 observablePdf = &pdf;
1271 }
1272 const RooSimultaneous *simPdf = dynamic_cast<const RooSimultaneous *>(observablePdf);
1273 if (!simPdf) {
1274 // generate data for non sim pdf
1275 return GenerateAsimovDataSinglePdf(*observablePdf, observables, weightVar, nullptr);
1276 }
1277
1278 std::map<std::string, std::unique_ptr<RooDataSet>> asimovDataMap;
1279
1280 //look at category of simpdf
1281 RooCategory& channelCat = const_cast<RooCategory&>(dynamic_cast<const RooCategory&>(simPdf->indexCat()));
1282 int nrIndices = channelCat.numTypes();
1283 if( nrIndices == 0 ) {
1284 oocoutW(nullptr,Generation) << "Simultaneous pdf does not contain any categories." << std::endl;
1285 }
1286 for (int i=0;i<nrIndices;i++){
1287 channelCat.setIndex(i);
1288 //iFrame++;
1289 // Get pdf associated with state from simpdf
1290 RooAbsPdf* pdftmp = simPdf->getPdf(channelCat.getCurrentLabel()) ;
1291 assert(pdftmp != nullptr);
1292
1293 if (printLevel > 1)
1294 {
1295 oocoutI(nullptr,Generation) << "on type " << channelCat.getCurrentLabel() << " " << channelCat.getCurrentIndex() << std::endl;
1296 }
1297
1298 std::unique_ptr<RooDataSet> dataSinglePdf{static_cast<RooDataSet*>(GenerateAsimovDataSinglePdf( *pdftmp, observables, weightVar, &channelCat))};
1299 if (!dataSinglePdf) {
1300 oocoutE(nullptr,Generation) << "Error generating an Asimov data set for pdf " << pdftmp->GetName() << std::endl;
1301 return nullptr;
1302 }
1303
1304 if (asimovDataMap.count(string(channelCat.getCurrentLabel())) != 0) {
1305 oocoutE(nullptr,Generation) << "AsymptoticCalculator::GenerateAsimovData(): The PDF for " << channelCat.getCurrentLabel()
1306 << " was already defined. It will be overridden. The faulty category definitions follow:" << std::endl;
1307 channelCat.Print("V");
1308 }
1309
1310 if (printLevel > 1)
1311 {
1312 oocoutI(nullptr,Generation) << "channel: " << channelCat.getCurrentLabel() << ", data: ";
1313 dataSinglePdf->Print();
1314 ooccoutI(nullptr,Generation) << std::endl;
1315 }
1316
1317 asimovDataMap[string(channelCat.getCurrentLabel())] = std::move(dataSinglePdf);
1318 }
1319
1320 RooArgSet obsAndWeight(observables);
1321 obsAndWeight.add(weightVar);
1322
1323
1324 return new RooDataSet("asimovDataFullModel","asimovDataFullModel",RooArgSet(obsAndWeight,channelCat),
1325 RooFit::Index(channelCat),RooFit::Import(asimovDataMap),RooFit::WeightVar(weightVar));
1326}
1327
1328////////////////////////////////////////////////////////////////////////////////
1329/// Make the Asimov data from the ModelConfig and list of poi
1330/// \param realData Real data
1331/// \param model Model config defining the pdf and the parameters
1332/// \param paramValues The snapshot of POI and parameters used for finding the best nuisance parameter values (conditioned at these values)
1333/// \param[out] asimovGlobObs Global observables set to values satisfying the constraints
1334/// \param genPoiValues Optional. A different set of POI values used for generating. By default the same POI are used for generating and for finding the nuisance parameters
1335/// given an observed data set, a model and a snapshot of the poi.
1336/// \return The asimov data set. The user takes ownership.
1337///
1338
1339RooAbsData * AsymptoticCalculator::MakeAsimovData(RooAbsData & realData, const ModelConfig & model, const RooArgSet & paramValues, RooArgSet & asimovGlobObs, const RooArgSet * genPoiValues ) {
1340
1341 int verbose = fgPrintLevel();
1342
1343
1344 RooArgSet poi(*model.GetParametersOfInterest());
1345 poi.assign(paramValues);
1346
1347 // set poi constant for conditional MLE
1348 // need to fit nuisance parameters at their conditional MLE value
1349 RooArgSet paramsSetConstant;
1350 for (auto *tmpPar : static_range_cast<RooRealVar *>(poi)) {
1351 tmpPar->setConstant();
1352 if (verbose>0)
1353 oocoutI(nullptr,Generation) << "MakeAsimov: Setting poi " << tmpPar->GetName() << " to a constant value = " << tmpPar->getVal() << std::endl;
1354 paramsSetConstant.add(*tmpPar);
1355 }
1356
1357 // find conditional value of the nuisance parameters
1358 bool hasFloatParams = false;
1359 RooArgSet constrainParams;
1360 if (model.GetNuisanceParameters()) {
1361 constrainParams.add(*model.GetNuisanceParameters());
1362 RooStats::RemoveConstantParameters(&constrainParams);
1363 if (!constrainParams.empty()) hasFloatParams = true;
1364
1365 } else {
1366 // Do we have free parameters anyway that need fitting?
1367 std::unique_ptr<RooArgSet> params(model.GetPdf()->getParameters(realData));
1368 for (auto const *rrv : dynamic_range_cast<RooRealVar *>(*params)) {
1369 if ( rrv != nullptr && rrv->isConstant() == false ) { hasFloatParams = true; break; }
1370 }
1371 }
1372 if (hasFloatParams) {
1373 // models need to be fitted to find best nuisance parameter values
1374
1375 TStopwatch tw2; tw2.Start();
1377 if (verbose>0) {
1378 oocoutP(nullptr,Generation) << "MakeAsimov: doing a conditional fit for finding best nuisance values " << std::endl;
1379 minimPrintLevel = verbose;
1380 if (verbose>1) {
1381 oocoutI(nullptr,Generation) << "POI values:\n"; poi.Print("v");
1382 if (verbose > 2) {
1383 oocoutI(nullptr,Generation) << "Nuis param values:\n";
1384 constrainParams.Print("v");
1385 }
1386 }
1387 }
1390
1391 std::string minimizerAlgo = ROOT::Math::MinimizerOptions::DefaultMinimizerAlgo();
1392 std::vector<RooCmdArg> args{
1393 RooFit::Minimizer("",minimizerAlgo.c_str()), // empty mimimizer type to select default
1395 RooFit::PrintLevel(minimPrintLevel-1),
1396 RooFit::Hesse(false),
1397 RooFit::Constrain(constrainParams),
1398 RooFit::Offset(GetGlobalRooStatsConfig().useLikelihoodOffset),
1400 };
1401
1402 RooLinkedList argList;
1403 for (auto& arg : args) {
1404 argList.Add(&arg);
1405 }
1406 model.fitTo(realData, argList);
1407 if (verbose>0) {
1408 tw2.Stop();
1409 oocoutP(nullptr,Generation) << "fit time : " << tw2.RealTime() << " s (real) " << tw2.CpuTime() << " s (cpu)" << std::endl;
1410 }
1411 if (verbose > 1) {
1412 // after the fit the nuisance parameters will have their best fit value
1413 if (model.GetNuisanceParameters() ) {
1414 oocoutI(nullptr,Generation) << "Nuisance parameters after fit for asimov dataset: " << std::endl;
1415 model.GetNuisanceParameters()->Print("V");
1416 }
1417 }
1418
1419 if (verbose < 2) RooMsgService::instance().setGlobalKillBelow(msglevel);
1420
1421 }
1422
1423 // restore the parameters which were set constant
1424 SetAllConstant(paramsSetConstant, false);
1425
1426 std::unique_ptr<RooArgSet> allParams{model.GetPdf()->getParameters(realData)};
1427
1428 RooStats::RemoveConstantParameters(allParams.get());
1429
1430 // if a RooArgSet of poi is passed , different poi will be used for generating the Asimov data set
1431 if (genPoiValues) {
1432 allParams->assign(*genPoiValues);
1433 }
1434
1435 // now do the actual generation of the AsimovData Set
1436 // no need to pass parameters values since we have set them before
1437 return MakeAsimovData(model, *allParams, asimovGlobObs);
1438}
1439
1440////////////////////////////////////////////////////////////////////////////////
1441/// \param model ModelConfig that contains the model pdf and the model parameters
1442/// \param allParamValues The parameters of the model will be set to the values given in this set
1443/// \param[out] asimovGlobObs Global observables set to values satisfying the constraints
1444/// \return Asimov data set. The user takes ownership.
1445///
1446/// The parameter values (including the nuisance parameter) can result from a fit to data or be at the nominal values.
1447///
1448
1449RooAbsData * AsymptoticCalculator::MakeAsimovData(const ModelConfig & model, const RooArgSet & allParamValues, RooArgSet & asimovGlobObs) {
1450
1451 int verbose = fgPrintLevel();
1452
1453 TStopwatch tw;
1454 tw.Start();
1455
1456 // set the parameter values (do I need the poi to be constant ? )
1457 // the nuisance parameter values could be set at their fitted value (the MLE)
1458 if (!allParamValues.empty()) {
1459 std::unique_ptr<RooArgSet> allVars{model.GetPdf()->getVariables()};
1460 allVars->assign(allParamValues);
1461 }
1462
1463
1464 // generate the Asimov data set for the observables
1465 RooAbsData * asimov = GenerateAsimovData(*model.GetPdf() , *model.GetObservables() );
1466
1467 if (verbose>0) {
1468 oocoutI(nullptr,Generation) << "Generated Asimov data for observables "; (model.GetObservables() )->Print();
1469 if (verbose > 1) {
1470 if (asimov->numEntries() == 1 ) {
1471 oocoutI(nullptr,Generation) << "--- Asimov data values \n";
1472 asimov->get()->Print("v");
1473 }
1474 else {
1475 oocoutI(nullptr,Generation) << "--- Asimov data numEntries = " << asimov->numEntries() << " sumOfEntries = " << asimov->sumEntries() << std::endl;
1476 }
1477 tw.Stop();
1478 oocoutI(nullptr,Generation) << "\ttime for generating : " << tw.RealTime() << " s (real) " << tw.CpuTime() << " s (cpu)" << std::endl;
1479 }
1480 }
1481
1482
1483 // Now need to have in ASIMOV the data sets also the global observables
1484 // Their values must be the one satisfying the constraint.
1485 // to do it make a nuisance pdf with all product of constraints and then
1486 // assign to each constraint a glob observable value = to the current fitted nuisance parameter value
1487 // IN general one should solve in general the system of equations f( gobs| nuispar ) = 0 where f are the
1488 // derivatives of the constraint with respect the nuisance parameter and they are evaluated at the best fit nuisance
1489 // parameter points
1490 // As simple solution assume that constrain has a direct dependence on the nuisance parameter, i.e.
1491 // Constraint (gobs, func( nuispar) ) and the condition is satisfied for
1492 // gobs = func( nuispar) where nunispar is at the MLE value
1493
1494
1495 if (model.GetGlobalObservables() && !model.GetGlobalObservables()->empty()) {
1496
1497 if (verbose>1) {
1498 oocoutI(nullptr,Generation) << "Generating Asimov data for global observables " << std::endl;
1499 }
1500
1501 RooArgSet gobs(*model.GetGlobalObservables());
1502
1503 // snapshot data global observables
1504 RooArgSet snapGlobalObsData;
1505 SetAllConstant(gobs, true);
1506 gobs.snapshot(snapGlobalObsData);
1507
1508
1509 RooArgSet nuis;
1510 if (model.GetNuisanceParameters()) nuis.add(*model.GetNuisanceParameters());
1511 if (nuis.empty()) {
1512 oocoutW(nullptr,Generation) << "AsymptoticCalculator::MakeAsimovData: model does not have nuisance parameters but has global observables"
1513 << " set global observables to model values " << std::endl;
1514 asimovGlobObs.assign(gobs);
1515 return asimov;
1516 }
1517
1518 // part 1: create the nuisance pdf
1519 std::unique_ptr<RooAbsPdf> nuispdf(RooStats::MakeNuisancePdf(model,"TempNuisPdf") );
1520 if (nuispdf == nullptr) {
1521 oocoutF(nullptr, Generation) << "AsymptoticCalculator::MakeAsimovData: model has nuisance parameters and "
1522 "global obs but no nuisance pdf "
1523 << std::endl;
1524 }
1525 // unfold the nuisance pdf if it is a prod pdf
1526 RooArgList pdfList;
1527 RooProdPdf *prod = dynamic_cast<RooProdPdf *>(nuispdf.get());
1528 if (prod ) {
1529 pdfList.add(prod->pdfList());
1530 } else {
1531 // nothing to unfold - just use the pdf
1532 pdfList.add(*nuispdf);
1533 }
1534
1535 for (auto *cterm : static_range_cast<RooAbsPdf *>(pdfList)) {
1536 assert(dynamic_cast<RooAbsPdf *>(static_cast<RooAbsArg *>(cterm)) &&
1537 "AsimovUtils: a factor of the nuisance pdf is not a Pdf!");
1538
1539 if (!cterm->dependsOn(nuis)) continue; // dummy constraints
1540 // skip also the case of uniform components
1541 if (typeid(*cterm) == typeid(RooUniform)) continue;
1542
1543 std::unique_ptr<RooArgSet> cpars(cterm->getParameters(&gobs));
1544 std::unique_ptr<RooArgSet> cgobs(cterm->getObservables(&gobs));
1545 if (cgobs->size() > 1) {
1546 oocoutE(nullptr,Generation) << "AsymptoticCalculator::MakeAsimovData: constraint term " << cterm->GetName()
1547 << " has multiple global observables -cannot generate - skip it" << std::endl;
1548 continue;
1549 }
1550 else if (cgobs->empty()) {
1551 oocoutW(nullptr, Generation)
1552 << "AsymptoticCalculator::MakeAsimovData: constraint term " << cterm->GetName()
1553 << " has no global observables - skip it" << std::endl;
1554 continue;
1555 }
1556 // the variable representing the global observable
1557 RooRealVar &rrv = dynamic_cast<RooRealVar &>(*cgobs->first());
1558
1559 // remove the constant parameters in cpars
1561 if (cpars->size() != 1) {
1562 oocoutE(nullptr, Generation)
1563 << "AsymptoticCalculator::MakeAsimovData:constraint term "
1564 << cterm->GetName() << " has multiple floating params - cannot generate - skip it " << std::endl;
1565 continue;
1566 }
1567
1568 bool foundServer = false;
1569 // note : this will work only for this type of constraints
1570 // expressed as RooPoisson, RooGaussian, RooLognormal, RooGamma
1571 TClass * cClass = cterm->IsA();
1572 if (verbose > 2) oocoutI(nullptr,Generation) << "Constraint " << cterm->GetName() << " of type " << cClass->GetName() << std::endl;
1573 if ( cClass != RooGaussian::Class() && cClass != RooPoisson::Class() &&
1574 cClass != RooGamma::Class() && cClass != RooLognormal::Class() &&
1575 cClass != RooBifurGauss::Class() ) {
1576 TString className = (cClass) ? cClass->GetName() : "undefined";
1577 oocoutW(nullptr, Generation)
1578 << "AsymptoticCalculator::MakeAsimovData:constraint term "
1579 << cterm->GetName() << " of type " << className
1580 << " is a non-supported type - result might be not correct " << std::endl;
1581 }
1582
1583 // in case of a Poisson constraint make sure the rounding is not set
1584 if (cClass == RooPoisson::Class() ) {
1585 RooPoisson * pois = static_cast<RooPoisson*>(cterm);
1586 assert(dynamic_cast<RooPoisson *>(cterm));
1587 pois->setNoRounding(true);
1588 }
1589
1590 // look at server of the constraint term and check if the global observable is part of the server
1591 RooAbsArg * arg = cterm->findServer(rrv);
1592 if (!arg) {
1593 // special case is for the Gamma where one might define the global observable n and you have a Gamma(b, n+1, ...._
1594 // in this case n+1 is the server and we don;t have a direct dependency, but we want to set n to the b value
1595 // so in case of the Gamma ignore this test
1596 if ( cClass != RooGamma::Class() ) {
1597 oocoutE(nullptr, Generation)
1598 << "AsymptoticCalculator::MakeAsimovData:constraint term "
1599 << cterm->GetName() << " has no direct dependence on global observable- cannot generate it " << std::endl;
1600 continue;
1601 }
1602 }
1603
1604 // loop on the server of the constraint term
1605 // need to treat the Gamma as a special case
1606 // the mode of the Gamma is (k-1)*theta where theta is the inverse of the rate parameter.
1607 // we assume that the global observable is defined as ngobs = k-1 and the theta parameter has the name theta otherwise we use other procedure which might be wrong
1608 RooAbsReal * thetaGamma = nullptr;
1609 if ( cClass == RooGamma::Class() ) {
1610 for (RooAbsArg *a2 : cterm->servers()) {
1611 if (TString(a2->GetName()).Contains("theta") ) {
1612 thetaGamma = dynamic_cast<RooAbsReal*>(a2);
1613 break;
1614 }
1615 }
1616 if (thetaGamma == nullptr) {
1617 oocoutI(nullptr, Generation)
1618 << "AsymptoticCalculator::MakeAsimovData:constraint term "
1619 << cterm->GetName() << " is a Gamma distribution and no server named theta is found. Assume that the Gamma scale is 1 " << std::endl;
1620 }
1621 else if (verbose>2) {
1622 oocoutI(nullptr,Generation) << "Gamma constraint has a scale " << thetaGamma->GetName() << " = " << thetaGamma->getVal() << std::endl;
1623 }
1624 }
1625 for (RooAbsArg *a2 : cterm->servers()) {
1626 RooAbsReal * rrv2 = dynamic_cast<RooAbsReal *>(a2);
1627 if (verbose > 2) oocoutI(nullptr,Generation) << "Loop on constraint server term " << a2->GetName() << std::endl;
1628 if (rrv2 && rrv2->dependsOn(nuis) ) {
1629
1630
1631 // found server depending on nuisance
1632 if (foundServer) {
1633 oocoutE(nullptr,Generation) << "AsymptoticCalculator::MakeAsimovData:constraint term "
1634 << cterm->GetName() << " constraint term has more server depending on nuisance- cannot generate it " <<
1635 std::endl;
1636 foundServer = false;
1637 break;
1638 }
1639 if (thetaGamma && thetaGamma->getVal() > 0) {
1640 rrv.setVal( rrv2->getVal() / thetaGamma->getVal() );
1641 } else {
1642 rrv.setVal(rrv2->getVal());
1643 }
1644 foundServer = true;
1645
1646 if (verbose > 2) {
1647 oocoutI(nullptr,Generation) << "setting global observable " << rrv.GetName() << " to value " << rrv.getVal()
1648 << " which comes from " << rrv2->GetName() << std::endl;
1649 }
1650 }
1651 }
1652
1653 if (!foundServer) {
1654 oocoutE(nullptr,Generation) << "AsymptoticCalculator::MakeAsimovData - can't find nuisance for constraint term - global observables will not be set to Asimov value " << cterm->GetName() << std::endl;
1655 oocoutE(nullptr,Generation) << "Parameters: " << std::endl;
1656 cpars->Print("V");
1657 oocoutE(nullptr,Generation) << "Observables: " << std::endl;
1658 cgobs->Print("V");
1659 }
1660 }
1661
1662 // make a snapshot of global observables
1663 // needed this ?? (LM)
1664
1665 asimovGlobObs.removeAll();
1666 SetAllConstant(gobs, true);
1667 gobs.snapshot(asimovGlobObs);
1668
1669 // revert global observables to the data value
1670 gobs.assign(snapGlobalObsData);
1671
1672 if (verbose>0) {
1673 oocoutI(nullptr,Generation) << "Generated Asimov data for global observables ";
1674 if (verbose == 1) gobs.Print();
1675 }
1676
1677 if (verbose > 1) {
1678 oocoutI(nullptr,Generation) << "\nGlobal observables for data: " << std::endl;
1679 gobs.Print("V");
1680 oocoutI(nullptr,Generation) << "\nGlobal observables for asimov: " << std::endl;
1681 asimovGlobObs.Print("V");
1682 }
1683
1684
1685 }
1686
1687 return asimov;
1688
1689}
#define f(i)
Definition RSha256.hxx:104
#define a(i)
Definition RSha256.hxx:99
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define oocoutW(o, a)
#define oocoutE(o, a)
#define oocoutF(o, a)
#define oocoutI(o, a)
#define ooccoutI(o, a)
#define ooccoutP(o, a)
#define oocoutP(o, a)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
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 char Point_t Rectangle_t WindowAttributes_t index
Class for finding the root of a one dimensional function using the Brent algorithm.
bool SetFunction(const ROOT::Math::IGenFunction &f, double xlow, double xup) override
Sets the function for the rest of the algorithms.
bool Solve(int maxIter=100, double absTol=1E-8, double relTol=1E-10) override
Returns the X value corresponding to the function value fy for (xmin<x<xmax).
double Root() const override
Returns root value.
static const std::string & DefaultMinimizerAlgo()
Template class to wrap any C++ callable object which takes one argument i.e.
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
void Print(Option_t *options=nullptr) const override
Print the object to the defaultPrintStream().
Definition RooAbsArg.h:239
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
bool isConstant() const
Check if the "Constant" attribute is set.
Definition RooAbsArg.h:284
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...
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
RooAbsArg * findServer(const char *name) const
Return server of this with name name. Returns nullptr if not found.
Definition RooAbsArg.h:148
virtual const char * getCurrentLabel() const
Return label string of current state.
Int_t numTypes(const char *=nullptr) const
Return number of types defined (in range named rangeName if rangeName!=nullptr)
bool contains(const char *name) const
Check if collection contains an argument with a specific name.
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
Storage_t const & get() const
Const access to the underlying stl container.
Int_t getSize() const
Return the number of elements in the collection.
const char * GetName() const override
Returns name of object.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
void assign(const RooAbsCollection &other) const
Sets the value, cache and constant attribute of any argument in our set that also appears in the othe...
Storage_t::size_type size() const
RooAbsArg * first() const
RooAbsArg * find(const char *name) const
Find object with given name in list.
void Print(Option_t *options=nullptr) const override
This method must be overridden when a class wants to print itself.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:55
virtual double sumEntries() const =0
Return effective number of entries in dataset, i.e., sum all weights.
virtual const RooArgSet * get() const
Definition RooAbsData.h:99
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
virtual double expectedEvents(const RooArgSet *nset) const
Return expected number of events to be used in calculation of extended likelihood.
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:214
virtual Int_t getBins(const char *name=nullptr) const
Get number of bins of currently defined range.
void setConstant(bool value=true)
static constexpr int DefaultNBins
Historical default number of bins, injected by routines that need a concrete bin count when a variabl...
virtual double getMax(const char *name=nullptr) const
Get maximum of currently defined range.
virtual double getMin(const char *name=nullptr) const
Get minimum of currently defined range.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
static bool hideOffset()
static void setHideOffset(bool flag)
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:159
static TClass * Class()
Object to represent discrete states.
Definition RooCategory.h:28
bool setIndex(Int_t index, bool printError=true) override
Set value by specifying the index code of the desired state.
value_type getCurrentIndex() const final
Return current index.
Definition RooCategory.h:40
static TClass * Class()
Container class to hold unbinned data.
Definition RooDataSet.h:32
void add(const RooArgSet &row, double weight, double weightError)
Add one ore more rows of data.
static TClass * Class()
Plain Gaussian p.d.f.
Definition RooGaussian.h:24
static TClass * Class()
RooAbsReal const & getX() const
Get the x variable.
Definition RooGaussian.h:45
RooAbsReal const & getMean() const
Get the mean parameter.
Definition RooGaussian.h:48
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
virtual void Add(TObject *arg)
static TClass * Class()
Wrapper class around ROOT::Math::Minimizer that provides a seamless interface between the minimizer f...
static RooMsgService & instance()
Return reference to singleton instance.
void setGlobalKillBelow(RooFit::MsgLevel level)
RooFit::MsgLevel globalKillBelow() const
Multivariate Gaussian p.d.f.
const RooArgList & xVec() const
const RooArgList & muVec() const
Poisson pdf.
Definition RooPoisson.h:18
RooAbsReal const & getX() const
Get the x variable.
Definition RooPoisson.h:44
static TClass * Class()
void setNoRounding(bool flag=true)
Switch off/on rounding of x to the nearest integer.
Definition RooPoisson.h:35
RooAbsReal const & getMean() const
Get the mean parameter.
Definition RooPoisson.h:47
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:35
const RooArgList & pdfList() const
Definition RooProdPdf.h:69
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
double getError() const
Definition RooRealVar.h:59
void setBins(Int_t nBins, const char *name=nullptr, bool shared=true)
Create a uniform binning under name 'name' for this variable.
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
RooAbsPdf * getPdf(RooStringView catName) const
Return the p.d.f associated with the given index category name.
const RooAbsCategoryLValue & indexCat() const
std::unique_ptr< RooFitResult > fFitResultCondObs
! result of conditional fit to observed data
std::unique_ptr< RooFitResult > fFitResultUncondAsimov
! result of fit to Asimov data at the alt POI
static double GetExpectedPValues(double pnull, double palt, double nsigma, bool usecls, bool oneSided=true)
function given the null and the alt p value - return the expected one given the N - sigma value
std::unique_ptr< RooFitResult > fFitResultCondAsimov
! result of conditional fit to Asimov data
static void SetPrintLevel(int level)
set print level (static function)
RooArgSet fAsimovGlobObs
snapshot of Asimov global observables
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...
int fUseQTilde
flag to indicate if using qtilde or not (-1 (default based on RooRealVar)), 0 false,...
bool fIsInitialized
! flag to check if calculator is initialized
HypoTestResult * GetHypoTest() const override
re-implement HypoTest computation using the asymptotic
bool fOneSided
for one sided PL test statistic (upper limits)
RooArgSet fBestFitParams
snapshot of all best fitted Parameter values
AsymptoticCalculator(RooAbsData &data, const ModelConfig &altModel, const ModelConfig &nullModel, bool nominalAsimov=false)
constructor for asymptotic calculator from Data set and ModelConfig
std::unique_ptr< RooFitResult > fFitResultUncondObs
! result of unconditional fit to observed data
bool fSigned
use signed (uncapped) PL test statistic
bool fOneSidedDiscovery
for one sided PL test statistic (for discovery)
RooAbsData * fAsimovData
asimov data set
RooArgSet fBestFitPoi
snapshot of best fitted POI values
static RooAbsData * MakeAsimovData(RooAbsData &data, const ModelConfig &model, const RooArgSet &poiValues, RooArgSet &globObs, const RooArgSet *genPoiValues=nullptr)
Make Asimov data.
bool fNominalAsimov
make Asimov at nominal parameter values
bool Initialize() const
initialize the calculator by performing a global fit and make the Asimov data set
Common base class for the Hypothesis Test Calculators.
const ModelConfig * GetNullModel(void) const
const ModelConfig * GetAlternateModel(void) const
HypoTestResult is a base class for results from hypothesis tests.
virtual double CLs() const
is simply (not a method, but a quantity)
< A class that holds configuration information for a model using a workspace as a store
Definition ModelConfig.h:34
std::unique_ptr< RooAbsReal > createNLL(RooAbsData &data, CmdArgs_t const &...cmdArgs) const
Wrapper around RooAbsPdf::createNLL(), where the pdf and some configuration options are retrieved fro...
const RooArgSet * GetParametersOfInterest() const
get RooArgSet containing the parameter of interest (return nullptr if not existing)
const RooArgSet * GetObservables() const
get RooArgSet for observables (return nullptr if not existing)
const RooArgSet * GetSnapshot() const
get RooArgSet for parameters for a particular hypothesis (return nullptr if not existing)
RooAbsPdf * GetPdf() const
get model PDF (return nullptr if pdf has not been specified or does not exist)
Flat p.d.f.
Definition RooUniform.h:24
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
TClass * IsA() const override
Definition TClass.h:638
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
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.
Double_t CpuTime()
Stop the stopwatch (if it is running) and return the cputime (in seconds) passed between the start an...
void Stop()
Stop the stopwatch.
Basic string class.
Definition TString.h:137
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:2460
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:642
RooCmdArg Index(RooCategory &icat)
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg Import(const char *state, TH1 &histo)
RooCmdArg Offset(std::string const &mode)
RooCmdArg Constrain(const RooArgSet &params)
RooCmdArg Minimizer(const char *type, const char *alg=nullptr)
RooCmdArg Hesse(bool flag=true)
RooCmdArg Strategy(Int_t code)
RooCmdArg EvalErrorWall(bool flag)
RooCmdArg PrintLevel(Int_t code)
double normal_cdf_c(double x, double sigma=1, double x0=0)
Complement of the cumulative distribution function of the normal (Gaussian) distribution (upper tail)...
double normal_cdf(double x, double sigma=1, double x0=0)
Cumulative distribution function of the normal (Gaussian) distribution (lower tail).
double normal_quantile(double z, double sigma)
Inverse ( ) of the cumulative distribution function of the lower tail of the normal (Gaussian) distri...
double normal_quantile_c(double z, double sigma)
Inverse ( ) of the cumulative distribution function of the upper tail of the normal (Gaussian) distri...
Double_t x[n]
Definition legend1.C:17
double nll(double pdf, double weight, int binnedL, int doBinOffset)
Definition MathFuncs.h:450
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
Namespace for the RooStats classes.
Definition CodegenImpl.h:67
bool SetAllConstant(const RooAbsCollection &coll, bool constant=true)
utility function to set all variable constant in a collection (from G.
void RemoveConstantParameters(RooArgSet *set)
std::string const & NLLOffsetMode()
Test what offsetting mode RooStats should use by default.
RooAbsPdf * MakeNuisancePdf(RooAbsPdf &pdf, const RooArgSet &observables, const char *name)
extract constraint terms from pdf
RooStatsConfig & GetGlobalRooStatsConfig()
Retrieve the config object which can be used to set flags for things like offsetting the likelihood o...
Bool_t IsNaN(Double_t x)
Definition TMath.h:905
Double_t QuietNaN()
Returns a quiet NaN as defined by IEEE 754.
Definition TMath.h:915
PaltFunction(double offset, double pval, int icase)