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