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