HybridStandardForm.C

Go to the documentation of this file.
00001 // A hypothesis testing example based on number counting  with background uncertainty.
00002 
00003 
00004 /*
00005 HybridStandardForm
00006 
00007 Authors: Kyle Cranmer, Wouter Verkerke, and Sven Kreiss
00008 date  May 2010 Part 1-3 
00009 date  Dec 2010 Part 4-6
00010 
00011 A hypothesis testing example based on number counting 
00012 with background uncertainty.
00013 
00014 NOTE: This example is like HybridInstructional, but the model is more clearly
00015 generalizable to an analysis with shapes.  There is a lot of flexability
00016 for how one models a problem in RooFit/RooStats.  Models come in a few 
00017 common forms:
00018   - standard form: extended PDF of some discriminating variable m:
00019   eg: P(m) ~ S*fs(m) + B*fb(m), with S+B events expected
00020   in this case the dataset has N rows corresponding to N events
00021   and the extended term is Pois(N|S+B)
00022 
00023   - fractional form: non-extended PDF of some discriminating variable m:
00024   eg: P(m) ~ s*fs(m) + (1-s)*fb(m), where s is a signal fraction
00025   in this case the dataset has N rows corresponding to N events
00026   and there is no extended term
00027 
00028   - number counting form: in which there is no discriminating variable
00029   and the counts are modeled directly (see HybridInstructional)
00030   eg: P(N) = Pois(N|S+B)
00031   in this case the dataset has 1 row corresponding to N events
00032   and the extended term is the PDF itself.
00033 
00034 Here we convert the number counting form into the standard form by 
00035 introducing a dummy discriminating variable m with a uniform distribution.
00036 
00037 This example:
00038  - demonstrates the usage of the HybridCalcultor (Part 4-6)
00039  - demonstrates the numerical integration of RooFit (Part 2)
00040  - validates the RooStats against an example with a known analytic answer
00041  - demonstrates usage of different test statistics
00042  - explains subtle choices in the prior used for hybrid methods
00043  - demonstrates usage of different priors for the nuisance parameters
00044  - demonstrates usage of PROOF
00045 
00046 The basic setup here is that a main measurement has observed x events with an 
00047 expectation of s+b.  One can choose an ad hoc prior for the uncertainty on b,
00048 or try to base it on an auxiliary measurement.  In this case, the auxiliary
00049 measurement (aka control measurement, sideband) is another counting experiment
00050 with measurement y and expectation tau*b.  With an 'original prior' on b, 
00051 called \eta(b) then one can obtain a posterior from the auxiliary measurement
00052 \pi(b) = \eta(b) * Pois(y|tau*b).  This is a principled choice for a prior
00053 on b in the main measurement of x, which can then be treated in a hybrid 
00054 Bayesian/Frequentist way.  Additionally, one can try to treat the two 
00055 measurements simultaneously, which is detailed in Part 6 of the tutorial.
00056 
00057 This tutorial is related to the FourBin.C tutorial in the modeling, but
00058 focuses on hypothesis testing instead of interval estimation.
00059 
00060 More background on this 'prototype problem' can be found in the 
00061 following papers:
00062 
00063 Evaluation of three methods for calculating statistical significance 
00064 when incorporating a systematic uncertainty into a test of the 
00065 background-only hypothesis for a Poisson process
00066 Authors: Robert D. Cousins, James T. Linnemann, Jordan Tucker
00067 http://arxiv.org/abs/physics/0702156
00068 NIM  A 595 (2008) 480--501
00069 
00070 Statistical Challenges for Searches for New Physics at the LHC
00071 Authors: Kyle Cranmer
00072 http://arxiv.org/abs/physics/0511028
00073 
00074  Measures of Significance in HEP and Astrophysics
00075  Authors: J. T. Linnemann
00076  http://arxiv.org/abs/physics/0312059
00077 */
00078 
00079 #include "RooGlobalFunc.h"
00080 #include "RooRealVar.h"
00081 #include "RooProdPdf.h"
00082 #include "RooWorkspace.h"
00083 #include "RooDataSet.h"
00084 #include "RooDataHist.h"
00085 #include "TCanvas.h"
00086 #include "TStopwatch.h"
00087 #include "TH1.h"
00088 #include "RooPlot.h"
00089 #include "RooMsgService.h"
00090 
00091 #include "RooStats/NumberCountingUtils.h"
00092 
00093 #include "RooStats/HybridCalculator.h"
00094 #include "RooStats/ToyMCSampler.h"
00095 #include "RooStats/HypoTestPlot.h"
00096 
00097 #include "RooStats/NumEventsTestStat.h"
00098 #include "RooStats/ProfileLikelihoodTestStat.h"
00099 #include "RooStats/SimpleLikelihoodRatioTestStat.h"
00100 #include "RooStats/RatioOfProfiledLikelihoodsTestStat.h"
00101 #include "RooStats/MaxLikelihoodEstimateTestStat.h"
00102 
00103 using namespace RooFit;
00104 using namespace RooStats;
00105 
00106 //////////////////////////////////////////////////
00107 // A New Test Statistic Class for this example.
00108 // It simply returns the sum of the values in a particular
00109 // column of a dataset. 
00110 // You can ignore this class and focus on the macro below
00111 //////////////////////////////////////////////////
00112 class BinCountTestStat : public TestStatistic {
00113 public:
00114   BinCountTestStat(void) : fColumnName("tmp") {}
00115   BinCountTestStat(string columnName) : fColumnName(columnName) {}
00116    
00117   virtual Double_t Evaluate(RooAbsData& data, RooArgSet& /*nullPOI*/) {
00118     // This is the main method in the interface
00119     Double_t value = 0.0;
00120     for(int i=0; i < data.numEntries(); i++) {
00121       value += data.get(i)->getRealValue(fColumnName.c_str());
00122     }
00123     return value;
00124       }
00125   virtual const TString GetVarName() const { return fColumnName; }
00126   
00127 private:
00128   string fColumnName;
00129 
00130 protected:
00131   ClassDef(BinCountTestStat,1)
00132 };
00133 
00134 ClassImp(BinCountTestStat)
00135 
00136 //////////////////////////////////////////////////
00137 // The Actual Tutorial Macro
00138 //////////////////////////////////////////////////
00139 
00140 void HybridStandardForm() {
00141   
00142   // This tutorial has 6 parts
00143   // Table of Contents
00144   // Setup
00145   //   1. Make the model for the 'prototype problem'
00146   // Special cases
00147   //   2. NOT RELEVANT HERE
00148   //   3. Use RooStats analytic solution for this problem 
00149   // RooStats HybridCalculator -- can be generalized
00150   //   4. RooStats ToyMC version of 2. & 3. 
00151   //   5. RooStats ToyMC with an equivalent test statistic
00152   //   6. RooStats ToyMC with simultaneous control & main measurement
00153 
00154   // Part 4 takes ~4 min without PROOF.
00155   // Part 5 takes about ~2 min with PROOF on 4 cores.
00156   // Of course, everything looks nicer with more toys, which takes longer.
00157 
00158 
00159   TStopwatch t;
00160   t.Start();
00161   TCanvas *c = new TCanvas;
00162   c->Divide(2,2);
00163 
00164   ///////////////////////////////////////////////////////
00165   // P A R T   1  :  D I R E C T   I N T E G R A T I O N 
00166   //////////////////////////////////////////////////////
00167   // Make model for prototype on/off problem
00168   // Pois(x | s+b) * Pois(y | tau b )
00169   // for Z_Gamma, use uniform prior on b.
00170   RooWorkspace* w = new RooWorkspace("w");
00171   
00172 
00173   // replace the pdf in 'number couting form' 
00174   //w->factory("Poisson::px(x[150,0,500],sum::splusb(s[0,0,100],b[100,0,300]))");
00175   // with one in standard form.  Now x is encoded in event count
00176   w->factory("Uniform::f(m[0,1])");//m is a dummy discriminanting variable
00177   w->factory("ExtendPdf::px(f,sum::splusb(s[0,0,100],b[100,0,300]))");
00178   w->factory("Poisson::py(y[100,0,500],prod::taub(tau[1.],b))"); 
00179   w->factory("PROD::model(px,py)");
00180   w->factory("Uniform::prior_b(b)");
00181 
00182   // We will control the output level in a few places to avoid
00183   // verbose progress messages.  We start by keeping track
00184   // of the current threshold on messages.
00185   RooFit::MsgLevel msglevel = RooMsgService::instance().globalKillBelow();
00186 
00187   // Use PROOF-lite on multi-core machines
00188   ProofConfig* pc = NULL;
00189   // uncomment below if you want to use PROOF
00190   pc = new ProofConfig(*w, 4, "workers=4", kFALSE); // machine with 4 cores
00191   //  pc = new ProofConfig(*w, 2, "workers=2", kFALSE); // machine with 2 cores
00192 
00193   /////////////////////////////////////////////////
00194   // P A R T   3  :  A N A L Y T I C   R E S U L T
00195   /////////////////////////////////////////////////
00196   // In this special case, the integrals are known analytically 
00197   // and they are implemented in RooStats::NumberCountingUtils
00198 
00199   // analytic Z_Bi
00200   double p_Bi = NumberCountingUtils::BinomialWithTauObsP(150, 100, 1);
00201   double Z_Bi = NumberCountingUtils::BinomialWithTauObsZ(150, 100, 1);
00202   cout << "-----------------------------------------"<<endl;
00203   cout << "Part 3" << endl;
00204   std::cout << "Z_Bi p-value (analytic): " << p_Bi << std::endl;
00205   std::cout << "Z_Bi significance (analytic): " << Z_Bi << std::endl;
00206   t.Stop();  t.Print(); t.Reset(); t.Start();
00207 
00208   ////////////////////////////////////////////////////////////////
00209   // P A R T   4  :  U S I N G   H Y B R I D   C A L C U L A T O R
00210   ////////////////////////////////////////////////////////////////
00211   // Now we demonstrate the RooStats HybridCalculator.
00212   //
00213   // Like all RooStats calculators it needs the data and a ModelConfig
00214   // for the relevant hypotheses.  Since we are doing hypothesis testing
00215   // we need a ModelConfig for the null (background only) and the alternate 
00216   // (signal+background) hypotheses.  We also need to specify the PDF, 
00217   // the parameters of interest, and the observables.  Furthermore, since
00218   // the parameter of interest is floating, we need to specify which values
00219   // of the parameter corresponds to the null and alternate (eg. s=0 and s=50)
00220   //
00221   // define some sets of variables obs={x} and poi={s}
00222   // note here, x is the only observable in the main measurement
00223   // and y is treated as a separate measurement, which is used 
00224   // to produce the prior that will be used in this calculation
00225   // to randomize the nuisance parameters.  
00226   w->defineSet("obs","m");
00227   w->defineSet("poi","s");
00228 
00229   // create a toy dataset with the x=150
00230   //  RooDataSet *data = new RooDataSet("d", "d", *w->set("obs"));
00231   //  data->add(*w->set("obs"));
00232   RooDataSet* data = w->pdf("px")->generate(*w->set("obs"),150);
00233 
00234   //////////////////////////////////////////////////////////
00235   // Part 3a : Setup ModelConfigs
00236   // create the null (background-only) ModelConfig with s=0
00237   ModelConfig b_model("B_model", w);
00238   b_model.SetPdf(*w->pdf("px"));
00239   b_model.SetObservables(*w->set("obs"));
00240   b_model.SetParametersOfInterest(*w->set("poi"));
00241   w->var("s")->setVal(0.0);  // important!
00242   b_model.SetSnapshot(*w->set("poi"));
00243 
00244   // create the alternate (signal+background) ModelConfig with s=50
00245   ModelConfig sb_model("S+B_model", w);
00246   sb_model.SetPdf(*w->pdf("px"));
00247   sb_model.SetObservables(*w->set("obs"));
00248   sb_model.SetParametersOfInterest(*w->set("poi"));
00249   w->var("s")->setVal(50.0); // important!
00250   sb_model.SetSnapshot(*w->set("poi"));
00251 
00252 
00253   //////////////////////////////////////////////////////////
00254   // Part 3b : Choose Test Statistic
00255   // To make an equivalent calculation we need to use x as the test 
00256   // statistic.  This is not a built-in test statistic in RooStats
00257   // so we define it above.  The new class inherits from the 
00258   // RooStats::TestStatistic interface, and simply returns the value
00259   // of x in the dataset.
00260 
00261   NumEventsTestStat eventCount(*w->pdf("px"));
00262 
00263   //////////////////////////////////////////////////////////
00264   // Part 3c : Define Prior used to randomize nuisance parameters
00265   //
00266   // The prior used for the hybrid calculator is the posterior
00267   // from the auxiliary measurement y.  The model for the aux.
00268   // measurement is Pois(y|tau*b), thus the likleihood function
00269   // is proportional to (has the form of) a Gamma distribution.
00270   // if the 'original prior' \eta(b) is uniform, then from
00271   // Bayes's theorem we have the posterior:
00272   //  \pi(b) = Pois(y|tau*b) * \eta(b)
00273   // If \eta(b) is flat, then we arrive at a Gamma distribution.
00274   // Since RooFit will normalize the PDF we can actually supply
00275   // py=Pois(y,tau*b) that will be equivalent to multiplying by a uniform.
00276   // 
00277   // Alternatively, we could explicitly use a gamma distribution:
00278   // w->factory("Gamma::gamma(b,sum::temp(y,1),1,0)");
00279   // 
00280   // or we can use some other ad hoc prior that do not naturally 
00281   // follow from the known form of the auxiliary measurement.
00282   // The common choice is the equivlaent Gaussian:
00283   w->factory("Gaussian::gauss_prior(b,y, expr::sqrty('sqrt(y)',y))");
00284   // this corresponds to the "Z_N" calculation.
00285   //
00286   // or one could use the analogous log-normal prior
00287   w->factory("Lognormal::lognorm_prior(b,y, expr::kappa('1+1./sqrt(y)',y))");
00288   //
00289   // Ideally, the HybridCalculator would be able to inspect the full
00290   // model Pois(x | s+b) * Pois(y | tau b ) and be given the original
00291   // prior \eta(b) to form \pi(b) = Pois(y|tau*b) * \eta(b).
00292   // This is not yet implemented because in the general case
00293   // it is not easy to identify the terms in the PDF that correspond
00294   // to the auxiliary measurement.  So for now, it must be set 
00295   // explicitly with:
00296   //  - ForcePriorNuisanceNull()
00297   //  - ForcePriorNuisanceAlt()
00298   // the name "ForcePriorNuisance" was chosen because we anticipate
00299   // this to be auto-detected, but will leave the option open
00300   // to force to a different prior for the nuisance parameters.
00301 
00302   //////////////////////////////////////////////////////////
00303   // Part 3d : Construct and configure the HybridCalculator
00304 
00305   HybridCalculator hc1(*data, sb_model, b_model);
00306   ToyMCSampler *toymcs1 = (ToyMCSampler*)hc1.GetTestStatSampler();
00307   //  toymcs1->SetNEventsPerToy(1); // because the model is in number counting form
00308   toymcs1->SetTestStatistic(&eventCount); // set the test statistic
00309   //  toymcs1->SetGenerateBinned();
00310   hc1.SetToys(30000,1000); 
00311   hc1.ForcePriorNuisanceAlt(*w->pdf("py"));
00312   hc1.ForcePriorNuisanceNull(*w->pdf("py"));
00313   // if you wanted to use the ad hoc Gaussian prior instead
00314   //  hc1.ForcePriorNuisanceAlt(*w->pdf("gauss_prior"));
00315   //  hc1.ForcePriorNuisanceNull(*w->pdf("gauss_prior"));
00316   // if you wanted to use the ad hoc log-normal prior instead
00317   //  hc1.ForcePriorNuisanceAlt(*w->pdf("lognorm_prior"));
00318   //  hc1.ForcePriorNuisanceNull(*w->pdf("lognorm_prior"));
00319 
00320   // enable proof
00321   // proof not enabled for this test statistic
00322   //  if(pc) toymcs1->SetProofConfig(pc);     
00323 
00324   // these lines save current msg level and then kill any messages below ERROR
00325   RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR);
00326   // Get the result
00327   HypoTestResult *r1 = hc1.GetHypoTest();
00328   RooMsgService::instance().setGlobalKillBelow(msglevel); // set it back
00329   cout << "-----------------------------------------"<<endl;
00330   cout << "Part 4" << endl;
00331   r1->Print();
00332   t.Stop();  t.Print(); t.Reset(); t.Start();
00333 
00334   c->cd(2);
00335   HypoTestPlot *p1 = new HypoTestPlot(*r1,30); // 30 bins, TS is discrete
00336   p1->Draw();
00337 
00338   return; // keep the running time sort by default
00339   ////////////////////////////////////////////////////////////////////////////
00340   // P A R T   5  :  U S I N G   H Y B R I D   C A L C U L A T O R   W I T H 
00341   //                 A N   A L T E R N A T I V E   T E S T   S T A T I S T I C
00342   /////////////////////////////////////////////////////////////////////////////
00343   // 
00344   // A likelihood ratio test statistics should be 1-to-1 with the count x
00345   // when the value of b is fixed in the likelihood.  This is implemented
00346   // by the SimpleLikelihoodRatioTestStat
00347 
00348   SimpleLikelihoodRatioTestStat slrts(*b_model.GetPdf(),*sb_model.GetPdf());
00349   slrts.SetNullParameters(*b_model.GetSnapshot());
00350   slrts.SetAltParameters(*sb_model.GetSnapshot());
00351 
00352   // HYBRID CALCULATOR
00353   HybridCalculator hc2(*data, sb_model, b_model);
00354   ToyMCSampler *toymcs2 = (ToyMCSampler*)hc2.GetTestStatSampler();
00355   //  toymcs2->SetNEventsPerToy(1);
00356   toymcs2->SetTestStatistic(&slrts);
00357   //  toymcs2->SetGenerateBinned();
00358   hc2.SetToys(20000,1000); 
00359   hc2.ForcePriorNuisanceAlt(*w->pdf("py"));
00360   hc2.ForcePriorNuisanceNull(*w->pdf("py"));
00361   // if you wanted to use the ad hoc Gaussian prior instead
00362   //  hc2.ForcePriorNuisanceAlt(*w->pdf("gauss_prior"));
00363   //  hc2.ForcePriorNuisanceNull(*w->pdf("gauss_prior"));
00364   // if you wanted to use the ad hoc log-normal prior instead
00365   //  hc2.ForcePriorNuisanceAlt(*w->pdf("lognorm_prior"));
00366   //  hc2.ForcePriorNuisanceNull(*w->pdf("lognorm_prior"));
00367 
00368   // enable proof
00369   if(pc) toymcs2->SetProofConfig(pc);     
00370 
00371   // these lines save current msg level and then kill any messages below ERROR
00372   RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR);
00373   // Get the result
00374   HypoTestResult *r2 = hc2.GetHypoTest();
00375   cout << "-----------------------------------------"<<endl;
00376   cout << "Part 5" << endl;
00377   r2->Print();
00378   t.Stop();  t.Print(); t.Reset(); t.Start();
00379   RooMsgService::instance().setGlobalKillBelow(msglevel);
00380 
00381   c->cd(3);
00382   HypoTestPlot *p2 = new HypoTestPlot(*r2,30); // 30 bins
00383   p2->Draw();
00384 
00385   return; // so standard tutorial runs faster
00386 
00387   ///////////////////////////////////////////////////////////
00388   // OUTPUT W/O PROOF (2.66 GHz Intel Core i7)
00389   ///////////////////////////////////////////////////////////
00390 
00391   /*
00392 -----------------------------------------
00393 Part 3
00394 Z_Bi p-value (analytic): 0.00094165
00395 Z_Bi significance (analytic): 3.10804
00396 Real time 0:00:00, CP time 0.610
00397 
00398 Results HybridCalculator_result: 
00399  - Null p-value = 0.00103333 +/- 0.000179406
00400  - Significance = 3.08048 sigma
00401  - Number of S+B toys: 1000
00402  - Number of B toys: 30000
00403  - Test statistic evaluated on data: 150
00404  - CL_b: 0.998967 +/- 0.000185496
00405  - CL_s+b: 0.495 +/- 0.0158106
00406  - CL_s: 0.495512 +/- 0.0158272
00407 Real time 0:04:43, CP time 283.780
00408 
00409   */
00410   /* With PROOF
00411 -----------------------------------------
00412 Part 5
00413 
00414 Results HybridCalculator_result: 
00415  - Null p-value = 0.00105 +/- 0.000206022
00416  - Significance = 3.07571 sigma
00417  - Number of S+B toys: 1000
00418  - Number of B toys: 20000
00419  - Test statistic evaluated on data: 10.8198
00420  - CL_b: 0.99895 +/- 0.000229008
00421  - CL_s+b: 0.491 +/- 0.0158088
00422  - CL_s: 0.491516 +/- 0.0158258
00423 Real time 0:02:22, CP time 0.990
00424   */
00425 
00426   //////////////////////////////////////////
00427   // Comparison
00428   ///////////////////////////////////////////
00429   // LEPStatToolsForLHC
00430   // https://plone4.fnal.gov:4430/P0/phystat/packages/0703002
00431   // Uses Gaussian prior
00432   // CL_b = 6.218476e-04, Significance = 3.228665 sigma
00433   //
00434   //////////////////////////////////////////
00435   // Comparison
00436   ///////////////////////////////////////////
00437   // Asymptotics
00438   // From the value of the profile likelihood ratio (5.0338) 
00439   // The significance can be estimated using Wilks's theorem
00440   // significance = sqrt(2*profileLR) = 3.1729 sigma
00441 
00442 
00443 }

Generated on Tue Jul 5 15:45:09 2011 for ROOT_528-00b_version by  doxygen 1.5.1