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

base/MathTest.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: MathTest.cpp 1029 2004-02-11 20:45:54Z jungd $
00019   $Revision: 1.12 $
00020   $Date: 2004-02-11 15:45:54 -0500 (Wed, 11 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <base/MathTest>
00026 
00027 #include <base/SVD>
00028 #include <base/Path>
00029 
00030 using base::MathTest;
00031 
00032 using base::consts;
00033 using base::Matrix;
00034 using base::Vector;
00035 using base::SVD;
00036 using base::Path;
00037 
00038 
00039 
00040 MathTest::MathTest()
00041   : I(4,4), A(4,4), S(3,3),
00042     b(4), x(4)
00043 {
00044 }
00045 
00046 
00047 void MathTest::setUp() 
00048 { 
00049   // identity
00050   I = identityMatrix(4,4);
00051   
00052   // a matrix with a unique inverse
00053   A(0,0) = 1; A(0,1) = -1; A(0,2) = 0; A(0,3) = 0;
00054   A(1,0) = 2; A(1,1) = -2; A(1,2) = 1; A(1,3) = 2;
00055   A(2,0) = 0; A(2,1) = 1;  A(2,2) = 0; A(2,3) = 1;
00056   A(3,0) = 0; A(3,1) = 0;  A(3,2) = 2; A(3,3) = 1;
00057   
00058   b[0] = 0; b[1] = 4; b[2] = 0; b[3] = 5;
00059   
00060   x[0] = -1; x[1]=-1; x[2] = 2; x[3] = 1; // Ax=b
00061 
00062   S(0,0) = 1; S(0,1) = 2; S(0,2) = 3; // singluar
00063   S(1,0) = 1; S(1,1) = 2; S(1,2) = 2;
00064   S(2,0) = 1; S(2,1) = 2; S(2,2) = 3;
00065   
00066   
00067   P.resize(3,9);
00068   P(0,0) = 1; P(0,1) = 0; P(0,2) = -0.71595; P(0,3) = -0.13884; P(0,4) = -0.46236; P(0,5) = -0.29068; P(0,6) = 0.06875; P(0,7) = -0.36885; P(0,8) = -0.24926;
00069   P(1,0) = 0; P(1,1) = 1; P(1,2) = 0.94988;  P(1,3) = 0.20359;  P(1,4) = -0.66261; P(1,5) = -0.98019; P(1,6) = -0.55012; P(1,7) = -0.10177; P(1,8) = 0.23327;
00070   P(2,0) = 0; P(2,1) = 0; P(2,2) = 0;        P(2,3) = 0;        P(2,4) = -0.11290; P(2,5) = -0.53204; P(2,6) = -0.54088; P(2,7) = -0.60098; P(2,8) = -0.20846;
00071   
00072 }
00073   
00074 
00075 void MathTest::tearDown()
00076 { 
00077 }
00078 
00079  
00080 void MathTest::testMatrixVector()
00081 {
00082   // just test some elementary Matrix & Vector ops first
00083   Matrix m(3,3);
00084   m = identityMatrix(3,3);
00085   for(Int r=0; r<3; r++)
00086     for(Int c=0; c<3; c++)
00087       CPPUNIT_ASSERT( m(r,c) == ((r==c)?1:0) );
00088 
00089   Vector u(3);
00090   u = unitVector(3,2);
00091   CPPUNIT_ASSERT( equals(norm_2(u),1) );
00092   CPPUNIT_ASSERT( u[2] == 1 );
00093   Vector mr(3), mc(3);
00094   mr = matrixRow(m,2);
00095   mc = matrixColumn(m,1);
00096 
00097   CPPUNIT_ASSERT( mr[2] == 1 );
00098   CPPUNIT_ASSERT( mc[1] == 1 );
00099 
00100   CPPUNIT_ASSERT( norm_2(zeroVector(3)) == 0 );
00101 
00102   Matrix z(3,3);
00103   z = zeroMatrix(3,3);
00104   CPPUNIT_ASSERT( norm_2(matrixRow(z,0)) == 0 );
00105 
00106   matrixRow(z,0) = unitVector(3,2); 
00107   CPPUNIT_ASSERT( norm_2(matrixRow(z,0)) == 1 );
00108 
00109   Vector v6(zeroVector(6));
00110   vectorRange(v6,Range(0,3)) = unitVector(3,0);
00111   CPPUNIT_ASSERT( v6[0] == 1 );
00112   
00113   Matrix sm(2,2);
00114   sm = matrixRange(m,Range(0,2), Range(0,2));
00115   CPPUNIT_ASSERT( base::equals(sm, identityMatrix(2,2)) );
00116 
00117   Matrix m1(3,3);
00118   m1(0,0) = 1; m1(0,1) = 2; m1(0,2) = 3;
00119   m1(1,0) = 4; m1(1,1) = 5; m1(1,2) = 6;
00120   m1(2,0) = 7; m1(2,1) = 8; m1(2,2) = 9;
00121 
00122   
00123   Matrix newm1(m1);
00124   matrixRange(newm1, Range(0,2), Range(0,2)) = identityMatrix(2,2);
00125   CPPUNIT_ASSERT( base::equals( matrixRange(newm1, Range(0,2), Range(0,2)), identityMatrix(2,2)) );
00126   
00127   
00128   Matrix m2(3,3);
00129   m2(0,0) = 0; m2(0,1) = 3; m2(0,2) = 6;
00130   m2(1,0) = 7; m2(1,1) = 3; m2(1,2) = 5;
00131   m2(2,0) = -4;m2(2,1) = 5; m2(2,2) = -3;
00132   
00133   // test simple multiplication
00134   Matrix m12(3,3); // m1*m2
00135   m12(0,0) = 2; m12(0,1) = 24; m12(0,2) = 7;
00136   m12(1,0) = 11;m12(1,1) = 57; m12(1,2) = 31;
00137   m12(2,0) = 20;m12(2,1) = 90; m12(2,2) = 55;
00138 
00139   CPPUNIT_ASSERT( base::equals(m1*m2, m12) );
00140   
00141   
00142   // and matrix*vector mult
00143   Vector v1(3);
00144   v1[0] = 2; v1[1] = 5; v1[2] = 7;
00145   
00146   Vector m1v1(3);
00147   m1v1[0] = 33; m1v1[1] = 75; m1v1[2] = 117;
00148   
00149   CPPUNIT_ASSERT( base::equals(m1*v1, m1v1) );
00150 
00151   // test correct negate behaviour
00152   Vector n(1); n[0]=1;
00153   -n;
00154   CPPUNIT_ASSERT( base::equals(n[0], 1) );
00155   n = -n;
00156   CPPUNIT_ASSERT( base::equals(n[0], -1) );
00157   
00158   Matrix nm(1,1);
00159   nm(0,0) = 1;
00160   -nm;
00161   CPPUNIT_ASSERT( base::equals(nm(0,0), 1) );
00162   nm = -nm;
00163   CPPUNIT_ASSERT( base::equals(nm(0,0), -1) );
00164   
00165 }
00166 
00167 
00168 void MathTest::testNullSpace()
00169 {
00170   Vector r(3); r[0]=1; r[1]=2; r[2]=3;
00171 
00172   Matrix A(3,3);
00173   matrixRow(A,0) = r;
00174   matrixRow(A,1) = r;
00175   matrixRow(A,2) = r;
00176 
00177   Int rank;
00178   Real k2;
00179   Matrix Z( Math::nullSpace(A,rank,k2) );
00180 
00181   CPPUNIT_ASSERT( equals(rank,2) ); // rank of null space (not of A)
00182 
00183   CPPUNIT_ASSERT( base::equals(A*Z, zeroMatrix(3,2), 0.00001) );
00184 
00185   Vector x(3);
00186   for(Int i=0; i<10; i++) {
00187     Real r = Math::random();
00188     Real s = r-1;
00189     x = matrixColumn(Z,0)*r + matrixColumn(Z,1)*s;
00190     CPPUNIT_ASSERT( equals(A*x, zeroVector(3), 0.00001) );
00191   }
00192 
00193 }
00194 
00195 
00196 void MathTest::testInverse() 
00197 {
00198   // check fixture first
00199   Assert( equals(A*x,b) ); 
00200   Assert( A.size1() == A.size2() ); // invert only works for square Matrices
00201 
00202   // calc inverse and check it's the right size
00203   Matrix Ainv( Math::inverse(A) );
00204   CPPUNIT_ASSERT( Ainv.size1() == A.size1() );
00205   CPPUNIT_ASSERT( Ainv.size2() == A.size2() );
00206 
00207   // mult A by its inverse, should give same size
00208   Matrix AAinv( A*Ainv );
00209   CPPUNIT_ASSERT( AAinv.size1() == A.size1() );
00210   CPPUNIT_ASSERT( AAinv.size2() == Ainv.size2() );
00211   
00212   // and = identity
00213   CPPUNIT_ASSERT( equals(AAinv, I) );
00214   
00215   // check we get x back
00216   CPPUNIT_ASSERT( equals(Ainv*b, x) );
00217 }
00218 
00219 
00220 void MathTest::testInverseSingular()
00221 {
00222   // should throw an std::invalid_argument exception
00223   Matrix Sinv( Math::inverse(S) );
00224 }
00225 
00226 
00227 void MathTest::testSVD() 
00228 {
00229   // decompose square A
00230   SVD svda(A);
00231   
00232   Matrix U(svda.U());
00233   Matrix V(svda.V());
00234   Matrix S(svda.S());
00235 
00236   Matrix NA( U * S * transpose(V) );
00237 
00238   CPPUNIT_ASSERT( NA.equals(A,0.000001) );
00239 
00240   // compare singular values with those calc'd using Octave
00241   Vector so(4);
00242   so[3] = 3.95858;
00243   so[2] = 2.16562;
00244   so[1] = 1.24951;
00245   so[0] = 0.28007;
00246 
00247   // sort from lowest to highest
00248   bool swapped=false;
00249   do {
00250     swapped = false;
00251     for(Int i=0; i<3; i++)
00252     if (S(i,i) > S(i+1,i+1)) {
00253       base::swap( S(i,i), S(i+1,i+1) );
00254       swapped=true;
00255     }
00256   } while (swapped);
00257   
00258   Assert(S.size1() == 4);
00259   for(Int i=0; i<S.size1(); i++) {
00260     CPPUNIT_ASSERT( Math::equals(S(i,i), so[i], 0.00001) );
00261   }
00262   
00263 
00264   // and a non-square matrix (underconstrained)
00265   Matrix A1(A.rows(), A.cols()+2);
00266   matrixRange(A1, Range(0,A.rows()), Range(0,A.cols())) = A;
00267   Vector v1( zeroVector(A.rows()) );
00268   v1[0] = 1; v1[1] = 5; v1[2] = -0.2; v1[v1.size()-1] = -4;
00269   MatrixColumn(A1, A.cols()) = v1;
00270   v1[0] = 3; v1[1] = -6; v1[2] = -0.3; v1[v1.size()-1] = 2;
00271   MatrixColumn(A1, A.cols()+1) = v1;
00272   
00273   const Int M1 = A1.rows();
00274   const Int N1 = A1.cols();
00275   
00276   SVD svda1(A1);
00277   
00278   Matrix U1(svda1.U());
00279   Matrix V1(svda1.V());
00280   Matrix S1(svda1.S());
00281   
00282   CPPUNIT_ASSERT( U1.rows() == M1 );
00283   CPPUNIT_ASSERT( U1.cols() == M1 );
00284   CPPUNIT_ASSERT( S1.rows() == M1 );
00285   CPPUNIT_ASSERT( S1.cols() == N1 );
00286   CPPUNIT_ASSERT( V1.rows() == N1 );
00287   CPPUNIT_ASSERT( V1.cols() == N1 );
00288   
00289   Matrix NA1( U1 * S1 * transpose(V1) );
00290 
00291   CPPUNIT_ASSERT( NA1.equals(A1,0.00001) );
00292 
00293   
00294   // a non-square matrix (overconstrained)
00295   Matrix A2(A.rows()+2, A.cols());
00296   matrixRange(A2, Range(0,A.rows()), Range(0,A.cols())) = A;
00297   Vector v( zeroVector(A.cols()) );
00298   v[0] = 1; v[1] = 5; v[2] = -0.2; v[v.size()-1] = -4;
00299   MatrixRow(A2, A.rows()) = v;
00300   v[0] = 3; v[1] = -6; v[2] = -0.3; v[v.size()-1] = 2;
00301   MatrixRow(A2, A.rows()+1) = v;
00302   
00303   const Int M = A2.rows();
00304   const Int N = A2.cols();
00305   
00306   SVD svda2(A2);
00307   
00308   Matrix U2(svda2.U());
00309   Matrix V2(svda2.V());
00310   Matrix S2(svda2.S());
00311 
00312   CPPUNIT_ASSERT( U2.rows() == M );
00313   CPPUNIT_ASSERT( U2.cols() == M );
00314   CPPUNIT_ASSERT( S2.rows() == M );
00315   CPPUNIT_ASSERT( S2.cols() == N );
00316   CPPUNIT_ASSERT( V2.rows() == N );
00317   CPPUNIT_ASSERT( V2.cols() == N );
00318   
00319   Matrix NA2( U2 * S2 * transpose(V2) );
00320 
00321   CPPUNIT_ASSERT( NA2.equals(A2,0.00001) );
00322 }
00323 
00324 
00325 
00326 void MathTest::testPseudoInverse()
00327 {
00328   Matrix PInv( Math::pseudoInverse(P) );
00329   
00330   Matrix PIO(9,3); // pinv according to Octave
00331   
00332   PIO(0,0) = 0.5819252;   PIO(0,1) = 0.1170694;  PIO(0,2) = -0.3666462;
00333   PIO(1,0) =  0.1170694;  PIO(1,1) = 0.3703392;  PIO(1,2) = -0.3903029;
00334   PIO(2,0) = -0.3054275;  PIO(2,1) = 0.2679620;  PIO(2,2) = -0.1082405;
00335   PIO(3,0) = -0.0569603;  PIO(3,1) = 0.0591435;  PIO(3,2) = -0.0285566;
00336   PIO(4,0) = -0.3052359;  PIO(4,1) = -0.2554535; PIO(4,2) =  0.2557433;
00337   PIO(5,0) = -0.0888338;  PIO(5,1) = -0.1893758; PIO(5,2) = -0.3232752;
00338   PIO(6,0) =  0.1739168;  PIO(6,1) = 0.0154245;  PIO(6,2) = -0.6364151;
00339   PIO(7,0) = -0.0062102;  PIO(7,1) = 0.1536938;  PIO(7,2) = -0.7427355;
00340   PIO(8,0) = -0.0413108;  PIO(8,1) = 0.1385709;  PIO(8,2) = -0.3179733;
00341 
00342   CPPUNIT_ASSERT( PInv.equals(PIO, 0.00001) );
00343 }
00344 
00345 
00346 
00347 
00348 void MathTest::testExpression()
00349 {
00350   Expression e("tan(0.1)*-2*sin(p[0])+-4.4e-1*((1+1.00)*0.5)/cos(p[0]+0.01+0*(100.0/-3.3e-8))");
00351   e.simplify();
00352   
00353   Vector p(1);
00354   p[0] = consts::Pi/2.0;
00355   
00356   CPPUNIT_ASSERT(Math::equals( e.evaluate(p), Math::tan(0.1)*-2.0*Math::sin(p[0])+-4.4e-1*((1+1)*0.5)/Math::cos(p[0]+0.01) ));
00357 }
00358 
00359 
00360 
00361 
00362 void MathTest::testPath() 
00363 {
00364   // check default path is 0
00365   Path zp;
00366   for(Real t=0.0; t<=1.0; t+=0.05) {
00367     CPPUNIT_ASSERT( zp.position(t).equalsZero() );
00368     CPPUNIT_ASSERT( zp.orientation(t).getVector3(Orient::EulerRPY).equalsZero() );
00369   }
00370 
00371   // unit length const orientation line segment
00372   Point3 p1(1,1,1);
00373   Point3 p2(2,2,2);
00374   Path up(p1,Orient(),p2,Orient());
00375   CPPUNIT_ASSERT( up.position(0.0).equals(p1) );
00376   CPPUNIT_ASSERT( up.position(1.0).equals(p2) );
00377   CPPUNIT_ASSERT( up.position(0.5).equals(Point3(1.5,1.5,1.5)) );
00378   CPPUNIT_ASSERT( up.orientation(0.5).getVector3(Orient::EulerRPY).equalsZero() );
00379 
00380   // test interpolation of both pos & orient
00381   Quat4 q1(Vector3(0,0,1),consts::Pi/4.0);
00382   Quat4 q2(Vector3(0,0,1),consts::Pi/2.0);
00383   Orient o1(q1), o2(q2);
00384   Path path(p1,o1,p2,o2);
00385   CPPUNIT_ASSERT( path.position(0.0).equals(p1) );
00386   CPPUNIT_ASSERT( path.position(1.0).equals(p2) );
00387   CPPUNIT_ASSERT( path.position(0.5).equals(Point3(1.5,1.5,1.5)) );
00388   CPPUNIT_ASSERT( path.orientation(0.0).equals(o1) );
00389   CPPUNIT_ASSERT( path.orientation(1.0).equals(o2) );
00390   CPPUNIT_ASSERT( path.orientation(0.5).equals(Orient::interpolate(o1,o2,0.5)) );
00391 
00392   array<Point3> ps(3);
00393   array<Orient> os(3);
00394   ps[0] = p1; ps[1] = Point3(1.5,3.0,-2.5); ps[2] = p2;
00395   Quat4 q3(Vector3(0,0,1),consts::Pi/3.0);
00396   Orient o3(q3);
00397   os[0] = o1; os[1] = o3; os[2] = o2;
00398   Path wpath(ps,os);
00399   CPPUNIT_ASSERT( wpath.position(0.0).equals(p1) );
00400   CPPUNIT_ASSERT( wpath.position(1.0).equals(p2) );
00401   CPPUNIT_ASSERT( wpath.position(0.5).equals(Point3(1.53099,2.93802,-2.22108),1e-4) );
00402   CPPUNIT_ASSERT( wpath.position(0.6).equals(Point3(1.62479,2.75042,-1.37687),1e-4) );
00403   CPPUNIT_ASSERT( wpath.position(0.8).equals(Point3(1.8124,2.37521,0.311566),1e-4) );
00404 
00405   CPPUNIT_ASSERT( wpath.orientation(0.0).equals(o1) );
00406   CPPUNIT_ASSERT( wpath.orientation(1.0).equals(o2) );
00407 
00408   // construct an orientation only (const position) path to test orientatin interpolation
00409   ps.clear();
00410   Path wpath2(ps,os);
00411   CPPUNIT_ASSERT( wpath2.orientation(0.0).equals(o1) );
00412   CPPUNIT_ASSERT( wpath2.orientation(1.0).equals(o2) );
00413   CPPUNIT_ASSERT( wpath2.orientation(0.5).getQuat4().equals(Quat4(Vector3(0,0,1),3.0*consts::Pi/8.0)) );
00414   
00415   
00416   // a parametric path
00417   ExpressionVector v(3);
00418   v[0] = base::cos( Expression::p[0] * 2.0 * consts::Pi ); v[0].simplify();
00419   v[1] = base::sin( Expression::p[0] * 2.0 * consts::Pi ); v[1].simplify();
00420   v[2] = 0;
00421   Path unitcircle(v);
00422   
00423   for(Real s=0.0; s<=1.0; s+=1/100.0) {
00424     Point3 p( Math::cos(s*2.0*consts::Pi), Math::sin(s*2.0*consts::Pi), 0);
00425     CPPUNIT_ASSERT( unitcircle.position(s).equals(p) );
00426   }
00427   
00428 }
00429 
00430 
00431 #ifdef DEBUG
00432 CPPUNIT_TEST_SUITE_REGISTRATION( MathTest );
00433 #endif
00434 

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