cannam@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@0
|
2 //---------------------------------------------------------------------------
|
cannam@0
|
3
|
cannam@0
|
4 #ifndef PolyfitHPP
|
cannam@0
|
5 #define PolyfitHPP
|
cannam@0
|
6 //---------------------------------------------------------------------------
|
cannam@0
|
7 // Least-squares curve fitting class for arbitrary data types
|
cannam@0
|
8 /*
|
cannam@0
|
9
|
cannam@0
|
10 { ******************************************
|
cannam@0
|
11 **** Scientific Subroutine Library ****
|
cannam@0
|
12 **** for C++ Builder ****
|
cannam@0
|
13 ******************************************
|
cannam@0
|
14
|
cannam@0
|
15 The following programs were written by Allen Miller and appear in the
|
cannam@0
|
16 book entitled "Pascal Programs For Scientists And Engineers" which is
|
cannam@0
|
17 published by Sybex, 1981.
|
cannam@0
|
18 They were originally typed and submitted to MTPUG in Oct. 1982
|
cannam@0
|
19 Juergen Loewner
|
cannam@0
|
20 Hoher Heckenweg 3
|
cannam@0
|
21 D-4400 Muenster
|
cannam@0
|
22 They have had minor corrections and adaptations for Turbo Pascal by
|
cannam@0
|
23 Jeff Weiss
|
cannam@0
|
24 1572 Peacock Ave.
|
cannam@0
|
25 Sunnyvale, CA 94087.
|
cannam@0
|
26
|
cannam@0
|
27
|
cannam@0
|
28 2000 Oct 28 Updated for Delphi 4, open array parameters.
|
cannam@0
|
29 This allows the routine to be generalised so that it is no longer
|
cannam@0
|
30 hard-coded to make a specific order of best fit or work with a
|
cannam@0
|
31 specific number of points.
|
cannam@0
|
32 2001 Jan 07 Update Web site address
|
cannam@0
|
33
|
cannam@0
|
34 Copyright © David J Taylor, Edinburgh and others listed above
|
cannam@0
|
35 Web site: www.satsignal.net
|
cannam@0
|
36 E-mail: davidtaylor@writeme.com
|
cannam@0
|
37 }*/
|
cannam@0
|
38
|
cannam@0
|
39 ///////////////////////////////////////////////////////////////////////////////
|
cannam@0
|
40 // Modified by CLandone for VC6 Aug 2004
|
cannam@0
|
41 ///////////////////////////////////////////////////////////////////////////////
|
cannam@0
|
42
|
cannam@0
|
43
|
cannam@0
|
44 using std::vector;
|
cannam@0
|
45
|
cannam@0
|
46
|
cannam@0
|
47 class TPolyFit
|
cannam@0
|
48 {
|
cannam@0
|
49 typedef vector<vector<double> > Matrix;
|
cannam@0
|
50 public:
|
cannam@0
|
51
|
cannam@0
|
52 static double PolyFit2 (const vector<double> &x, // does the work
|
cannam@0
|
53 const vector<double> &y,
|
cannam@0
|
54 vector<double> &coef);
|
cannam@0
|
55
|
cannam@0
|
56
|
cannam@0
|
57 private:
|
cannam@0
|
58 TPolyFit &operator = (const TPolyFit &); // disable assignment
|
cannam@0
|
59 TPolyFit(); // and instantiation
|
cannam@0
|
60 TPolyFit(const TPolyFit&); // and copying
|
cannam@0
|
61
|
cannam@0
|
62
|
cannam@0
|
63 static void Square (const Matrix &x, // Matrix multiplication routine
|
cannam@0
|
64 const vector<double> &y,
|
cannam@0
|
65 Matrix &a, // A = transpose X times X
|
cannam@0
|
66 vector<double> &g, // G = Y times X
|
cannam@0
|
67 const int nrow, const int ncol);
|
cannam@0
|
68 // Forms square coefficient matrix
|
cannam@0
|
69
|
cannam@0
|
70 static bool GaussJordan (Matrix &b, // square matrix of coefficients
|
cannam@0
|
71 const vector<double> &y, // constant vector
|
cannam@0
|
72 vector<double> &coef); // solution vector
|
cannam@0
|
73 // returns false if matrix singular
|
cannam@0
|
74
|
cannam@0
|
75 static bool GaussJordan2(Matrix &b,
|
cannam@0
|
76 const vector<double> &y,
|
cannam@0
|
77 Matrix &w,
|
cannam@0
|
78 vector<vector<int> > &index);
|
cannam@0
|
79 };
|
cannam@0
|
80
|
cannam@0
|
81 // some utility functions
|
cannam@0
|
82
|
cannam@0
|
83 namespace NSUtility
|
cannam@0
|
84 {
|
cannam@0
|
85 inline void swap(double &a, double &b) {double t = a; a = b; b = t;}
|
cannam@0
|
86 void zeroise(vector<double> &array, int n);
|
cannam@0
|
87 void zeroise(vector<int> &array, int n);
|
cannam@0
|
88 void zeroise(vector<vector<double> > &matrix, int m, int n);
|
cannam@0
|
89 void zeroise(vector<vector<int> > &matrix, int m, int n);
|
cannam@0
|
90 inline double sqr(const double &x) {return x * x;}
|
cannam@0
|
91 };
|
cannam@0
|
92
|
cannam@0
|
93 //---------------------------------------------------------------------------
|
cannam@0
|
94 // Implementation
|
cannam@0
|
95 //---------------------------------------------------------------------------
|
cannam@0
|
96 using namespace NSUtility;
|
cannam@0
|
97 //------------------------------------------------------------------------------------------
|
cannam@0
|
98
|
cannam@0
|
99
|
cannam@0
|
100 // main PolyFit routine
|
cannam@0
|
101
|
cannam@0
|
102 double TPolyFit::PolyFit2 (const vector<double> &x,
|
cannam@0
|
103 const vector<double> &y,
|
cannam@0
|
104 vector<double> &coefs)
|
cannam@0
|
105 // nterms = coefs.size()
|
cannam@0
|
106 // npoints = x.size()
|
cannam@0
|
107 {
|
cannam@0
|
108 int i, j;
|
cannam@0
|
109 double xi, yi, yc, srs, sum_y, sum_y2;
|
cannam@0
|
110 Matrix xmatr; // Data matrix
|
cannam@0
|
111 Matrix a;
|
cannam@0
|
112 vector<double> g; // Constant vector
|
cannam@0
|
113 const int npoints(x.size());
|
cannam@0
|
114 const int nterms(coefs.size());
|
cannam@0
|
115 double correl_coef;
|
cannam@0
|
116 zeroise(g, nterms);
|
cannam@0
|
117 zeroise(a, nterms, nterms);
|
cannam@0
|
118 zeroise(xmatr, npoints, nterms);
|
cannam@0
|
119 if(nterms < 1)
|
cannam@0
|
120 throw "PolyFit called with less than one term";
|
cannam@0
|
121 if(npoints < 2)
|
cannam@0
|
122 throw "PolyFit called with less than two points";
|
cannam@0
|
123 if(npoints != y.size())
|
cannam@0
|
124 throw "PolyFit called with x and y of unequal size";
|
cannam@0
|
125 for(i = 0; i < npoints; ++i)
|
cannam@0
|
126 {
|
cannam@0
|
127 // { setup x matrix }
|
cannam@0
|
128 xi = x[i];
|
cannam@0
|
129 xmatr[i][0] = 1.0; // { first column }
|
cannam@0
|
130 for(j = 1; j < nterms; ++j)
|
cannam@0
|
131 xmatr[i][j] = xmatr [i][j - 1] * xi;
|
cannam@0
|
132 }
|
cannam@0
|
133 Square (xmatr, y, a, g, npoints, nterms);
|
cannam@0
|
134 if(!GaussJordan (a, g, coefs))
|
cannam@0
|
135 return -1;
|
cannam@0
|
136 sum_y = 0.0;
|
cannam@0
|
137 sum_y2 = 0.0;
|
cannam@0
|
138 srs = 0.0;
|
cannam@0
|
139 for(i = 0; i < npoints; ++i)
|
cannam@0
|
140 {
|
cannam@0
|
141 yi = y[i];
|
cannam@0
|
142 yc = 0.0;
|
cannam@0
|
143 for(j = 0; j < nterms; ++j)
|
cannam@0
|
144 yc += coefs [j] * xmatr [i][j];
|
cannam@0
|
145 srs += sqr (yc - yi);
|
cannam@0
|
146 sum_y += yi;
|
cannam@0
|
147 sum_y2 += yi * yi;
|
cannam@0
|
148 }
|
cannam@0
|
149
|
cannam@0
|
150 // If all Y values are the same, avoid dividing by zero
|
cannam@0
|
151 correl_coef = sum_y2 - sqr (sum_y) / npoints;
|
cannam@0
|
152 // Either return 0 or the correct value of correlation coefficient
|
cannam@0
|
153 if (correl_coef != 0)
|
cannam@0
|
154 correl_coef = srs / correl_coef;
|
cannam@0
|
155 if (correl_coef >= 1)
|
cannam@0
|
156 correl_coef = 0.0;
|
cannam@0
|
157 else
|
cannam@0
|
158 correl_coef = sqrt (1.0 - correl_coef);
|
cannam@0
|
159 return correl_coef;
|
cannam@0
|
160 }
|
cannam@0
|
161
|
cannam@0
|
162
|
cannam@0
|
163 //------------------------------------------------------------------------
|
cannam@0
|
164
|
cannam@0
|
165 // Matrix multiplication routine
|
cannam@0
|
166 // A = transpose X times X
|
cannam@0
|
167 // G = Y times X
|
cannam@0
|
168
|
cannam@0
|
169 // Form square coefficient matrix
|
cannam@0
|
170
|
cannam@0
|
171 void TPolyFit::Square (const Matrix &x,
|
cannam@0
|
172 const vector<double> &y,
|
cannam@0
|
173 Matrix &a,
|
cannam@0
|
174 vector<double> &g,
|
cannam@0
|
175 const int nrow,
|
cannam@0
|
176 const int ncol)
|
cannam@0
|
177 {
|
cannam@0
|
178 int i, k, l;
|
cannam@0
|
179 for(k = 0; k < ncol; ++k)
|
cannam@0
|
180 {
|
cannam@0
|
181 for(l = 0; l < k + 1; ++l)
|
cannam@0
|
182 {
|
cannam@0
|
183 a [k][l] = 0.0;
|
cannam@0
|
184 for(i = 0; i < nrow; ++i)
|
cannam@0
|
185 {
|
cannam@0
|
186 a[k][l] += x[i][l] * x [i][k];
|
cannam@0
|
187 if(k != l)
|
cannam@0
|
188 a[l][k] = a[k][l];
|
cannam@0
|
189 }
|
cannam@0
|
190 }
|
cannam@0
|
191 g[k] = 0.0;
|
cannam@0
|
192 for(i = 0; i < nrow; ++i)
|
cannam@0
|
193 g[k] += y[i] * x[i][k];
|
cannam@0
|
194 }
|
cannam@0
|
195 }
|
cannam@0
|
196 //---------------------------------------------------------------------------------
|
cannam@0
|
197
|
cannam@0
|
198
|
cannam@0
|
199 bool TPolyFit::GaussJordan (Matrix &b,
|
cannam@0
|
200 const vector<double> &y,
|
cannam@0
|
201 vector<double> &coef)
|
cannam@0
|
202 //b square matrix of coefficients
|
cannam@0
|
203 //y constant vector
|
cannam@0
|
204 //coef solution vector
|
cannam@0
|
205 //ncol order of matrix got from b.size()
|
cannam@0
|
206
|
cannam@0
|
207
|
cannam@0
|
208 {
|
cannam@0
|
209 /*
|
cannam@0
|
210 { Gauss Jordan matrix inversion and solution }
|
cannam@0
|
211
|
cannam@0
|
212 { B (n, n) coefficient matrix becomes inverse }
|
cannam@0
|
213 { Y (n) original constant vector }
|
cannam@0
|
214 { W (n, m) constant vector(s) become solution vector }
|
cannam@0
|
215 { DETERM is the determinant }
|
cannam@0
|
216 { ERROR = 1 if singular }
|
cannam@0
|
217 { INDEX (n, 3) }
|
cannam@0
|
218 { NV is number of constant vectors }
|
cannam@0
|
219 */
|
cannam@0
|
220
|
cannam@0
|
221 int ncol(b.size());
|
cannam@0
|
222 int irow, icol;
|
cannam@0
|
223 vector<vector<int> >index;
|
cannam@0
|
224 Matrix w;
|
cannam@0
|
225
|
cannam@0
|
226 zeroise(w, ncol, ncol);
|
cannam@0
|
227 zeroise(index, ncol, 3);
|
cannam@0
|
228
|
cannam@0
|
229 if(!GaussJordan2(b, y, w, index))
|
cannam@0
|
230 return false;
|
cannam@0
|
231
|
cannam@0
|
232 // Interchange columns
|
cannam@0
|
233 int m;
|
cannam@0
|
234 for (int i = 0; i < ncol; ++i)
|
cannam@0
|
235 {
|
cannam@0
|
236 m = ncol - i - 1;
|
cannam@0
|
237 if(index [m][0] != index [m][1])
|
cannam@0
|
238 {
|
cannam@0
|
239 irow = index [m][0];
|
cannam@0
|
240 icol = index [m][1];
|
cannam@0
|
241 for(int k = 0; k < ncol; ++k)
|
cannam@0
|
242 swap (b[k][irow], b[k][icol]);
|
cannam@0
|
243 }
|
cannam@0
|
244 }
|
cannam@0
|
245
|
cannam@0
|
246 for(int k = 0; k < ncol; ++k)
|
cannam@0
|
247 {
|
cannam@0
|
248 if(index [k][2] != 0)
|
cannam@0
|
249 {
|
cannam@0
|
250 throw "Error in GaussJordan: matrix is singular";
|
cannam@0
|
251 }
|
cannam@0
|
252 }
|
cannam@0
|
253
|
cannam@0
|
254 for( int i = 0; i < ncol; ++i)
|
cannam@0
|
255 coef[i] = w[i][0];
|
cannam@0
|
256
|
cannam@0
|
257
|
cannam@0
|
258 return true;
|
cannam@0
|
259 } // end; { procedure GaussJordan }
|
cannam@0
|
260 //----------------------------------------------------------------------------------------------
|
cannam@0
|
261
|
cannam@0
|
262
|
cannam@0
|
263 bool TPolyFit::GaussJordan2(Matrix &b,
|
cannam@0
|
264 const vector<double> &y,
|
cannam@0
|
265 Matrix &w,
|
cannam@0
|
266 vector<vector<int> > &index)
|
cannam@0
|
267 {
|
cannam@0
|
268 //GaussJordan2; // first half of GaussJordan
|
cannam@0
|
269 // actual start of gaussj
|
cannam@0
|
270
|
cannam@0
|
271 double big, t;
|
cannam@0
|
272 double pivot;
|
cannam@0
|
273 double determ;
|
cannam@0
|
274 int irow, icol;
|
cannam@0
|
275 int ncol(b.size());
|
cannam@0
|
276 int nv = 1; // single constant vector
|
cannam@0
|
277 for(int i = 0; i < ncol; ++i)
|
cannam@0
|
278 {
|
cannam@0
|
279 w[i][0] = y[i]; // copy constant vector
|
cannam@0
|
280 index[i][2] = -1;
|
cannam@0
|
281 }
|
cannam@0
|
282 determ = 1.0;
|
cannam@0
|
283 for(int i = 0; i < ncol; ++i)
|
cannam@0
|
284 {
|
cannam@0
|
285 // Search for largest element
|
cannam@0
|
286 big = 0.0;
|
cannam@0
|
287 for(int j = 0; j < ncol; ++j)
|
cannam@0
|
288 {
|
cannam@0
|
289 if(index[j][2] != 0)
|
cannam@0
|
290 {
|
cannam@0
|
291 for(int k = 0; k < ncol; ++k)
|
cannam@0
|
292 {
|
cannam@0
|
293 if(index[k][2] > 0)
|
cannam@0
|
294 throw "Error in GaussJordan2: matrix is singular";
|
cannam@0
|
295
|
cannam@0
|
296 if(index[k][2] < 0 && fabs(b[j][k]) > big)
|
cannam@0
|
297 {
|
cannam@0
|
298 irow = j;
|
cannam@0
|
299 icol = k;
|
cannam@0
|
300 big = fabs(b[j][k]);
|
cannam@0
|
301 }
|
cannam@0
|
302 } // { k-loop }
|
cannam@0
|
303 }
|
cannam@0
|
304 } // { j-loop }
|
cannam@0
|
305 index [icol][2] = index [icol][2] + 1;
|
cannam@0
|
306 index [i][0] = irow;
|
cannam@0
|
307 index [i][1] = icol;
|
cannam@0
|
308
|
cannam@0
|
309 // Interchange rows to put pivot on diagonal
|
cannam@0
|
310 // GJ3
|
cannam@0
|
311 if(irow != icol)
|
cannam@0
|
312 {
|
cannam@0
|
313 determ = -determ;
|
cannam@0
|
314 for(int m = 0; m < ncol; ++m)
|
cannam@0
|
315 swap (b [irow][m], b[icol][m]);
|
cannam@0
|
316 if (nv > 0)
|
cannam@0
|
317 for (int m = 0; m < nv; ++m)
|
cannam@0
|
318 swap (w[irow][m], w[icol][m]);
|
cannam@0
|
319 } // end GJ3
|
cannam@0
|
320
|
cannam@0
|
321 // divide pivot row by pivot column
|
cannam@0
|
322 pivot = b[icol][icol];
|
cannam@0
|
323 determ *= pivot;
|
cannam@0
|
324 b[icol][icol] = 1.0;
|
cannam@0
|
325
|
cannam@0
|
326 for(int m = 0; m < ncol; ++m)
|
cannam@0
|
327 b[icol][m] /= pivot;
|
cannam@0
|
328 if(nv > 0)
|
cannam@0
|
329 for(int m = 0; m < nv; ++m)
|
cannam@0
|
330 w[icol][m] /= pivot;
|
cannam@0
|
331
|
cannam@0
|
332 // Reduce nonpivot rows
|
cannam@0
|
333 for(int n = 0; n < ncol; ++n)
|
cannam@0
|
334 {
|
cannam@0
|
335 if(n != icol)
|
cannam@0
|
336 {
|
cannam@0
|
337 t = b[n][icol];
|
cannam@0
|
338 b[n][icol] = 0.0;
|
cannam@0
|
339 for(int m = 0; m < ncol; ++m)
|
cannam@0
|
340 b[n][m] -= b[icol][m] * t;
|
cannam@0
|
341 if(nv > 0)
|
cannam@0
|
342 for(int m = 0; m < nv; ++m)
|
cannam@0
|
343 w[n][m] -= w[icol][m] * t;
|
cannam@0
|
344 }
|
cannam@0
|
345 }
|
cannam@0
|
346 } // { i-loop }
|
cannam@0
|
347 return true;
|
cannam@0
|
348 }
|
cannam@0
|
349 //----------------------------------------------------------------------------------------------
|
cannam@0
|
350
|
cannam@0
|
351 //------------------------------------------------------------------------------------
|
cannam@0
|
352
|
cannam@0
|
353 // Utility functions
|
cannam@0
|
354 //--------------------------------------------------------------------------
|
cannam@0
|
355
|
cannam@0
|
356 // fills a vector with zeros.
|
cannam@0
|
357 void NSUtility::zeroise(vector<double> &array, int n)
|
cannam@0
|
358 {
|
cannam@0
|
359 array.clear();
|
cannam@0
|
360 for(int j = 0; j < n; ++j)
|
cannam@0
|
361 array.push_back(0);
|
cannam@0
|
362 }
|
cannam@0
|
363 //--------------------------------------------------------------------------
|
cannam@0
|
364
|
cannam@0
|
365 // fills a vector with zeros.
|
cannam@0
|
366 void NSUtility::zeroise(vector<int> &array, int n)
|
cannam@0
|
367 {
|
cannam@0
|
368 array.clear();
|
cannam@0
|
369 for(int j = 0; j < n; ++j)
|
cannam@0
|
370 array.push_back(0);
|
cannam@0
|
371 }
|
cannam@0
|
372 //--------------------------------------------------------------------------
|
cannam@0
|
373
|
cannam@0
|
374 // fills a (m by n) matrix with zeros.
|
cannam@0
|
375 void NSUtility::zeroise(vector<vector<double> > &matrix, int m, int n)
|
cannam@0
|
376 {
|
cannam@0
|
377 vector<double> zero;
|
cannam@0
|
378 zeroise(zero, n);
|
cannam@0
|
379 matrix.clear();
|
cannam@0
|
380 for(int j = 0; j < m; ++j)
|
cannam@0
|
381 matrix.push_back(zero);
|
cannam@0
|
382 }
|
cannam@0
|
383 //--------------------------------------------------------------------------
|
cannam@0
|
384
|
cannam@0
|
385 // fills a (m by n) matrix with zeros.
|
cannam@0
|
386 void NSUtility::zeroise(vector<vector<int> > &matrix, int m, int n)
|
cannam@0
|
387 {
|
cannam@0
|
388 vector<int> zero;
|
cannam@0
|
389 zeroise(zero, n);
|
cannam@0
|
390 matrix.clear();
|
cannam@0
|
391 for(int j = 0; j < m; ++j)
|
cannam@0
|
392 matrix.push_back(zero);
|
cannam@0
|
393 }
|
cannam@0
|
394 //--------------------------------------------------------------------------
|
cannam@0
|
395
|
cannam@0
|
396
|
cannam@0
|
397 #endif
|
cannam@0
|
398
|