comparison dsp/maths/Polyfit.h @ 225:49844bc8a895

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