annotate maths/Polyfit.h @ 515:08bcc06c38ec tip master

Remove fast-math
author Chris Cannam <cannam@all-day-breakfast.com>
date Tue, 28 Jan 2020 15:27:37 +0000
parents 701233f8ed41
children
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
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