annotate maths/Polyfit.h @ 486:7132d952b9a6

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