#include "RooAbsPdf.h"
#include "RooAbsData.h"
#include "RooRealVar.h"
#include "TMath.h"
#include "RooStats/HybridCalculator.h"
#include "RooStats/HybridResult.h"
#include "RooStats/HypoTestInverter.h"
ClassImp(RooStats::HypoTestInverter)
using namespace RooStats;
HypoTestInverter::HypoTestInverter( ) :
fCalculator0(0),
fScannedVariable(0),
fResults(0),
fUseCLs(false),
fSize(0)
{
}
HypoTestInverter::HypoTestInverter( HypoTestCalculator& myhc0,
RooRealVar& scannedVariable, double size ) :
TNamed( ),
fCalculator0(&myhc0),
fScannedVariable(&scannedVariable),
fResults(0),
fUseCLs(false),
fSize(size)
{
SetName("HypoTestInverter");
HybridCalculator * hc = dynamic_cast<HybridCalculator *> (fCalculator0);
if (hc == 0) {
Fatal("HypoTestInverter","Using non HybridCalculator class IS NOT SUPPORTED");
}
}
HypoTestInverter::~HypoTestInverter()
{
if (fResults) delete fResults;
}
void HypoTestInverter::CreateResults() {
if (fResults == 0) {
TString results_name = this->GetName();
results_name += "_results";
fResults = new HypoTestInverterResult(results_name,*fScannedVariable,ConfidenceLevel());
fResults->SetTitle("HypoTestInverter Result");
}
fResults->UseCLs(fUseCLs);
}
bool HypoTestInverter::RunAutoScan( double xMin, double xMax, double target, double epsilon, unsigned int numAlgorithm )
{
if ( xMin>=xMax || xMin< fScannedVariable->getMin() || xMax>fScannedVariable->getMax() ) {
std::cout << "Error: problem with the specified range\n";
return false;
}
if ( target<=0 || target>=1 ) {
std::cout << "Error: problem with target value\n";
return false;
}
if ( epsilon>0.5-fabs(0.5-target) ) {
std::cout << "Error: problem with error value\n";
return false;
}
if ( numAlgorithm!=0 && numAlgorithm!=1 ) {
std::cout << "Error: invalid interpolation algorithm\n";
return false;
}
CreateResults();
if ( fabs(1-target/(1-Size()/2))<DBL_EPSILON ) {
fResults->fInterpolateLowerLimit = false;
std::cout << "Target matches lower limit: de-activate interpolation in HypoTestInverterResult\n";
}
if ( fabs(1-target/((Size()/2)))<DBL_EPSILON ) {
fResults->fInterpolateUpperLimit = false;
std::cout << "Target matches upper limit: de-activate interpolation in HypoTestInverterResult\n";
}
const double nSigma = 1;
const unsigned int nToys_backup = ((HybridCalculator*)fCalculator0)->GetNumberOfToys();
double leftX = xMin;
if (!RunOnePoint(leftX)) return false;
double leftCL = fResults->GetYValue(fResults->ArraySize()-1);
double leftCLError = fResults->GetYError(fResults->ArraySize()-1);
double rightX = xMax;
if (!RunOnePoint(rightX)) return false;
double rightCL = fResults->GetYValue(fResults->ArraySize()-1);
double rightCLError = fResults->GetYError(fResults->ArraySize()-1);
if ( rightCL>target && leftCL>target ) {
std::cout << "The confidence level at both boundaries are both too large ( " << leftCL << " and " << rightCL << std::endl << "Run again with other boundaries or larger toy-MC statistics\n";
return false;
}
if ( rightCL<target && leftCL<target ) {
std::cout << "The confidence level at both boundaries are both too small ( " << leftCL << " and " << rightCL << std::endl << "Run again with other boundaries or larger toy-MC statistics\n";
return false;
}
unsigned int nIteration = 2;
bool quitThisLoop = false;
double centerCL = 0;
double centerCLError = 0;
do {
double x = 0;
if (leftCL==rightCL) {
std::cout << "This cannot (and should not) happen... quit\n";
quitThisLoop = true;
} else if (leftX==rightX) {
std::cout << "This cannot (and should not) happen... quit\n";
quitThisLoop = true;
} else {
if (numAlgorithm==0) {
if (!leftCL) leftCL = DBL_EPSILON;
if (!rightCL) rightCL = DBL_EPSILON;
double a = (log(leftCL) - log(rightCL)) / (leftX - rightX);
double b = leftCL / exp(a * leftX);
x = (log(target) - log(b)) / a;
if (x<xMin || x>xMax || isnan(x)) {
std::cout << "Extrapolated value out of range or nan: exits\n";
quitThisLoop = true;
}
} else if (numAlgorithm==1) {
double a = (leftCL-rightCL)/(leftX-rightX);
double b = leftCL-a*leftX;
x = (target-b)/a;
if (x<xMin || x>xMax || isnan(x)) {
std::cout << "Extrapolated value out of range or nan: exits\n";
quitThisLoop = true;
}
}
}
if ( x==leftX || x==rightX ) {
std::cout << "Error: exit because interpolated value equals to a previous iteration\n";
quitThisLoop = true;
}
bool success = false;
if (!quitThisLoop) success = RunOnePoint(x);
if (success) {
nIteration++;
centerCL = fResults->GetYValue(fResults->ArraySize()-1);
centerCLError = fResults->GetYError(fResults->ArraySize()-1);
if ( (leftCL > target) == (rightCL < target) ) {
if ( (centerCL > target) == (leftCL > target) ) {
leftX = x;
leftCL = centerCL;
leftCLError = centerCLError;
} else {
rightX = x;
rightCL = centerCL;
rightCLError = centerCLError;
}
} else if ( (fabs(leftCL - target) / leftCLError) >
(fabs(rightCL - target) / rightCLError) ) {
leftX = x;
leftCL = centerCL;
leftCLError = centerCLError;
} else {
rightX = x;
rightCL = centerCL;
rightCLError = centerCLError;
}
if ( fabs(centerCL-target) < nSigma*centerCLError && centerCLError > epsilon ) {
do {
int nToys = ((HybridCalculator*)fCalculator0)->GetNumberOfToys();
int nToysTarget = (int) TMath::Max(nToys*1.5, 1.2*nToys*pow(centerCLError/epsilon,2));
std::cout << "Increasing the number of toys to: " << nToysTarget << std::endl;
((HybridCalculator*)fCalculator0)->SetNumberOfToys(nToysTarget-nToys);
if (!RunOnePoint(x)) quitThisLoop=true;
nIteration++;
centerCL = fResults->GetYValue(fResults->ArraySize()-1);
centerCLError = fResults->GetYError(fResults->ArraySize()-1);
((HybridCalculator*)fCalculator0)->SetNumberOfToys(nToysTarget);
} while ( fabs(centerCL-target) < nSigma*centerCLError && centerCLError > epsilon && quitThisLoop==false );
}
if (leftCL==rightCL) {
std::cout << "Algorithm failed: left and right CL are equal (no intrapolation possible or more toy-MC statistics needed)\n";
quitThisLoop = true;
}
}
} while ( ( fabs(centerCL-target) > nSigma*centerCLError || centerCLError > epsilon ) && quitThisLoop==false );
((HybridCalculator*)fCalculator0)->SetNumberOfToys(nToys_backup);
if ( quitThisLoop==true ) {
std::cout << "Aborted the search because something happened\n";
return false;
}
std::cout << "Converged in " << fResults->ArraySize() << " iterations\n";
return true;
}
bool HypoTestInverter::RunFixedScan( int nBins, double xMin, double xMax )
{
CreateResults();
if ( nBins<=0 ) {
std::cout << "Please provide nBins>0\n";
return false;
}
if ( nBins==1 && xMin!=xMax ) {
std::cout << "nBins==1 -> I will run for xMin (" << xMin << ")\n";
}
if ( xMin==xMax && nBins>1 ) {
std::cout << "xMin==xMax -> I will enforce nBins==1\n";
nBins = 1;
}
if ( xMin>xMax ) {
std::cout << "Please provide xMin (" << xMin << ") smaller that xMax (" << xMax << ")\n";
return false;
}
for (int i=0; i<nBins; i++) {
double thisX = xMin+i*(xMax-xMin)/(nBins-1);
bool status = RunOnePoint(thisX);
if ( status==false ) {
std::cout << "Loop interupted because of failed status\n";
return false;
}
}
return true;
}
bool HypoTestInverter::RunOnePoint( double thisX )
{
CreateResults();
if ( thisX<fScannedVariable->getMin() ) {
std::cout << "Out of range: using the lower bound on the scanned variable rather than " << thisX<< "\n";
thisX = fScannedVariable->getMin();
}
if ( thisX>fScannedVariable->getMax() ) {
std::cout << "Out of range: using the upper bound on the scanned variable rather than " << thisX<< "\n";
thisX = fScannedVariable->getMax();
}
double oldValue = fScannedVariable->getVal();
fScannedVariable->setVal(thisX);
std::cout << "Running for " << fScannedVariable->GetName() << " = " << thisX << endl;
HypoTestResult* myHybridResult = fCalculator0->GetHypoTest();
double lastXtested;
if ( fResults->ArraySize()!=0 ) lastXtested = fResults->GetXValue(fResults->ArraySize()-1);
else lastXtested = -999;
if ( lastXtested==thisX ) {
std::cout << "Merge with previous result\n";
HybridResult* latestResult = (HybridResult*) fResults->GetResult(fResults->ArraySize()-1);
latestResult->Add((HybridResult*)myHybridResult);
delete myHybridResult;
} else {
fResults->fXValues.push_back(thisX);
fResults->fYObjects.Add(myHybridResult);
}
fScannedVariable->setVal(oldValue);
return true;
}