Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

base/Math.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2002 David Jung <opensim@pobox.com>
00003 
00004   This program/file is free software; you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation; either version 2 of the License, or
00007   (at your option) any later version.
00008   
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details. (http://www.gnu.org)
00013   
00014   You should have received a copy of the GNU General Public License
00015   along with this program; if not, write to the Free Software
00016   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017   
00018   $Id: Math.cpp 1029 2004-02-11 20:45:54Z jungd $
00019   $Revision: 1.17 $
00020   $Date: 2004-02-11 15:45:54 -0500 (Wed, 11 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <base/Math>
00026 
00027 #include <base/SVD>
00028 
00029 using base::Math;
00030 
00031 using base::Matrix;
00032 using base::Vector;
00033 using base::swap;
00034 using base::SVD;
00035 
00036 
00037 // define this to use the old IKORv2 mat_null for null-space computation instead of
00038 //  the class SVD in method nullSpace().  Although both provide the null-space of
00039 //  a matrix, the spanning vector set is not unique - and each routine typically
00040 //  produces a different set.  This is useful for comparison with old code.
00041 //  (NB: the old IKORv2 code must be included in the build via externally defining
00042 //   BUILDOLDIKOR)
00043 //#define USEOLDIKOR_NULLSPACE
00044 
00045 #ifdef BUILDOLDIKOR
00046   #ifdef USEOLDIKOR_NULLSPACE
00047   extern "C" {
00048     #include <robot/control/oldikor/IKOR/general.h>
00049     #include <robot/control/oldikor/IKOR/headers.h>
00050     #include <robot/control/oldikor/UTILS/matrix.h>
00051   }
00052   #endif
00053 #else
00054   #undef USEOLDIKOR_NULLSPACE
00055 #endif
00056 
00057 
00058 
00059 Real Math::angleDifference(Real angle1, Real angle2)
00060 {
00061   Real a1 = normalizeAngle(angle1);
00062   Real a2 = normalizeAngle(angle2);
00063   if (a1 >= 0) {
00064     if (a2 >= 0)
00065       return a1-a2;
00066     else {
00067       if ((a1-a2) <= consts::Pi)
00068         return a1-a2;
00069       else
00070         return (-consts::Pi-a2)-(consts::Pi-a1);
00071     }
00072   }
00073   else {
00074     if (a2 < 0)
00075       return a1-a2;
00076     else {
00077       if ((a1-a2) > -consts::Pi)
00078         return a1-a2;
00079       else
00080         return (consts::Pi-a2)-(-consts::Pi-a1);
00081     }
00082   }
00083 }
00084 
00085 
00086 
00087 
00088 
00089 
00090 
00091 void Math::decomposeLUP(const Matrix& A, Matrix& L, Matrix& U, Vector& Pi, Real epsilon)
00092 {
00093   const Int n = A.size1();
00094 
00095   Assertm( (L.size1() == n) && (L.size2() == n)
00096            && (U.size1() == n) && (U.size2() == n) && (Pi.size() == n),
00097            "L,U,Pi are square and of same dimension as A");
00098 
00099   Real p,a;
00100   Int kp,i,j,k;
00101   Matrix AA(A);
00102 
00103   for(i=0; i<n; i++) Pi[i] = i;
00104   for(k=0; k<n-1; k++) {
00105     p = Real(0); kp = 0;
00106     for(i=k; i<n; i++) {
00107       a = Real(abs(AA(i,k)));
00108       if ( a > p ) {
00109         p = a;
00110         kp = i;
00111       }
00112     }
00113     if (equals(p,0,epsilon)) 
00114       throw std::invalid_argument(Exception("Matrix A is singular"));
00115     
00116     swap(Pi[k],Pi[kp]);
00117     for(i=0; i<n;i++) swap(AA(k,i),AA(kp,i));
00118     
00119     for(i=k+1; i<n;i++) {
00120       AA(i,k) /= AA(k,k);
00121       for(j=k+1; j<n; j++)
00122         AA(i,j) -= AA(i,k)*AA(k,j);
00123     }
00124   }
00125   
00126   for(i=0;i<n;i++) {
00127     for(j=0;j<n;j++)
00128       if (i>j) {
00129         L(i,j) = AA(i,j); U(i,j) = 0;
00130       }
00131       else {
00132         U(i,j) = AA(i,j); L(i,j) = 0;
00133       }
00134     L(i,i) = Real(1);
00135     
00136   }
00137 
00138 }
00139 
00140 
00141 Vector Math::solveLUP(const Matrix& L, const Matrix& U, const Vector& Pi, const Vector& b)
00142 {  
00143   Int n = L.size1();
00144 
00145   Assertm( (b.size() == n) && (L.size1() == n)
00146            && (U.size1()==n) || (U.size2() == n) && (Pi.size() == n),
00147            "L,U,Pi must are square and of same dimension (that of b)");
00148 
00149   SInt i,j;
00150   Vector y(n), x(n);
00151   Real s;
00152   // forward subst.
00153   for(i=0;i<SInt(n);i++) {
00154     s = 0;
00155     for(j=0;j<i;j++) s += L(i,j)*y[j];
00156     y[i] = b[Int(Pi[i])] - s;
00157   }
00158   // backward subst.
00159   for(i=n-1;i>=0;i--) {
00160     s = 0;
00161     for(j=i+1;j<SInt(n);j++) s += U(i,j)*x[j]; 
00162     x[i] = (y[i] - s)/(U(i,i));
00163   }
00164   return x;
00165 }
00166 
00167 
00168 
00169 
00170 Matrix Math::inverse(const Matrix& A, Real epsilon)
00171 {
00172   const Int nr = A.size1();
00173   const Int nc = A.size2();
00174 
00175   if (nr == nc) {
00176     // Solve AX = I by solving eqns: Ax = e (for each e a col of I)
00177     Matrix X(nr,nc);
00178     Vector x(nr);
00179     
00180     Matrix L(nr,nc),U(nr,nc);
00181     Vector Pi(nr);
00182     
00183     decomposeLUP(A, L, U, Pi, epsilon);
00184     
00185     for(Int c=0; c<nc; c++) {
00186       x = solveLUP(L,U,Pi,unitVector(nc,c));
00187       for(Int r=0; r<nr; r++) X(r,c) = x[r];
00188     }
00189     
00190     return X;
00191   }
00192   else
00193     throw std::invalid_argument(Exception("Can't invert a non-square matrix"));
00194   
00195 }
00196 
00197 
00198 Matrix Math::nullSpace(const Matrix& A, Int& nullSpaceRank, Real& k2)
00199 {
00200   if ((A.size1() == 0) || (A.size2() == 0))
00201     throw std::invalid_argument(Exception("Can't compute null-space of a 0x0 matrix!"));
00202 
00203 #ifndef USEOLDIKOR_NULLSPACE
00204   // this is a transliteration of the IKORv2.0 function mat_null() (in matrix.c)
00205   // (except that mat_null uses the Numerical Recipies in C function svdcmp() and
00206   //  we use class SVD)
00207 
00208   Assert(A.size1() <= A.size2());
00209 
00210   const Int R = A.size1(); // M
00211   const Int C = A.size2(); // N
00212 
00213   // make R == C by adding rows of zeros if necessary
00214   Matrix Asqr(C,C);
00215   if (R < C) {
00216     matrixRange(Asqr,Range(0,R), Range(0,C)) = A;
00217     matrixRange(Asqr,Range(R,C), Range(0,C)) = zeroMatrix(C-R,C);
00218   }
00219   else
00220     Asqr = A;
00221  
00222   SVD svd(Asqr);
00223   
00224   // reorder (and record the reordering in 'order')
00225   const Matrix& V(svd.V());
00226   Vector  S(svd.diag());
00227 
00228   IVector order(C);
00229   for(Int i=0; i<C; i++) order[i]=i;
00230 
00231   for(SInt i=C-1; i>0; i--)
00232     for(SInt j=i-1; j>=0; j--)
00233       if ( Math::abs(S[i]) > Math::abs(S[j]) ) {
00234         base::swap(S[i],S[j]);
00235         base::swap(order[i],order[j]);
00236       }
00237 
00238   Real s_min;
00239   if (R<=C)
00240     s_min = Math::abs(S[R-1]);
00241   else
00242     s_min = Math::abs(S[C-1]);
00243   Real s_max = Math::abs(S[0]);
00244 
00245 
00246   Matrix ns(C,C); // truncate unused columns before return
00247   nullSpaceRank=0;
00248   while (Math::abs(S[C-1-nullSpaceRank]) < SVD::minSingValue) {
00249     for(Int j=0; j<C; j++) 
00250       ns(j,nullSpaceRank) = V(j,order[C-1-nullSpaceRank]);
00251     nullSpaceRank++;
00252     if (nullSpaceRank == C) break;
00253   }
00254 
00255   if (nullSpaceRank < C) {
00256     if ( (Math::abs(s_min)>=SVD::minSingValue) && (Math::abs(s_min/s_max) < SVD::minSingValue)) {
00257       for(Int j=0; j<C; j++) 
00258         ns(j,nullSpaceRank) = V(order[j],C-1-nullSpaceRank);
00259       nullSpaceRank++;
00260     }
00261   }
00262 
00263   if (equals(s_min,0,SVD::minSingValue))
00264     k2 = consts::maxReal;
00265   else
00266     k2 = s_max/s_min;
00267 
00268   Matrix n(C,nullSpaceRank);
00269   if ((C > 0) && (nullSpaceRank > 0))
00270     n = matrixRange(ns, Range(0,C), Range(0,nullSpaceRank));
00271 
00272   return n;
00273 
00274 #else
00275   // use the old IKOR mat_null() function so we get exactly the same
00276   //  vectors to aid debugging
00277 
00278   MATRIX *a = mat_malloc(A.size1(), A.size2());
00279   for(Int r=0; r<A.size1(); r++)
00280     for(Int c=0; c<A.size2(); c++)
00281       a->p[r][c] = A(r,c);
00282 
00283   //  Int m = Math::maximum(A.size1(), A.size2());
00284   MATRIX *n = mat_malloc(A.size2(), A.size1());
00285   int n_rank;
00286   float K2;
00287   mat_null(a, &n_rank, n, &K2);
00288 
00289   nullSpaceRank = n_rank;
00290   k2 = K2;
00291 
00292   Matrix nn(A.size2(), nullSpaceRank);
00293   for(Int r=0; r<A.size2(); r++)
00294     for(Int c=0; c<nullSpaceRank; c++)
00295       nn(r,c) = n->p[r][c];
00296 
00297   mat_free(a);
00298   mat_free(n);
00299 
00300   return nn;
00301 #endif
00302 }
00303 
00304 
00305 Matrix Math::pseudoInverse(const Matrix& A)
00306 {
00307   // pad first if necessary
00308   const Int M=A.size1(); // rows
00309   const Int N=A.size2(); // cols
00310 
00311   // Use Single-Value-Decomposition (SVD)
00312   //  this requires that A be MxN where M>=N.  If the system
00313   //  is under-determined, then M<N, so we need to 
00314   //  pad A with rows of 0s to make it square
00315 
00316   if (M<N) {
00317     Matrix pA(N,N);
00318     matrixRange(pA,Range(0,M), Range(0,N)) = A;
00319     matrixRange(pA,Range(M,N), Range(0,N)) = zeroMatrix(N-M,N); // pad
00320 
00321     Matrix pAinv( SVD(pA).inv() ); // padded pseudo inverse of A
00322     
00323     return matrixRange(pAinv, Range(0,N), Range(0,M)); // remove padding
00324   }
00325 
00326   // M>=N
00327   return SVD(A).inv();
00328 }
00329 
00330 

Generated on Thu Jul 29 15:56:06 2004 for OpenSim by doxygen 1.3.6