Jpp 21.0.0-rc.1
the software that should make you happy
Loading...
Searching...
No Matches
JAspera.hh
Go to the documentation of this file.
1#ifndef __JASTRONOMY__JASPERA__
2#define __JASTRONOMY__JASPERA__
3
4#include <vector>
5#include <cmath>
6
7
8/**
9 * \file
10 *
11 * Per aspera ad astra.
12 * \author mdejong
13 */
14namespace JASTRONOMY {}
15namespace JPP { using namespace JASTRONOMY; }
16
17namespace JASTRONOMY {
18
19 /**
20 * Auxiliary data structure to fit signal strength using likelihood ratio.
21 */
22 struct JAspera :
23 public std::vector<double> // N/S values
24 {
25
26 static constexpr double EPSILON = 1.0e-3; //!< precision determination of signal strength
27
28
29 /**
30 * Result of fit.
31 */
32 struct fit_type {
33 double likelihood = 0.0; //!< likelihood
34 double signal = 0.0; //!< signal strength
35 };
36
37
38 /**
39 * Put signal and background to list of pre-computed N/S values.
40 *
41 * \param s signal
42 * \param b background
43 */
44 void put(const double s,
45 const double b)
46 {
47 push_back(b/s);
48
49 ws += s;
50 }
51
52
53 /**
54 * Put signal and background to list of pre-computed N/S values.
55 *
56 * \param n data
57 * \param s signal
58 * \param b background
59 */
60 void put(const size_t n,
61 const double s,
62 const double b)
63 {
64 for (size_t i = 0; i != n; ++i) {
65 push_back(b/s);
66 }
67
68 ws += s;
69 }
70
71
72 /**
73 * Get likelihood for given signal strength.
74 *
75 * \param p signal strength
76 * \return likelihood
77 */
78 double getLikelihood(const double p) const
79 {
80 double y = -p * ws;
81
82 for (const double i : static_cast<const std::vector<double>&>(*this)) {
83 y += log1p(p/i);
84 }
85
86 return y;
87 }
88
89
90 /**
91 * Get derivative of likelihood for given signal strength.
92 *
93 * \param p signal strength
94 * \return derivative of likelihood
95 */
96 double getDerivative(const double p) const
97 {
98 double y = -ws;
99
100 for (const double i : static_cast<const std::vector<double>&>(*this)) {
101 y += 1.0 / (p + i);
102 }
103
104 return y;
105 }
106
107
108#if defined(NEWTON_RAPHSON)
109
110 /**
111 * Auxiliary data structure for 1st and 2nd derivatives.
112 */
113 struct derivatives_type {
114 double fp = 0.0;
115 double fpp = 0.0;
116 };
117
118
119 /**
120 * Get derivatives of likelihood for given signal strength.
121 *
122 * \param p signal strength
123 * \return derivatives of likelihood
124 */
125 derivatives_type getDerivatives(const double p) const
126 {
127 derivatives_type y;
128
129 y.fp = -ws;
130
131 for (const double x : static_cast<const std::vector<double>&>(*this)) {
132
133 const double z = 1.0 / (p + x);
134
135 y.fp += z;
136 y.fpp -= z*z;
137 }
138
139 return y;
140 }
141#endif
142
143 /**
144 * Fit signal strength.
145 *
146 * \param ns allow for negative signal
147 * \return result
148 */
149 fit_type operator()(const bool ns = false) const
150 {
151 using namespace std;
152
153 if (this->empty()) {
154
155 // nothing to be done
156
157 return { 0.0, 0.0 };
158
159 } else if (this->size() == 1 ) {
160
161 // analytical solution
162
163 const double x = 1.0/ws - (*this)[0];
164
165 if (x > 0.0 || ns)
166 return { getLikelihood(x), x };
167 else
168 return { 0.0, 0.0 };
169 }
170
171 double x1 = 0.0;
172 double x2 = 0.0;
173
174 double f1 = getDerivative(0.0); // discriminator between positive and negative signal
175 double f2 = 0.0;
176
177 if (f1 == 0.0) {
178
179 return { 0.0, 0.0 };
180
181 } else if (f1 > 0.0) { // positive signal
182
183 x1 = 0.0; // lower limit corresponds to no signal
184 x2 = (double) this->size() / ws; // upper limit corresponds to no background (i.e. all N/S = 0)
185
186 f2 = getDerivative(x2);
187
188 } else if (ns) { // negative signal
189
190 x2 = 0.0; // upper limit corresponds to no signal
191 x1 = -(*this)[0]; // lower limit corresponds to largest negated N/S ratio
192
193 for (const double x : static_cast<const std::vector<double>&>(*this)) {
194 if (-x > x1) {
195 x1 = -x;
196 }
197 }
198
199 x1 += 1.0 / ws; // offset
200
201 f2 = f1;
202 f1 = getDerivative(x1);
203
204 } else {
205
206 return { 0.0, 0.0 };
207 }
208
209#if defined(BISECTION)
210
211 // binary search
212
213 while (x2 - x1 > EPSILON) {
214
215 const double x = 0.5 * (x1 + x2);
216
217 if (getDerivative(x) > 0.0)
218 x1 = x;
219 else
220 x2 = x;
221 }
222
223 const double x = 0.5 * (x1 + x2);
224
225 return { getLikelihood(x), x };
226
227#elif defined(NEWTON_RAPHSON)
228
229 // Newton-Raphson method
230
231 double dx = x2 - x1;
232 double x = 0.5 * (x1 + x2);
233
234 derivatives_type y = getDerivatives(x);
235
236 struct {
237 double dx;
238 } old;
239
240 old.dx = dx;
241
242 for ( ; ; ) {
243
244 if ((y.fp + (x2 - x)*y.fpp)*(y.fp + (x1 - x)*y.fpp) > 0.0 || fabs(2.0*y.fp) > fabs(old.dx*y.fpp)) {
245
246 old.dx = dx;
247
248 dx = 0.5 * (x2 - x1);
249 x = x1 + dx;
250
251 } else {
252
253 old.dx = dx;
254
255 dx = y.fp/y.fpp;
256 x -= dx;
257 }
258
259 if (fabs(dx) <= EPSILON) {
260 break;
261 }
262
263 y = getDerivatives(x);
264
265 if (y.fp < 0.0)
266 x2 = x;
267 else
268 x1 = x;
269 }
270
271 return { getLikelihood(x), x };
272
273#else
274
275 // Ridder's method
276
277 while (x2 - x1 > EPSILON) {
278
279 const double xm = 0.5 * (x1 + x2);
280 const double fm = getDerivative(xm);
281
282 const double s = sqrt(fm*fm - f1*f2);
283
284 if (s == 0.0) {
285 break;
286 }
287
288 const double xn = xm + (xm - x1) * fm/s;
289 const double fn = getDerivative(xn);
290
291 if (fn == 0.0) {
292 return { getLikelihood(xn), xn };
293 }
294
295 if (signbit(fn) != signbit(fm)) {
296
297 x1 = xm;
298 f1 = fm;
299 x2 = xn;
300 f2 = fn;
301
302 } else {
303
304 if (signbit(fn)) {
305
306 x2 = xn;
307 f2 = fn;
308
309 } else {
310
311 x1 = xn;
312 f1 = fn;
313 }
314 }
315 }
316
317 const double x = 0.5 * (x1 + x2);
318
319 return { getLikelihood(x), x };
320#endif
321 }
322
323
324 /**
325 * Get test statistic for given signal strength.
326 * See formula 16 in this <a href="https://link.springer.com/article/10.1140/epjc/s10052-011-1554-0">reference</a>.
327 *
328 * \param ps signal strength
329 * \return test statistic
330 */
331 inline double getTestStatisticForUpperLimit(const double ps) const
332 {
333 const fit_type result = (*this)(true);
334
335 if (result.signal <= 0.0)
336 return 0.0 - this->getLikelihood(ps);
337 else if (result.signal <= ps)
338 return result.likelihood - this->getLikelihood(ps);
339 else
340 return 0.0;
341 }
342
343
344 /**
345 * Get total signal strength.
346 *
347 * \return signal strength
348 */
349 double getSignal() const
350 {
351 return ws;
352 }
353
354
355 /**
356 * Set signal strength.
357 *
358 * \param wS signal strength
359 */
360 void setSignal(const double wS)
361 {
362 ws = wS;
363 }
364
365
366 /**
367 * Add signal strength.
368 *
369 * \param wS signal strength
370 */
371 void addSignal(const double wS)
372 {
373 ws += wS;
374 }
375
376 protected:
377
378 double ws = 0.0; //!< total signal strength
379 };
380}
381
382#endif
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).
double signal
signal strength
Definition JAspera.hh:34
double likelihood
likelihood
Definition JAspera.hh:33
Auxiliary data structure to fit signal strength using likelihood ratio.
Definition JAspera.hh:24
void addSignal(const double wS)
Add signal strength.
Definition JAspera.hh:371
double getSignal() const
Get total signal strength.
Definition JAspera.hh:349
double getLikelihood(const double p) const
Get likelihood for given signal strength.
Definition JAspera.hh:78
void setSignal(const double wS)
Set signal strength.
Definition JAspera.hh:360
void put(const double s, const double b)
Put signal and background to list of pre-computed N/S values.
Definition JAspera.hh:44
double getTestStatisticForUpperLimit(const double ps) const
Get test statistic for given signal strength.
Definition JAspera.hh:331
double ws
total signal strength
Definition JAspera.hh:378
static constexpr double EPSILON
precision determination of signal strength
Definition JAspera.hh:26
double getDerivative(const double p) const
Get derivative of likelihood for given signal strength.
Definition JAspera.hh:96
fit_type operator()(const bool ns=false) const
Fit signal strength.
Definition JAspera.hh:149
void put(const size_t n, const double s, const double b)
Put signal and background to list of pre-computed N/S values.
Definition JAspera.hh:60