1 |
algomez |
1.1 |
#include <cassert>
|
2 |
|
|
#include <sstream>
|
3 |
|
|
#include <cmath>
|
4 |
|
|
#include <limits>
|
5 |
|
|
|
6 |
|
|
#include <TF1.h>
|
7 |
|
|
#include <TMath.h>
|
8 |
|
|
#include <TH1D.h>
|
9 |
|
|
#include <TGraph.h>
|
10 |
|
|
|
11 |
|
|
#include "fit.hh"
|
12 |
|
|
#include "binneddata.hh"
|
13 |
|
|
#include "statistics.hh"
|
14 |
|
|
|
15 |
|
|
Fitter* Fitter::theFitter_;
|
16 |
|
|
|
17 |
|
|
// ---------------------------------------------------------
|
18 |
|
|
Fitter::Fitter()
|
19 |
|
|
{
|
20 |
|
|
data_=0;
|
21 |
|
|
functionIntegral_=0;
|
22 |
|
|
printlevel_=3;
|
23 |
|
|
strategy_=2;
|
24 |
|
|
minuit_.SetErrorDef(0.5); // likelihood
|
25 |
|
|
rand_=new TRandom3(31415);
|
26 |
|
|
parameters_=0;
|
27 |
|
|
poiIndex_=-1;
|
28 |
|
|
callLimitReached_=false;
|
29 |
|
|
poiBestFit_ = 0;
|
30 |
|
|
poiUserError_ = 0;
|
31 |
|
|
parRangeSet_=false;
|
32 |
|
|
}
|
33 |
|
|
|
34 |
|
|
// ---------------------------------------------------------
|
35 |
|
|
Fitter::Fitter(TH1D* data, integral_ptr_t functionIntegral, int maxpar) :
|
36 |
|
|
minuit_(maxpar)
|
37 |
|
|
{
|
38 |
|
|
data_=data;
|
39 |
|
|
functionIntegral_=functionIntegral;
|
40 |
|
|
printlevel_=3;
|
41 |
|
|
strategy_=2;
|
42 |
|
|
minuit_.SetErrorDef(0.5); // likelihood
|
43 |
|
|
rand_=new TRandom3(31415);
|
44 |
|
|
parameters_=0;
|
45 |
|
|
poiIndex_=-1;
|
46 |
|
|
callLimitReached_=false;
|
47 |
|
|
poiBestFit_ = 0;
|
48 |
|
|
poiUserError_ = 0;
|
49 |
|
|
parRangeSet_=false;
|
50 |
|
|
}
|
51 |
|
|
|
52 |
|
|
// ---------------------------------------------------------
|
53 |
|
|
Fitter::~Fitter()
|
54 |
|
|
{
|
55 |
|
|
if(parameters_) delete[] parameters_;
|
56 |
|
|
|
57 |
|
|
delete rand_;
|
58 |
|
|
|
59 |
|
|
// remove nuisance parameter priors
|
60 |
|
|
for(std::map<int, RandomPrior*>::const_iterator it=priors_.begin(); it!=priors_.end(); ++it) delete it->second;
|
61 |
|
|
}
|
62 |
|
|
|
63 |
|
|
// ---------------------------------------------------------
|
64 |
|
|
void Fitter::doFit(void)
|
65 |
|
|
{
|
66 |
|
|
// setup the fitter so the information can be retrieved by nll
|
67 |
|
|
Fitter::theFitter_=this;
|
68 |
|
|
|
69 |
|
|
// setup TMinuit
|
70 |
|
|
minuit_.SetPrintLevel(printlevel_); // printlevel = -1 quiet (also suppresse all warnings), = 0 normal, = 1 verbose
|
71 |
|
|
|
72 |
|
|
// set the strategy
|
73 |
|
|
std::ostringstream command;
|
74 |
|
|
command << "SET STR " << strategy_;
|
75 |
|
|
minuit_.Command(command.str().c_str());
|
76 |
|
|
|
77 |
|
|
// do the fit
|
78 |
|
|
minuit_.SetFCN(nll);
|
79 |
|
|
Double_t arglist[1] = {20000.0};
|
80 |
|
|
Int_t err = 0;
|
81 |
|
|
minuit_.mnexcm("MIGRAD",arglist,1,err); /// MIGRAD: Causes minimization of the function by the method of Migrad, the most efficient and complete single method, recommended for general functions (see also MINImize). The minimization produces as a by-product the error matrix of the parameters, which is usually reliable unless warning messages are produced. The optional argument [maxcalls] specifies the (approximate) maximum number of function calls after which the calculation will be stopped even if it has not yet converged. The optional argument [tolerance] specifies required tolerance on the function value at the minimum. The default tolerance is 0.1, and the minimization will stop when the estimated vertical distance to the minimum (EDM) is less than 0.001*[tolerance]*UP (see SET ERR).
|
82 |
|
|
|
83 |
|
|
return;
|
84 |
|
|
}
|
85 |
|
|
|
86 |
|
|
// ---------------------------------------------------------
|
87 |
|
|
void Fitter::doFit(double* emat, int ndim)
|
88 |
|
|
{
|
89 |
|
|
// setup the fitter so the information can be retrieved by nll
|
90 |
|
|
Fitter::theFitter_=this;
|
91 |
|
|
|
92 |
|
|
// setup TMinuit
|
93 |
|
|
minuit_.SetPrintLevel(printlevel_);
|
94 |
|
|
|
95 |
|
|
// set the strategy
|
96 |
|
|
std::ostringstream command;
|
97 |
|
|
command << "SET STR " << strategy_;
|
98 |
|
|
minuit_.Command(command.str().c_str());
|
99 |
|
|
|
100 |
|
|
// do the fit
|
101 |
|
|
minuit_.SetFCN(nll);
|
102 |
|
|
Double_t arglist[1] = {20000.0};
|
103 |
|
|
Int_t err = 0;
|
104 |
|
|
minuit_.mnexcm("MIGRAD",arglist,1,err);
|
105 |
|
|
minuit_.mnemat(emat, ndim);
|
106 |
|
|
|
107 |
|
|
return;
|
108 |
|
|
}
|
109 |
|
|
|
110 |
|
|
// ---------------------------------------------------------
|
111 |
|
|
TH1D* Fitter::calcPull(const char* name)
|
112 |
|
|
{
|
113 |
|
|
const double alpha = 1 - 0.6827;
|
114 |
|
|
|
115 |
|
|
TH1D* hPull=dynamic_cast<TH1D*>(data_->Clone(name));
|
116 |
|
|
hPull->SetTitle("Pull Distribution");
|
117 |
|
|
|
118 |
|
|
hPull->SetBinContent(0, 0.);
|
119 |
|
|
hPull->SetBinContent(hPull->GetNbinsX()+1, 0.);
|
120 |
|
|
hPull->SetBinError(0, 0.);
|
121 |
|
|
hPull->SetBinError(hPull->GetNbinsX()+1, 0.);
|
122 |
|
|
for(int bin=1; bin<=data_->GetNbinsX(); bin++) {
|
123 |
|
|
double binwidth=data_->GetBinWidth(bin);
|
124 |
|
|
double N=data_->GetBinContent(bin)*binwidth;
|
125 |
|
|
double l = 0.5*TMath::ChisquareQuantile(alpha/2,2*N);
|
126 |
|
|
double h = 0.5*TMath::ChisquareQuantile(1-alpha/2,2*(N+1));
|
127 |
|
|
double el = N-l;
|
128 |
|
|
double eh = h-N;
|
129 |
|
|
|
130 |
|
|
double x0=data_->GetBinLowEdge(bin);
|
131 |
|
|
double xf=data_->GetBinLowEdge(bin+1);
|
132 |
|
|
double mu=functionIntegral_(&x0, &xf, getParameters());
|
133 |
|
|
|
134 |
|
|
double err = (el + eh)/2.;
|
135 |
|
|
if(N>=mu) err = el;
|
136 |
|
|
if(N<mu) err = eh;
|
137 |
|
|
|
138 |
|
|
double pull=(N-mu)/err;
|
139 |
|
|
hPull->SetBinContent(bin, pull);
|
140 |
|
|
hPull->SetBinError(bin, 1.0);
|
141 |
|
|
}
|
142 |
|
|
return hPull;
|
143 |
|
|
}
|
144 |
|
|
|
145 |
|
|
// ---------------------------------------------------------
|
146 |
|
|
TH1D* Fitter::calcDiff(const char* name)
|
147 |
|
|
{
|
148 |
|
|
TH1D* hDiff=dynamic_cast<TH1D*>(data_->Clone(name));
|
149 |
|
|
hDiff->SetTitle("Difference Distribution");
|
150 |
|
|
|
151 |
|
|
hDiff->SetBinContent(0, 0.);
|
152 |
|
|
hDiff->SetBinContent(hDiff->GetNbinsX()+1, 0.);
|
153 |
|
|
hDiff->SetBinError(0, 0.);
|
154 |
|
|
hDiff->SetBinError(hDiff->GetNbinsX()+1, 0.);
|
155 |
|
|
for(int bin=1; bin<=data_->GetNbinsX(); bin++) {
|
156 |
|
|
double binwidth=data_->GetBinWidth(bin);
|
157 |
|
|
double N=data_->GetBinContent(bin)*binwidth;
|
158 |
|
|
double err=Fitter::histError(N);
|
159 |
|
|
|
160 |
|
|
double x0=data_->GetBinLowEdge(bin);
|
161 |
|
|
double xf=data_->GetBinLowEdge(bin+1);
|
162 |
|
|
double mu=functionIntegral_(&x0, &xf, getParameters());
|
163 |
|
|
|
164 |
|
|
double diff=(N-mu)/mu;
|
165 |
|
|
hDiff->SetBinContent(bin, diff);
|
166 |
|
|
hDiff->SetBinError(bin, err/mu);
|
167 |
|
|
}
|
168 |
|
|
return hDiff;
|
169 |
|
|
}
|
170 |
|
|
|
171 |
|
|
// ---------------------------------------------------------
|
172 |
|
|
int Fitter::setParameter(int parno, double value)
|
173 |
|
|
{
|
174 |
|
|
int err;
|
175 |
|
|
double tmp[2];
|
176 |
|
|
tmp[0]=parno+1;
|
177 |
|
|
tmp[1]=value;
|
178 |
|
|
|
179 |
|
|
minuit_.mnexcm("SET PAR", tmp, 2, err);
|
180 |
|
|
return err;
|
181 |
|
|
}
|
182 |
|
|
|
183 |
|
|
// ---------------------------------------------------------
|
184 |
|
|
int Fitter::setParLimits(int parno, double loLimit, double hiLimit)
|
185 |
|
|
{
|
186 |
|
|
int err;
|
187 |
|
|
double tmp[3];
|
188 |
|
|
tmp[0]=parno+1;
|
189 |
|
|
tmp[1]=loLimit;
|
190 |
|
|
tmp[2]=hiLimit;
|
191 |
|
|
|
192 |
|
|
minuit_.mnexcm("SET LIM", tmp, 3, err);
|
193 |
|
|
return err;
|
194 |
|
|
}
|
195 |
|
|
|
196 |
|
|
// ---------------------------------------------------------
|
197 |
|
|
double Fitter::getParameter(int parno) const
|
198 |
|
|
{
|
199 |
|
|
double val, err;
|
200 |
|
|
getParameter(parno, val, err);
|
201 |
|
|
return val;
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
// ---------------------------------------------------------
|
205 |
|
|
double Fitter::getParError(int parno) const
|
206 |
|
|
{
|
207 |
|
|
double val, err;
|
208 |
|
|
getParameter(parno, val, err);
|
209 |
|
|
return err;
|
210 |
|
|
}
|
211 |
|
|
|
212 |
|
|
// ---------------------------------------------------------
|
213 |
|
|
void Fitter::getParLimits(int parno, double &loLimit, double &hiLimit) const
|
214 |
|
|
{
|
215 |
|
|
TString _name;
|
216 |
|
|
double _val, _err;
|
217 |
|
|
int _iuint;
|
218 |
|
|
minuit_.mnpout(parno, _name, _val, _err, loLimit, hiLimit, _iuint);
|
219 |
|
|
return;
|
220 |
|
|
}
|
221 |
|
|
|
222 |
|
|
// ---------------------------------------------------------
|
223 |
|
|
double* Fitter::getParameters(void)
|
224 |
|
|
{
|
225 |
|
|
// remove what is there
|
226 |
|
|
if(parameters_) delete[] parameters_;
|
227 |
|
|
|
228 |
|
|
int nPars=minuit_.GetNumPars();
|
229 |
|
|
parameters_ = new double[nPars];
|
230 |
|
|
for(int i=0; i<nPars; i++) {
|
231 |
|
|
double value, err;
|
232 |
|
|
getParameter(i, value, err);
|
233 |
|
|
parameters_[i]=value;
|
234 |
|
|
}
|
235 |
|
|
|
236 |
|
|
return parameters_;
|
237 |
|
|
}
|
238 |
|
|
|
239 |
|
|
// ---------------------------------------------------------
|
240 |
|
|
int Fitter::defineParameter(int parno, const char *name, double value, double error, double lo, double hi, int isNuisance)
|
241 |
|
|
{
|
242 |
|
|
parameterIsNuisance_[parno]=isNuisance;
|
243 |
|
|
if(poiIndex_>=0 && parno==poiIndex_)
|
244 |
|
|
if(poiUserError_==0.) poiUserError_=error;
|
245 |
|
|
return minuit_.DefineParameter(parno, name, value, error, lo, hi);
|
246 |
|
|
}
|
247 |
|
|
|
248 |
|
|
// ---------------------------------------------------------
|
249 |
|
|
TGraph* Fitter::calculatePosterior(int nSamples)
|
250 |
|
|
{
|
251 |
|
|
// we need a parameter of index defined
|
252 |
|
|
assert(poiIndex_>=0);
|
253 |
|
|
|
254 |
|
|
// set the number of samples for subsequent functions
|
255 |
|
|
nSamples_=nSamples;
|
256 |
|
|
nCalls_=0;
|
257 |
|
|
|
258 |
|
|
// fit for the best value of the POI
|
259 |
|
|
int nPars=getNumPars();
|
260 |
|
|
double loLimit, hiLimit;
|
261 |
|
|
getParLimits(poiIndex_, loLimit, hiLimit);
|
262 |
|
|
for(int i=0; i<nPars; i++) if(i==poiIndex_) floatParameter(i); else fixParameter(i);
|
263 |
|
|
setParameter(poiIndex_, 0.1*(loLimit+hiLimit));
|
264 |
|
|
doFit();
|
265 |
|
|
fixParameter(poiIndex_);
|
266 |
|
|
poiBestFit_=getParameter(poiIndex_);
|
267 |
|
|
|
268 |
|
|
// setup Fitter
|
269 |
|
|
Fitter::theFitter_=this;
|
270 |
|
|
|
271 |
|
|
// now float all parameters
|
272 |
|
|
for(int i=0; i<nPars; i++) if(i!=poiIndex_) floatParameter(i);
|
273 |
|
|
|
274 |
|
|
// evalulate NLL at the POI fit value for future normalizations
|
275 |
|
|
double nllNormalization=evalNLL();
|
276 |
|
|
|
277 |
|
|
// recursively evaluate the posterior
|
278 |
|
|
std::map<double, double> fcnEvaluations;
|
279 |
|
|
evaluateForPosterior(loLimit, poiBestFit_, hiLimit, nllNormalization, fcnEvaluations);
|
280 |
|
|
|
281 |
|
|
// dump the info into a graph
|
282 |
|
|
int cntr=0;
|
283 |
|
|
double maximumVal=-9999.;
|
284 |
|
|
TGraph* graph=new TGraph(fcnEvaluations.size());
|
285 |
|
|
for(std::map<double, double>::const_iterator it=fcnEvaluations.begin(); it!=fcnEvaluations.end(); ++it) {
|
286 |
|
|
graph->SetPoint(cntr++, it->first, it->second);
|
287 |
|
|
if(it->second>maximumVal) maximumVal=it->second;
|
288 |
|
|
}
|
289 |
|
|
|
290 |
|
|
// identify trivially small points on the left
|
291 |
|
|
std::vector<int> pointsToRemove;
|
292 |
|
|
// for(int i=0; i<graph->GetN()-1; i++) {
|
293 |
|
|
// double x, y, nextx, nexty;
|
294 |
|
|
// graph->GetPoint(i, x, y);
|
295 |
|
|
// graph->GetPoint(i+1, nextx, nexty);
|
296 |
|
|
//
|
297 |
|
|
// if(y/maximumVal<1.E-3 && nexty/maximumVal<1.E-3) pointsToRemove.push_back(i);
|
298 |
|
|
// else break;
|
299 |
|
|
// }
|
300 |
|
|
|
301 |
|
|
// identify trivially small points on the right
|
302 |
|
|
for(int i=graph->GetN()-1; i>=1; i--) {
|
303 |
|
|
double x, y, nextx, nexty;
|
304 |
|
|
graph->GetPoint(i, x, y);
|
305 |
|
|
graph->GetPoint(i-1, nextx, nexty);
|
306 |
|
|
|
307 |
|
|
if(y/maximumVal<1.E-3 && nexty/maximumVal<1.E-3) pointsToRemove.push_back(i);
|
308 |
|
|
else break;
|
309 |
|
|
}
|
310 |
|
|
|
311 |
|
|
// sort the points to remove from first to last
|
312 |
|
|
std::sort(pointsToRemove.begin(), pointsToRemove.end());
|
313 |
|
|
|
314 |
|
|
// remove the points
|
315 |
|
|
for(int i=pointsToRemove.size()-1; i>=0; i--)
|
316 |
|
|
graph->RemovePoint(pointsToRemove[i]);
|
317 |
|
|
|
318 |
|
|
return graph;
|
319 |
|
|
}
|
320 |
|
|
|
321 |
|
|
// ---------------------------------------------------------
|
322 |
|
|
double Fitter::evalNLL(void)
|
323 |
|
|
{
|
324 |
|
|
Fitter::theFitter_=this;
|
325 |
|
|
int a;
|
326 |
|
|
double f;
|
327 |
|
|
nll(a, 0, f, getParameters(), 0);
|
328 |
|
|
return f;
|
329 |
|
|
}
|
330 |
|
|
|
331 |
|
|
// ---------------------------------------------------------
|
332 |
|
|
double Fitter::histError(double val)
|
333 |
|
|
{
|
334 |
|
|
const double alpha = 1 - 0.6827;
|
335 |
|
|
|
336 |
|
|
if(val<25. && val>0.) return (0.5*TMath::ChisquareQuantile(1-alpha/2,2*(val+1))-0.5*TMath::ChisquareQuantile(alpha/2,2*val))/2.0; // this is not exactly correct since it symmetrizes what are otherwise asymmetric error bars
|
337 |
|
|
else if(val==0.) return 0.5*TMath::ChisquareQuantile(1-alpha/2,2*(val+1)); // special case of 0 events with one-sided error bar
|
338 |
|
|
else return sqrt(val); // for val>25 error bars with correct coverage are essentially symmetric
|
339 |
|
|
}
|
340 |
|
|
|
341 |
|
|
// ---------------------------------------------------------
|
342 |
|
|
void Fitter::nll(int &, double *, double &f, double *par, int)
|
343 |
|
|
{
|
344 |
|
|
assert(Fitter::theFitter_);
|
345 |
|
|
TH1D* data=Fitter::theFitter_->data_;
|
346 |
|
|
integral_ptr_t functionIntegral=Fitter::theFitter_->functionIntegral_;
|
347 |
|
|
|
348 |
|
|
f=0.0;
|
349 |
|
|
for(int bin=1; bin<=data->GetNbinsX(); bin++) {
|
350 |
|
|
double binwidth=data->GetBinWidth(bin);
|
351 |
|
|
double N=data->GetBinContent(bin)*binwidth;
|
352 |
|
|
|
353 |
|
|
double x0=data->GetBinLowEdge(bin);
|
354 |
|
|
double xf=data->GetBinLowEdge(bin+1);
|
355 |
|
|
double mu=functionIntegral(&x0, &xf, par);
|
356 |
|
|
|
357 |
|
|
if(N==0.0) f += mu;
|
358 |
|
|
else f -= (N*TMath::Log(mu) - TMath::LnGamma(N+1) - mu);
|
359 |
|
|
}
|
360 |
|
|
return;
|
361 |
|
|
}
|
362 |
|
|
|
363 |
|
|
// ---------------------------------------------------------
|
364 |
|
|
TH1D* Fitter::makePseudoData(const char* name, double* parameters)
|
365 |
|
|
{
|
366 |
|
|
if(!parameters) parameters=getParameters();
|
367 |
|
|
|
368 |
|
|
// start with a copy of the original dataset
|
369 |
|
|
TH1D* hData=dynamic_cast<TH1D*>(data_->Clone(name));
|
370 |
|
|
|
371 |
|
|
for(int bin=1; bin<=hData->GetNbinsX(); ++bin) {
|
372 |
|
|
double lobin=hData->GetBinLowEdge(bin);
|
373 |
|
|
double hibin=hData->GetBinLowEdge(bin+1);
|
374 |
|
|
double integral=functionIntegral_(&lobin, &hibin, parameters);
|
375 |
|
|
double val=rand_->Poisson(integral);
|
376 |
|
|
double binWidth=hData->GetBinWidth(bin);
|
377 |
|
|
hData->SetBinContent(bin, val/binWidth);
|
378 |
|
|
hData->SetBinError(bin, histError(val)/binWidth);
|
379 |
|
|
}
|
380 |
|
|
return hData;
|
381 |
|
|
}
|
382 |
|
|
|
383 |
|
|
// ---------------------------------------------------------
|
384 |
|
|
void Fitter::evaluateForPosterior(double lo, double mid, double hi, double nllNormalization, std::map<double, double>& fcnEval_)
|
385 |
|
|
{
|
386 |
|
|
if((nCalls_++)>1000) {
|
387 |
|
|
callLimitReached_=true;
|
388 |
|
|
return;
|
389 |
|
|
}
|
390 |
|
|
|
391 |
|
|
// get the low value
|
392 |
|
|
std::map<double, double>::iterator findit;
|
393 |
|
|
findit = fcnEval_.find(lo);
|
394 |
|
|
double loVal;
|
395 |
|
|
if(findit==fcnEval_.end()) {
|
396 |
|
|
loVal=computeLikelihoodWithSystematics(lo, nllNormalization);
|
397 |
|
|
fcnEval_[lo]=loVal;
|
398 |
|
|
} else {
|
399 |
|
|
loVal=fcnEval_[lo];
|
400 |
|
|
}
|
401 |
|
|
|
402 |
|
|
// get the middle value
|
403 |
|
|
findit = fcnEval_.find(mid);
|
404 |
|
|
double midVal;
|
405 |
|
|
if(findit==fcnEval_.end()) {
|
406 |
|
|
midVal=computeLikelihoodWithSystematics(mid, nllNormalization);
|
407 |
|
|
fcnEval_[mid]=midVal;
|
408 |
|
|
} else {
|
409 |
|
|
midVal=fcnEval_[mid];
|
410 |
|
|
}
|
411 |
|
|
|
412 |
|
|
// get the high value
|
413 |
|
|
findit = fcnEval_.find(hi);
|
414 |
|
|
double hiVal;
|
415 |
|
|
if(findit==fcnEval_.end()) {
|
416 |
|
|
hiVal=computeLikelihoodWithSystematics(hi, nllNormalization);
|
417 |
|
|
fcnEval_[hi]=hiVal;
|
418 |
|
|
} else {
|
419 |
|
|
hiVal=fcnEval_[hi];
|
420 |
|
|
}
|
421 |
|
|
|
422 |
|
|
//double maximumValX = 0.;
|
423 |
|
|
double maximumVal = -999.;
|
424 |
|
|
for(std::map<double, double>::const_iterator it=fcnEval_.begin(); it!=fcnEval_.end(); ++it)
|
425 |
|
|
if(maximumVal<it->second)
|
426 |
|
|
{
|
427 |
|
|
//maximumValX=it->first;
|
428 |
|
|
maximumVal=it->second;
|
429 |
|
|
}
|
430 |
|
|
|
431 |
|
|
// for debugging
|
432 |
|
|
//std::cout << "nCalls: " << nCalls_ << std::endl
|
433 |
|
|
// << "nllNormalization: " << nllNormalization << std::endl
|
434 |
|
|
// << "lo, mid, high: " << lo << ", " << mid << ", " << hi << std::endl
|
435 |
|
|
// << "loval, midval, hival: " << loVal << ", " << midVal << ", " << hiVal << std::endl
|
436 |
|
|
// << "maximumValX, maximumVal: " << maximumValX << ", " << maximumVal << std::endl << std::endl;
|
437 |
|
|
|
438 |
|
|
if(fabs(loVal-midVal)>0.04*maximumVal || fabs(hiVal-midVal)>0.04*maximumVal) {
|
439 |
|
|
if(fabs(hi-mid)/fabs(hi)>0.01 && fabs(hi-mid)/fabs(poiBestFit_)>0.01 && fabs(hi-mid)>poiUserError_) evaluateForPosterior(mid, 0.5*(mid+hi), hi, nllNormalization, fcnEval_); // important to go to the mid-high range first to get a nicely falling posterior tail in case the number of calls limit is reached
|
440 |
|
|
if(fabs(lo-mid)/fabs(mid)>0.01 && fabs(lo-mid)/fabs(poiBestFit_)>0.01 && fabs(lo-mid)>poiUserError_) evaluateForPosterior(lo, 0.5*(lo+mid), mid, nllNormalization, fcnEval_);
|
441 |
|
|
}
|
442 |
|
|
|
443 |
|
|
return;
|
444 |
|
|
}
|
445 |
|
|
|
446 |
|
|
// ---------------------------------------------------------
|
447 |
|
|
double Fitter::computeLikelihoodWithSystematics(double poiVal, double nllNormalization)
|
448 |
|
|
{
|
449 |
|
|
// setup parameters
|
450 |
|
|
int a;
|
451 |
|
|
double f;
|
452 |
|
|
double *pars=getParameters();
|
453 |
|
|
pars[poiIndex_]=poiVal;
|
454 |
|
|
|
455 |
|
|
// no systematics
|
456 |
|
|
if(nSamples_==0) {
|
457 |
|
|
nll(a, 0, f, pars, 0);
|
458 |
|
|
return TMath::Exp(-f+nllNormalization);
|
459 |
|
|
}
|
460 |
|
|
|
461 |
|
|
// setup nuisance parameter priors
|
462 |
|
|
if( priors_.size()==0 && parameterIsNuisance_.size()>0 )
|
463 |
|
|
{
|
464 |
|
|
for(std::map<int, int>::const_iterator it=parameterIsNuisance_.begin(); it!=parameterIsNuisance_.end(); ++it)
|
465 |
|
|
{
|
466 |
|
|
if(it->second>0) {
|
467 |
|
|
assert(it->first!=poiIndex_);
|
468 |
|
|
double parval, parerr;
|
469 |
|
|
double lolim, uplim;
|
470 |
|
|
// find the optimal integration range for nuisance parameters with uniform priors
|
471 |
|
|
if(it->second>=4 && !parRangeSet_)
|
472 |
|
|
{
|
473 |
|
|
getParameter(it->first, parval, parerr);
|
474 |
|
|
getParLimits(it->first, lolim, uplim);
|
475 |
|
|
double tempval=parval, tempuplim=parval, templolim=parval;
|
476 |
|
|
bool uplimFound=false, lolimFound=false;
|
477 |
|
|
pars[poiIndex_]=poiBestFit_; // here we need to use the best-fit value in order to probe the posterior likelihood close to its maximum
|
478 |
|
|
|
479 |
|
|
while(tempval<=uplim)
|
480 |
|
|
{
|
481 |
|
|
pars[it->first]=tempval;
|
482 |
|
|
nll(a,0,f,pars,0);
|
483 |
|
|
if(TMath::Exp(-f+nllNormalization)<1.E-3)
|
484 |
|
|
{
|
485 |
|
|
tempuplim=tempval+1.0*(tempval-parval);
|
486 |
|
|
uplimFound=true;
|
487 |
|
|
break;
|
488 |
|
|
}
|
489 |
|
|
tempval=tempval+parerr;
|
490 |
|
|
}
|
491 |
|
|
tempval=parval;
|
492 |
|
|
while(tempval>=lolim)
|
493 |
|
|
{
|
494 |
|
|
pars[it->first]=tempval;
|
495 |
|
|
nll(a,0,f,pars,0);
|
496 |
|
|
if(TMath::Exp(-f+nllNormalization)<1.E-3)
|
497 |
|
|
{
|
498 |
|
|
templolim=tempval-1.0*(parval-tempval);
|
499 |
|
|
lolimFound=true;
|
500 |
|
|
break;
|
501 |
|
|
}
|
502 |
|
|
tempval=tempval-parerr;
|
503 |
|
|
}
|
504 |
|
|
|
505 |
|
|
if((!uplimFound || tempuplim>uplim) || (!lolimFound || templolim<lolim))
|
506 |
|
|
{
|
507 |
|
|
std::cout << "WARNING! The integration range for parameter " << (it->first+1) << " [" << lolim << ", " << uplim << "] is likely too narrow. Please extend the range." << std::endl;
|
508 |
|
|
priors_[it->first]=new RandomPrior(it->second, parval, parerr, lolim, uplim);
|
509 |
|
|
}
|
510 |
|
|
else
|
511 |
|
|
{
|
512 |
|
|
double maxdiff = std::max(tempuplim-parval,parval-templolim);
|
513 |
|
|
templolim = parval - maxdiff;
|
514 |
|
|
tempuplim = parval + maxdiff;
|
515 |
|
|
std::cout << "The integration range for parameter " << (it->first+1) << " set to [" << templolim << ", " << tempuplim << "]" << std::endl;
|
516 |
|
|
setParLimits(it->first, templolim, tempuplim);
|
517 |
|
|
priors_[it->first]=new RandomPrior(it->second, parval, parerr, templolim, tempuplim);
|
518 |
|
|
}
|
519 |
|
|
|
520 |
|
|
// return the parameters to their original values
|
521 |
|
|
pars[it->first]=parval;
|
522 |
|
|
pars[poiIndex_]=poiVal;
|
523 |
|
|
}
|
524 |
|
|
else
|
525 |
|
|
{
|
526 |
|
|
getParameter(it->first, parval, parerr);
|
527 |
|
|
getParLimits(it->first, lolim, uplim);
|
528 |
|
|
priors_[it->first]=new RandomPrior(it->second, parval, parerr, lolim, uplim);
|
529 |
|
|
}
|
530 |
|
|
}
|
531 |
|
|
}
|
532 |
|
|
}
|
533 |
|
|
if(!parRangeSet_) parRangeSet_=true;
|
534 |
|
|
|
535 |
|
|
// calculate average likelihood value over nuisance parameters
|
536 |
|
|
double total=0.0;
|
537 |
|
|
for(int sample=0; sample<=nSamples_; sample++) {
|
538 |
|
|
for(std::map<int, RandomPrior*>::const_iterator it=priors_.begin(); it!=priors_.end(); ++it)
|
539 |
|
|
pars[it->first]=it->second->getRandom();
|
540 |
|
|
nll(a,0,f,pars,0);
|
541 |
|
|
double like=TMath::Exp(-f+nllNormalization);
|
542 |
|
|
|
543 |
|
|
total+=like;
|
544 |
|
|
}
|
545 |
|
|
|
546 |
|
|
return total/nSamples_;
|
547 |
|
|
}
|
548 |
|
|
|
549 |
|
|
// ---------------------------------------------------------
|
550 |
|
|
double Fitter::calculateUpperBoundWithCLs(int nSamples, double alpha)
|
551 |
|
|
{
|
552 |
|
|
// we need a parameter of index defined
|
553 |
|
|
assert(poiIndex_>=0);
|
554 |
|
|
|
555 |
|
|
// set the number of samples for subsequent functions
|
556 |
|
|
nSamples_=nSamples;
|
557 |
|
|
|
558 |
|
|
// fit for the best value of the POI
|
559 |
|
|
int nPars=getNumPars();
|
560 |
|
|
double loLimit, hiLimit;
|
561 |
|
|
getParLimits(poiIndex_, loLimit, hiLimit);
|
562 |
|
|
for(int i=0; i<nPars; i++) if(i==poiIndex_) floatParameter(i); else fixParameter(i);
|
563 |
|
|
setParameter(poiIndex_, 0.1*(loLimit+hiLimit));
|
564 |
|
|
doFit();
|
565 |
|
|
double poiBestFit=getParameter(poiIndex_);
|
566 |
|
|
double poiBestFitErr=getParError(poiIndex_);
|
567 |
|
|
fixParameter(poiIndex_);
|
568 |
|
|
|
569 |
|
|
// now float all parameters
|
570 |
|
|
for(int i=0; i<nPars; i++) if(i!=poiIndex_) floatParameter(i);
|
571 |
|
|
|
572 |
|
|
// scan the CLs in steps of error, first
|
573 |
|
|
double poiVal;
|
574 |
|
|
double prevPoiVal=0.0;
|
575 |
|
|
double prevCLsVal=nSamples;
|
576 |
|
|
double CLsVal=0.0;
|
577 |
|
|
for(poiVal=poiBestFit; poiVal<hiLimit; poiVal+=poiBestFitErr) {
|
578 |
|
|
|
579 |
|
|
// calculate CLs
|
580 |
|
|
std::vector<double> CLb, CLsb;
|
581 |
|
|
std::pair<int, int> numden=calculateCLs_(poiVal, CLb, CLsb);
|
582 |
|
|
double A=static_cast<double>(numden.first)/nSamples;
|
583 |
|
|
double B=static_cast<double>(numden.second)/nSamples;
|
584 |
|
|
double Aerr=sqrt(A*(1-A)/nSamples);
|
585 |
|
|
double Berr=sqrt(B*(1-B)/nSamples);
|
586 |
|
|
CLsVal = B==0 ? nSamples*2 : A/B;
|
587 |
|
|
double CLsErr = Aerr==0 || Berr==0 ? 0. : CLsVal*sqrt(Aerr*Aerr/A/A+Berr*Berr/B/B);
|
588 |
|
|
double diff=fabs(CLsVal-alpha);
|
589 |
|
|
|
590 |
|
|
std::cout << "Scan: CLsVal=" << CLsVal << "; poiVal=" << poiVal << std::endl;
|
591 |
|
|
|
592 |
|
|
if(diff<CLsErr) return poiVal;
|
593 |
|
|
|
594 |
|
|
if((prevCLsVal>=alpha && CLsVal<=alpha) || (prevCLsVal<=alpha && CLsVal>=alpha)) break;
|
595 |
|
|
|
596 |
|
|
prevPoiVal=poiVal;
|
597 |
|
|
prevCLsVal=CLsVal;
|
598 |
|
|
}
|
599 |
|
|
if(poiVal>=hiLimit) return -999.;
|
600 |
|
|
|
601 |
|
|
// now, try to converge on best CLs point
|
602 |
|
|
double poiValLo=prevPoiVal;
|
603 |
|
|
double poiValHi=poiVal;
|
604 |
|
|
double CLsValLo=prevCLsVal;
|
605 |
|
|
double CLsValHi=CLsVal;
|
606 |
|
|
int cntr=0;
|
607 |
|
|
do {
|
608 |
|
|
poiVal=(alpha-CLsValLo)*(poiValHi-poiValLo)/(CLsValHi-CLsValLo)+poiValLo;
|
609 |
|
|
std::vector<double> CLb, CLsb;
|
610 |
|
|
std::pair<int, int> numden=calculateCLs_(poiVal, CLb, CLsb);
|
611 |
|
|
double A=static_cast<double>(numden.first)/nSamples;
|
612 |
|
|
double B=static_cast<double>(numden.second)/nSamples;
|
613 |
|
|
double Aerr=sqrt(A*(1-A)/nSamples);
|
614 |
|
|
double Berr=sqrt(B*(1-B)/nSamples);
|
615 |
|
|
double CLsVal = B==0 ? nSamples*2 : A/B;
|
616 |
|
|
double CLsErr = Aerr==0 || Berr==0 ? 0. : CLsVal*sqrt(Aerr*Aerr/A/A+Berr*Berr/B/B);
|
617 |
|
|
double diff=fabs(CLsVal-alpha);
|
618 |
|
|
|
619 |
|
|
std::cout << "poiVal=" << poiVal << "; poiValLo=" << poiValLo << "; poiValHi=" << poiValHi
|
620 |
|
|
<< "; CLsVal=" << CLsVal << "; CLsErr=" << CLsErr << "; CLSValLo=" << CLsValLo << "; CLsValHi=" << CLsValHi << std::endl;
|
621 |
|
|
std::cout << "A=" << A << "; Aerr=" << Aerr
|
622 |
|
|
<<"; B=" << B << "; Berr=" << Berr << std::endl;
|
623 |
|
|
|
624 |
|
|
if(diff>CLsErr) {
|
625 |
|
|
if(alpha>CLsVal) {
|
626 |
|
|
poiValHi=poiVal;
|
627 |
|
|
CLsValHi=CLsVal;
|
628 |
|
|
} else {
|
629 |
|
|
poiValLo=poiVal;
|
630 |
|
|
CLsValLo=CLsVal;
|
631 |
|
|
}
|
632 |
|
|
} else {
|
633 |
|
|
break;
|
634 |
|
|
}
|
635 |
|
|
|
636 |
|
|
}while(cntr<100);
|
637 |
|
|
|
638 |
|
|
return poiVal;
|
639 |
|
|
}
|
640 |
|
|
|
641 |
|
|
// ---------------------------------------------------------
|
642 |
|
|
std::pair<int, int> Fitter::calculateCLs_(double poiVal, std::vector<double>& CLb, std::vector<double>& CLsb)
|
643 |
|
|
{
|
644 |
|
|
// setup parameters
|
645 |
|
|
int a;
|
646 |
|
|
double f;
|
647 |
|
|
double *pars=getParameters();
|
648 |
|
|
|
649 |
|
|
// setup nuisance parameter priors
|
650 |
|
|
if( priors_.size()==0 && parameterIsNuisance_.size()>0 )
|
651 |
|
|
{
|
652 |
|
|
for(std::map<int, int>::const_iterator it=parameterIsNuisance_.begin(); it!=parameterIsNuisance_.end(); ++it)
|
653 |
|
|
{
|
654 |
|
|
if(it->second>0) {
|
655 |
|
|
assert(it->first!=poiIndex_);
|
656 |
|
|
double parval, parerr;
|
657 |
|
|
double lolim, uplim;
|
658 |
|
|
getParameter(it->first, parval, parerr);
|
659 |
|
|
getParLimits(it->first, lolim, uplim);
|
660 |
|
|
priors_[it->first]=new RandomPrior(it->second, parval, parerr, lolim, uplim);
|
661 |
|
|
}
|
662 |
|
|
}
|
663 |
|
|
}
|
664 |
|
|
|
665 |
|
|
TH1D* theData=data_;
|
666 |
|
|
|
667 |
|
|
for(int i=0; i<nSamples_; i++) {
|
668 |
|
|
|
669 |
|
|
// create the pseudodata
|
670 |
|
|
TH1D* CLSB_pdata;
|
671 |
|
|
TH1D* CLB_pdata;
|
672 |
|
|
if(i>0) {
|
673 |
|
|
for(std::map<int, RandomPrior*>::const_iterator it=priors_.begin(); it!=priors_.end(); ++it)
|
674 |
|
|
pars[it->first]=it->second->getRandom();
|
675 |
|
|
|
676 |
|
|
pars[poiIndex_]=poiVal;
|
677 |
|
|
data_=theData;
|
678 |
|
|
CLSB_pdata=makePseudoData("CLSB_pdata", pars);
|
679 |
|
|
pars[poiIndex_]=0.0;
|
680 |
|
|
CLB_pdata=makePseudoData("CLB_pdata", pars);
|
681 |
|
|
} else {
|
682 |
|
|
CLSB_pdata=theData;
|
683 |
|
|
CLB_pdata=theData;
|
684 |
|
|
}
|
685 |
|
|
|
686 |
|
|
// CL_{s+b}
|
687 |
|
|
data_=CLSB_pdata;
|
688 |
|
|
pars[poiIndex_]=poiVal;
|
689 |
|
|
nll(a, 0, f, pars, 0);
|
690 |
|
|
double llnum=f;
|
691 |
|
|
pars[poiIndex_]=0.0;
|
692 |
|
|
nll(a, 0, f, pars, 0);
|
693 |
|
|
double llden=f;
|
694 |
|
|
CLsb.push_back(2*llnum-2*llden);
|
695 |
|
|
|
696 |
|
|
// CL_{b}
|
697 |
|
|
data_=CLB_pdata;
|
698 |
|
|
pars[poiIndex_]=poiVal;
|
699 |
|
|
nll(a, 0, f, pars, 0);
|
700 |
|
|
llnum=f;
|
701 |
|
|
pars[poiIndex_]=0.0;
|
702 |
|
|
nll(a, 0, f, pars, 0);
|
703 |
|
|
llden=f;
|
704 |
|
|
CLb.push_back(2*llnum-2*llden);
|
705 |
|
|
|
706 |
|
|
// remove the pseudodata
|
707 |
|
|
if(i>0) {
|
708 |
|
|
delete CLSB_pdata;
|
709 |
|
|
delete CLB_pdata;
|
710 |
|
|
}
|
711 |
|
|
}
|
712 |
|
|
|
713 |
|
|
// set the data back
|
714 |
|
|
data_=theData;
|
715 |
|
|
|
716 |
|
|
// calculate the CLs
|
717 |
|
|
int nNum=0, nDen=0;
|
718 |
|
|
for(unsigned int i=1; i<CLb.size(); i++) {
|
719 |
|
|
|
720 |
|
|
bool numPass=(CLsb[i]>=CLsb[0]);
|
721 |
|
|
bool denPass=(CLb[i]>CLb[0]);
|
722 |
|
|
|
723 |
|
|
nNum+=numPass;
|
724 |
|
|
nDen+=denPass;
|
725 |
|
|
}
|
726 |
|
|
return std::pair<int, int>(nNum, nDen);
|
727 |
|
|
}
|