annotate maths/Polyfit.h @ 116:fac40444b8c3 pvoc

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