Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

least_squares.h

Go to the documentation of this file.
00001 #ifndef INCLUDED_least_squares_h
00002 #define INCLUDED_least_squares_h
00003 
00004 #include <valarray>
00005 #include <stdexcept>
00006 
00007 /*!
00008 
00009 Least Squares Solver
00010 by Ethan Tira-Thompson, © 2011
00011 
00012 Solves linear systems using Ordinary Least Squares.
00013 
00014 Provides the namespace 'least_squares'.
00015 
00016 There are two solver functions:
00017 
00018 linearFit(x,y)
00019     Returns the parameters 'a' such that y = x·a minimizes squared error.
00020     x should be a valarray< valarray<T> >, where each x[i] is a column
00021     of data with length matching that of the valarray y.
00022 
00023 polyFit(degree,x,y)
00024     Performs a polynomial fit of degree n:
00025       y = [ 1 x .. xⁿ ] · a
00026     given x and y (as vector or valarray)  Results will be in order of
00027     increasing power (i.e. index 0 is constant term, 1 is linear term, etc.)
00028 
00029 Additionally, there are two helper functions:
00030 
00031 polyApply(poly,x)
00032     Computes the polynomial of 'x' given polynomial terms 'poly'.
00033     These arguments can be either vector or valarray.
00034 
00035 invert(x)
00036     Inverts a square valarray matrix 'x', overwriting results in-place.
00037     Throws std::underflow_error if the matrix is deficient.
00038 
00039 TODO: does not handle degenerate/low-rank conditions very well.
00040 Ideally, the fit functions should handle internally instead of allowing
00041 invert()'s std::underflow_error to pass through
00042 
00043 LICENSE:
00044 least_squares is free software: you can redistribute it and/or modify
00045 it under the terms of the Lesser GNU General Public License as published
00046 by the Free Software Foundation, either version 2 of the License, or
00047 (at your option) any later version, waiving any requirement to
00048 distribute the license document itself.
00049 
00050 least_squares is distributed in the hope that it will be useful,
00051 but WITHOUT ANY WARRANTY; without even the implied warranty of
00052 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00053 Lesser GNU General Public License for more details.
00054 
00055 */
00056 
00057 namespace least_squares {
00058 
00059 // can't template a typedef, so use macro :-P
00060 // (we'll undef these again at the end so we don't pollute global namespace)
00061 #define V(T) std::valarray< T >
00062 #define M(T) std::valarray< V(T) >
00063 
00064 
00065 //! Returns parameters 'a' via ordinary least squares such that y = x·a
00066 /*! Each element of @a x should be a valarray with length matching that of @a y.
00067  *  Will then compute invert(x' * x) * x' * y to find the fit.
00068  *
00069  *  TODO: does not handle degenerate/low-rank conditions.  Ideally, this
00070  *  should be handled internally instead of allowing invert()'s
00071  *  std::underflow_error to pass through. */
00072 template<class T>
00073 V(T) linearFit(const M(T)& x, const V(T)& y);
00074 
00075 //! Returns fit 'a' of @a degree powers of @a x: y = [ 1 x .. xⁿ ] · a
00076 /*! Accepts vector or valarray... results will be in order of
00077  *  increasing power (i.e. 0 is constant term, 1 is linear term, etc.)
00078  *
00079  *  TODO: does not handle degenerate/low-rank conditions.  Ideally, this
00080  *  should be handled internally instead of allowing invert()'s
00081  *  std::underflow_error to pass through. */
00082 template<class Vx, class Vy>
00083 V(typename Vx::value_type) polyFit(size_t degree, const Vx& x, const Vy& y);
00084 
00085 //! Computes the polynomial of x given terms from @a poly.
00086 /*! Accepts either vector and valarray arguments... @a poly terms should
00087  *  be in order of increasing degree à la polyFit() */
00088 template<class Vp, class Vx>
00089 V(typename Vx::value_type) polyApply(const Vp& poly, const Vx& x);
00090 
00091 //! Invert a matrix, in-place, using Gauss-Jordan elimination
00092 /*! Throws std::underflow_error if the matrix is deficient. */
00093 template<class T>
00094 void invert(M(T)& m);
00095 
00096 
00097 /* Extern specializations allow us to move this template code to the .cpp file.
00098  * The downside is template will be undefined other than float and double...
00099  * Add more specializations here (e.g. complex) or else move template code
00100  * back into header. */
00101 extern template void invert(M(float)& m);
00102 extern template void invert(M(double)& m);
00103 extern template V(float) linearFit(const M(float)& x, const V(float)& y);
00104 extern template V(double) linearFit(const M(double)& x, const V(double)& y);
00105 
00106 
00107 template<class Vx, class Vy>
00108 V(typename Vx::value_type) polyFit(size_t degree, const Vx& x, const Vy& y) {
00109 #ifdef DEBUG
00110   if(degree>0) {
00111     if(x.size()!=y.size()) {
00112       throw std::invalid_argument("polyFit(): x and y length mismatch");
00113     }
00114   }
00115   if(y.size()<degree) {
00116     throw std::invalid_argument("polyFit(): insufficient values for degree");
00117   }
00118 #endif
00119   typedef typename Vx::value_type T;
00120   const size_t N=y.size();
00121   M(T) xn(V(T)(1,N),degree+1);
00122   if(degree>0) {
00123     memcpy(&xn[1][0], &x[0], sizeof(T)*N);
00124     for(size_t i=2; i<=degree; ++i) {
00125       xn[i] = xn[i-1]*xn[1];
00126     }
00127   }
00128   return linearFit(xn,V(T)(&y[0],N));
00129 }
00130 
00131 
00132 template<class Vp, class Vx>
00133 V(typename Vx::value_type) polyApply(const Vp& poly, const Vx& x) {
00134   typedef typename Vx::value_type T;
00135   const size_t N=x.size();
00136   if(poly.size()==0)
00137     return V(T)(N);
00138   if(poly.size()==1)
00139     return V(T)(poly[0],N);
00140   V(T) v(N); // need to copy x into valarray in case it's a vector
00141   memcpy(&v[0],&x[0],sizeof(T)*N);
00142   V(T) acc(v*poly[1]); // linear term
00143   acc+=poly[0]; // constant term
00144   if(poly.size()>2) {
00145     V(T) p(v); // going to have to compute powers
00146     for(size_t i=2; i<poly.size(); ++i) {
00147       p*=v;
00148       acc += p*poly[i];
00149     }
00150   }
00151   return acc;
00152 }
00153 
00154 #undef V
00155 #undef M
00156 
00157 }
00158 
00159 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:43 2016 by Doxygen 1.6.3