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@43
|
43 #include <iostream>
|
cannam@0
|
44
|
cannam@0
|
45 using std::vector;
|
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@43
|
119 if (nterms < 1) {
|
cannam@43
|
120 std::cerr << "ERROR: PolyFit called with less than one term" << std::endl;
|
cannam@43
|
121 return 0;
|
cannam@43
|
122 }
|
cannam@43
|
123 if(npoints < 2) {
|
cannam@43
|
124 std::cerr << "ERROR: PolyFit called with less than two points" << std::endl;
|
cannam@43
|
125 return 0;
|
cannam@43
|
126 }
|
Chris@102
|
127 if(npoints != (int)y.size()) {
|
cannam@43
|
128 std::cerr << "ERROR: PolyFit called with x and y of unequal size" << std::endl;
|
cannam@43
|
129 return 0;
|
cannam@43
|
130 }
|
cannam@0
|
131 for(i = 0; i < npoints; ++i)
|
cannam@0
|
132 {
|
cannam@0
|
133 // { setup x matrix }
|
cannam@0
|
134 xi = x[i];
|
cannam@0
|
135 xmatr[i][0] = 1.0; // { first column }
|
cannam@0
|
136 for(j = 1; j < nterms; ++j)
|
cannam@0
|
137 xmatr[i][j] = xmatr [i][j - 1] * xi;
|
cannam@0
|
138 }
|
cannam@0
|
139 Square (xmatr, y, a, g, npoints, nterms);
|
cannam@0
|
140 if(!GaussJordan (a, g, coefs))
|
cannam@0
|
141 return -1;
|
cannam@0
|
142 sum_y = 0.0;
|
cannam@0
|
143 sum_y2 = 0.0;
|
cannam@0
|
144 srs = 0.0;
|
cannam@0
|
145 for(i = 0; i < npoints; ++i)
|
cannam@0
|
146 {
|
cannam@0
|
147 yi = y[i];
|
cannam@0
|
148 yc = 0.0;
|
cannam@0
|
149 for(j = 0; j < nterms; ++j)
|
cannam@0
|
150 yc += coefs [j] * xmatr [i][j];
|
cannam@0
|
151 srs += sqr (yc - yi);
|
cannam@0
|
152 sum_y += yi;
|
cannam@0
|
153 sum_y2 += yi * yi;
|
cannam@0
|
154 }
|
cannam@0
|
155
|
cannam@0
|
156 // If all Y values are the same, avoid dividing by zero
|
cannam@0
|
157 correl_coef = sum_y2 - sqr (sum_y) / npoints;
|
cannam@0
|
158 // Either return 0 or the correct value of correlation coefficient
|
cannam@0
|
159 if (correl_coef != 0)
|
cannam@0
|
160 correl_coef = srs / correl_coef;
|
cannam@0
|
161 if (correl_coef >= 1)
|
cannam@0
|
162 correl_coef = 0.0;
|
cannam@0
|
163 else
|
cannam@0
|
164 correl_coef = sqrt (1.0 - correl_coef);
|
cannam@0
|
165 return correl_coef;
|
cannam@0
|
166 }
|
cannam@0
|
167
|
cannam@0
|
168
|
cannam@0
|
169 //------------------------------------------------------------------------
|
cannam@0
|
170
|
cannam@0
|
171 // Matrix multiplication routine
|
cannam@0
|
172 // A = transpose X times X
|
cannam@0
|
173 // G = Y times X
|
cannam@0
|
174
|
cannam@0
|
175 // Form square coefficient matrix
|
cannam@0
|
176
|
cannam@0
|
177 void TPolyFit::Square (const Matrix &x,
|
cannam@0
|
178 const vector<double> &y,
|
cannam@0
|
179 Matrix &a,
|
cannam@0
|
180 vector<double> &g,
|
cannam@0
|
181 const int nrow,
|
cannam@0
|
182 const int ncol)
|
cannam@0
|
183 {
|
cannam@0
|
184 int i, k, l;
|
cannam@0
|
185 for(k = 0; k < ncol; ++k)
|
cannam@0
|
186 {
|
cannam@0
|
187 for(l = 0; l < k + 1; ++l)
|
cannam@0
|
188 {
|
cannam@0
|
189 a [k][l] = 0.0;
|
cannam@0
|
190 for(i = 0; i < nrow; ++i)
|
cannam@0
|
191 {
|
cannam@0
|
192 a[k][l] += x[i][l] * x [i][k];
|
cannam@0
|
193 if(k != l)
|
cannam@0
|
194 a[l][k] = a[k][l];
|
cannam@0
|
195 }
|
cannam@0
|
196 }
|
cannam@0
|
197 g[k] = 0.0;
|
cannam@0
|
198 for(i = 0; i < nrow; ++i)
|
cannam@0
|
199 g[k] += y[i] * x[i][k];
|
cannam@0
|
200 }
|
cannam@0
|
201 }
|
cannam@0
|
202 //---------------------------------------------------------------------------------
|
cannam@0
|
203
|
cannam@0
|
204
|
cannam@0
|
205 bool TPolyFit::GaussJordan (Matrix &b,
|
cannam@0
|
206 const vector<double> &y,
|
cannam@0
|
207 vector<double> &coef)
|
cannam@0
|
208 //b square matrix of coefficients
|
cannam@0
|
209 //y constant vector
|
cannam@0
|
210 //coef solution vector
|
cannam@0
|
211 //ncol order of matrix got from b.size()
|
cannam@0
|
212
|
cannam@0
|
213
|
cannam@0
|
214 {
|
cannam@0
|
215 /*
|
cannam@0
|
216 { Gauss Jordan matrix inversion and solution }
|
cannam@0
|
217
|
cannam@0
|
218 { B (n, n) coefficient matrix becomes inverse }
|
cannam@0
|
219 { Y (n) original constant vector }
|
cannam@0
|
220 { W (n, m) constant vector(s) become solution vector }
|
cannam@0
|
221 { DETERM is the determinant }
|
cannam@0
|
222 { ERROR = 1 if singular }
|
cannam@0
|
223 { INDEX (n, 3) }
|
cannam@0
|
224 { NV is number of constant vectors }
|
cannam@0
|
225 */
|
cannam@0
|
226
|
cannam@0
|
227 int ncol(b.size());
|
cannam@0
|
228 int irow, icol;
|
cannam@0
|
229 vector<vector<int> >index;
|
cannam@0
|
230 Matrix w;
|
cannam@0
|
231
|
cannam@0
|
232 zeroise(w, ncol, ncol);
|
cannam@0
|
233 zeroise(index, ncol, 3);
|
cannam@0
|
234
|
cannam@0
|
235 if(!GaussJordan2(b, y, w, index))
|
cannam@0
|
236 return false;
|
cannam@0
|
237
|
cannam@0
|
238 // Interchange columns
|
cannam@0
|
239 int m;
|
cannam@0
|
240 for (int i = 0; i < ncol; ++i)
|
cannam@0
|
241 {
|
cannam@0
|
242 m = ncol - i - 1;
|
cannam@0
|
243 if(index [m][0] != index [m][1])
|
cannam@0
|
244 {
|
cannam@0
|
245 irow = index [m][0];
|
cannam@0
|
246 icol = index [m][1];
|
cannam@0
|
247 for(int k = 0; k < ncol; ++k)
|
cannam@0
|
248 swap (b[k][irow], b[k][icol]);
|
cannam@0
|
249 }
|
cannam@0
|
250 }
|
cannam@0
|
251
|
cannam@0
|
252 for(int k = 0; k < ncol; ++k)
|
cannam@0
|
253 {
|
cannam@0
|
254 if(index [k][2] != 0)
|
cannam@0
|
255 {
|
cannam@43
|
256 std::cerr << "ERROR: Error in PolyFit::GaussJordan: matrix is singular" << std::endl;
|
cannam@43
|
257 return false;
|
cannam@0
|
258 }
|
cannam@0
|
259 }
|
cannam@0
|
260
|
cannam@0
|
261 for( int i = 0; i < ncol; ++i)
|
cannam@0
|
262 coef[i] = w[i][0];
|
cannam@0
|
263
|
cannam@0
|
264
|
cannam@0
|
265 return true;
|
cannam@0
|
266 } // end; { procedure GaussJordan }
|
cannam@0
|
267 //----------------------------------------------------------------------------------------------
|
cannam@0
|
268
|
cannam@0
|
269
|
cannam@0
|
270 bool TPolyFit::GaussJordan2(Matrix &b,
|
cannam@0
|
271 const vector<double> &y,
|
cannam@0
|
272 Matrix &w,
|
cannam@0
|
273 vector<vector<int> > &index)
|
cannam@0
|
274 {
|
cannam@0
|
275 //GaussJordan2; // first half of GaussJordan
|
cannam@0
|
276 // actual start of gaussj
|
cannam@0
|
277
|
cannam@0
|
278 double big, t;
|
cannam@0
|
279 double pivot;
|
cannam@0
|
280 double determ;
|
cannam@0
|
281 int irow, icol;
|
cannam@0
|
282 int ncol(b.size());
|
cannam@0
|
283 int nv = 1; // single constant vector
|
cannam@0
|
284 for(int i = 0; i < ncol; ++i)
|
cannam@0
|
285 {
|
cannam@0
|
286 w[i][0] = y[i]; // copy constant vector
|
cannam@0
|
287 index[i][2] = -1;
|
cannam@0
|
288 }
|
cannam@0
|
289 determ = 1.0;
|
cannam@0
|
290 for(int i = 0; i < ncol; ++i)
|
cannam@0
|
291 {
|
cannam@0
|
292 // Search for largest element
|
cannam@0
|
293 big = 0.0;
|
cannam@0
|
294 for(int j = 0; j < ncol; ++j)
|
cannam@0
|
295 {
|
cannam@0
|
296 if(index[j][2] != 0)
|
cannam@0
|
297 {
|
cannam@0
|
298 for(int k = 0; k < ncol; ++k)
|
cannam@0
|
299 {
|
cannam@43
|
300 if(index[k][2] > 0) {
|
cannam@43
|
301 std::cerr << "ERROR: Error in PolyFit::GaussJordan2: matrix is singular" << std::endl;
|
cannam@43
|
302 return false;
|
cannam@43
|
303 }
|
cannam@0
|
304
|
cannam@0
|
305 if(index[k][2] < 0 && fabs(b[j][k]) > big)
|
cannam@0
|
306 {
|
cannam@0
|
307 irow = j;
|
cannam@0
|
308 icol = k;
|
cannam@0
|
309 big = fabs(b[j][k]);
|
cannam@0
|
310 }
|
cannam@0
|
311 } // { k-loop }
|
cannam@0
|
312 }
|
cannam@0
|
313 } // { j-loop }
|
cannam@0
|
314 index [icol][2] = index [icol][2] + 1;
|
cannam@0
|
315 index [i][0] = irow;
|
cannam@0
|
316 index [i][1] = icol;
|
cannam@0
|
317
|
cannam@0
|
318 // Interchange rows to put pivot on diagonal
|
cannam@0
|
319 // GJ3
|
cannam@0
|
320 if(irow != icol)
|
cannam@0
|
321 {
|
cannam@0
|
322 determ = -determ;
|
cannam@0
|
323 for(int m = 0; m < ncol; ++m)
|
cannam@0
|
324 swap (b [irow][m], b[icol][m]);
|
cannam@0
|
325 if (nv > 0)
|
cannam@0
|
326 for (int m = 0; m < nv; ++m)
|
cannam@0
|
327 swap (w[irow][m], w[icol][m]);
|
cannam@0
|
328 } // end GJ3
|
cannam@0
|
329
|
cannam@0
|
330 // divide pivot row by pivot column
|
cannam@0
|
331 pivot = b[icol][icol];
|
cannam@0
|
332 determ *= pivot;
|
cannam@0
|
333 b[icol][icol] = 1.0;
|
cannam@0
|
334
|
cannam@0
|
335 for(int m = 0; m < ncol; ++m)
|
cannam@0
|
336 b[icol][m] /= pivot;
|
cannam@0
|
337 if(nv > 0)
|
cannam@0
|
338 for(int m = 0; m < nv; ++m)
|
cannam@0
|
339 w[icol][m] /= pivot;
|
cannam@0
|
340
|
cannam@0
|
341 // Reduce nonpivot rows
|
cannam@0
|
342 for(int n = 0; n < ncol; ++n)
|
cannam@0
|
343 {
|
cannam@0
|
344 if(n != icol)
|
cannam@0
|
345 {
|
cannam@0
|
346 t = b[n][icol];
|
cannam@0
|
347 b[n][icol] = 0.0;
|
cannam@0
|
348 for(int m = 0; m < ncol; ++m)
|
cannam@0
|
349 b[n][m] -= b[icol][m] * t;
|
cannam@0
|
350 if(nv > 0)
|
cannam@0
|
351 for(int m = 0; m < nv; ++m)
|
cannam@0
|
352 w[n][m] -= w[icol][m] * t;
|
cannam@0
|
353 }
|
cannam@0
|
354 }
|
cannam@0
|
355 } // { i-loop }
|
cannam@0
|
356 return true;
|
cannam@0
|
357 }
|
cannam@0
|
358 //----------------------------------------------------------------------------------------------
|
cannam@0
|
359
|
cannam@0
|
360 //------------------------------------------------------------------------------------
|
cannam@0
|
361
|
cannam@0
|
362 // Utility functions
|
cannam@0
|
363 //--------------------------------------------------------------------------
|
cannam@0
|
364
|
cannam@0
|
365 // fills a vector with zeros.
|
cannam@0
|
366 void NSUtility::zeroise(vector<double> &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 vector with zeros.
|
cannam@0
|
375 void NSUtility::zeroise(vector<int> &array, int n)
|
cannam@0
|
376 {
|
cannam@0
|
377 array.clear();
|
cannam@0
|
378 for(int j = 0; j < n; ++j)
|
cannam@0
|
379 array.push_back(0);
|
cannam@0
|
380 }
|
cannam@0
|
381 //--------------------------------------------------------------------------
|
cannam@0
|
382
|
cannam@0
|
383 // fills a (m by n) matrix with zeros.
|
cannam@0
|
384 void NSUtility::zeroise(vector<vector<double> > &matrix, int m, int n)
|
cannam@0
|
385 {
|
cannam@0
|
386 vector<double> zero;
|
cannam@0
|
387 zeroise(zero, n);
|
cannam@0
|
388 matrix.clear();
|
cannam@0
|
389 for(int j = 0; j < m; ++j)
|
cannam@0
|
390 matrix.push_back(zero);
|
cannam@0
|
391 }
|
cannam@0
|
392 //--------------------------------------------------------------------------
|
cannam@0
|
393
|
cannam@0
|
394 // fills a (m by n) matrix with zeros.
|
cannam@0
|
395 void NSUtility::zeroise(vector<vector<int> > &matrix, int m, int n)
|
cannam@0
|
396 {
|
cannam@0
|
397 vector<int> zero;
|
cannam@0
|
398 zeroise(zero, n);
|
cannam@0
|
399 matrix.clear();
|
cannam@0
|
400 for(int j = 0; j < m; ++j)
|
cannam@0
|
401 matrix.push_back(zero);
|
cannam@0
|
402 }
|
cannam@0
|
403 //--------------------------------------------------------------------------
|
cannam@0
|
404
|
cannam@0
|
405
|
cannam@0
|
406 #endif
|
cannam@0
|
407
|