Mercurial > hg > qm-dsp
diff maths/Polyfit.h @ 241:a98dd8ec96f8
* Move dsp/maths to maths ; bring PCA and HMM across from Soundbite
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Wed, 09 Jan 2008 10:31:29 +0000 |
parents | |
children | 9aedf5ea8c35 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/maths/Polyfit.h Wed Jan 09 10:31:29 2008 +0000 @@ -0,0 +1,398 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +//--------------------------------------------------------------------------- + +#ifndef PolyfitHPP +#define PolyfitHPP +//--------------------------------------------------------------------------- +// Least-squares curve fitting class for arbitrary data types +/* + +{ ****************************************** + **** Scientific Subroutine Library **** + **** for C++ Builder **** + ****************************************** + + The following programs were written by Allen Miller and appear in the + book entitled "Pascal Programs For Scientists And Engineers" which is + published by Sybex, 1981. + They were originally typed and submitted to MTPUG in Oct. 1982 + Juergen Loewner + Hoher Heckenweg 3 + D-4400 Muenster + They have had minor corrections and adaptations for Turbo Pascal by + Jeff Weiss + 1572 Peacock Ave. + Sunnyvale, CA 94087. + + +2000 Oct 28 Updated for Delphi 4, open array parameters. + This allows the routine to be generalised so that it is no longer + hard-coded to make a specific order of best fit or work with a + specific number of points. +2001 Jan 07 Update Web site address + +Copyright © David J Taylor, Edinburgh and others listed above +Web site: www.satsignal.net +E-mail: davidtaylor@writeme.com +}*/ + + /////////////////////////////////////////////////////////////////////////////// + // Modified by CLandone for VC6 Aug 2004 + /////////////////////////////////////////////////////////////////////////////// + + +using std::vector; + + +class TPolyFit +{ + typedef vector<vector<double> > Matrix; +public: + + static double PolyFit2 (const vector<double> &x, // does the work + const vector<double> &y, + vector<double> &coef); + + +private: + TPolyFit &operator = (const TPolyFit &); // disable assignment + TPolyFit(); // and instantiation + TPolyFit(const TPolyFit&); // and copying + + + static void Square (const Matrix &x, // Matrix multiplication routine + const vector<double> &y, + Matrix &a, // A = transpose X times X + vector<double> &g, // G = Y times X + const int nrow, const int ncol); + // Forms square coefficient matrix + + static bool GaussJordan (Matrix &b, // square matrix of coefficients + const vector<double> &y, // constant vector + vector<double> &coef); // solution vector + // returns false if matrix singular + + static bool GaussJordan2(Matrix &b, + const vector<double> &y, + Matrix &w, + vector<vector<int> > &index); +}; + +// some utility functions + +namespace NSUtility +{ + inline void swap(double &a, double &b) {double t = a; a = b; b = t;} + void zeroise(vector<double> &array, int n); + void zeroise(vector<int> &array, int n); + void zeroise(vector<vector<double> > &matrix, int m, int n); + void zeroise(vector<vector<int> > &matrix, int m, int n); + inline double sqr(const double &x) {return x * x;} +}; + +//--------------------------------------------------------------------------- +// Implementation +//--------------------------------------------------------------------------- +using namespace NSUtility; +//------------------------------------------------------------------------------------------ + + +// main PolyFit routine + +double TPolyFit::PolyFit2 (const vector<double> &x, + const vector<double> &y, + vector<double> &coefs) +// nterms = coefs.size() +// npoints = x.size() +{ + int i, j; + double xi, yi, yc, srs, sum_y, sum_y2; + Matrix xmatr; // Data matrix + Matrix a; + vector<double> g; // Constant vector + const int npoints(x.size()); + const int nterms(coefs.size()); + double correl_coef; + zeroise(g, nterms); + zeroise(a, nterms, nterms); + zeroise(xmatr, npoints, nterms); + if(nterms < 1) + throw "PolyFit called with less than one term"; + if(npoints < 2) + throw "PolyFit called with less than two points"; + if(npoints != y.size()) + throw "PolyFit called with x and y of unequal size"; + for(i = 0; i < npoints; ++i) + { + // { setup x matrix } + xi = x[i]; + xmatr[i][0] = 1.0; // { first column } + for(j = 1; j < nterms; ++j) + xmatr[i][j] = xmatr [i][j - 1] * xi; + } + Square (xmatr, y, a, g, npoints, nterms); + if(!GaussJordan (a, g, coefs)) + return -1; + sum_y = 0.0; + sum_y2 = 0.0; + srs = 0.0; + for(i = 0; i < npoints; ++i) + { + yi = y[i]; + yc = 0.0; + for(j = 0; j < nterms; ++j) + yc += coefs [j] * xmatr [i][j]; + srs += sqr (yc - yi); + sum_y += yi; + sum_y2 += yi * yi; + } + + // If all Y values are the same, avoid dividing by zero + correl_coef = sum_y2 - sqr (sum_y) / npoints; + // Either return 0 or the correct value of correlation coefficient + if (correl_coef != 0) + correl_coef = srs / correl_coef; + if (correl_coef >= 1) + correl_coef = 0.0; + else + correl_coef = sqrt (1.0 - correl_coef); + return correl_coef; +} + + +//------------------------------------------------------------------------ + +// Matrix multiplication routine +// A = transpose X times X +// G = Y times X + +// Form square coefficient matrix + +void TPolyFit::Square (const Matrix &x, + const vector<double> &y, + Matrix &a, + vector<double> &g, + const int nrow, + const int ncol) +{ + int i, k, l; + for(k = 0; k < ncol; ++k) + { + for(l = 0; l < k + 1; ++l) + { + a [k][l] = 0.0; + for(i = 0; i < nrow; ++i) + { + a[k][l] += x[i][l] * x [i][k]; + if(k != l) + a[l][k] = a[k][l]; + } + } + g[k] = 0.0; + for(i = 0; i < nrow; ++i) + g[k] += y[i] * x[i][k]; + } +} +//--------------------------------------------------------------------------------- + + +bool TPolyFit::GaussJordan (Matrix &b, + const vector<double> &y, + vector<double> &coef) +//b square matrix of coefficients +//y constant vector +//coef solution vector +//ncol order of matrix got from b.size() + + +{ +/* + { Gauss Jordan matrix inversion and solution } + + { B (n, n) coefficient matrix becomes inverse } + { Y (n) original constant vector } + { W (n, m) constant vector(s) become solution vector } + { DETERM is the determinant } + { ERROR = 1 if singular } + { INDEX (n, 3) } + { NV is number of constant vectors } +*/ + + int ncol(b.size()); + int irow, icol; + vector<vector<int> >index; + Matrix w; + + zeroise(w, ncol, ncol); + zeroise(index, ncol, 3); + + if(!GaussJordan2(b, y, w, index)) + return false; + + // Interchange columns + int m; + for (int i = 0; i < ncol; ++i) + { + m = ncol - i - 1; + if(index [m][0] != index [m][1]) + { + irow = index [m][0]; + icol = index [m][1]; + for(int k = 0; k < ncol; ++k) + swap (b[k][irow], b[k][icol]); + } + } + + for(int k = 0; k < ncol; ++k) + { + if(index [k][2] != 0) + { + throw "Error in GaussJordan: matrix is singular"; + } + } + + for( int i = 0; i < ncol; ++i) + coef[i] = w[i][0]; + + + return true; +} // end; { procedure GaussJordan } +//---------------------------------------------------------------------------------------------- + + +bool TPolyFit::GaussJordan2(Matrix &b, + const vector<double> &y, + Matrix &w, + vector<vector<int> > &index) +{ + //GaussJordan2; // first half of GaussJordan + // actual start of gaussj + + double big, t; + double pivot; + double determ; + int irow, icol; + int ncol(b.size()); + int nv = 1; // single constant vector + for(int i = 0; i < ncol; ++i) + { + w[i][0] = y[i]; // copy constant vector + index[i][2] = -1; + } + determ = 1.0; + for(int i = 0; i < ncol; ++i) + { + // Search for largest element + big = 0.0; + for(int j = 0; j < ncol; ++j) + { + if(index[j][2] != 0) + { + for(int k = 0; k < ncol; ++k) + { + if(index[k][2] > 0) + throw "Error in GaussJordan2: matrix is singular"; + + if(index[k][2] < 0 && fabs(b[j][k]) > big) + { + irow = j; + icol = k; + big = fabs(b[j][k]); + } + } // { k-loop } + } + } // { j-loop } + index [icol][2] = index [icol][2] + 1; + index [i][0] = irow; + index [i][1] = icol; + + // Interchange rows to put pivot on diagonal + // GJ3 + if(irow != icol) + { + determ = -determ; + for(int m = 0; m < ncol; ++m) + swap (b [irow][m], b[icol][m]); + if (nv > 0) + for (int m = 0; m < nv; ++m) + swap (w[irow][m], w[icol][m]); + } // end GJ3 + + // divide pivot row by pivot column + pivot = b[icol][icol]; + determ *= pivot; + b[icol][icol] = 1.0; + + for(int m = 0; m < ncol; ++m) + b[icol][m] /= pivot; + if(nv > 0) + for(int m = 0; m < nv; ++m) + w[icol][m] /= pivot; + + // Reduce nonpivot rows + for(int n = 0; n < ncol; ++n) + { + if(n != icol) + { + t = b[n][icol]; + b[n][icol] = 0.0; + for(int m = 0; m < ncol; ++m) + b[n][m] -= b[icol][m] * t; + if(nv > 0) + for(int m = 0; m < nv; ++m) + w[n][m] -= w[icol][m] * t; + } + } + } // { i-loop } + return true; +} +//---------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------ + +// Utility functions +//-------------------------------------------------------------------------- + +// fills a vector with zeros. +void NSUtility::zeroise(vector<double> &array, int n) +{ + array.clear(); + for(int j = 0; j < n; ++j) + array.push_back(0); +} +//-------------------------------------------------------------------------- + +// fills a vector with zeros. +void NSUtility::zeroise(vector<int> &array, int n) +{ + array.clear(); + for(int j = 0; j < n; ++j) + array.push_back(0); +} +//-------------------------------------------------------------------------- + +// fills a (m by n) matrix with zeros. +void NSUtility::zeroise(vector<vector<double> > &matrix, int m, int n) +{ + vector<double> zero; + zeroise(zero, n); + matrix.clear(); + for(int j = 0; j < m; ++j) + matrix.push_back(zero); +} +//-------------------------------------------------------------------------- + +// fills a (m by n) matrix with zeros. +void NSUtility::zeroise(vector<vector<int> > &matrix, int m, int n) +{ + vector<int> zero; + zeroise(zero, n); + matrix.clear(); + for(int j = 0; j < m; ++j) + matrix.push_back(zero); +} +//-------------------------------------------------------------------------- + + +#endif +