72 ClassImp(RooStats::HypoTestInverter);
74 using namespace RooStats;
78 double HypoTestInverter::fgCLAccuracy = 0.005;
79 unsigned int HypoTestInverter::fgNToys = 500;
81 double HypoTestInverter::fgAbsAccuracy = 0.05;
82 double HypoTestInverter::fgRelAccuracy = 0.05;
83 std::string HypoTestInverter::fgAlgo =
"logSecant";
85 bool HypoTestInverter::fgCloseProof =
false;
89 template<
class HypoTestType>
90 struct HypoTestWrapper {
92 static void SetToys(HypoTestType * h,
int toyNull,
int toyAlt) { h->SetToys(toyNull,toyAlt); }
99 void HypoTestInverter::SetCloseProof(Bool_t flag) {
107 RooRealVar * HypoTestInverter::GetVariableToScan(
const HypoTestCalculatorGeneric &hc) {
109 RooRealVar * varToScan = 0;
110 const ModelConfig * mc = hc.GetNullModel();
112 const RooArgSet * poi = mc->GetParametersOfInterest();
113 if (poi) varToScan =
dynamic_cast<RooRealVar*
> (poi->first() );
116 mc = hc.GetAlternateModel();
118 const RooArgSet * poi = mc->GetParametersOfInterest();
119 if (poi) varToScan =
dynamic_cast<RooRealVar*
> (poi->first() );
128 void HypoTestInverter::CheckInputModels(
const HypoTestCalculatorGeneric &hc,
const RooRealVar & scanVariable) {
129 const ModelConfig * modelSB = hc.GetNullModel();
130 const ModelConfig * modelB = hc.GetAlternateModel();
131 if (!modelSB || ! modelB)
132 oocoutF((TObject*)0,InputArguments) <<
"HypoTestInverter - model are not existing" << std::endl;
133 assert(modelSB && modelB);
135 oocoutI((TObject*)0,InputArguments) <<
"HypoTestInverter ---- Input models: \n"
136 <<
"\t\t using as S+B (null) model : "
137 << modelSB->GetName() <<
"\n"
138 <<
"\t\t using as B (alternate) model : "
139 << modelB->GetName() <<
"\n" << std::endl;
142 RooAbsPdf * bPdf = modelB->GetPdf();
143 const RooArgSet * bObs = modelB->GetObservables();
144 if (!bPdf || !bObs) {
145 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - B model has no pdf or observables defined" << std::endl;
148 RooArgSet * bParams = bPdf->getParameters(*bObs);
150 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - pdf of B model has no parameters" << std::endl;
153 if (bParams->find(scanVariable.GetName() ) ) {
154 const RooArgSet * poiB = modelB->GetSnapshot();
155 if (!poiB || !poiB->find(scanVariable.GetName()) ||
156 ( (RooRealVar*) poiB->find(scanVariable.GetName()) )->getVal() != 0 )
157 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter - using a B model with POI "
158 << scanVariable.GetName() <<
" not equal to zero "
159 <<
" user must check input model configurations " << endl;
160 if (poiB)
delete poiB;
168 HypoTestInverter::HypoTestInverter( ) :
178 fCalcType(kUndefined),
179 fNBins(0), fXmin(1), fXmax(1),
193 HypoTestInverter::HypoTestInverter( HypoTestCalculatorGeneric& hc,
194 RooRealVar* scannedVariable,
double size ) :
198 fScannedVariable(scannedVariable),
204 fCalcType(kUndefined),
205 fNBins(0), fXmin(1), fXmax(1),
209 if (!fScannedVariable) {
210 fScannedVariable = HypoTestInverter::GetVariableToScan(hc);
212 if (!fScannedVariable)
213 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - Cannot guess the variable to scan " << std::endl;
215 CheckInputModels(hc,*fScannedVariable);
217 HybridCalculator * hybCalc =
dynamic_cast<HybridCalculator*
>(&hc);
220 fCalculator0 = hybCalc;
223 FrequentistCalculator * freqCalc =
dynamic_cast<FrequentistCalculator*
>(&hc);
225 fCalcType = kFrequentist;
226 fCalculator0 = freqCalc;
229 AsymptoticCalculator * asymCalc =
dynamic_cast<AsymptoticCalculator*
>(&hc);
231 fCalcType = kAsymptotic;
232 fCalculator0 = asymCalc;
235 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - Type of hypotest calculator is not supported " <<std::endl;
246 HypoTestInverter::HypoTestInverter( HybridCalculator& hc,
247 RooRealVar* scannedVariable,
double size ) :
251 fScannedVariable(scannedVariable),
258 fNBins(0), fXmin(1), fXmax(1),
262 if (!fScannedVariable) {
263 fScannedVariable = GetVariableToScan(hc);
265 if (!fScannedVariable)
266 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - Cannot guess the variable to scan " << std::endl;
268 CheckInputModels(hc,*fScannedVariable);
279 HypoTestInverter::HypoTestInverter( FrequentistCalculator& hc,
280 RooRealVar* scannedVariable,
double size ) :
284 fScannedVariable(scannedVariable),
290 fCalcType(kFrequentist),
291 fNBins(0), fXmin(1), fXmax(1),
295 if (!fScannedVariable) {
296 fScannedVariable = GetVariableToScan(hc);
298 if (!fScannedVariable)
299 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - Cannot guess the variable to scan " << std::endl;
301 CheckInputModels(hc,*fScannedVariable);
311 HypoTestInverter::HypoTestInverter( AsymptoticCalculator& hc,
312 RooRealVar* scannedVariable,
double size ) :
316 fScannedVariable(scannedVariable),
322 fCalcType(kAsymptotic),
323 fNBins(0), fXmin(1), fXmax(1),
327 if (!fScannedVariable) {
328 fScannedVariable = GetVariableToScan(hc);
330 if (!fScannedVariable)
331 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - Cannot guess the variable to scan " << std::endl;
333 CheckInputModels(hc,*fScannedVariable);
344 HypoTestInverter::HypoTestInverter( RooAbsData& data, ModelConfig &sbModel, ModelConfig &bModel,
345 RooRealVar * scannedVariable, ECalculatorType type,
double size) :
349 fScannedVariable(scannedVariable),
356 fNBins(0), fXmin(1), fXmax(1),
359 if(fCalcType==kFrequentist) fHC.reset(
new FrequentistCalculator(data, bModel, sbModel));
360 if(fCalcType==kHybrid) fHC.reset(
new HybridCalculator(data, bModel, sbModel)) ;
361 if(fCalcType==kAsymptotic) fHC.reset(
new AsymptoticCalculator(data, bModel, sbModel));
362 fCalculator0 = fHC.get();
364 if (!fScannedVariable) {
365 fScannedVariable = GetVariableToScan(*fCalculator0);
367 if (!fScannedVariable)
368 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - Cannot guess the variable to scan " << std::endl;
370 CheckInputModels(*fCalculator0,*fScannedVariable);
380 HypoTestInverter::HypoTestInverter(
const HypoTestInverter & rhs) :
381 IntervalCalculator(),
383 fCalculator0(0), fScannedVariable(0),
395 HypoTestInverter & HypoTestInverter::operator= (
const HypoTestInverter & rhs) {
396 if (
this == &rhs)
return *
this;
398 fMaxToys = rhs.fMaxToys;
399 fCalculator0 = rhs.fCalculator0;
400 fScannedVariable = rhs.fScannedVariable;
401 fUseCLs = rhs.fUseCLs;
402 fScanLog = rhs.fScanLog;
404 fVerbose = rhs.fVerbose;
405 fCalcType = rhs.fCalcType;
409 fNumErr = rhs.fNumErr;
417 HypoTestInverter::~HypoTestInverter()
419 if (fResults)
delete fResults;
426 TestStatistic * HypoTestInverter::GetTestStatistic( )
const
428 if(fCalculator0 && fCalculator0->GetTestStatSampler()){
429 return fCalculator0->GetTestStatSampler()->GetTestStatistic();
438 bool HypoTestInverter::SetTestStatistic(TestStatistic& stat)
440 if(fCalculator0 && fCalculator0->GetTestStatSampler()){
441 fCalculator0->GetTestStatSampler()->SetTestStatistic(&stat);
450 void HypoTestInverter::Clear() {
451 if (fResults)
delete fResults;
453 fLimitPlot.reset(
nullptr);
459 void HypoTestInverter::CreateResults()
const {
461 TString results_name =
"result_";
462 results_name += fScannedVariable->GetName();
463 fResults =
new HypoTestInverterResult(results_name,*fScannedVariable,ConfidenceLevel());
464 TString title =
"HypoTestInverter Result For ";
465 title += fScannedVariable->GetName();
466 fResults->SetTitle(title);
468 fResults->UseCLs(fUseCLs);
469 fResults->SetConfidenceLevel(1.-fSize);
473 AsymptoticCalculator * ac =
dynamic_cast<AsymptoticCalculator*
>(fCalculator0);
475 fResults->fIsTwoSided = ac->IsTwoSided();
478 TestStatSampler * sampler = fCalculator0->GetTestStatSampler();
480 ProfileLikelihoodTestStat * pl =
dynamic_cast<ProfileLikelihoodTestStat*
>(sampler->GetTestStatistic());
481 if (pl && pl->IsTwoSided() ) fResults->fIsTwoSided =
true;
491 HypoTestInverterResult* HypoTestInverter::GetInterval()
const {
494 if (fResults && fResults->ArraySize() >= 1) {
495 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::GetInterval - return an already existing interval " << std::endl;
496 return (HypoTestInverterResult*)(fResults->Clone());
500 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::GetInterval - run a fixed scan" << std::endl;
501 bool ret = RunFixedScan(fNBins, fXmin, fXmax, fScanLog);
503 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter::GetInterval - error running a fixed scan " << std::endl;
506 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::GetInterval - run an automatic scan" << std::endl;
507 double limit(0),err(0);
508 bool ret = RunLimit(limit,err);
510 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter::GetInterval - error running an auto scan " << std::endl;
513 if (fgCloseProof) ProofConfig::CloseProof();
515 return (HypoTestInverterResult*) (fResults->Clone());
522 HypoTestResult * HypoTestInverter::Eval(HypoTestCalculatorGeneric &hc,
bool adaptive,
double clsTarget)
const {
534 HypoTestResult * hcResult = hc.GetHypoTest();
536 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter::Eval - HypoTest failed" << std::endl;
541 hcResult->SetBackgroundAsAlt(
true);
549 if (hcResult->GetPValueIsRightTail() )
550 hcResult->SetTestStatisticData(hcResult->GetTestStatisticData()-fNumErr);
552 hcResult->SetTestStatisticData(hcResult->GetTestStatisticData()+fNumErr);
554 double clsMid = (fUseCLs ? hcResult->CLs() : hcResult->CLsplusb());
555 double clsMidErr = (fUseCLs ? hcResult->CLsError() : hcResult->CLsplusbError());
561 if (fCalcType == kHybrid) HypoTestWrapper<HybridCalculator>::SetToys((HybridCalculator*)&hc, fUseCLs ? fgNToys : 1, 4*fgNToys);
562 if (fCalcType == kFrequentist) HypoTestWrapper<FrequentistCalculator>::SetToys((FrequentistCalculator*)&hc, fUseCLs ? fgNToys : 1, 4*fgNToys);
564 while (clsMidErr >= fgCLAccuracy && (clsTarget == -1 || fabs(clsMid-clsTarget) < 3*clsMidErr) ) {
565 std::unique_ptr<HypoTestResult> more(hc.GetHypoTest());
570 hcResult->Append(more.get());
571 clsMid = (fUseCLs ? hcResult->CLs() : hcResult->CLsplusb());
572 clsMidErr = (fUseCLs ? hcResult->CLsError() : hcResult->CLsplusbError());
573 if (fVerbose) std::cout << (fUseCLs ?
"\tCLs = " :
"\tCLsplusb = ") << clsMid <<
" +/- " << clsMidErr << std::endl;
578 oocoutP((TObject*)0,Eval) <<
"P values for " << fScannedVariable->GetName() <<
" = " <<
579 fScannedVariable->getVal() <<
"\n" <<
580 "\tCLs = " << hcResult->CLs() <<
" +/- " << hcResult->CLsError() <<
"\n" <<
581 "\tCLb = " << hcResult->CLb() <<
" +/- " << hcResult->CLbError() <<
"\n" <<
582 "\tCLsplusb = " << hcResult->CLsplusb() <<
" +/- " << hcResult->CLsplusbError() <<
"\n" <<
586 if (fCalcType == kFrequentist || fCalcType == kHybrid) {
587 fTotalToysRun += (hcResult->GetAltDistribution()->GetSize() + hcResult->GetNullDistribution()->GetSize());
590 TString nullDistName = TString::Format(
"%s_%s_%4.2f",hcResult->GetNullDistribution()->GetName(),
591 fScannedVariable->GetName(), fScannedVariable->getVal() );
592 TString altDistName = TString::Format(
"%s_%s_%4.2f",hcResult->GetAltDistribution()->GetName(),
593 fScannedVariable->GetName(), fScannedVariable->getVal() );
595 hcResult->GetNullDistribution()->SetName(nullDistName);
596 hcResult->GetAltDistribution()->SetName(altDistName);
605 bool HypoTestInverter::RunFixedScan(
int nBins,
double xMin,
double xMax,
bool scanLog )
const
610 fResults->fFittedLowerLimit =
false;
611 fResults->fFittedUpperLimit =
false;
615 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::RunFixedScan - Please provide nBins>0\n";
618 if ( nBins==1 && xMin!=xMax ) {
619 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter::RunFixedScan - nBins==1 -> I will run for xMin (" << xMin <<
")\n";
621 if ( xMin==xMax && nBins>1 ) {
622 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter::RunFixedScan - xMin==xMax -> I will enforce nBins==1\n";
626 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::RunFixedScan - Please provide xMin ("
627 << xMin <<
") smaller than xMax (" << xMax <<
")\n";
631 if (xMin < fScannedVariable->getMin()) {
632 xMin = fScannedVariable->getMin();
633 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter::RunFixedScan - xMin < lower bound, using xmin = "
634 << xMin << std::endl;
636 if (xMax > fScannedVariable->getMax()) {
637 xMax = fScannedVariable->getMax();
638 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter::RunFixedScan - xMax > upper bound, using xmax = "
639 << xMax << std::endl;
642 if (xMin <= 0. && scanLog) {
643 oocoutE((TObject*)
nullptr, InputArguments) <<
"HypoTestInverter::RunFixedScan - cannot go in log steps if xMin <= 0" << std::endl;
648 for (
int i=0; i<nBins; i++) {
652 thisX = exp( log(xMin) + i*(log(xMax)-log(xMin))/(nBins-1) );
654 thisX = xMin + i*(xMax-xMin)/(nBins-1);
657 bool status = RunOnePoint(thisX);
660 if ( status==
false ) {
661 std::cout <<
"\t\tLoop interrupted because of failed status\n";
672 bool HypoTestInverter::RunOnePoint(
double rVal,
bool adaptive,
double clTarget)
const
678 if ( rVal < fScannedVariable->getMin() ) {
679 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::RunOnePoint - Out of range: using the lower bound "
680 << fScannedVariable->getMin()
681 <<
" on the scanned variable rather than " << rVal<<
"\n";
682 rVal = fScannedVariable->getMin();
684 if ( rVal > fScannedVariable->getMax() ) {
686 if ( rVal > fScannedVariable->getMax()*(1.+1.E-12) )
687 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::RunOnePoint - Out of range: using the upper bound "
688 << fScannedVariable->getMax()
689 <<
" on the scanned variable rather than " << rVal<<
"\n";
690 rVal = fScannedVariable->getMax();
694 double oldValue = fScannedVariable->getVal();
697 fScannedVariable->setVal(rVal);
700 const ModelConfig * sbModel = fCalculator0->GetNullModel();
701 RooArgSet poi; poi.add(*sbModel->GetParametersOfInterest());
703 poi = RooArgSet(*fScannedVariable);
704 const_cast<ModelConfig*
>(sbModel)->SetSnapshot(poi);
707 oocoutP((TObject*)0,Eval) <<
"Running for " << fScannedVariable->GetName() <<
" = " << fScannedVariable->getVal() << endl;
710 HypoTestResult* result = Eval(*fCalculator0,adaptive,clTarget);
712 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter - Error running point " << fScannedVariable->GetName() <<
" = " <<
713 fScannedVariable->getVal() << endl;
717 if (TMath::IsNaN(result->NullPValue() ) && TMath::IsNaN(result->AlternatePValue() ) ) {
718 oocoutW((TObject*)0,Eval) <<
"HypoTestInverter - Skip invalid result for point " << fScannedVariable->GetName() <<
" = " <<
719 fScannedVariable->getVal() << endl;
724 if ( fResults->ArraySize()!=0 ) lastXtested = fResults->GetXValue(fResults->ArraySize()-1);
725 else lastXtested = -999;
727 if ( (std::abs(rVal) < 1 && TMath::AreEqualAbs(rVal, lastXtested,1.E-12) ) ||
728 (std::abs(rVal) >= 1 && TMath::AreEqualRel(rVal, lastXtested,1.E-12) ) ) {
730 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunOnePoint - Merge with previous result for "
731 << fScannedVariable->GetName() <<
" = " << rVal << std::endl;
732 HypoTestResult* prevResult = fResults->GetResult(fResults->ArraySize()-1);
733 if (prevResult && prevResult->GetNullDistribution() && prevResult->GetAltDistribution()) {
734 prevResult->Append(result);
739 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunOnePoint - replace previous empty result\n";
740 fResults->fYObjects.Remove( prevResult);
741 fResults->fYObjects.Add(result);
747 fResults->fXValues.push_back(rVal);
748 fResults->fYObjects.Add(result);
755 fScannedVariable->setVal(oldValue);
772 bool HypoTestInverter::RunLimit(
double &limit,
double &limitErr,
double absAccuracy,
double relAccuracy,
const double*hint)
const {
778 RooRealVar *r = fScannedVariable;
781 if ((hint != 0) && (*hint > r->getMin())) {
782 r->setMax(std::min<double>(3.0 * (*hint), r->getMax()));
783 r->setMin(std::max<double>(0.3 * (*hint), r->getMin()));
784 oocoutI((TObject*)0,InputArguments) <<
"HypoTestInverter::RunLimit - Use hint value " << *hint
785 <<
" search in interval " << r->getMin() <<
" , " << r->getMax() << std::endl;
789 if (absAccuracy <= 0) absAccuracy = fgAbsAccuracy;
790 if (relAccuracy <= 0) relAccuracy = fgRelAccuracy;
792 typedef std::pair<double,double> CLs_t;
793 double clsTarget = fSize;
794 CLs_t clsMin(1,0), clsMax(0,0), clsMid(0,0);
795 double rMin = r->getMin(), rMax = r->getMax();
796 limit = 0.5*(rMax + rMin);
797 limitErr = 0.5*(rMax - rMin);
800 TF1 expoFit(
"expoFit",
"[0]*exp([1]*(x-[2]))", rMin, rMax);
824 fLimitPlot.reset(
new TGraphErrors());
826 if (fVerbose > 0) std::cout <<
"Search for upper limit to the limit" << std::endl;
827 for (
int tries = 0; tries < 6; ++tries) {
829 if (! RunOnePoint(rMax) ) {
830 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Hypotest failed" << std::endl;
833 clsMax = std::make_pair( fResults->GetLastYValue(), fResults->GetLastYError() );
834 if (clsMax.first == 0 || clsMax.first + 3 * fabs(clsMax.second) < clsTarget )
break;
837 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Cannot set higher limit: at " << r->GetName()
838 <<
" = " << rMax <<
" still get "
839 << (fUseCLs ?
"CLs" :
"CLsplusb") <<
" = " << clsMax.first << std::endl;
844 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Search for lower limit to the limit" << std::endl;
847 if ( fUseCLs && rMin == 0 ) {
851 if (! RunOnePoint(rMin) )
return false;
852 clsMin = std::make_pair( fResults->GetLastYValue(), fResults->GetLastYError() );
854 if (clsMin.first != 1 && clsMin.first - 3 * fabs(clsMin.second) < clsTarget) {
860 for (
int tries = 0; tries < 6; ++tries) {
861 if (! RunOnePoint(rMax) )
return false;
862 clsMin = std::make_pair( fResults->GetLastYValue(), fResults->GetLastYError() );
863 if (clsMin.first == 1 || clsMin.first - 3 * fabs(clsMin.second) > clsTarget)
break;
866 oocoutE((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Cannot set lower limit: at " << r->GetName()
867 <<
" = " << rMin <<
" still get " << (fUseCLs ?
"CLs" :
"CLsplusb")
868 <<
" = " << clsMin.first << std::endl;
876 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Now doing proper bracketing & bisection" << std::endl;
880 if (fMaxToys > 0 && fTotalToysRun > fMaxToys ) {
881 oocoutW((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - maximum number of toys reached " << std::endl;
887 limit = 0.5*(rMin+rMax); limitErr = 0.5*(rMax-rMin);
888 if (fgAlgo ==
"logSecant" && clsMax.first != 0) {
889 double logMin = log(clsMin.first), logMax = log(clsMax.first), logTarget = log(clsTarget);
890 limit = rMin + (rMax-rMin) * (logTarget - logMin)/(logMax - logMin);
891 if (clsMax.second != 0 && clsMin.second != 0) {
892 limitErr = hypot((logTarget-logMax) * (clsMin.second/clsMin.first), (logTarget-logMin) * (clsMax.second/clsMax.first));
893 limitErr *= (rMax-rMin)/((logMax-logMin)*(logMax-logMin));
896 r->setError(limitErr);
899 if (limitErr < std::max(absAccuracy, relAccuracy * limit)) {
901 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - reached accuracy " << limitErr
902 <<
" below " << std::max(absAccuracy, relAccuracy * limit) << std::endl;
909 if (! RunOnePoint(limit,
true, clsTarget) )
return false;
910 clsMid = std::make_pair( fResults->GetLastYValue(), fResults->GetLastYError() );
912 if (clsMid.second == -1) {
913 std::cerr <<
"Hypotest failed" << std::endl;
918 if (fabs(clsMid.first-clsTarget) >= 2*clsMid.second) {
919 if ((clsMid.first>clsTarget) == (clsMax.first>clsTarget)) {
920 rMax = limit; clsMax = clsMid;
922 rMin = limit; clsMin = clsMid;
925 if (fVerbose > 0) std::cout <<
"Trying to move the interval edges closer" << std::endl;
926 double rMinBound = rMin, rMaxBound = rMax;
928 while (clsMin.second == 0 || fabs(rMin-limit) > std::max(absAccuracy, relAccuracy * limit)) {
929 rMin = 0.5*(rMin+limit);
930 if (!RunOnePoint(rMin,
true, clsTarget) )
return false;
931 clsMin = std::make_pair( fResults->GetLastYValue(), fResults->GetLastYError() );
933 if (fabs(clsMin.first-clsTarget) <= 2*clsMin.second)
break;
936 while (clsMax.second == 0 || fabs(rMax-limit) > std::max(absAccuracy, relAccuracy * limit)) {
937 rMax = 0.5*(rMax+limit);
939 if (!RunOnePoint(rMax,
true,clsTarget) )
return false;
940 clsMax = std::make_pair( fResults->GetLastYValue(), fResults->GetLastYError() );
941 if (fabs(clsMax.first-clsTarget) <= 2*clsMax.second)
break;
944 expoFit.SetRange(rMinBound,rMaxBound);
951 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Before fit --- \n";
952 std::cout <<
"Limit: " << r->GetName() <<
" < " << limit <<
" +/- " << limitErr <<
" [" << rMin <<
", " << rMax <<
"]\n";
955 expoFit.FixParameter(0,clsTarget);
956 expoFit.SetParameter(1,log(clsMax.first/clsMin.first)/(rMax-rMin));
957 expoFit.SetParameter(2,limit);
958 double rMinBound, rMaxBound; expoFit.GetRange(rMinBound, rMaxBound);
959 limitErr = std::max(fabs(rMinBound-limit), fabs(rMaxBound-limit));
962 HypoTestInverterPlot plot(
"plot",
"plot",fResults);
963 fLimitPlot.reset(plot.MakePlot() );
966 for (
int j = 0; j < fLimitPlot->GetN(); ++j) {
967 if (fLimitPlot->GetX()[j] >= rMinBound && fLimitPlot->GetX()[j] <= rMaxBound) npoints++;
969 for (
int i = 0, imax = 8; i <= imax; ++i, ++npoints) {
971 fLimitPlot->Fit(&expoFit,(fVerbose <= 1 ?
"QNR EX0" :
"NR EXO"));
973 oocoutI((TObject*)0,Eval) <<
"Fit to " << npoints <<
" points: " << expoFit.GetParameter(2) <<
" +/- " << expoFit.GetParError(2) << std::endl;
975 if ((rMin < expoFit.GetParameter(2)) && (expoFit.GetParameter(2) < rMax) && (expoFit.GetParError(2) < 0.5*(rMaxBound-rMinBound))) {
977 limit = expoFit.GetParameter(2);
978 limitErr = expoFit.GetParError(2);
979 if (limitErr < std::max(absAccuracy, relAccuracy * limit))
break;
982 double rTry = RooRandom::uniform()*(rMaxBound-rMinBound)+rMinBound;
984 if (!RunOnePoint(rTry,
true,clsTarget) )
return false;
992 if (fLimitPlot.get() && fLimitPlot->GetN() > 0) {
995 fLimitPlot->SetLineWidth(2);
996 double xmin = r->getMin(), xmax = r->getMax();
997 for (
int j = 0; j < fLimitPlot->GetN(); ++j) {
998 if (fLimitPlot->GetY()[j] > 1.4*clsTarget || fLimitPlot->GetY()[j] < 0.6*clsTarget)
continue;
999 xmin = std::min(fLimitPlot->GetX()[j], xmin);
1000 xmax = std::max(fLimitPlot->GetX()[j], xmax);
1002 fLimitPlot->GetXaxis()->SetRangeUser(xmin,xmax);
1003 fLimitPlot->GetYaxis()->SetRangeUser(0.5*clsTarget, 1.5*clsTarget);
1004 fLimitPlot->Draw(
"AP");
1005 expoFit.Draw(
"SAME");
1006 TLine line(fLimitPlot->GetX()[0], clsTarget, fLimitPlot->GetX()[fLimitPlot->GetN()-1], clsTarget);
1007 line.SetLineColor(kRed); line.SetLineWidth(2); line.Draw();
1008 line.DrawLine(limit, 0, limit, fLimitPlot->GetY()[0]);
1009 line.SetLineWidth(1); line.SetLineStyle(2);
1010 line.DrawLine(limit-limitErr, 0, limit-limitErr, fLimitPlot->GetY()[0]);
1011 line.DrawLine(limit+limitErr, 0, limit+limitErr, fLimitPlot->GetY()[0]);
1015 oocoutI((TObject*)0,Eval) <<
"HypoTestInverter::RunLimit - Result: \n"
1016 <<
"\tLimit: " << r->GetName() <<
" < " << limit <<
" +/- " << limitErr <<
" @ " << (1-fSize) * 100 <<
"% CL\n";
1017 if (fVerbose > 1) oocoutI((TObject*)0,Eval) <<
"Total toys: " << fTotalToysRun << std::endl;
1020 fResults->fUpperLimit = limit;
1021 fResults->fUpperLimitError = limitErr;
1022 fResults->fFittedUpperLimit =
true;
1024 fResults->fLowerLimit = fScannedVariable->getMin();
1025 fResults->fLowerLimitError = 0;
1026 fResults->fFittedLowerLimit =
true;
1038 SamplingDistribution * HypoTestInverter::GetLowerLimitDistribution(
bool rebuild,
int nToys) {
1041 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::GetLowerLimitDistribution(false) - result not existing\n";
1044 return fResults->GetLowerLimitDistribution();
1047 TList * clsDist = 0;
1048 TList * clsbDist = 0;
1049 if (fUseCLs) clsDist = &fResults->fExpPValues;
1050 else clsbDist = &fResults->fExpPValues;
1052 return RebuildDistributions(
false, nToys,clsDist, clsbDist);
1065 SamplingDistribution * HypoTestInverter::GetUpperLimitDistribution(
bool rebuild,
int nToys) {
1068 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::GetUpperLimitDistribution(false) - result not existing\n";
1071 return fResults->GetUpperLimitDistribution();
1074 TList * clsDist = 0;
1075 TList * clsbDist = 0;
1076 if (fUseCLs) clsDist = &fResults->fExpPValues;
1077 else clsbDist = &fResults->fExpPValues;
1079 return RebuildDistributions(
true, nToys,clsDist, clsbDist);
1084 void HypoTestInverter::SetData(RooAbsData & data) {
1085 if (fCalculator0) fCalculator0->SetData(data);
1097 SamplingDistribution * HypoTestInverter::RebuildDistributions(
bool isUpper,
int nToys, TList * clsDist, TList * clsbDist, TList * clbDist,
const char *outputfile) {
1099 if (!fScannedVariable || !fCalculator0)
return 0;
1101 const ModelConfig * bModel = fCalculator0->GetAlternateModel();
1102 const ModelConfig * sbModel = fCalculator0->GetNullModel();
1103 if (!bModel || ! sbModel)
return 0;
1104 RooArgSet paramPoint;
1105 if (!sbModel->GetParametersOfInterest())
return 0;
1106 paramPoint.add(*sbModel->GetParametersOfInterest());
1108 const RooArgSet * poibkg = bModel->GetSnapshot();
1110 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter::RebuildDistribution - background snapshot not existing"
1111 <<
" assume is for POI = 0" << std::endl;
1112 fScannedVariable->setVal(0);
1113 paramPoint = RooArgSet(*fScannedVariable);
1116 paramPoint = *poibkg;
1119 ToyMCSampler * toymcSampler =
dynamic_cast<ToyMCSampler *
>(fCalculator0->GetTestStatSampler() );
1120 if (!toymcSampler) {
1121 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter::RebuildDistribution - no toy MC sampler existing" << std::endl;
1125 if (dynamic_cast<RooStats::AsymptoticCalculator*>(fCalculator0) ) {
1126 toymcSampler->SetObservables(*sbModel->GetObservables() );
1127 toymcSampler->SetParametersForTestStat(*sbModel->GetParametersOfInterest());
1128 toymcSampler->SetPdf(*sbModel->GetPdf());
1129 toymcSampler->SetNuisanceParameters(*sbModel->GetNuisanceParameters());
1130 if (sbModel->GetGlobalObservables() ) toymcSampler->SetGlobalObservables(*sbModel->GetGlobalObservables() );
1132 if (!sbModel->GetPdf()->canBeExtended())
1133 toymcSampler->SetNEventsPerToy(1);
1137 int nPoints = fNBins;
1139 bool storePValues = clsDist || clsbDist || clbDist;
1140 if (fNBins <=0 && storePValues) {
1141 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter::RebuildDistribution - cannot return p values distribution with the auto scan" << std::endl;
1142 storePValues =
false;
1147 if (fResults) nPoints = fResults->ArraySize();
1149 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - result is not existing and number of point to scan is not set"
1155 if (nToys <= 0) nToys = 100;
1157 std::vector<std::vector<double> > CLs_values(nPoints);
1158 std::vector<std::vector<double> > CLsb_values(nPoints);
1159 std::vector<std::vector<double> > CLb_values(nPoints);
1162 for (
int i = 0; i < nPoints; ++i) {
1163 CLs_values[i].reserve(nToys);
1164 CLb_values[i].reserve(nToys);
1165 CLsb_values[i].reserve(nToys);
1169 std::vector<double> limit_values; limit_values.reserve(nToys);
1171 oocoutI((TObject*)0,InputArguments) <<
"HypoTestInverter - rebuilding the p value distributions by generating ntoys = "
1172 << nToys << std::endl;
1175 oocoutI((TObject*)0,InputArguments) <<
"Rebuilding using parameter of interest point: ";
1176 RooStats::PrintListContent(paramPoint, oocoutI((TObject*)0,InputArguments) );
1177 if (sbModel->GetNuisanceParameters() ) {
1178 oocoutI((TObject*)0,InputArguments) <<
"And using nuisance parameters: ";
1179 RooStats::PrintListContent(*sbModel->GetNuisanceParameters(), oocoutI((TObject*)0,InputArguments) );
1182 assert(bModel->GetPdf() );
1183 assert(bModel->GetObservables() );
1184 RooArgSet * allParams = bModel->GetPdf()->getParameters( *bModel->GetObservables() );
1185 RooArgSet saveParams;
1186 allParams->snapshot(saveParams);
1188 TFile * fileOut = TFile::Open(outputfile,
"RECREATE");
1190 oocoutE((TObject*)0,InputArguments) <<
"HypoTestInverter - RebuildDistributions - Error opening file " << outputfile
1191 <<
" - the resulting limits will not be stored" << std::endl;
1194 TH1D * hL =
new TH1D(
"lowerLimitDist",
"Rebuilt lower limit distribution",100,1.,0.);
1195 TH1D * hU =
new TH1D(
"upperLimitDist",
"Rebuilt upper limit distribution",100,1.,0.);
1196 TH1D * hN =
new TH1D(
"nObs",
"Observed events",100,1.,0.);
1197 hL->SetBuffer(2*nToys);
1198 hU->SetBuffer(2*nToys);
1199 std::vector<TH1*> hCLb;
1200 std::vector<TH1*> hCLsb;
1201 std::vector<TH1*> hCLs;
1203 for (
int i = 0; i < nPoints; ++i) {
1204 hCLb.push_back(
new TH1D(TString::Format(
"CLbDist_bin%d",i),
"CLb distribution",100,1.,0.));
1205 hCLs.push_back(
new TH1D(TString::Format(
"ClsDist_bin%d",i),
"CLs distribution",100,1.,0.));
1206 hCLsb.push_back(
new TH1D(TString::Format(
"CLsbDist_bin%d",i),
"CLs+b distribution",100,1.,0.));
1212 for (
int itoy = 0; itoy < nToys; ++itoy) {
1214 oocoutP((TObject*)0,Eval) <<
"\nHypoTestInverter - RebuildDistributions - running toy # " << itoy <<
" / "
1215 << nToys << std::endl;
1218 printf(
"\n\nshnapshot of s+b model \n");
1219 sbModel->GetSnapshot()->Print(
"v");
1222 if (itoy> 0) *allParams = saveParams;
1226 toymcSampler->SetPdf(*bModel->GetPdf() );
1229 RooAbsData * bkgdata = toymcSampler->GenerateToyData(paramPoint);
1231 double nObs = bkgdata->sumEntries();
1233 if (bkgdata->numEntries() ==1 && !bModel->GetPdf()->canBeExtended()) {
1234 oocoutP((TObject*)0,Generation) <<
"Generate observables are : ";
1235 RooArgList genObs(*bkgdata->get(0));
1236 RooStats::PrintListContent(genObs, oocoutP((TObject*)0,Generation) );
1238 for (
int i = 0; i < genObs.getSize(); ++i) {
1239 RooRealVar * x =
dynamic_cast<RooRealVar*
>(&genObs[i]);
1240 if (x) nObs += x->getVal();
1246 HypoTestInverter inverter = *
this;
1247 inverter.SetData(*bkgdata);
1250 auto gobs = bModel->GetPdf()->getVariables()->selectCommon(* sbModel->GetGlobalObservables() );
1253 HypoTestInverterResult * r = inverter.GetInterval();
1255 if (r == 0)
continue;
1257 double value = (isUpper) ? r->UpperLimit() : r->LowerLimit();
1258 limit_values.push_back( value );
1259 hU->Fill(r->UpperLimit() );
1260 hL->Fill(r->LowerLimit() );
1263 std::cout <<
"The computed upper limit for toy #" << itoy <<
" is " << value << std::endl;
1266 if (itoy%10 == 0 || itoy == nToys-1) {
1267 hU->Write(
"",TObject::kOverwrite);
1268 hL->Write(
"",TObject::kOverwrite);
1269 hN->Write(
"",TObject::kOverwrite);
1272 if (!storePValues)
continue;
1274 if (nPoints < r->ArraySize()) {
1275 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter: skip extra points" << std::endl;
1277 else if (nPoints > r->ArraySize()) {
1278 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter: missing some points" << std::endl;
1282 for (
int ipoint = 0; ipoint < nPoints; ++ipoint) {
1283 HypoTestResult * hr = r->GetResult(ipoint);
1285 CLs_values[ipoint].push_back( hr->CLs() );
1286 CLsb_values[ipoint].push_back( hr->CLsplusb() );
1287 CLb_values[ipoint].push_back( hr->CLb() );
1288 hCLs[ipoint]->Fill( hr->CLs() );
1289 hCLb[ipoint]->Fill( hr->CLb() );
1290 hCLsb[ipoint]->Fill( hr->CLsplusb() );
1293 oocoutW((TObject*)0,InputArguments) <<
"HypoTestInverter: missing result for point: x = "
1294 << fResults->GetXValue(ipoint) << std::endl;
1298 if (itoy%10 == 0 || itoy == nToys-1) {
1299 for (
int ipoint = 0; ipoint < nPoints; ++ipoint) {
1300 hCLs[ipoint]->Write(
"",TObject::kOverwrite);
1301 hCLb[ipoint]->Write(
"",TObject::kOverwrite);
1302 hCLsb[ipoint]->Write(
"",TObject::kOverwrite);
1313 if (clsDist) clsDist->SetOwner(
true);
1314 if (clbDist) clbDist->SetOwner(
true);
1315 if (clsbDist) clsbDist->SetOwner(
true);
1317 oocoutI((TObject*)0,InputArguments) <<
"HypoTestInverter: storing rebuilt p values " << std::endl;
1319 for (
int ipoint = 0; ipoint < nPoints; ++ipoint) {
1321 TString name = TString::Format(
"CLs_distrib_%d",ipoint);
1322 clsDist->Add(
new SamplingDistribution(name,name,CLs_values[ipoint] ) );
1325 TString name = TString::Format(
"CLb_distrib_%d",ipoint);
1326 clbDist->Add(
new SamplingDistribution(name,name,CLb_values[ipoint] ) );
1329 TString name = TString::Format(
"CLsb_distrib_%d",ipoint);
1330 clsbDist->Add(
new SamplingDistribution(name,name,CLsb_values[ipoint] ) );
1342 for (
int i = 0; i < nPoints && storePValues; ++i) {
1349 const char * disName = (isUpper) ?
"upperLimit_dist" :
"lowerLimit_dist";
1350 return new SamplingDistribution(disName, disName, limit_values);