Belle II Software  release-05-01-25
WaveFitter.cc
1 #include <framework/logging/Logger.h>
2 #include <svd/reconstruction/WaveFitter.h>
3 #include <iostream>
4 #include <math.h>
5 #include <utility>
6 #include <boost/math/tools/minima.hpp>
7 
8 using namespace Belle2::SVD;
9 using namespace std;
10 
11 double DefaultWave::s_default_tau = 50;
12 
14 
15 void WaveFitter::setPrecision(int ndigits) { s_minimizer_precision = ndigits; }
16 
17 double WaveFitter::Chi(double t0)
18 {
19  // This we only need to calculate once
20  array<double, 6> waves;
21  double sumTemplateByTemplate = 0;
22  for (int i = 0; i < 6; ++i) {
23  waves[i] = m_wave(m_times[i] - t0);
24  sumTemplateByTemplate += waves[i] * waves[i];
25  }
26  // Calculate amplitudes
27  vector<double> amplitudes(m_data.size());
28  for (unsigned int row = 0; row < m_data.size(); ++row) {
29  double sumDataByTemplate = 0;
30  for (int i = 0; i < 6; i++) {
31  sumDataByTemplate += m_data[row][i] * waves[i];
32  }
33  amplitudes[row] = sumDataByTemplate / sumTemplateByTemplate;
34  }
35  double result = 0;
36  for (unsigned int row = 0; row < m_data.size(); ++row)
37  for (int i = 0; i < 6; i++) {
38  double diff =
39  (m_data[row][i] - amplitudes[row] * waves[i]) / m_noises[row];
40  result += diff * diff;
41  }
42  return result / m_ndf;
43 }
44 
46 {
47  // wave values
48  array<double, 6> w;
49  for (int i = 0; i < 6; ++i) w[i] = m_wave(m_times[i] - t);
50  double sum_w2 = 0;
51  for (int i = 0; i < 6; ++i) sum_w2 += w[i] * w[i];
52  double lognorm = 0.5 * m_data.size() * log(sum_w2);
53  double sum_ksi = 0.0;
54  for (unsigned int row = 0; row < m_data.size(); ++row) {
55  double ksi = 0;
56  for (int i = 1; i < 6; ++i)
57  for (int j = 0; j < i; ++j) {
58  double cross = w[i] * m_data[row][j] - w[j] * m_data[row][i];
59  ksi += cross * cross;
60  }
61  sum_ksi += ksi;
62  }
63  return lognorm + 0.5 * sum_ksi / sum_w2;
64 }
65 
67 {
68  // Degrees of freedom:
69  // # samples - # strips (amplitudes) - 1 (timeshift) - 1 (sum = 0)
70  m_ndf = 6 * m_data.size() - m_data.size() - 2;
71  if (m_ndf < 1) { // We have no data, so there'll be no fit and an error
72  B2ERROR("SVD::WaveFitterter : Fit required with no data provided.");
73  m_fittedTime = 0;
74  m_fittedTimeError = 0;
75  m_fittedAmplitudes.clear();
76  m_fittedAmplitudeErrors.clear();
77  m_fittedLik = 1.0e10;
78  m_fittedData.clear();
79  // We don't need to repeat this
80  m_hasFit = true;
81  return;
82  }
83  // Normal operation: minimize wrt. t0 in (-dt,0) and (0,dt),
84  // take the solution giving a better fit
85  auto result_low = boost::math::tools::brent_find_minima(
86  [this](double t)->double { return negLogLikelihood(t); }, -m_dt, 0.0, s_minimizer_precision
87  );
88  double t0_low = result_low.first;
89  double lik_low = result_low.second;
90  auto result_hi = boost::math::tools::brent_find_minima(
91  [this](double t)->double { return negLogLikelihood(t); }, 0.0, m_dt, s_minimizer_precision
92  );
93  double t0_hi = result_hi.first;
94  double lik_hi = result_hi.second;
95  if (lik_low < lik_hi) {
96  m_fittedTime = t0_low;
97  m_fittedLik = lik_low;
98  } else {
99  m_fittedTime = t0_hi;
100  m_fittedLik = lik_hi;
101  }
102  calculateAmplitudes();
103  calculateFitErrors();
104  calculateFittedData();
105  m_hasFit = true;
106 }
107 
109 {
110  // This we only need to calculate once
111  array<double, 6> waves;
112  double sumTemplateByTemplate = 0;
113  for (int i = 0; i < 6; ++i) {
114  waves[i] = m_wave(m_times[i] - m_fittedTime);
115  sumTemplateByTemplate += waves[i] * waves[i];
116  }
117  // Calculate amplitudes
118  for (unsigned int row = 0; row < m_data.size(); ++row) {
119  double sumDataByTemplate = 0;
120  for (int i = 0; i < 6; i++) {
121  sumDataByTemplate += m_data[row][i] * waves[i];
122  }
123  m_fittedAmplitudes.push_back(sumDataByTemplate / sumTemplateByTemplate);
124  }
125 }
126 
128 {
129  // Errors on amplitudes - simple from the linear model
130  double sum_w2 = 0;
131  for (int i = 0; i < 6; ++i) {
132  double wave = m_wave(m_times[i] - m_fittedTime);
133  sum_w2 += wave * wave;
134  }
135  double one_by_sqrtSumw2 = 1.0 / sqrt(sum_w2);
136  for (unsigned int row = 0; row < m_data.size(); ++row)
137  m_fittedAmplitudeErrors.push_back(one_by_sqrtSumw2 * m_noises[row]);
138  // For time: ignore correlation with amplitudes
139  // We will need derivatives, calculate them numerically
140  double timestep = 0.1 * m_dt;
141  double dwave_sq = 0;
142  for (int i = 0; i < 6; ++i) {
143  double dwave = (wave(m_times[i] - m_fittedTime + 0.5 * timestep) - wave(m_times[i] - m_fittedTime - 0.5 * timestep)) / timestep;
144  dwave_sq += dwave * dwave;
145  }
146  double sn_sq = 0;
147  for (unsigned int row = 0; row < m_data.size(); ++row) {
148  double sn = m_fittedAmplitudes[row] / m_noises[row];
149  sn_sq += sn * sn;
150  }
151  m_fittedTimeError = 1.0 / sqrt(sn_sq * dwave_sq);
152 }
153 
155 {
156  m_fittedData.resize(m_data.size());
157  for (int i = 0; i < 6; ++i) {
158  double wave = m_wave(m_times[i] - m_fittedTime);
159  for (unsigned int row = 0; row < m_data.size(); ++row)
160  m_fittedData[row][i] = m_fittedAmplitudes[row] * wave;
161  }
162 }
163 
164 double WaveFitter::integral12(double lower, double upper, std::function<double(double)> f)
165 {
166  const unsigned int half_order = 6;
167  const double knots[half_order] = {
168  0.1252334085114689154724414,
169  0.3678314989981801937526915,
170  0.5873179542866174472967024,
171  0.7699026741943046870368938,
172  0.9041172563704748566784659,
173  0.9815606342467192506905491
174  };
175  const double weights[half_order] = {
176  0.2491470458134027850005624,
177  0.2334925365383548087608499,
178  0.2031674267230659217490645,
179  0.1600783285433462263346525,
180  0.1069393259953184309602547,
181  0.0471753363865118271946160
182  };
183  double span = 0.5 * (upper - lower);
184  double center = 0.5 * (upper + lower);
185  double result = 0;
186  for (unsigned int iknot = 0; iknot < half_order; ++iknot) {
187  result += weights[iknot] * f(center + span * knots[iknot]);
188  result += weights[iknot] * f(center - span * knots[iknot]);
189  }
190  result *= span;
191  return result;
192 }
193 
194 double WaveFitter::integral20(double lower, double upper, std::function<double(double)> f)
195 {
196  const unsigned int half_order = 10;
197  const double knots[half_order] = {
198  0.0765265211,
199  0.2277858511,
200  0.3737060887,
201  0.510867002,
202  0.6360536807,
203  0.7463319065,
204  0.8391169718,
205  0.9122344283,
206  0.9639719273,
207  0.9931285992
208  };
209  const double weights[half_order] = {
210  0.1527533871,
211  0.1491729865,
212  0.1420961093,
213  0.1316886384,
214  0.118194532,
215  0.1019301198,
216  0.0832767416,
217  0.0626720483,
218  0.0406014298,
219  0.0176140071
220  };
221  double span = 0.5 * (upper - lower);
222  double center = 0.5 * (upper + lower);
223  double result = 0;
224  for (unsigned int iknot = 0; iknot < half_order; ++iknot) {
225  result += weights[iknot] * f(center + span * knots[iknot]);
226  result += weights[iknot] * f(center - span * knots[iknot]);
227  }
228  result *= span;
229  return result;
230 }
231 
233 {
234  const double outer = m_dt;
235  const double inner = 5; // ns
236  if (!m_hasFit) doFit();
237  auto integrand = [this](double t)->double { return exp(-negLogLikelihood(t));};
238 
239  double i1 = integral20(-outer, -inner, integrand);
240  double i2 = integral20(-inner, 0, integrand);
241  double i3 = integral20(0, inner, integrand);
242  double i4 = integral20(inner, outer, integrand);
243  double L_background = 1.0 / (2 * outer) * (i1 + i2 + i3 + i4);
244  double L_signal = 1.0 / (2 * inner) * (i2 + i3);
245  double prob = (L_signal / (L_background + L_signal));
246  //cout << L_background << " <><> " << L_signal << " <> " << prob << endl;
247  return prob;
248 }
249 
250 double WaveFitter::lrSignal(double low, double high)
251 {
252  const double outer = m_dt;
253  if (!m_hasFit) doFit();
254  auto integrand = [this](double t)->double { return exp(-negLogLikelihood(t));};
255  // Calculate total integral and integral between a and b:
256  double int_total = integral20(-outer, 0, integrand);
257  int_total += integral20(0, outer, integrand);
258  double int_inner = 0;
259  if (low * high < 0) {
260  int_inner += integral20(low, 0, integrand);
261  int_inner += integral20(0, high, integrand);
262  } else {
263  int_inner += integral20(low, high, integrand);
264  }
265  double int_outer = int_total - int_inner;
266  double LR = int_inner / int_outer;
267  //cout << L_background << " <><> " << L_signal << " <> " << prob << endl;
268  return LR;
269 }
270 
Belle2::SVD::WaveFitter::calculateFitErrors
void calculateFitErrors()
Calculate fit errors.
Definition: WaveFitter.cc:127
Belle2::SVD::WaveFitter::calculateAmplitudes
void calculateAmplitudes()
Calculate amplitudes.
Definition: WaveFitter.cc:108
Belle2::SVD::WaveFitter::integral12
double integral12(double lower, double upper, std::function< double(double)> f)
High-order Gauss-Legendre quadrature for likelihood integrals.
Definition: WaveFitter.cc:164
Belle2::SVD::WaveFitter::s_minimizer_precision
static int s_minimizer_precision
minimizer precision
Definition: WaveFitter.h:189
Belle2::SVD::DefaultWave::s_default_tau
static double s_default_tau
Default waveform decay time.
Definition: WaveFitter.h:26
Belle2::SVD::WaveFitter::doFit
void doFit()
Perform fit on the data.
Definition: WaveFitter.cc:66
Belle2::SVD::WaveFitter::pSignal
double pSignal()
Calculate probability of the hit being a signal, with given priors for signal and background.
Definition: WaveFitter.cc:232
Belle2::SVD
Namespace to encapsulate code needed for simulation and reconstrucion of the SVD.
Definition: GeoSVDCreator.h:35
Belle2::SVD::WaveFitter::negLogLikelihood
double negLogLikelihood(double t)
Get negative profile log-likelihood for a given time shift.
Definition: WaveFitter.cc:45
Belle2::SVD::WaveFitter::calculateFittedData
void calculateFittedData()
Calculate fitted data.
Definition: WaveFitter.cc:154
Belle2::SVD::WaveFitter::lrSignal
double lrSignal(double a, double b)
Calculate likelihood ratio Lsignal/Lbackground for acceptance (signal) window given as arguments.
Definition: WaveFitter.cc:250
Belle2::SVD::WaveFitter::Chi
double Chi(double t)
Get standardized chi2 for a given time shift.
Definition: WaveFitter.cc:17
Belle2::SVD::WaveFitter::setPrecision
static void setPrecision(int ndigits)
Set precision of the minimzer.
Definition: WaveFitter.cc:15
Belle2::SVD::WaveFitter::integral20
double integral20(double lower, double upper, std::function< double(double)> f)
High-order Gauss-Legendre quadrature for likelihood integrals.
Definition: WaveFitter.cc:194