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