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