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