annotate maths/Polyfit.h @ 482:cbe668c7d724

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