From 768c291a2415364051a20781fa907cb1ce871ab4 Mon Sep 17 00:00:00 2001 From: Lorenzo Moneta <Lorenzo.Moneta@cern.ch> Date: Thu, 3 May 2012 16:38:46 +0000 Subject: [PATCH] fix asymptotic calculator for 2-sided test statistic and for one sided discovery - add also method to retrieve the global best fit parameters git-svn-id: http://root.cern.ch/svn/root/trunk@44101 27541ba8-7e3a-0410-8455-c3a389f83636 --- .../inc/RooStats/AsymptoticCalculator.h | 16 +- roofit/roostats/src/AsymptoticCalculator.cxx | 184 ++++++++++++------ 2 files changed, 133 insertions(+), 67 deletions(-) diff --git a/roofit/roostats/inc/RooStats/AsymptoticCalculator.h b/roofit/roostats/inc/RooStats/AsymptoticCalculator.h index 99cb7c88cd3..5ed225d341b 100644 --- a/roofit/roostats/inc/RooStats/AsymptoticCalculator.h +++ b/roofit/roostats/inc/RooStats/AsymptoticCalculator.h @@ -73,14 +73,21 @@ namespace RooStats { // function given the null and the alt p value - return the expected one given the N - sigma value static double GetExpectedPValues(double pnull, double palt, double nsigma, bool usecls ); - // get expected limit -// static void GetExpectedLimit(double nsigma, double alpha, double &clsblimit, double &clslimit); - + // set test statistic for one sided (upper limits) void SetOneSided(bool on) { fOneSided = on; } + // set the test statistics for one-sided discovery + void SetOneSidedDiscovery(bool on) { fOneSidedDiscovery = on; } + // set using of qtilde, by default is controlled if RoORealVar is limited or not void SetQTilde(bool on) { fUseQTilde = on; } + // return snapshot of the best fit parameter + const RooArgSet & GetBestFitPoi() const { return fBestFitPoi; } + // return best fit parameter (firs of poi) + const RooRealVar * GetMuHat() const { return dynamic_cast<RooRealVar*>(fBestFitPoi.first()); } + // return best fit value for all parameters + const RooArgSet & GetBestFitParams() const { return fBestFitPoi; } static void SetPrintLevel(int level); @@ -112,7 +119,8 @@ namespace RooStats { private: - bool fOneSided; // for one sided PL test statistic + bool fOneSided; // for one sided PL test statistic (upper limits) + bool fOneSidedDiscovery; // for one sided PL test statistic (for discovery) mutable int fUseQTilde; // flag to indicate if using qtilde or not (-1 (default based on RooRealVar)), 0 false, 1 (true) static int fgPrintLevel; // control print level (0 minimal, 1 normal, 2 debug) mutable double fNLLObs; diff --git a/roofit/roostats/src/AsymptoticCalculator.cxx b/roofit/roostats/src/AsymptoticCalculator.cxx index 324ba8a64c3..375af7972b4 100644 --- a/roofit/roostats/src/AsymptoticCalculator.cxx +++ b/roofit/roostats/src/AsymptoticCalculator.cxx @@ -435,15 +435,19 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const { - if (qmu < 0) { - - oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Found a negative value of the qmu - retry to do the unconditional fit " - << std::endl; + if (qmu < 0 || TMath::IsNaN(fNLLObs) ) { + + if (qmu < 0) + oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Found a negative value of the qmu - retry to do the unconditional fit " + << std::endl; + else + oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: unconditional fit failed before - retry to do it now " + << std::endl; double nll = EvaluateNLL( *nullPdf, const_cast<RooAbsData&>(*GetData())); - if (nll < fNLLObs) { + if (nll < fNLLObs || (TMath::IsNaN(fNLLObs) && !TMath::IsNaN(nll) ) ) { oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Found a better unconditional minimum " << " old NLL = " << fNLLObs << " old muHat " << muHat->getVal() << std::endl; @@ -463,35 +467,28 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const { qmu = 2.*(condNLL - fNLLObs); - std::cout << " New qmu value is " << qmu << std::endl; + if (verbose > 0) + oocoutP((TObject*)0,Eval) << "After unconditional refit, new qmu value is " << qmu << std::endl; } } - if (qmu < 0) { - oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: qmu is still < 0 for mu = " - << muTest << " resulting p-value will not be meaningful " + if (qmu < 0 ) { + oocoutE((TObject*)0,Minimization) << "AsymptoticCalculator: qmu is still < 0 for mu = " + << muTest << " return a dummy result " << std::endl; + return new HypoTestResult(); } - - //check for one side condition (remember this is valid only for one poi) - if (fOneSided ) { - if ( muHat->getVal() > muTest->getVal() ) { - oocoutI((TObject*)0,Eval) << "Using one-sided qmu - setting qmu to zero muHat = " << muHat->getVal() - << " muTest = " << muTest->getVal() << std::endl; - qmu = 0; - } + if (TMath::IsNaN(qmu) ) { + oocoutE((TObject*)0,Minimization) << "AsymptoticCalculator: failure in fitting for qmu or qmuA " + << muTest << " return a dummy result " + << std::endl; + return new HypoTestResult(); } - // asymptotic formula for pnull (for only one POI) - // From fact that qmu is a chi2 with ndf=1 - double sqrtqmu = (qmu > 0) ? std::sqrt(qmu) : 0; - double pnull = ROOT::Math::normal_cdf_c( sqrtqmu, 1.); - - // compute conditional ML on Asimov data set // (need to const cast because it uses fitTo which is a non const method @@ -518,15 +515,19 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const { if (verbose > 0) oocoutP((TObject*)0,Eval) << "\t ASIMOV data qmu_A = " << qmu_A << " condNLL = " << condNLL_A << " uncond " << fNLLAsimov << std::endl; - if (qmu_A < 0) { + if (qmu_A < 0 || TMath::IsNaN(fNLLAsimov) ) { - oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Found a negative value of the qmu Asimov- retry to do the unconditional fit " - << std::endl; + if (qmu_A < 0) + oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Found a negative value of the qmu Asimov- retry to do the unconditional fit " + << std::endl; + else + oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Fit failed for unconditional the qmu Asimov- retry unconditional fit " + << std::endl; double nll = EvaluateNLL( *nullPdf, *fAsimovData ); - if (nll < fNLLAsimov) { + if (nll < fNLLAsimov || (TMath::IsNaN(fNLLAsimov) && !TMath::IsNaN(nll) )) { oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: Found a better unconditional minimum for Asimov data set" << " old NLL = " << fNLLAsimov << std::endl; @@ -537,64 +538,121 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const { << " NLL = " << fNLLAsimov << std::endl; qmu_A = 2.*(condNLL_A - fNLLAsimov); - std::cout << " New qmu value is " << qmu_A << std::endl; + if (verbose > 0) + oocoutP((TObject*)0,Eval) << "After unconditional Asimov refit, new qmu_A value is " << qmu_A << std::endl; } } if (qmu_A < 0) { - oocoutW((TObject*)0,Minimization) << "AsymptoticCalculator: qmu_A is still < 0 for mu = " - << muTest << " resulting p-value will not be meaningful " + oocoutE((TObject*)0,Minimization) << "AsymptoticCalculator: qmu_A is still < 0 for mu = " + << muTest << " return a dummy result " + << std::endl; + return new HypoTestResult(); + } + if (TMath::IsNaN(qmu) ) { + oocoutE((TObject*)0,Minimization) << "AsymptoticCalculator: failure in fitting for qmu or qmuA " + << muTest << " return a dummy result " << std::endl; + return new HypoTestResult(); } // restore previous value of global observables globObs = globObsSnapshot; + // now we compute p-values using the asumptotic formulae + // described in the paper + // Cowan et al, Eur.Phys.J. C (2011) 71:1554 - // do I need to do something for Asimov on the one-sided case ??? -// if (fOneSided) { -// if ( muHat->getVal() > muTest->getVal() ) qmu_A = 0; -// } -// } - - - - // asymptotic formula for palt based on Asimov data set - // See Eur.Phys.J C(2011( 71:1554 - - double sqrtqmu_A = (qmu_A > 0) ? std::sqrt(qmu_A) : 0; - - double palt = ROOT::Math::normal_cdf( sqrtqmu_A - sqrtqmu, 1.); - - // formula for Qtilde (need to distinguish case when qmu > qmuA + // first try to guess autoatically if needed to use qtilde (or ttilde in case of two sided) + // if explicitly fUseQTilde this was not set + // qtilde is in this case used if poi is bounded at the value of the alt hypothesis + // for Qtilde (need to distinguish case when qmu > qmuA = mu^2/ sigma^2) // (see Cowan et al, Eur.Phys.J. C(2011) 71:1554 paper equations 64 and 65 // (remember qmu_A = mu^2/sigma^2 ) bool useQTilde = false; // default case (check if poi is limited or not to a zero value) - if (fUseQTilde == -1) { - // alternate snapshot is value for which background is zero - RooRealVar * muAlt = dynamic_cast<RooRealVar*>( GetAlternateModel()->GetSnapshot()->first() ); - assert(muAlt != 0); - if (muTest->getMin() == muAlt->getVal() ) { - fUseQTilde = 1; - oocoutI((TObject*)0,InputArguments) << "Minimum of POI is " << muTest->getMin() << " corresponds to alt snapshot " << muAlt->getVal() << " - using qtilde asymptotic formulae " << std::endl; - } else { - fUseQTilde = 0; - oocoutI((TObject*)0,InputArguments) << "Minimum of POI is " << muTest->getMin() << " is different to alt snapshot " << muAlt->getVal() << " - using standard q asymptotic formulae " << std::endl; - } + if (!fOneSidedDiscovery) { // qtilde is not a discovery test + if (fUseQTilde == -1 && !fOneSidedDiscovery) { + // alternate snapshot is value for which background is zero (for limits) + RooRealVar * muAlt = dynamic_cast<RooRealVar*>( GetAlternateModel()->GetSnapshot()->first() ); + // null snapshot is value for which background is zero (for discovery) + //RooRealVar * muNull = dynamic_cast<RooRealVar*>( GetNullModel()->GetSnapshot()->first() ); + assert(muAlt != 0 ); + if (muTest->getMin() == muAlt->getVal() ) { + fUseQTilde = 1; + oocoutI((TObject*)0,InputArguments) << "Minimum of POI is " << muTest->getMin() << " corresponds to alt snapshot - using qtilde asymptotic formulae " << std::endl; + } else { + fUseQTilde = 0; + oocoutI((TObject*)0,InputArguments) << "Minimum of POI is " << muTest->getMin() << " is different to alt snapshot " << muAlt->getVal() + << " - using standard q asymptotic formulae " << std::endl; + } + } + else + useQTilde = fUseQTilde; } - else - useQTilde = fUseQTilde; - - if (useQTilde && qmu > qmu_A) { - pnull = ROOT::Math::normal_cdf_c( (qmu + qmu_A)/(2 * sqrtqmu_A), 1.); - palt = ROOT::Math::normal_cdf_c( (qmu - qmu_A)/(2 * sqrtqmu_A), 1.); + + //check for one side condition (remember this is valid only for one poi) + if (fOneSided ) { + if ( muHat->getVal() > muTest->getVal() ) { + oocoutI((TObject*)0,Eval) << "Using one-sided qmu - setting qmu to zero muHat = " << muHat->getVal() + << " muTest = " << muTest->getVal() << std::endl; + qmu = 0; + } } + if (fOneSidedDiscovery ) { + if ( muHat->getVal() < muTest->getVal() ) { + oocoutI((TObject*)0,Eval) << "Using one-sided qmu - setting qmu to zero muHat = " << muHat->getVal() + << " muTest = " << muTest->getVal() << std::endl; + qmu = 0; + } + } + + // asymptotic formula for pnull and from paper Eur.Phys.J C 2011 71:1554 + // we have 4 different cases: + // t(mu), t_tilde(mu) for the 2-sided + // q(mu) and q_tilde(mu) for the one -sided test statistics + + double pnull = -1, palt = -1; + + // asymptotic formula for pnull (for only one POI) + // From fact that qmu is a chi2 with ndf=1 + + double sqrtqmu = (qmu > 0) ? std::sqrt(qmu) : 0; + double sqrtqmu_A = (qmu_A > 0) ? std::sqrt(qmu_A) : 0; + if (fOneSided || fOneSidedDiscovery) { + pnull = ROOT::Math::normal_cdf_c( sqrtqmu, 1.); + palt = ROOT::Math::normal_cdf( sqrtqmu_A - sqrtqmu, 1.); + } + else { + // for 2-sided PL + pnull = 2.*ROOT::Math::normal_cdf_c( sqrtqmu, 1.); + palt = ROOT::Math::normal_cdf_c( sqrtqmu + sqrtqmu_A, 1.) + + ROOT::Math::normal_cdf_c( sqrtqmu - sqrtqmu_A, 1.); + + } + + if (useQTilde ) { + if (fOneSided) { + if ( qmu > qmu_A) { + pnull = ROOT::Math::normal_cdf_c( (qmu + qmu_A)/(2 * sqrtqmu_A), 1.); + palt = ROOT::Math::normal_cdf_c( (qmu - qmu_A)/(2 * sqrtqmu_A), 1.); + } + } + else { // for 2 sided test statistic (for one sided discovery qtilde is false) + if ( qmu > qmu_A) { + pnull = ROOT::Math::normal_cdf_c(sqrtqmu,1.) + + ROOT::Math::normal_cdf_c( (qmu + qmu_A)/(2 * sqrtqmu_A), 1.); + palt = ROOT::Math::normal_cdf_c( sqrtqmu_A + sqrtqmu, 1.) + + ROOT::Math::normal_cdf_c( (qmu - qmu_A)/(2 * sqrtqmu_A), 1.); + } + } + } + // create an HypoTest result but where the sampling distributions are set to zero -- GitLab