LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
MarqFitAlg.cxx
Go to the documentation of this file.
1 #include "MarqFitAlg.h"
2 #include <limits>
3 
4 namespace gshf {
5 
6  /* multi-Gaussian function, number of Gaussians is npar divided by 3 */
7  void MarqFitAlg::fgauss(const float yd[],
8  const float p[],
9  const int npar,
10  const int ndat,
11  std::vector<float>& res)
12  {
13 #if defined WITH_OPENMP
14 #pragma omp simd
15 #endif
16  for (int i = 0; i < ndat; i++) {
17  float yf = 0.;
18  for (int j = 0; j < npar; j += 3) {
19  yf = yf + p[j] * std::exp(-0.5 * std::pow((float(i) - p[j + 1]) / p[j + 2], 2));
20  }
21  res[i] = yd[i] - yf;
22  }
23  }
24 
25  /* analytic derivatives for multi-Gaussian function in fgauss */
26  void MarqFitAlg::dgauss(const float p[], const int npar, const int ndat, std::vector<float>& dydp)
27  {
28 #if defined WITH_OPENMP
29 #pragma omp simd
30 #ifdef __INTEL_COMPILER
31 #pragma ivdep
32 #endif
33 #endif
34  for (int i = 0; i < ndat; i++) {
35  for (int j = 0; j < npar; j += 3) {
36  const float xmu = float(i) - p[j + 1];
37  const float xmu_sg = xmu / p[j + 2];
38  const float xmu_sg2 = xmu_sg * xmu_sg;
39  dydp[i * npar + j] = std::exp(-0.5 * xmu_sg2);
40  dydp[i * npar + j + 1] = p[j] * dydp[i * npar + j] * xmu_sg / p[j + 2];
41  dydp[i * npar + j + 2] = dydp[i * npar + j + 1] * xmu_sg;
42  }
43  }
44  }
45 
46  /* calculate ChiSquared */
47  float MarqFitAlg::cal_xi2(const std::vector<float>& res, const int ndat)
48  {
49  int i;
50  float xi2;
51  xi2 = 0.;
52  for (i = 0; i < ndat; i++) {
53  xi2 += res[i] * res[i];
54  }
55  return xi2;
56  }
57 
58  /* setup the beta and (curvature) matrices */
59  void MarqFitAlg::setup_matrix(const std::vector<float>& res,
60  const std::vector<float>& dydp,
61  const int npar,
62  const int ndat,
63  std::vector<float>& beta,
64  std::vector<float>& alpha)
65  {
66  int i, j, k;
67 
68 /* ... Calculate beta */
69 #if defined WITH_OPENMP
70 #pragma omp simd
71 #ifdef __INTEL_COMPILER
72 #pragma ivdep
73 #endif
74 #endif
75  for (j = 0; j < npar; j++) {
76  beta[j] = 0.0;
77  for (i = 0; i < ndat; i++) {
78  beta[j] += res[i] * dydp[i * npar + j];
79  }
80  }
81 
82 /* ... Calculate alpha */
83 #if defined WITH_OPENMP
84 #pragma omp simd
85 #ifdef __INTEL_COMPILER
86 #pragma ivdep
87 #endif
88 #endif
89  for (j = 0; j < npar; j++) {
90  for (k = j; k < npar; k++) {
91  alpha[j * npar + k] = 0.0;
92  for (i = 0; i < ndat; i++) {
93  alpha[j * npar + k] += dydp[i * npar + j] * dydp[i * npar + k];
94  }
95  if (k != j) alpha[k * npar + j] = alpha[j * npar + k];
96  }
97  }
98  }
99 
100  /* solve system of linear equations */
101  void MarqFitAlg::solve_matrix(const std::vector<float>& beta,
102  const std::vector<float>& alpha,
103  const int npar,
104  std::vector<float>& dp)
105  {
106  int i, j, k, imax;
107  float hmax, hsav;
108 
109  std::vector<std::vector<float>> h(npar, std::vector<float>(npar + 1, 0));
110 
111  /* ... set up augmented N x N+1 matrix */
112  for (i = 0; i < npar; i++) {
113  h[i][npar] = beta[i];
114  for (j = 0; j < npar; j++) {
115  h[i][j] = alpha[i * npar + j];
116  }
117  }
118 
119  /* ... diagonalize N x N matrix but do only terms required for solution */
120  for (i = 0; i < npar; i++) {
121  hmax = h[i][i];
122  imax = i;
123  for (j = i + 1; j < npar; j++) {
124  if (h[j][i] > hmax) {
125  hmax = h[j][i];
126  imax = j;
127  }
128  }
129  if (imax != i) {
130  for (k = 0; k <= npar; k++) {
131  hsav = h[i][k];
132  h[i][k] = h[imax][k];
133  h[imax][k] = hsav;
134  }
135  }
136  for (j = 0; j < npar; j++) {
137  if (j == i) continue;
138  for (k = i; k < npar; k++) {
139  h[j][k + 1] -= h[i][k + 1] * h[j][i] / h[i][i];
140  }
141  }
142  }
143  /* ... scale (N+1)'th column with factor which normalizes the diagonal */
144  for (i = 0; i < npar; i++) {
145  dp[i] = h[i][npar] / h[i][i];
146  }
147  }
148 
149  float MarqFitAlg::invrt_matrix(std::vector<float>& alphaf, const int npar)
150  {
151  /*
152  Inverts the curvature matrix alpha using Gauss-Jordan elimination and
153  returns the determinant. This is based on the implementation in "Data
154  Reduction and Error Analysis for the Physical Sciences" by P. R. Bevington.
155  That implementation, in turn, uses the algorithm of the subroutine MINV,
156  described on page 44 of the IBM System/360 Scientific Subroutine Package
157  Program Description Manual (GH20-0586-0). This only needs to be called
158  once after the fit has converged if the parameter errors are desired.
159  */
160 
161  //turn input alphas into doubles
162  std::vector<double> alpha(npar * npar);
163 
164  int i, j, k;
165  std::vector<int> ik(npar);
166  std::vector<int> jk(npar);
167  double aMax, save, det;
168  float detf;
169 
170  for (i = 0; i < npar * npar; i++) {
171  alpha[i] = alphaf[i];
172  }
173 
174  det = 0;
175  /* ... search for the largest element which we will then put in the diagonal */
176  for (k = 0; k < npar; k++) {
177  aMax = 0;
178  for (i = k; i < npar; i++) {
179  for (j = k; j < npar; j++) {
180 
181  // alpha[i*npar+j]=alphaf[i*npar+j];
182 
183  if (fabs(alpha[i * npar + j]) > fabs(aMax)) {
184  aMax = alpha[i * npar + j];
185  ik[k] = i;
186  jk[k] = j;
187  }
188  }
189  }
190  if (aMax == 0) return (det); /* return 0 determinant to signal problem */
191  det = 1;
192  /* ... interchange rows if necessary to put aMax in diag */
193  i = ik[k];
194  if (i > k) {
195  for (j = 0; j < npar; j++) {
196  save = alpha[k * npar + j];
197  alpha[k * npar + j] = alpha[i * npar + j];
198  alpha[i * npar + j] = -save;
199  }
200  }
201  /* ... interchange columns if necessary to put aMax in diag */
202  j = jk[k];
203  if (j > k) {
204  for (i = 0; i < npar; i++) {
205  save = alpha[i * npar + k];
206  alpha[i * npar + k] = alpha[i * npar + j];
207  alpha[i * npar + j] = -save;
208  }
209  }
210  /* ... accumulate elements of inverse matrix */
211  for (i = 0; i < npar; i++) {
212  if (i != k) alpha[i * npar + k] = -alpha[i * npar + k] / aMax;
213  }
214 #if defined WITH_OPENMP
215 #pragma omp simd
216 #ifdef __INTEL_COMPILER
217 #pragma ivdep
218 #endif
219 #endif
220  for (i = 0; i < npar; i++) {
221  for (j = 0; j < npar; j++) {
222  if ((i != k) && (j != k))
223  alpha[i * npar + j] = alpha[i * npar + j] + alpha[i * npar + k] * alpha[k * npar + j];
224  }
225  }
226  for (j = 0; j < npar; j++) {
227  if (j != k) alpha[k * npar + j] = alpha[k * npar + j] / aMax;
228  }
229  alpha[k * npar + k] = 1 / aMax;
230  det = det * aMax;
231  }
232 
233  /* ... restore ordering of matrix */
234  for (k = npar - 1; k >= 0; k--) {
235  j = ik[k];
236  if (j > k) {
237  for (i = 0; i < npar; i++) {
238  save = alpha[i * npar + k];
239  alpha[i * npar + k] = -alpha[i * npar + j];
240  alpha[i * npar + j] = save;
241  }
242  }
243  i = jk[k];
244  if (i > k) {
245  for (j = 0; j < npar; j++) {
246  save = alpha[k * npar + j];
247  alpha[k * npar + j] = -alpha[i * npar + j];
248  alpha[i * npar + j] = save;
249  }
250  }
251  }
252 
253  for (i = 0; i < npar * npar; i++) {
254  alphaf[i] = alpha[i];
255  }
256 
257  detf = det;
258  return (detf);
259  }
260 
261  /* Calculate parameter errors */
262  int MarqFitAlg::cal_perr(float p[], float y[], const int nParam, const int nData, float perr[])
263  {
264  int i, j;
265  float det;
266 
267  std::vector<float> res(nData);
268  std::vector<float> dydp(nData * nParam);
269  std::vector<float> beta(nParam);
270  std::vector<float> alpha(nParam * nParam);
271  std::vector<std::vector<float>> alpsav(nParam, std::vector<float>(nParam));
272 
273  fgauss(y, p, nParam, nData, res);
274  dgauss(p, nParam, nData, dydp);
275  setup_matrix(res, dydp, nParam, nData, beta, alpha);
276  for (i = 0; i < nParam; i++) {
277  for (j = 0; j < nParam; j++) {
278  alpsav[i][j] = alpha[i * nParam + j];
279  }
280  }
281  det = invrt_matrix(alpha, nParam);
282 
283  if (det == 0) return 1;
284  for (i = 0; i < nParam; i++) {
285  if (alpha[i * nParam + i] >= 0.) { perr[i] = sqrt(alpha[i * nParam + i]); }
286  else {
287  perr[i] = alpha[i * nParam + i];
288  }
289  }
290 
291  return 0;
292  }
293 
294  int MarqFitAlg::mrqdtfit(float& lambda,
295  float p[],
296  float y[],
297  const int nParam,
298  const int nData,
299  float& chiSqr,
300  float& dchiSqr)
301  {
302  std::vector<float> plimmin(nParam, std::numeric_limits<float>::lowest());
303  std::vector<float> plimmax(nParam, std::numeric_limits<float>::max());
304  return mrqdtfit(lambda, p, &plimmin[0], &plimmax[0], y, nParam, nData, chiSqr, dchiSqr);
305  }
306 
307  int MarqFitAlg::mrqdtfit(float& lambda,
308  float p[],
309  float plimmin[],
310  float plimmax[],
311  float y[],
312  const int nParam,
313  const int nData,
314  float& chiSqr,
315  float& dchiSqr)
316  {
317  int j;
318  float nu, rho, lzmlh, amax, chiSq0;
319 
320  std::vector<float> res(nData);
321  std::vector<float> beta(nParam);
322  std::vector<float> dp(nParam);
323  std::vector<float> alpsav(nParam);
324  std::vector<float> psav(nParam);
325  std::vector<float> dydp(nData * nParam);
326  std::vector<float> alpha(nParam * nParam);
327 
328  bool haslimits = false;
329  for (j = 0; j < nParam; j++) {
330  if (plimmin[j] > std::numeric_limits<float>::lowest() ||
331  plimmax[j] < std::numeric_limits<float>::max()) {
332  haslimits = true;
333  break;
334  }
335  }
336 
337  fgauss(y, p, nParam, nData, res);
338  chiSq0 = cal_xi2(res, nData);
339  dgauss(p, nParam, nData, dydp);
340  setup_matrix(res, dydp, nParam, nData, beta, alpha);
341  if (lambda < 0.) {
342  amax = -999.;
343  for (j = 0; j < nParam; j++) {
344  if (alpha[j * nParam + j] > amax) amax = alpha[j * nParam + j];
345  }
346  lambda = 0.001 * amax;
347  }
348  for (j = 0; j < nParam; j++) {
349  alpsav[j] = alpha[j * nParam + j];
350  alpha[j * nParam + j] = alpsav[j] + lambda;
351  }
352  solve_matrix(beta, alpha, nParam, dp);
353 
354  nu = 2.;
355  rho = -1.;
356 
357  do {
358  for (j = 0; j < nParam; j++) {
359  psav[j] = p[j];
360  p[j] = p[j] + dp[j];
361  }
362  fgauss(y, p, nParam, nData, res);
363  chiSqr = cal_xi2(res, nData);
364  if (haslimits) {
365  for (j = 0; j < nParam; j++) {
366  if (p[j] <= plimmin[j] || p[j] >= plimmax[j])
367  chiSqr *= 10000; //penalty for going out of limits!
368  }
369  }
370 
371  lzmlh = 0.;
372  for (j = 0; j < nParam; j++) {
373  lzmlh += dp[j] * (lambda * dp[j] + beta[j]);
374  }
375  rho = 2. * (chiSq0 - chiSqr) / lzmlh;
376  if (rho < 0.) {
377  for (j = 0; j < nParam; j++)
378  p[j] = psav[j];
379  chiSqr = chiSq0;
380  lambda = nu * lambda;
381  nu = 2. * nu;
382  for (j = 0; j < nParam; j++) {
383  alpha[j * nParam + j] = alpsav[j] + lambda;
384  }
385  solve_matrix(beta, alpha, nParam, dp);
386  }
387  } while (rho < 0.);
388  lambda = lambda * fmax(0.333333, 1. - pow(2. * rho - 1., 3));
389  dchiSqr = chiSqr - chiSq0;
390  return 0;
391  }
392 
393 } //end namespace gshf
void solve_matrix(const std::vector< float > &beta, const std::vector< float > &alpha, const int npar, std::vector< float > &dp)
Definition: MarqFitAlg.cxx:101
Float_t y
Definition: compare.C:6
int mrqdtfit(float &lambda, float p[], float y[], const int nParam, const int nData, float &chiSqr, float &dchiSqr)
Definition: MarqFitAlg.cxx:294
int cal_perr(float p[], float y[], const int nParam, const int nData, float perr[])
Definition: MarqFitAlg.cxx:262
void fgauss(const float yd[], const float p[], const int npar, const int ndat, std::vector< float > &res)
Definition: MarqFitAlg.cxx:7
float cal_xi2(const std::vector< float > &res, const int ndat)
Definition: MarqFitAlg.cxx:47
void dgauss(const float p[], const int npar, const int ndat, std::vector< float > &dydp)
Definition: MarqFitAlg.cxx:26
float invrt_matrix(std::vector< float > &alphaf, const int npar)
Definition: MarqFitAlg.cxx:149
void setup_matrix(const std::vector< float > &res, const std::vector< float > &dydp, const int npar, const int ndat, std::vector< float > &beta, std::vector< float > &alpha)
Definition: MarqFitAlg.cxx:59