89 #include "RConfigure.h"
91 ClassImp(RooStats::BayesianCalculator);
100 #ifdef R__HAS_MATHMORE
101 const ROOT::Math::RootFinder::EType kRootFinderType = ROOT::Math::RootFinder::kGSL_BRENT;
103 const ROOT::Math::RootFinder::EType kRootFinderType = ROOT::Math::RootFinder::kBRENT;
109 struct LikelihoodFunction {
110 LikelihoodFunction(RooFunctor & f, RooFunctor * prior = 0,
double offset = 0) :
111 fFunc(f), fPrior(prior),
112 fOffset(offset), fMaxL(0) {
113 fFunc.binding().resetNumCall();
116 void SetPrior(RooFunctor * prior) { fPrior = prior; }
118 double operator() (
const double *x )
const {
119 double nll = fFunc(x) - fOffset;
120 double likelihood = std::exp(-nll);
122 if (fPrior) likelihood *= (*fPrior)(x);
124 int nCalls = fFunc.binding().numCall();
125 if (nCalls > 0 && nCalls % 1000 == 0) {
126 ooccoutD((TObject*)0,Eval) <<
"Likelihood evaluation ncalls = " << nCalls
127 <<
" x0 " << x[0] <<
" nll = " << nll+fOffset;
128 if (fPrior) ooccoutD((TObject*)0,Eval) <<
" prior(x) = " << (*fPrior)(x);
129 ooccoutD((TObject*)0,Eval) <<
" likelihood " << likelihood
130 <<
" max Likelihood " << fMaxL << std::endl;
133 if (likelihood > fMaxL ) {
135 if ( likelihood > 1.E10) {
136 ooccoutW((TObject*)0,Eval) <<
"LikelihoodFunction::() WARNING - Huge likelihood value found for parameters ";
137 for (
int i = 0; i < fFunc.nObs(); ++i)
138 ooccoutW((TObject*)0,Eval) <<
" x[" << i <<
" ] = " << x[i];
139 ooccoutW((TObject*)0,Eval) <<
" nll = " << nll <<
" L = " << likelihood << std::endl;
147 double operator() (
double x)
const {
149 assert(fFunc.nObs() == 1);
151 return (*
this)(&tmp);
157 mutable double fMaxL;
165 class PosteriorCdfFunction :
public ROOT::Math::IGenFunction {
169 PosteriorCdfFunction(RooAbsReal & nll, RooArgList & bindParams, RooAbsReal * prior = 0,
const char * integType = 0,
double nllMinimum = 0) :
170 fFunctor(nll, bindParams, RooArgList() ),
172 fLikelihood(fFunctor, 0, nllMinimum),
173 fIntegrator(ROOT::Math::IntegratorMultiDim::GetType(integType) ),
174 fXmin(bindParams.getSize() ),
175 fXmax(bindParams.getSize() ),
176 fNorm(1.0), fNormErr(0.0), fOffset(0), fMaxPOI(0),
177 fHasNorm(false), fUseOldValues(true), fError(false)
181 fPriorFunc = std::make_shared<RooFunctor>(*prior, bindParams, RooArgList());
182 fLikelihood.SetPrior(fPriorFunc.get() );
185 fIntegrator.SetFunction(fLikelihood, bindParams.getSize() );
187 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorCdfFunction::Compute integral of posterior in nuisance and poi. "
188 <<
" nllMinimum is " << nllMinimum << std::endl;
190 std::vector<double> par(bindParams.getSize());
191 for (
unsigned int i = 0; i < fXmin.size(); ++i) {
192 RooRealVar & var = (RooRealVar &) bindParams[i];
193 fXmin[i] = var.getMin();
194 fXmax[i] = var.getMax();
195 par[i] = var.getVal();
196 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorFunction::Integrate" << var.GetName()
197 <<
" in interval [ " << fXmin[i] <<
" , " << fXmax[i] <<
" ] " << std::endl;
200 fIntegrator.Options().Print(ooccoutD((TObject*)0,NumIntegration));
206 fNorm = (*this)( fMaxPOI );
207 if (fError) ooccoutE((TObject*)0,NumIntegration) <<
"PosteriorFunction::Error computing normalization - norm = " << fNorm << std::endl;
209 fNormCdfValues.insert(std::make_pair(fXmin[0], 0) );
210 fNormCdfValues.insert(std::make_pair(fXmax[0], 1.0) );
217 PosteriorCdfFunction(
const PosteriorCdfFunction & rhs) :
218 ROOT::Math::IGenFunction(),
219 fFunctor(rhs.fFunctor),
221 fPriorFunc(rhs.fPriorFunc),
222 fLikelihood(fFunctor, fPriorFunc.get(), rhs.fLikelihood.fOffset),
223 fIntegrator(ROOT::Math::IntegratorMultiDim::GetType( rhs.fIntegrator.Name().c_str() ) ),
227 fNormErr( rhs.fNormErr),
228 fOffset(rhs.fOffset),
229 fMaxPOI(rhs.fMaxPOI),
230 fHasNorm(rhs.fHasNorm),
231 fUseOldValues(rhs.fUseOldValues),
233 fNormCdfValues(rhs.fNormCdfValues)
235 fIntegrator.SetFunction(fLikelihood, fXmin.size() );
244 bool HasError()
const {
return fError; }
247 ROOT::Math::IGenFunction * Clone()
const {
248 ooccoutD((TObject*)0,NumIntegration) <<
" cloning function .........." << std::endl;
249 return new PosteriorCdfFunction(*
this);
253 void SetOffset(
double offset) { fOffset = offset; }
258 PosteriorCdfFunction& operator=(
const PosteriorCdfFunction &) {
262 double DoEval (
double x)
const {
266 if (x <= fXmin[0] )
return -fOffset;
268 if (x >= fMaxPOI && fHasNorm)
return 1. - fOffset;
272 if (fHasNorm && fUseOldValues) {
274 std::map<double,double>::iterator itr = fNormCdfValues.upper_bound(x);
276 if (itr != fNormCdfValues.end() ) {
277 fXmin[0] = itr->first;
278 normcdf0 = itr->second;
284 fFunctor.binding().resetNumCall();
286 double cdf = fIntegrator.Integral(&fXmin[0],&fXmax[0]);
287 double error = fIntegrator.Error();
288 double normcdf = cdf/fNorm;
290 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorCdfFunction: poi = [" << fXmin[0] <<
" , "
291 << fXmax[0] <<
"] integral = " << cdf <<
" +/- " << error
292 <<
" norm-integ = " << normcdf <<
" cdf(x) = " << normcdf+normcdf0
293 <<
" ncalls = " << fFunctor.binding().numCall() << std::endl;
295 if (TMath::IsNaN(cdf) || cdf > std::numeric_limits<double>::max()) {
296 ooccoutE((TObject*)0,NumIntegration) <<
"PosteriorFunction::Error computing integral - cdf = "
301 if (cdf != 0 && error/cdf > 0.2 )
302 oocoutW((TObject*)0,NumIntegration) <<
"PosteriorCdfFunction: integration error is larger than 20 % x0 = " << fXmin[0]
303 <<
" x = " << x <<
" cdf(x) = " << cdf <<
" +/- " << error << std::endl;
306 oocoutI((TObject*)0,NumIntegration) <<
"PosteriorCdfFunction - integral of posterior = "
307 << cdf <<
" +/- " << error << std::endl;
316 fNormCdfValues.insert(std::make_pair(x, normcdf) );
319 double errnorm = sqrt( error*error + normcdf*normcdf * fNormErr * fNormErr )/fNorm;
320 if (normcdf > 1. + 3 * errnorm) {
321 oocoutW((TObject*)0,NumIntegration) <<
"PosteriorCdfFunction: normalized cdf values is larger than 1"
322 <<
" x = " << x <<
" normcdf(x) = " << normcdf <<
" +/- " << error/fNorm << std::endl;
325 return normcdf - fOffset;
328 mutable RooFunctor fFunctor;
329 mutable std::shared_ptr<RooFunctor> fPriorFunc;
330 LikelihoodFunction fLikelihood;
331 mutable ROOT::Math::IntegratorMultiDim fIntegrator;
332 mutable std::vector<double> fXmin;
333 mutable std::vector<double> fXmax;
335 mutable double fNormErr;
341 mutable std::map<double,double> fNormCdfValues;
349 class PosteriorFunction :
public ROOT::Math::IGenFunction {
354 PosteriorFunction(RooAbsReal & nll, RooRealVar & poi, RooArgList & nuisParams, RooAbsReal * prior = 0,
const char * integType = 0,
double
355 norm = 1.0,
double nllOffset = 0,
int niter = 0) :
356 fFunctor(nll, nuisParams, RooArgList() ),
358 fLikelihood(fFunctor, 0, nllOffset),
360 fXmin(nuisParams.getSize() ),
361 fXmax(nuisParams.getSize() ),
367 fPriorFunc = std::make_shared<RooFunctor>(*prior, nuisParams, RooArgList());
368 fLikelihood.SetPrior(fPriorFunc.get() );
371 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorFunction::Evaluate the posterior function by integrating the nuisances: " << std::endl;
372 for (
unsigned int i = 0; i < fXmin.size(); ++i) {
373 RooRealVar & var = (RooRealVar &) nuisParams[i];
374 fXmin[i] = var.getMin();
375 fXmax[i] = var.getMax();
376 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorFunction::Integrate " << var.GetName()
377 <<
" in interval [" << fXmin[i] <<
" , " << fXmax[i] <<
" ] " << std::endl;
379 if (fXmin.size() == 1) {
380 fIntegratorOneDim.reset(
new ROOT::Math::Integrator(ROOT::Math::IntegratorOneDim::GetType(integType) ) );
382 fIntegratorOneDim->SetFunction(fLikelihood);
385 fIntegratorOneDim->Options().Print(ooccoutD((TObject*)0,NumIntegration) );
387 else if (fXmin.size() > 1) {
388 fIntegratorMultiDim.reset(
new ROOT::Math::IntegratorMultiDim(ROOT::Math::IntegratorMultiDim::GetType(integType) ) );
389 fIntegratorMultiDim->SetFunction(fLikelihood, fXmin.size());
390 ROOT::Math::IntegratorMultiDimOptions opt = fIntegratorMultiDim->Options();
392 opt.SetNCalls(niter);
393 fIntegratorMultiDim->SetOptions(opt);
397 opt.Print(ooccoutD((TObject*)0,NumIntegration) );
402 ROOT::Math::IGenFunction * Clone()
const {
407 double Error()
const {
return fError;}
411 double DoEval (
double x)
const {
416 fFunctor.binding().resetNumCall();
420 if (fXmin.size() == 1) {
421 f = fIntegratorOneDim->Integral(fXmin[0],fXmax[0]);
422 error = fIntegratorOneDim->Error();
424 else if (fXmin.size() > 1) {
425 f = fIntegratorMultiDim->Integral(&fXmin[0],&fXmax[0]);
426 error = fIntegratorMultiDim->Error();
433 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorFunction: POI value = "
434 << x <<
"\tf(x) = " << f <<
" +/- " << error
435 <<
" norm-f(x) = " << f/fNorm
436 <<
" ncalls = " << fFunctor.binding().numCall() << std::endl;
441 if (f != 0 && error/f > 0.2 )
442 ooccoutW((TObject*)0,NumIntegration) <<
"PosteriorFunction::DoEval - Error from integration in "
443 << fXmin.size() <<
" Dim is larger than 20 % "
444 <<
"x = " << x <<
" p(x) = " << f <<
" +/- " << error << std::endl;
446 fError = error / fNorm;
450 mutable RooFunctor fFunctor;
451 mutable std::shared_ptr<RooFunctor> fPriorFunc;
452 LikelihoodFunction fLikelihood;
454 std::unique_ptr<ROOT::Math::Integrator> fIntegratorOneDim;
455 std::unique_ptr<ROOT::Math::IntegratorMultiDim> fIntegratorMultiDim;
456 std::vector<double> fXmin;
457 std::vector<double> fXmax;
459 mutable double fError;
465 class PosteriorFunctionFromToyMC :
public ROOT::Math::IGenFunction {
470 PosteriorFunctionFromToyMC(RooAbsReal & nll, RooAbsPdf & pdf, RooRealVar & poi, RooArgList & nuisParams, RooAbsReal * prior = 0,
double
471 nllOffset = 0,
int niter = 0,
bool redoToys =
true ) :
472 fFunctor(nll, nuisParams, RooArgList() ),
474 fLikelihood(fFunctor, 0, nllOffset),
477 fNuisParams(nuisParams),
479 fNumIterations(niter),
483 if (niter == 0) fNumIterations = 100;
486 fPriorFunc = std::make_shared<RooFunctor>(*prior, nuisParams, RooArgList());
487 fLikelihood.SetPrior(fPriorFunc.get() );
490 ooccoutI((TObject*)0,InputArguments) <<
"PosteriorFunctionFromToyMC::Evaluate the posterior function by randomizing the nuisances: niter " << fNumIterations << std::endl;
492 ooccoutI((TObject*)0,InputArguments) <<
"PosteriorFunctionFromToyMC::Pdf used for randomizing the nuisance is " << fPdf->GetName() << std::endl;
494 RooArgSet * vars = fPdf->getVariables();
495 for (
int i = 0; i < fNuisParams.getSize(); ++i) {
496 if (!vars->find( fNuisParams[i].GetName() ) ) {
497 ooccoutW((TObject*)0,InputArguments) <<
"Nuisance parameter " << fNuisParams[i].GetName()
498 <<
" is not part of sampling pdf. "
499 <<
"they will be treated as constant " << std::endl;
505 ooccoutI((TObject*)0,InputArguments) <<
"PosteriorFunctionFromToyMC::Generate nuisance toys only one time (for all POI points)" << std::endl;
510 virtual ~PosteriorFunctionFromToyMC() {
if (fGenParams)
delete fGenParams; }
513 void GenerateToys()
const {
514 if (fGenParams)
delete fGenParams;
515 fGenParams = fPdf->generate(fNuisParams, fNumIterations);
517 ooccoutE((TObject*)0,InputArguments) <<
"PosteriorFunctionFromToyMC - failed to generate nuisance parameters" << std::endl;
521 double Error()
const {
return fError;}
523 ROOT::Math::IGenFunction * Clone()
const {
533 double DoEval(
double x)
const {
535 int npar = fNuisParams.getSize();
540 if (fRedoToys) GenerateToys();
541 if (!fGenParams)
return 0;
551 for(
int iter=0; iter<fNumIterations; ++iter) {
554 std::vector<double> p(npar);
555 for (
int i = 0; i < npar; ++i) {
556 const RooArgSet* genset=fGenParams->get(iter);
557 RooAbsArg * arg = genset->find( fNuisParams[i].GetName() );
558 RooRealVar * var =
dynamic_cast<RooRealVar*
>(arg);
560 p[i] = var->getVal();
561 ((RooRealVar &) fNuisParams[i]).setVal(p[i]);
565 double fval = fLikelihood( &p.front() );
570 RooArgSet arg(fNuisParams);
571 double nuisPdfVal = fPdf->getVal(&arg);
575 if( fval > std::numeric_limits<double>::max() ) {
576 ooccoutE((TObject*)0,Eval) <<
"BayesianCalculator::EvalPosteriorFunctionFromToy : "
577 <<
"Likelihood evaluates to infinity " << std::endl;
578 ooccoutE((TObject*)0,Eval) <<
"poi value = " << x << std::endl;
579 ooccoutE((TObject*)0,Eval) <<
"Nuisance parameter values : ";
580 for (
int i = 0; i < npar; ++i)
581 ooccoutE((TObject*)0,Eval) << fNuisParams[i].GetName() <<
" = " << p[i] <<
" ";
582 ooccoutE((TObject*)0,Eval) <<
" - return 0 " << std::endl;
587 if( TMath::IsNaN(fval) ) {
588 ooccoutE((TObject*)0,Eval) <<
"BayesianCalculator::EvalPosteriorFunctionFromToy : "
589 <<
"Likelihood is a NaN " << std::endl;
590 ooccoutE((TObject*)0,Eval) <<
"poi value = " << x << std::endl;
591 ooccoutE((TObject*)0,Eval) <<
"Nuisance parameter values : ";
592 for (
int i = 0; i < npar; ++i)
593 ooccoutE((TObject*)0,Eval) << fNuisParams[i].GetName() <<
" = " << p[i] <<
" ";
594 ooccoutE((TObject*)0,Eval) <<
" - return 0 " << std::endl;
606 double val = sum/double(fNumIterations);
607 double dval2 = std::max( sum2/
double(fNumIterations) - val*val, 0.0);
608 fError = std::sqrt( dval2 / fNumIterations);
611 ooccoutD((TObject*)0,NumIntegration) <<
"PosteriorFunctionFromToyMC: POI value = "
612 << x <<
"\tp(x) = " << val <<
" +/- " << fError << std::endl;
615 if (val != 0 && fError/val > 0.2 ) {
616 ooccoutW((TObject*)0,NumIntegration) <<
"PosteriorFunctionFromToyMC::DoEval"
617 <<
" - Error in estimating posterior is larger than 20% ! "
618 <<
"x = " << x <<
" p(x) = " << val <<
" +/- " << fError << std::endl;
625 mutable RooFunctor fFunctor;
626 mutable std::shared_ptr<RooFunctor> fPriorFunc;
627 LikelihoodFunction fLikelihood;
628 mutable RooAbsPdf * fPdf;
630 RooArgList fNuisParams;
631 mutable RooDataSet * fGenParams;
633 mutable double fError;
645 BayesianCalculator::BayesianCalculator() :
650 fProductPdf (0), fLogLike(0), fLikelihood (0), fIntegratedLikelihood (0), fPosteriorPdf(0),
651 fPosteriorFunction(0), fApproxPosterior(0),
652 fLower(0), fUpper(0),
654 fSize(0.05), fLeftSideFraction(0.5),
655 fBrfPrecision(0.00005),
658 fValidInterval(false)
667 BayesianCalculator::BayesianCalculator(
670 const RooArgSet& POI,
672 const RooArgSet* nuisanceParameters ) :
676 fPriorPdf(&priorPdf),
678 fProductPdf (0), fLogLike(0), fLikelihood (0), fIntegratedLikelihood (0), fPosteriorPdf(0),
679 fPosteriorFunction(0), fApproxPosterior(0),
680 fLower(0), fUpper(0),
682 fSize(0.05), fLeftSideFraction(0.5),
683 fBrfPrecision(0.00005),
686 fValidInterval(false)
689 if (nuisanceParameters) fNuisanceParameters.add(*nuisanceParameters);
691 RemoveConstantParameters(&fNuisanceParameters);
698 BayesianCalculator::BayesianCalculator( RooAbsData& data,
699 ModelConfig & model) :
701 fPdf(model.GetPdf()),
702 fPriorPdf( model.GetPriorPdf()),
704 fProductPdf (0), fLogLike(0), fLikelihood (0), fIntegratedLikelihood (0), fPosteriorPdf(0),
705 fPosteriorFunction(0), fApproxPosterior(0),
706 fLower(0), fUpper(0),
708 fSize(0.05), fLeftSideFraction(0.5),
709 fBrfPrecision(0.00005),
712 fValidInterval(false)
718 BayesianCalculator::~BayesianCalculator()
727 void BayesianCalculator::ClearAll()
const {
728 if (fProductPdf)
delete fProductPdf;
729 if (fLogLike)
delete fLogLike;
730 if (fLikelihood)
delete fLikelihood;
731 if (fIntegratedLikelihood)
delete fIntegratedLikelihood;
732 if (fPosteriorPdf)
delete fPosteriorPdf;
733 if (fPosteriorFunction)
delete fPosteriorFunction;
734 if (fApproxPosterior)
delete fApproxPosterior;
736 fPosteriorFunction = 0;
740 fIntegratedLikelihood = 0;
744 fValidInterval =
false;
752 void BayesianCalculator::SetModel(
const ModelConfig & model) {
754 fPdf = model.GetPdf();
755 fPriorPdf = model.GetPriorPdf();
758 fNuisanceParameters.removeAll();
759 fConditionalObs.removeAll();
760 fGlobalObs.removeAll();
761 if (model.GetParametersOfInterest()) fPOI.add( *(model.GetParametersOfInterest()) );
762 if (model.GetNuisanceParameters()) fNuisanceParameters.add( *(model.GetNuisanceParameters() ) );
763 if (model.GetConditionalObservables()) fConditionalObs.add( *(model.GetConditionalObservables() ) );
764 if (model.GetGlobalObservables()) fGlobalObs.add( *(model.GetGlobalObservables() ) );
766 RemoveConstantParameters(&fNuisanceParameters);
783 RooAbsReal* BayesianCalculator::GetPosteriorFunction()
const
786 if (fIntegratedLikelihood)
return fIntegratedLikelihood;
787 if (fLikelihood)
return fLikelihood;
791 coutE(InputArguments) <<
"BayesianCalculator::GetPosteriorPdf - missing pdf model" << std::endl;
794 if (fPOI.getSize() == 0) {
795 coutE(InputArguments) <<
"BayesianCalculator::GetPosteriorPdf - missing parameter of interest" << std::endl;
798 if (fPOI.getSize() > 1) {
799 coutE(InputArguments) <<
"BayesianCalculator::GetPosteriorPdf - current implementation works only on 1D intervals" << std::endl;
804 RooArgSet* constrainedParams = fPdf->getParameters(*fData);
806 RemoveConstantParameters(constrainedParams);
811 fLogLike = fPdf->createNLL(*fData, RooFit::Constrain(*constrainedParams), RooFit::ConditionalObservables(fConditionalObs), RooFit::GlobalObservables(fGlobalObs) );
815 ccoutD(Eval) <<
"BayesianCalculator::GetPosteriorFunction : "
816 <<
" pdf value " << fPdf->getVal()
817 <<
" neglogLikelihood = " << fLogLike->getVal() << std::endl;
820 ccoutD(Eval) <<
"\t\t\t priorPOI value " << fPriorPdf->getVal() << std::endl;
823 double nllVal = fLogLike->getVal();
824 if ( nllVal > std::numeric_limits<double>::max() ) {
825 coutE(Eval) <<
"BayesianCalculator::GetPosteriorFunction : "
826 <<
" Negative log likelihood evaluates to infinity " << std::endl
827 <<
" Non-const Parameter values : ";
828 RooArgList p(*constrainedParams);
829 for (
int i = 0; i < p.getSize(); ++i) {
830 RooRealVar * v =
dynamic_cast<RooRealVar *
>(&p[i] );
831 if (v!=0) ccoutE(Eval) << v->GetName() <<
" = " << v->getVal() <<
" ";
833 ccoutE(Eval) << std::endl;
834 ccoutE(Eval) <<
"-- Perform a full likelihood fit of the model before or set more reasonable parameter values"
836 coutE(Eval) <<
"BayesianCalculator::GetPosteriorFunction : " <<
" cannot compute posterior function " << std::endl;
845 RooFunctor * nllFunc = fLogLike->functor(fPOI);
847 ROOT::Math::Functor1D wnllFunc(*nllFunc);
848 RooRealVar* poi =
dynamic_cast<RooRealVar*
>( fPOI.first() );
853 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::CountErrors);
856 coutI(Eval) <<
"BayesianCalculator::GetPosteriorFunction : "
857 <<
" nll value " << nllVal <<
" poi value = " << poi->getVal() << std::endl;
860 ROOT::Math::BrentMinimizer1D minim;
861 minim.SetFunction(wnllFunc,poi->getMin(),poi->getMax() );
862 bool ret = minim.Minimize(100,1.E-3,1.E-3);
864 if (ret) fNLLMin = minim.FValMinimum();
866 coutI(Eval) <<
"BayesianCalculator::GetPosteriorFunction : minimum of NLL vs POI for POI = "
867 << poi->getVal() <<
" min NLL = " << fNLLMin << std::endl;
871 delete constrainedParams;
874 if ( fNuisanceParameters.getSize() == 0 || fIntegrationType.Contains(
"ROOFIT") ) {
876 ccoutD(Eval) <<
"BayesianCalculator::GetPosteriorFunction : use ROOFIT integration "
879 #ifdef DOLATER // (not clear why this does not work)
881 TString likeName = TString(
"likelihood_times_prior_") + TString(fPriorPdf->GetName());
883 formula.Form(
"exp(-@0+%f+log(@1))",fNLLMin);
884 fLikelihood =
new RooFormulaVar(likeName,formula,RooArgList(*fLogLike,*fPriorPdf));
888 if (fLogLike)
delete fLogLike;
895 RooAbsPdf * pdfAndPrior = fPdf;
897 TString prodName = TString(
"product_") + TString(fPdf->GetName()) + TString(
"_") + TString(fPriorPdf->GetName() );
899 fProductPdf =
new RooProdPdf(prodName,
"",RooArgList(*fPdf,*fPriorPdf));
900 pdfAndPrior = fProductPdf;
903 RooArgSet* constrParams = fPdf->getParameters(*fData);
905 RemoveConstantParameters(constrParams);
906 fLogLike = pdfAndPrior->createNLL(*fData, RooFit::Constrain(*constrParams),RooFit::ConditionalObservables(fConditionalObs),RooFit::GlobalObservables(fGlobalObs) );
909 TString likeName = TString(
"likelihood_times_prior_") + TString(pdfAndPrior->GetName());
911 formula.Form(
"exp(-@0+%f)",fNLLMin);
912 fLikelihood =
new RooFormulaVar(likeName,formula,RooArgList(*fLogLike));
917 if (fNuisanceParameters.getSize() == 0) {
918 fIntegratedLikelihood = fLikelihood;
923 fIntegratedLikelihood = fLikelihood->createIntegral(fNuisanceParameters);
928 else if ( fIntegrationType.Contains(
"TOYMC") ) {
932 RooArgList nuisParams(fNuisanceParameters);
934 bool doToysEveryIteration =
true;
936 if ( fIntegrationType.Contains(
"1") || fIntegrationType.Contains(
"ONE") ) doToysEveryIteration =
false;
938 RooAbsPdf * samplingPdf = (fNuisancePdf) ? fNuisancePdf : fPdf;
940 ccoutI(Eval) <<
"BayesianCalculator::GetPosteriorFunction : no nuisance pdf is provided, try using global pdf (this will be slower)"
943 fPosteriorFunction =
new PosteriorFunctionFromToyMC(*fLogLike, *samplingPdf, *poi, nuisParams, fPriorPdf, fNLLMin,
944 fNumIterations, doToysEveryIteration );
946 TString name =
"toyposteriorfunction_from_";
947 name += fLogLike->GetName();
948 fIntegratedLikelihood =
new RooFunctor1DBinding(name,name,*fPosteriorFunction,*poi);
951 if (fNScanBins <= 0) fNScanBins = 100;
959 RooArgList nuisParams(fNuisanceParameters);
960 fPosteriorFunction =
new PosteriorFunction(*fLogLike, *poi, nuisParams, fPriorPdf, fIntegrationType, 1., fNLLMin, fNumIterations );
962 TString name =
"posteriorfunction_from_";
963 name += fLogLike->GetName();
964 fIntegratedLikelihood =
new RooFunctor1DBinding(name,name,*fPosteriorFunction,*poi);
969 if (RooAbsReal::numEvalErrors() > 0)
970 coutW(Eval) <<
"BayesianCalculator::GetPosteriorFunction : " << RooAbsReal::numEvalErrors() <<
" errors reported in evaluating log-likelihood function "
972 RooAbsReal::clearEvalErrorLog();
973 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::PrintErrors);
975 return fIntegratedLikelihood;
984 RooAbsPdf* BayesianCalculator::GetPosteriorPdf()
const
987 RooAbsReal * plike = GetPosteriorFunction();
988 if (!plike)
return 0;
992 TString posteriorName = this->GetName() + TString(
"_posteriorPdf_") + plike->GetName();
994 RooAbsPdf * posteriorPdf =
new RooGenericPdf(posteriorName,
"@0",*plike);
1008 TH1 * BayesianCalculator::GetPosteriorHistogram()
const
1010 return (fApproxPosterior) ? fApproxPosterior->GetHistogram() :
nullptr;
1018 RooPlot* BayesianCalculator::GetPosteriorPlot(
bool norm,
double precision )
const
1021 GetPosteriorFunction();
1025 ApproximatePosterior();
1027 RooAbsReal * posterior = fIntegratedLikelihood;
1030 if (fPosteriorPdf)
delete fPosteriorPdf;
1031 fPosteriorPdf = GetPosteriorPdf();
1032 posterior = fPosteriorPdf;
1034 if (!posterior)
return 0;
1036 if (!fValidInterval) GetInterval();
1038 RooAbsRealLValue* poi =
dynamic_cast<RooAbsRealLValue*
>( fPOI.first() );
1042 RooPlot* plot = poi->frame();
1043 if (!plot)
return 0;
1046 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::CountErrors);
1048 plot->SetTitle(TString(
"Posterior probability of parameter \"")+TString(poi->GetName())+TString(
"\""));
1049 posterior->plotOn(plot,RooFit::Range(fLower,fUpper,kFALSE),RooFit::VLines(),RooFit::DrawOption(
"F"),RooFit::MoveToBack(),RooFit::FillColor(kGray),RooFit::Precision(precision));
1050 posterior->plotOn(plot);
1051 plot->GetYaxis()->SetTitle(
"posterior function");
1054 RooAbsReal::clearEvalErrorLog();
1055 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::PrintErrors);
1090 void BayesianCalculator::SetIntegrationType(
const char * type) {
1092 fIntegrationType = TString(type);
1093 fIntegrationType.ToUpper();
1112 SimpleInterval* BayesianCalculator::GetInterval()
const
1116 coutW(Eval) <<
"BayesianCalculator::GetInterval - recomputing interval for the same CL and same model" << std::endl;
1118 RooRealVar* poi =
dynamic_cast<RooRealVar*
>( fPOI.first() );
1120 coutE(Eval) <<
"BayesianCalculator::GetInterval - no parameter of interest is set " << std::endl;
1127 GetPosteriorFunction();
1130 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::CountErrors);
1132 if (fLeftSideFraction < 0 ) {
1134 ComputeShortestInterval();
1139 double lowerCutOff = fLeftSideFraction * fSize;
1140 double upperCutOff = 1. - (1.- fLeftSideFraction) * fSize;
1143 if (fNScanBins > 0) {
1144 ComputeIntervalFromApproxPosterior(lowerCutOff, upperCutOff);
1149 if (fNuisanceParameters.getSize() > 0) {
1150 ComputeIntervalFromCdf(lowerCutOff, upperCutOff);
1154 ComputeIntervalUsingRooFit(lowerCutOff, upperCutOff);
1157 if (!fValidInterval) {
1159 coutW(Eval) <<
"BayesianCalculator::GetInterval - computing integral from cdf failed - do a scan in "
1160 << fNScanBins <<
" nbins " << std::endl;
1161 ComputeIntervalFromApproxPosterior(lowerCutOff, upperCutOff);
1168 if (RooAbsReal::numEvalErrors() > 0)
1169 coutW(Eval) <<
"BayesianCalculator::GetInterval : " << RooAbsReal::numEvalErrors() <<
" errors reported in evaluating log-likelihood function "
1172 RooAbsReal::clearEvalErrorLog();
1173 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::PrintErrors);
1175 if (!fValidInterval) {
1176 fLower = 1; fUpper = 0;
1177 coutE(Eval) <<
"BayesianCalculator::GetInterval - cannot compute a valid interval - return a dummy [1,0] interval"
1181 coutI(Eval) <<
"BayesianCalculator::GetInterval - found a valid interval : [" << fLower <<
" , "
1182 << fUpper <<
" ]" << std::endl;
1185 TString interval_name = TString(
"BayesianInterval_a") + TString(this->GetName());
1186 SimpleInterval * interval =
new SimpleInterval(interval_name,*poi,fLower,fUpper,ConfidenceLevel());
1187 interval->SetTitle(
"SimpleInterval from BayesianCalculator");
1199 double BayesianCalculator::GetMode()
const {
1201 ApproximatePosterior();
1202 TH1 * h = fApproxPosterior->GetHistogram();
1203 return h->GetBinCenter(h->GetMaximumBin() );
1210 void BayesianCalculator::ComputeIntervalUsingRooFit(
double lowerCutOff,
double upperCutOff )
const {
1212 coutI(Eval) <<
"BayesianCalculator: Compute interval using RooFit: posteriorPdf + createCdf + RooBrentRootFinder " << std::endl;
1214 RooRealVar* poi =
dynamic_cast<RooRealVar*
>( fPOI.first() );
1217 fValidInterval =
false;
1218 if (!fPosteriorPdf) fPosteriorPdf = (RooAbsPdf*) GetPosteriorPdf();
1219 if (!fPosteriorPdf)
return;
1221 RooAbsReal* cdf = fPosteriorPdf->createCdf(fPOI,RooFit::ScanNoCdf());
1224 RooAbsFunc* cdf_bind = cdf->bindVars(fPOI,&fPOI);
1225 if (!cdf_bind)
return;
1227 RooBrentRootFinder brf(*cdf_bind);
1228 brf.setTol(fBrfPrecision);
1230 double tmpVal = poi->getVal();
1233 if (lowerCutOff > 0) {
1234 double y = lowerCutOff;
1235 ret &= brf.findRoot(fLower,poi->getMin(),poi->getMax(),y);
1238 fLower = poi->getMin();
1240 if (upperCutOff < 1.0) {
1241 double y=upperCutOff;
1242 ret &= brf.findRoot(fUpper,poi->getMin(),poi->getMax(),y);
1245 fUpper = poi->getMax();
1246 if (!ret) coutE(Eval) <<
"BayesianCalculator::GetInterval "
1247 <<
"Error returned from Root finder, estimated interval is not fully correct"
1250 fValidInterval =
true;
1253 poi->setVal(tmpVal);
1262 void BayesianCalculator::ComputeIntervalFromCdf(
double lowerCutOff,
double upperCutOff )
const {
1264 fValidInterval =
false;
1266 coutI(InputArguments) <<
"BayesianCalculator:GetInterval Compute the interval from the posterior cdf " << std::endl;
1268 RooRealVar* poi =
dynamic_cast<RooRealVar*
>( fPOI.first() );
1270 if (GetPosteriorFunction() == 0) {
1271 coutE(InputArguments) <<
"BayesianCalculator::GetInterval() cannot make posterior Function " << std::endl;
1276 RooArgList bindParams;
1277 bindParams.add(fPOI);
1278 bindParams.add(fNuisanceParameters);
1284 PosteriorCdfFunction cdf(*fLogLike, bindParams, fPriorPdf, fIntegrationType, fNLLMin );
1285 if( cdf.HasError() ) {
1286 coutE(Eval) <<
"BayesianCalculator: Numerical error computing CDF integral - try a different method " << std::endl;
1292 ROOT::Math::RootFinder rf(kRootFinderType);
1294 ccoutD(Eval) <<
"BayesianCalculator::GetInterval - finding roots of posterior using RF " << rf.Name()
1295 <<
" with precision = " << fBrfPrecision;
1297 if (lowerCutOff > 0) {
1298 cdf.SetOffset(lowerCutOff);
1299 ccoutD(NumIntegration) <<
"Integrating posterior to get cdf and search lower limit at p =" << lowerCutOff << std::endl;
1300 bool ok = rf.Solve(cdf, poi->getMin(),poi->getMax() , 200,fBrfPrecision, fBrfPrecision);
1301 if( cdf.HasError() )
1302 coutW(Eval) <<
"BayesianCalculator: Numerical error integrating the CDF " << std::endl;
1304 coutE(NumIntegration) <<
"BayesianCalculator::GetInterval - Error from root finder when searching lower limit !" << std::endl;
1310 fLower = poi->getMin();
1312 if (upperCutOff < 1.0) {
1313 cdf.SetOffset(upperCutOff);
1314 ccoutD(NumIntegration) <<
"Integrating posterior to get cdf and search upper interval limit at p =" << upperCutOff << std::endl;
1315 bool ok = rf.Solve(cdf, fLower,poi->getMax() , 200, fBrfPrecision, fBrfPrecision);
1316 if( cdf.HasError() )
1317 coutW(Eval) <<
"BayesianCalculator: Numerical error integrating the CDF " << std::endl;
1319 coutE(NumIntegration) <<
"BayesianCalculator::GetInterval - Error from root finder when searching upper limit !" << std::endl;
1325 fUpper = poi->getMax();
1328 fValidInterval =
true;
1338 void BayesianCalculator::ApproximatePosterior()
const {
1340 if (fApproxPosterior) {
1342 if (fApproxPosterior->GetNpx() >= fNScanBins)
return;
1344 delete fApproxPosterior;
1345 fApproxPosterior = 0;
1349 RooAbsReal * posterior = GetPosteriorFunction();
1350 if (!posterior)
return;
1353 TF1 * tmp = posterior->asTF(fPOI);
1356 if (fNScanBins > 0) tmp->SetNpx(fNScanBins);
1358 coutI(Eval) <<
"BayesianCalculator - scan posterior function in nbins = " << tmp->GetNpx() << std::endl;
1360 fApproxPosterior = (TF1*) tmp->Clone();
1364 TString name = posterior->GetName() + TString(
"_approx");
1365 TString title = posterior->GetTitle() + TString(
"_approx");
1366 RooAbsReal * posterior2 =
new RooTFnBinding(name,title,fApproxPosterior,fPOI);
1367 if (posterior == fIntegratedLikelihood) {
1368 delete fIntegratedLikelihood;
1369 fIntegratedLikelihood = posterior2;
1371 else if (posterior == fLikelihood) {
1373 fLikelihood = posterior2;
1383 void BayesianCalculator::ComputeIntervalFromApproxPosterior(
double lowerCutOff,
double upperCutOff )
const {
1385 ccoutD(Eval) <<
"BayesianCalculator: Compute interval from the approximate posterior " << std::endl;
1387 ApproximatePosterior();
1388 if (!fApproxPosterior)
return;
1391 double limits[2] = {0,0};
1392 prob[0] = lowerCutOff;
1393 prob[1] = upperCutOff;
1394 fApproxPosterior->GetQuantiles(2,limits,prob);
1397 fValidInterval =
true;
1404 void BayesianCalculator::ComputeShortestInterval( )
const {
1405 coutI(Eval) <<
"BayesianCalculator - computing shortest interval with CL = " << 1.-fSize << std::endl;
1408 ApproximatePosterior();
1409 if (!fApproxPosterior)
return;
1411 TH1D * h1 =
dynamic_cast<TH1D*
>(fApproxPosterior->GetHistogram() );
1413 h1->SetName(fApproxPosterior->GetName());
1415 double * bins = h1->GetArray();
1417 int n = h1->GetSize()-2;
1418 std::vector<int> index(n);
1420 TMath::Sort(n, bins+1, &index[0]);
1423 double actualCL = 0;
1424 double upper = h1->GetXaxis()->GetXmin();
1425 double lower = h1->GetXaxis()->GetXmax();
1426 double norm = h1->GetSumOfWeights();
1428 for (
int i = 0; i < n; ++i) {
1430 double p = bins[ idx] / norm;
1432 if (sum > 1.-fSize ) {
1438 if ( h1->GetBinLowEdge(idx+1) < lower)
1439 lower = h1->GetBinLowEdge(idx+1);
1440 if ( h1->GetXaxis()->GetBinUpEdge(idx+1) > upper)
1441 upper = h1->GetXaxis()->GetBinUpEdge(idx+1);
1444 ccoutD(Eval) <<
"BayesianCalculator::ComputeShortestInterval - actual interval CL = "
1445 << actualCL <<
" difference from requested is " << (actualCL-(1.-fSize))/fSize*100. <<
"% "
1446 <<
" limits are [ " << lower <<
" , " <<
" upper ] " << std::endl;
1449 if (lower < upper) {
1455 if ( std::abs(actualCL-(1.-fSize)) > 0.1*(1.-fSize) )
1456 coutW(Eval) <<
"BayesianCalculator::ComputeShortestInterval - actual interval CL = "
1457 << actualCL <<
" differs more than 10% from desired CL value - must increase nbins "
1458 << n <<
" to an higher value " << std::endl;
1461 coutE(Eval) <<
"BayesianCalculator::ComputeShortestInterval " << n <<
" bins are not sufficient " << std::endl;
1463 fValidInterval =
true;