MaCh3  2.6.0
Reference Guide
SampleHandlerInterface.cpp
Go to the documentation of this file.
2 
3 // ***************************************************************************
5 // ***************************************************************************
6  nSamples = 0;
7 }
8 
9 // ***************************************************************************
11 // ***************************************************************************
12 }
13 
14 // ***************************************************************************
15 // Poisson likelihood calc for data and MC event rates
16 double SampleHandlerInterface::GetPoissonLLH(const double data, const double mc) const {
17 // ***************************************************************************
18  // Return MC if there are no data, returns 0 for data == 0 && mc == 0
19  if ( data == 0 ) return mc;
20 
21  // If there are some data, but the prediction falls below the MC bound => return Poisson LogL for the low MC bound
22  if ( mc < M3::_LOW_MC_BOUND_ ) {
23  if ( data > M3::_LOW_MC_BOUND_ ) return ( M3::_LOW_MC_BOUND_ - data + data * std::log( data/M3::_LOW_MC_BOUND_ ) );
24  else if ( data >= mc ) return 0.;
25  }
26 
27  // Otherwise, just return usual Poisson LogL using Stirling's approximation
28  // http://hyperphysics.phy-astr.gsu.edu/hbase/math/stirling.html
29  return ( mc - data + data * std::log( data / mc ) );
30 }
31 
32 // *************************
33 // data is data, mc is mc, w2 is Sum(w_{i}^2) (sum of weights squared), which is sigma^2_{MC stats}
34 double SampleHandlerInterface::GetTestStatLLH(const double data, const double mc, const double w2) const {
35 // *************************
36  switch (fTestStatistic)
37  {
38  //CW: Not full Barlow-Beeston or what is referred to as "light": we're not introducing any more parameters
39  // Assume the MC has a Gaussian distribution around generated
40  // As in https://arxiv.org/abs/1103.0354 eq 10, 11
41  //CW: Calculate the Barlow-Beeston likelihood contribution from MC statistics
42  // Assumes the beta scaling parameters are Gaussian distributed
43  // Follows arXiv:1103.0354 section 5 and equation 8, 9, 10, 11 on page 4/5
44  // Essentially solves equation 11
45  case (kBarlowBeeston):
46  {
47  // The MC used in the likelihood calculation is allowed to be changed by Barlow Beeston beta parameters
48  double newmc = mc;
49 
50  // If MC falls below the low MC bound, use low MC bound for newmc
51  if ( mc < M3::_LOW_MC_BOUND_ ) {
52  if ( data > M3::_LOW_MC_BOUND_ ) newmc = M3::_LOW_MC_BOUND_;
53  else if ( data >= mc ) return 0.;
54  }
55 
56  // Barlow-Beeston uses fractional uncertainty on MC, so sqrt(sum[w^2])/mc
57  const double fractional = std::sqrt( w2 ) / newmc;
58  // fractional^2 to avoid doing same operation several times
59  const double fractional2 = fractional * fractional;
60  // b in quadratic equation
61  const double temp = newmc * fractional2 - 1;
62  // b^2 - 4ac in quadratic equation
63  const double temp2 = temp * temp + 4 * data * fractional2;
64  if ( temp2 < 0 ) {
65  MACH3LOG_ERROR("Negative square root in Barlow Beeston coefficient calculation!");
66  throw MaCh3Exception(__FILE__ , __LINE__ );
67  }
68  // Solve for the positive beta
69  const double beta = ( -1 * temp + sqrt( temp2 ) ) / 2.;
70 
71  // If there is no data, test-stat shall return only MC*beta
72  double stat = mc * beta;
73  // With data, test-stat shall return LogL for newMC*beta which includes the low MC bound
74  if ( data > 0 ) {
75  newmc *= beta;
76  stat = newmc - data + data * std::log( data / newmc );
77  }
78 
79  // Now, MC stat penalty
80  // The penalty from MC statistics using Conways approach (https://cds.cern.ch/record/1333496?)
81  double penalty = 0;
82  if ( fractional > 0 ) penalty = ( beta - 1 ) * ( beta - 1 ) / ( 2 * fractional2 );
83 
84  // Returns test-stat plus the MC stat penalty
85  return stat+penalty;
86  }
87  break;
88  //KS: Alternative calculation of Barlow-Beeston following Hans Dembinski and Ahmed Abdelmottele arXiv:2206.12346v2
90  {
91  //KS: code follows authors implementation from:
92  //https://github.com/scikit-hep/iminuit/blob/059d06b00cae097ebf340b218b4eb57357111df8/src/iminuit/cost.py#L274-L300
93 
94  // If there is no MC stat error for any reason, return Poisson LogL
95  if ( w2 == 0 ) return GetPoissonLLH(data,mc);
96 
97  // The MC can be changed
98  double newmc = mc;
99 
100  // If MC falls below the low MC bound, use low MC bound for newmc
101  if ( mc < M3::_LOW_MC_BOUND_ ) {
102  if ( data > M3::_LOW_MC_BOUND_ ) newmc = M3::_LOW_MC_BOUND_;
103  else if ( data >= mc ) return 0.;
104  }
105 
106  //the so-called effective count
107  const double k = newmc * newmc / w2;
108  //Calculate beta which is scaling factor between true and generated MC
109  const double beta = ( data + k ) / ( newmc + k );
110 
111  newmc *= beta;
112 
113  // And penalise the movement in beta relative the mc uncertainty
114  const double penalty = k * beta - k + k * std::log( k / ( k * beta ) );
115 
116  // If there are no data, this shall return newmc
117  double stat = newmc;
118  // All likelihood calculations may use the bare Poisson likelihood, so calculate here
119  // Only if there are some data
120  if ( data > 0 ) stat = newmc - data + data * std::log( data / newmc );
121 
122  // Return the statistical contribution and penalty
123  return stat+penalty;
124  }
125  break;
126  //CW: Also try the IceCube likelihood
127  // It does not modify the MC content
128  // https://arxiv.org/abs/1901.04645
129  // Argüelles, C.A., Schneider, A. & Yuan, T. J. High Energ. Phys. (2019) 2019: 30. https://doi.org/10.1007/JHEP06(2019)030
130  // We essentially construct eq 3.16 and take the logarithm
131  // in eq 3.16, mu is MC, sigma2 is w2, k is data
132  case (kIceCube):
133  {
134  // IceCube low MC bound is implemented to return Poisson(data, _LOW_MC_BOUND_)
135  // up until the IceCube(data, mc) test-statistic is less than Poisson(data, _LOW_MC_BOUND_)
136  // The 0 MC limit is set to Poisson(data, 0.) as there is no way to get a non-diverging and reasonable guess on w2
137 
138  // If there is 0 MC uncertainty (technically also when MC is 0) => Return Poisson(data, mc)
139  if ( w2 == 0 ) return GetPoissonLLH(data,mc);
140 
141  // Auxiliary variables
142  const long double b = mc / w2;
143  const long double a = mc * b + 1;
144 
145  // Use C99's implementation of log of gamma function to not be C++11 dependent
146  const double stat = double( -1 * ( a * logl( b ) + lgammal( data + a ) - lgammal( data + 1 ) - ( ( data + a ) * log1pl( b ) ) - lgammal( a ) ) );
147 
148  // Check whether the stat is more than Poisson-like bound for low mc (mc < data)
149  // TN: I believe this might get some extra optimization
150  if ( mc <= data ) {
151  if ( data <= M3::_LOW_MC_BOUND_ ) return 0.;
152  const double poisson = GetPoissonLLH(data, M3::_LOW_MC_BOUND_);
153  if ( stat > poisson ) return poisson;
154  }
155 
156  // Otherwise, return IceCube test-stat
157  return stat;
158  }
159  break;
160  //KS: Pearson works on assumption that event distribution in each bin is described by a Gaussian which in our case is not fulfilled for all bins, hence use it at your own risk
161  case (kPearson):
162  {
163  //KS: 2 is because this function returns -LLH not -2LLH
164  // With no data return the MC/2.
165  if ( data == 0 ) return mc/2.;
166 
167  // If MC is lower than the low MC bound, return the test-stat at the bound
168  if ( mc < M3::_LOW_MC_BOUND_ ) {
169  if ( data > M3::_LOW_MC_BOUND_ ) return ( data - M3::_LOW_MC_BOUND_ ) * ( data - M3::_LOW_MC_BOUND_ ) / ( 2. * M3::_LOW_MC_BOUND_ );
170  else if ( data >= mc ) return 0.;
171  }
172 
173  // Return the Pearson metric
174  return ( data - mc ) * ( data - mc ) / ( 2 * mc );
175  }
176  break;
177  case (kPoisson):
178  {
179  //Just call GetPoissonLLH which doesn't take in weights
180  //and is a Poisson likelihood comparison.
181  return GetPoissonLLH(data, mc);
182  }
183  break;
185  MACH3LOG_ERROR("kNTestStatistics is not a valid TestStatistic!");
186  throw MaCh3Exception(__FILE__, __LINE__);
187  default:
188  MACH3LOG_ERROR("Couldn't find TestStatistic {} exiting!", static_cast<int>(fTestStatistic));
189  throw MaCh3Exception(__FILE__ , __LINE__ );
190  } // end switch
191 }
192 
193 // ***************************************************************************
194 // CW: Silence cout and cerr. Last is risky but psyche persists on spamming both
196 // ***************************************************************************
197  #if MACH3_DEBUG > 0
198  return;
199  #else
200  buf = std::cout.rdbuf();
201  errbuf = std::cerr.rdbuf();
202  std::cout.rdbuf( nullptr );
203  std::cerr.rdbuf( nullptr );
204  #endif
205 }
206 
207 // ***************************************************************************
208 // CW: Reset cout and cerr
210 // ***************************************************************************
211  #if MACH3_DEBUG > 0
212  return;
213  #else
214  std::cout.rdbuf(buf);
215  std::cerr.rdbuf(errbuf);
216  #endif
217 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
@ kNTestStatistics
Number of test statistics.
@ kPearson
Standard Pearson likelihood .
@ kBarlowBeeston
Barlow-Beeston () following Conway approximation ()
@ kIceCube
Based on .
@ kDembinskiAbdelmotteleb
Based on .
@ kPoisson
Standard Poisson likelihood .
Custom exception class used throughout MaCh3.
TestStatistic fTestStatistic
Test statistic tells what kind of likelihood sample is using.
std::streambuf * errbuf
Keep the cerr buffer.
std::streambuf * buf
Keep the cout buffer.
double GetPoissonLLH(const double data, const double mc) const
Calculate test statistic for a single bin using Poisson.
double GetTestStatLLH(const double data, const double mc, const double w2) const
Calculate test statistic for a single bin. Calculation depends on setting of fTestStatistic....
M3::int_t nSamples
Contains how many samples we've got.
virtual ~SampleHandlerInterface()
destructor
void NowTalk()
CW: Redirect std::cout to silence some experiment specific libraries.
SampleHandlerInterface()
The main constructor.
void QuietPlease()
CW: Redirect std::cout to silence some experiment specific libraries.
constexpr static const double _LOW_MC_BOUND_
MC prediction lower bound in bin to identify problematic binning definitions and handle LogL calculat...
Definition: Core.h:83