ProfLike.cc
Go to the documentation of this file.
1 #include "ProfLike.h"
2 
3 #include <iostream>
4 #include <sstream>
5 #include <algorithm>
6 #include <numeric>
7 #include <cfloat>
8 #include <boost/math/special_functions/fpclassify.hpp>
9 
10 #include <utl/ErrorLogger.h>
11 
12 #include <TMath.h>
13 #include <TF1.h>
14 
15 #include <Minuit2/MnMinimize.h>
16 #include <Minuit2/MnHesse.h>
17 #include <Minuit2/FCNBase.h>
18 #include <Minuit2/FunctionMinimum.h>
19 
20 #include <Minuit2/MnUserCovariance.h>
21 #include <Minuit2/MnPrint.h>
22 #include <Minuit2/MnMigrad.h>
23 
24 using std::cout;
25 using std::endl;
26 
27 
28 namespace MdLDFFinderAG {
29 
30  const double ProfLike::kInitError = 0.1;
31 
32  const double ProfLike::satLikeLimit = 0.001;
33 
34  const double ProfLike::kMuPoissonApprox = 0.01;
35 
36 
37  ProfLike::ProfLike(const unsigned int n0, const utl::TraceUI& trace)
38  {
39  // Convert the trace to the good old vector we used so far
40  std::vector<unsigned int> k0;
41  for (utl::TraceUI::ConstIterator it = trace.Begin(); it != trace.End(); ++it)
42  k0.push_back(*it);
43 
44  fNumberSegments = n0;
45 
46  // Sort k0 (necessary to count the number of windows with the same number of segments on)
47  std::sort(k0.begin(), k0.end());
48 
49  // find unique occurrences in k0
50  std::vector<unsigned int> kuniq(k0); // kuniq holds unique occurrences in k0
51  std::vector<unsigned int>::iterator it = std::unique(kuniq.begin(), kuniq.end());
52  kuniq.resize(std::distance(kuniq.begin(), it));
53 
54  // Fill fSegmentsOnMulti
55  for (std::vector<unsigned int>::iterator it = kuniq.begin(); it != kuniq.end(); ++it) {
56  if(*it) { // skip k=0 windows if there are more than 1 window
57  const unsigned int count = std::count(k0.begin(), k0.end(), *it);
58  fSegmentsOnMulti.push_back(std::make_pair(*it,count));
59  }
60  }
61 
62  // if fSegmentsOnMulti is empty generate a single window with 0
63  if (fSegmentsOnMulti.empty())
64  fSegmentsOnMulti.push_back(std::make_pair(0, 1));
65 
66  // set the maximum number of segments on and its multiplicity
67  fMaxSegments = fSegmentsOnMulti.back().first;
68  fMaxMultiplicity = fSegmentsOnMulti.back().second;
69 
70  /*
71  DEBUGLOG("");
72  // cout << "In ProfLike::ProfLike: fSegmentsOnMulti = " << endl;
73  for(SegmentsOnMulti::iterator it = fSegmentsOnMulti.begin();
74  it != fSegmentsOnMulti.end(); ++it) {
75  // cout << (*it).first << " " << (*it).second << endl;
76  }
77  */
78  }
79 
80 
81  // Calculate the global minimum
82  double
84  const
85  {
86  double rvalue = 0;
87  for (SegmentsOnMulti::const_iterator it1 = fSegmentsOnMulti.begin();
88  it1 != fSegmentsOnMulti.end(); ++it1) {
89  const double k = it1->first;
90  const double m = it1->second;
91  if (k != fNumberSegments && k) {
92  rvalue += -m*(k*log(k / fNumberSegments) + (fNumberSegments - k)*log(1-k/fNumberSegments));
93  } // else if k=fNumberSegments or k=0 GlobalMin=0
94  }
95 
96  //cout << "Global minimum = " << rvalue << endl;
97  return rvalue;
98  }
99 
100 
101  // Set the minuit seed
102  ROOT::Minuit2::MnUserParameters
103  ProfLike::SetSeed(const double mu)
104  const
105  {
106  std::vector<double> vseed; // seed for the minimisation
107 
108  unsigned int ksum = 0; // sum of all k's
109  for (SegmentsOnMulti::const_iterator it = fSegmentsOnMulti.begin();
110  it != fSegmentsOnMulti.end(); ++it) {
111  ksum += it->first * it->second;
112  }
113 
114  // Fill the seed vector
115  for (SegmentsOnMulti::const_iterator it = fSegmentsOnMulti.begin();
116  it != fSegmentsOnMulti.end(); ++it) {
117  vseed.push_back((it->first) * mu/ksum);
118  }
119 
120  // Trim the last element of vseed
121  vseed.pop_back();
122 
123  //for (std::vector<double>::iterator it = vseed.begin(); it != vseed.end(); ++it) {
124  // cout << "In ProfLike::SetSeed(double): seed = " << *it << endl;
125  //}
126 
127  return SetMnUserParameters(vseed);
128  }
129 
130 
131  // Build MnUserParameters from the seeds vector
132  ROOT::Minuit2::MnUserParameters
133  ProfLike::SetMnUserParameters(const std::vector<double>& vseed)
134  const
135  {
136  // Create Minuit user parameters
137  ROOT::Minuit2::MnUserParameters upar;
138  std::stringstream ss;
139  unsigned int i = 1;
140  for (std::vector<double>::const_iterator it = vseed.begin(); it != vseed.end(); ++it) {
141  ss.str("");
142  ss << "mu" << i;
143  upar.Add(ss.str(), *it, kInitError);
144  upar.SetLowerLimit(ss.str(), 0);
145  //upar.SetLimits(ss1.str(), 0, mu); // Set limits in the parameters
146  ++i;
147  }
148 
149  return upar;
150  }
151 
152 
153  double
154  ProfLike::operator()(const double mu)
155  const
156  {
157  /*
158  DEBUGLOG("Starting");
159  cout << "In ProfLike::operator()(double): mu = " << mu << endl;
160  cout << "Number of windows " << fSegmentsOnMulti.size() << endl;
161  for (SegmentsOnMulti::const_iterator it1=fSegmentsOnMulti.begin() ; it1!=fSegmentsOnMulti.end(); ++it1) {
162  cout << "scints on = " << (*it1).first;
163  cout << ", multiplicity = " << (*it1).second << endl;
164  }
165  */
166  double rvalue;
167 
168  // limit for the "large mu" analytical approximation of the likelihood
169  // (avoids minimization issues when mu is large)
170  const double mulimit = -double(fNumberSegments) * log(satLikeLimit/double(fNumberSegments));
171 
172  // calculate the mu left for the hottest window
173  double mua = 0; // sum of the mle of mu over all but the hottest window
174  for (SegmentsOnMulti::const_iterator it = fSegmentsOnMulti.begin();
175  it != fSegmentsOnMulti.end(); ++it) { // calculate mua
176 
177  const double k = double(it->first);
178  const double m = double(it->second);
179 
180  if (it->first != fMaxSegments)
181  mua += -double(fNumberSegments) * log(1 - k/fNumberSegments)*m;
182 
183  }
184  const double mub = (mu - mua) / fMaxMultiplicity;
185 
186  // Try different limiting cases before attempting the profile likelihood
187  if (fMaxSegments == 0) { // if there no segments on
188  rvalue = mu;
189  } else if (mu < kMuPoissonApprox) { // if mu is small use Poisson
190  const double mu2 = std::max(mu, DBL_EPSILON); // avoid mu values lower than the machine precision
191 
192  //cout << "Using a Poisson likelihood" << endl;
193  const double k = GetTotalSegmentsOn();
194  rvalue = (mu2 - k) - k*log(mu2 / k); // Poisson likelihood
195  } else if (fSegmentsOnMulti.size() == 1) { // if there is only 1 time window
196 
197  const unsigned int segmentsOn = fSegmentsOnMulti[0].first;
198  const unsigned int multiplicity = fSegmentsOnMulti[0].second;
199 
200  double like1 = multiplicity*Like1(mu/multiplicity, segmentsOn);
201 
202 // cout << "Using 1 window likelihood. mu = " << mu << endl;
203 // cout << "Like1 = " << like1 << endl;
204 // cout << "GlobalMin = " << GlobalMin() << endl;
205 
206  rvalue = like1 - GlobalMin(); // minimum wrt to the global minimum
207 
208  } else if (mub > mulimit) { // if mu is large
209 
210  // large mu approximation (the minimisation is unstable for very large mu)
211  // use likelihood of the hottest window only - assumes the other as minmized
212 
213  // cast for convenience
214  const double k = fMaxSegments;
215  const double n = fNumberSegments;
216 
217 // cout << "Using large mu approx. mub = " << mub << endl;
218 
219  // multiplicity already considered in mub...
220  double likeMaxWindow = Like1(mub, fMaxSegments);
221 
222 // cout << "rvalue before minimum substraction = " << rvalue << endl;
223 
224  double nLogLikeMin = 0;
225 
226 // cout << "fMaxSegments = " << fMaxSegments << ", fNumberSegments = ";
227 // cout << fNumberSegments << endl;
228 
229  if (fMaxSegments == fNumberSegments || fMaxSegments == 0) {
230  nLogLikeMin = 0;
231  } else {
232  nLogLikeMin = - k*log(k/n) - (n-k)*log(1-k/n);
233  }
234 
235 // cout << "nLogLikeMin = " << nLogLikeMin << endl;
236 
237  rvalue = fMaxMultiplicity*(likeMaxWindow - nLogLikeMin);
238  // refer to minimum use multiplicity of the hottest window
239 
240 // cout << "rvalue after offset = " << rvalue << endl;
241 
242  } else { // calculate the profile likelihood via Minuit minimization
243 
244 // cout << "Calculating the profile likelihood using mu = " << mu << endl;
245 
246  ROOT::Minuit2::MnUserParameters upar = SetSeed(mu); // Create the initial parameters
247 
248  LikeFCN fullLike(fNumberSegments, fSegmentsOnMulti, mu); // Create the minimization function
249 
250  ROOT::Minuit2::MnMigrad migrad(fullLike, upar); // Create the minimizer
251 // DEBUGLOG("After creating the minimizer");
252 
253  gErrorIgnoreLevel = 1001; // set the print level of Minuit (skip info messages)
254 
255  ROOT::Minuit2::FunctionMinimum min = migrad(); // minimize
256 // DEBUGLOG("After running the minimizer");
257 
258 // cout << min << endl;
259 // cout << "In ProfLike::operator()(double). Valid minimum = ";
260 // cout << std::boolalpha << min.IsValid() << endl;
261 
262  // check that the minimization converged
263  assert(min.IsValid());
264  assert(min.IsValid());
265  assert(min.HasValidCovariance());
266  assert(!min.HesseFailed());
267  assert(!min.HasReachedCallLimit());
268 
269  rvalue = min.UserState().Fval() - GlobalMin(); // minimum wrt to the global minimum
270 
271  }
272 
273  // check the return value is finite
274  assert(boost::math::isfinite(rvalue));
275 
276 // DEBUGLOG("Finishing");
277 
278  return rvalue;
279  }
280 
281 
282  double
283  ProfLike::Like1(const double mu, const unsigned int k)
284  const
285  {
286  //cout << "Starting ProfLike::Like1(double mu)" << endl;
287 
288  double nlnLike = 0;
289 
290  if (mu < 1e-6) {
291  nlnLike = (!k ? 0 : 1e6); // if k!=0 set -lnL to some "ridiculous" high number
292  } else if (k != fNumberSegments) {
293  nlnLike = mu*(1 - double(k)/fNumberSegments) - k*log(1 - exp(-mu/fNumberSegments));
294  } else { // if k==fNumberSegments
295  nlnLike = -k * log(1 - exp(-mu/fNumberSegments)); // use alternative formula to avoid divergences
296  }
297 
298  //cout << "mu = " << mu << ", nlnLike = " << nlnLike << endl;
299 
300  //cout << "Ending ProfLike::Like1(double mu)" << endl;
301 
302  return nlnLike;
303  }
304 
305 
306  double
308  const
309  {
310  double estimatedMuons = 0;
311 
312  for (SegmentsOnMulti::const_iterator it1 = fSegmentsOnMulti.begin();
313  it1 != fSegmentsOnMulti.end(); ++it1) {
314 
315  const unsigned int k = it1->first;
316  const unsigned int m = it1->second;
317 
318  assert(k != fNumberSegments); // make sure there is no saturation
319 
320  double windowMuons = (-1)*double(m) * double(fNumberSegments) * log( 1 - double(k)/double(fNumberSegments) );
321  estimatedMuons += windowMuons;
322  }
323 
324  return estimatedMuons;
325  }
326 
327 
328  double
329  ProfLike::Inverse(const double y, const double xmin, const double xmax)
330  const
331  {
332  TF1 f1("f1", *this, xmin, xmax, 0);
333  const double x = f1.GetX(y, xmin, xmax);
334  return x;
335  }
336 
337 
338  double
340  const
341  {
342  const double muhat = GetMLE();
343 
344  // increment in the likelihood in 0.5 from the minimum to find the limit
345  const double up = 0.5;
346 
347  // find xmax such as f(xmax) > up
348  double xmax = muhat + 1;
349  while (this->operator()(xmax) < up)
350  xmax *= 2;
351 
352  const double xup = Inverse(up, muhat, xmax);
353  const double xerror = xup - muhat;
354 
355  return xerror;
356  }
357 
358 
359  double
361  const
362  {
363  const double muhat = GetMLE();
364  const double xlow = GetLowLimitUnsaturated();
365  const double xerror = muhat - xlow;
366  return xerror;
367  }
368 
369 
370  double
372  const
373  {
374  const double muhat = GetMLE();
375 
376  // increment in the likelihood in 0.5 from the minimum to find the limit
377  const double up = 0.5;
378 
379  // if the event is empty
380  if (!fMaxSegments)
381  return 0; // the interval length is zero
382 
383  // find xmin such as f(xmin) > up
384  double xmin = muhat / 2;
385  while (this->operator()(xmin) < up)
386  xmin /= 2;
387 
388  const double xup = Inverse(up, xmin, muhat);
389 
390  return xup;
391  }
392 
393 
394  unsigned int
396  const
397  {
398  unsigned int totalSegmentsOn = 0;
399 
400  for (SegmentsOnMulti::const_iterator it1 = fSegmentsOnMulti.begin();
401  it1 != fSegmentsOnMulti.end(); ++it1) {
402 
403  const unsigned int k = it1->first;
404  const unsigned int m = it1->second;
405 
406  totalSegmentsOn += k*m;
407  }
408 
409  return totalSegmentsOn;
410 
411  }
412 
413 
414  double
416  const
417  {
418  // if the event is empty
419  if (!fMaxSegments)
420  return 0; // the interval length is zero
421 
422  // if the module is not saturated
424  return GetLowLimitUnsaturated();
425 
426  // Calculate the limit in the saturated case
427  // increment in the likelihood in 0.5 from the minimum to find the limit
428  const double up = 0.5;
429  const double xmin = 0.1;
430  const double xmaxSeed = 1;
431 
432  // find xmax such as f(xmin) > up
433  double xmax = xmaxSeed;
434  while (this->operator()(xmax) > up)
435  xmax *= 2;
436 
437  const double limit = Inverse(up, xmin, xmax);
438 
439  return limit;
440  }
441 
442 }
static const double kInitError
Definition: ProfLike.h:25
ROOT::Minuit2::MnUserParameters SetSeed(const double mu) const
Definition: ProfLike.cc:103
double GetErrorLow() const
Definition: ProfLike.cc:360
double GetLowLimit() const
Definition: ProfLike.cc:415
static const double kMuPoissonApprox
Definition: ProfLike.h:30
static const double satLikeLimit
Definition: ProfLike.h:27
ProfLike(const unsigned int, const utl::TraceUI &)
Definition: ProfLike.cc:37
std::vector< T >::const_iterator ConstIterator
Definition: Trace.h:60
Iterator Begin()
Definition: Trace.h:75
#define max(a, b)
double GetErrorHigh() const
Definition: ProfLike.cc:339
double GetMLE() const
Definition: ProfLike.cc:307
unsigned int fMaxSegments
Definition: ProfLike.h:60
unsigned int fNumberSegments
Definition: ProfLike.h:56
double Like1(const double mu, const unsigned int k) const
Definition: ProfLike.cc:283
double operator()(const double mu) const
Definition: ProfLike.cc:154
ROOT::Minuit2::MnUserParameters SetMnUserParameters(const std::vector< double > &) const
Definition: ProfLike.cc:133
Template class for a FADC data or calibrated data container. Use the typedefs (TraceD, TraceI, etc.) defined in Trace-fwd.h.
Definition: Trace-fwd.h:19
unsigned int GetTotalSegmentsOn() const
Definition: ProfLike.cc:395
unsigned int fMaxMultiplicity
Definition: ProfLike.h:62
Iterator End()
Definition: Trace.h:76
double GlobalMin() const
Definition: ProfLike.cc:83
constexpr double m
Definition: AugerUnits.h:121
double GetLowLimitUnsaturated() const
Definition: ProfLike.cc:371
SegmentsOnMulti fSegmentsOnMulti
Definition: ProfLike.h:58
double Inverse(const double y, const double xmin, const double xmax) const
Definition: ProfLike.cc:329

, generated on Tue Sep 26 2023.