3 from __future__
import print_function
13 return N*math.log(mu)-mu
14 elif mu==0.
and N==0.:
17 raise ValueError(
"Bad poisson params: N={}, mu={}".format(N,mu))
30 def fit(self,S,Shat = None):
32 return (self.
B, self.
R, S)
34 T = self.
B*(1.+self.
R)**2+S
35 Rhat = self.
B*self.
R*(1.+self.
R)/(self.
B*(1.+self.
R)+S)
37 return (Bhat, Rhat, Shat)
39 c3 = S+self.
B*(self.
R+1.)**2
40 c2 = -(S*S + 2.*self.
B*self.
R*S + 2.*self.
B*S - Shat*S + self.
B*self.
B*self.
R*self.
R - 2.*Shat*self.
B*self.
R*self.
R + 2.*self.
B*self.
B*self.
R - 4.*Shat*self.
B*self.
R + self.
B*self.
B - Shat*self.
B)
41 c1 = -Shat*self.
B*self.
R*(2.*S + 2.*self.
B*self.
R - Shat*self.
R + 2.*self.
B - 2.*Shat)
42 c0 = -Shat*Shat*self.
B*self.
B*self.
R*self.
R 44 coeffs = (c3,c2,c1,c0)
46 lower = self.
B*self.
R/(self.
R+2.)
47 upper = (self.
B*(self.
R+1.)+S)**2/(self.
B*(self.
R+1.)**2+S)
48 Bhat = lower+0.5*(upper-lower)
49 while upper>Bhat
and Bhat>lower:
50 if eval(Bhat, coeffs)>0.:
54 Bhat = lower+0.5*(upper-lower)
56 Rhat = (math.sqrt(Bhat*(4.*self.
B*self.
R*self.
R+4.*self.
B*self.
R+Bhat))-Bhat)/(2.*Bhat)
57 return (Bhat, Rhat, Shat)
59 def nll(self, S, Bhat, Rhat, Shat):
60 return -(
poisson(self.
B+S, Bhat+Shat)
62 +
poisson(self.
B*self.
R*self.
R, Bhat*Rhat*Rhat))
65 Bfix, Rfix, Sfix = self.
fit(S,0.)
66 Bfloat, Rfloat, Sfloat = self.
fit(S)
67 return 2.*(self.
nll(S, Bfix, Rfix, Sfix)-self.
nll(S, Bfloat, Rfloat, Sfloat))
70 Bfix, Rfix, Sfix = self.
fit(0.,Stest)
71 Bfloat, Rfloat, Sfloat = self.
fit(0.)
74 return 2.*(self.
nll(0., Bfix, Rfix, Sfix)-self.
nll(0., Bfloat, Rfloat, Sfloat))
80 qtilde = max(self.
qtilde(Stest), 0.)
81 return scipy.stats.norm.sf(math.sqrt(qtilde))
84 return self.
clsb(Stest)/self.
clb()
87 return self.
q0(S) > 25.
90 return self.
cls(S) < 0.05
97 if upper >= 1000000000.:
100 mid = lower+0.5*(upper-lower)
101 while upper>mid
and mid>lower:
106 mid = lower + 0.5*(upper-lower)
115 mid = lower+0.5*(upper-lower)
116 while upper>mid
and mid>lower:
121 mid = lower + 0.5*(upper-lower)
126 ";Background Yield in Signal Region;Sideband-to-Signal Ratio;Signal Events for Exclusion/Discovery",
127 len(bins_B)-1, bins_B, len(bins_R)-1, bins_R)
131 h.SetLineStyle(line_style)
133 h.SetTitleOffset(1.05,
"XYZ")
134 h.SetTitleSize(0.045,
"XYZ")
138 one_max = math.floor(math.log(the_max,10.))
139 three_max = math.floor(math.log(the_max/3.,10.))
141 one_list = [ 10.**p
for p
in range(one_max+1) ]
142 three_list = [ 3.*10.**p
for p
in range(one_max+1) ]
144 full_list = one_list + three_list
147 return numpy.array(full_list)
149 def makePlot(range_B, range_R, num_bins_B, num_bins_R):
150 ROOT.gStyle.SetPalette(ROOT.kRainBow)
151 ROOT.gStyle.SetNumberContours(999)
154 bins_B = numpy.logspace(math.log(range_B[0],2.),
155 math.log(range_B[1],2.),
156 num_bins_B+1, base=2.)
157 bins_R = numpy.logspace(math.log(range_R[0],2.),
158 math.log(range_R[1],2.),
159 num_bins_R+1, base=2.)
160 h_disc =
makeHist(bins_B, bins_R, 1)
161 h_excl =
makeHist(bins_B, bins_R, 3)
162 h_dumb =
makeHist(bins_B, bins_R, 1)
164 for ib
in range(num_bins_B):
165 for ir
in range(num_bins_R):
166 abcd.B = math.sqrt(bins_B[ib]*bins_B[ib+1])
167 abcd.R = math.sqrt(bins_R[ir]*bins_R[ir+1])
168 h_disc.SetBinContent(ib+1, ir+1, abcd.discoverySensitivity())
169 h_excl.SetBinContent(ib+1, ir+1, abcd.upperLimit())
176 canvas = ROOT.TCanvas(
"canvas",
"canvas", 800, 750)
180 canvas.SetMargin(left_margin, right_margin, bottom_margin, top_margin)
182 the_max = h_excl.GetBinContent(h_excl.GetMaximumBin())*1.001
184 h_disc.SetMaximum(the_max)
185 h_excl.SetMaximum(the_max)
186 h_dumb.SetMaximum(the_max)
190 h_disc.SetContour(len(contours), contours)
191 h_excl.SetContour(len(contours), contours)
193 ROOT.gStyle.SetNumberContours(999)
194 h_excl.Draw(
"cont1z")
195 h_disc.Draw(
"cont1 same")
197 legend = ROOT.TLegend(left_margin, 1.-top_margin, 1.-right_margin, 1.)
198 legend.SetNColumns(2)
199 legend.SetBorderSize(0)
200 legend.AddEntry(h_disc,
"5#sigma Discovery",
"l")
201 legend.AddEntry(h_excl,
"95% C.L. Exclusion",
"l")
204 canvas.Print(
"abcd_sensitivity_thresholds.pdf")
206 if __name__ ==
"__main__":
207 parser = argparse.ArgumentParser(description=
"Computes minimum number of signal events needed for ABCD sensistivity.",
208 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
209 parser.add_argument(
"--rangeB", type=float, nargs=2, default=[0.1,1000.], help=
"Range of number of background events to test")
210 parser.add_argument(
"--rangeR", type=float, nargs=2, default=[0.1,1000.], help=
"Range of sideband-to-signal ratios to test")
211 parser.add_argument(
"--binsB", type=int, default=100, help=
"Number of bins to use along background axis")
212 parser.add_argument(
"--binsR", type=int, default=100, help=
"Number of bins to use along ratio axis")
213 args = parser.parse_args()
215 makePlot(args.rangeB, args.rangeR, args.binsB, args.binsR)
def makeHist(bins_B, bins_R, line_style)
def discoverySensitivity(self)
def fit(self, S, Shat=None)
def nll(self, S, Bhat, Rhat, Shat)
def makePlot(range_B, range_R, num_bins_B, num_bins_R)