From ba0b5df59e9c719a6e67ee028355a980ee3256d4 Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Fri, 21 Feb 2014 17:45:07 -0500 Subject: [PATCH] Finish matrix unit tests --- Base/include/Matrix2x2.h | 47 +++++++++++++ Base/include/Matrix3x3.h | 14 ++-- Base/include/Matrix4x4.h | 6 +- Base/include/MatrixBase.h | 7 ++ Base/include/MatrixSquare.h | 74 +++++++++++++------ Base/test/TestMatrix.cpp | 137 +++++++++++++++++++++++++++++++++--- 6 files changed, 242 insertions(+), 43 deletions(-) create mode 100644 Base/include/Matrix2x2.h diff --git a/Base/include/Matrix2x2.h b/Base/include/Matrix2x2.h new file mode 100644 index 0000000..25afd20 --- /dev/null +++ b/Base/include/Matrix2x2.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2014 Pavel Krajcevski + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + ******************************************************************************/ + +#ifndef BASE_INCLUDE_MATRIX2X2_H_ +#define BASE_INCLUDE_MATRIX2X2_H_ + +#include "MatrixSquare.h" + +namespace FasTC { + + template + class Matrix2x2 : public MatrixSquare { + public: + // Constructors + Matrix2x2() { } + Matrix2x2(const Matrix2x2 &other) + : MatrixSquare(other) { } + Matrix2x2(const MatrixSquare &other) + : MatrixSquare(other) { } + Matrix2x2(const MatrixBase &other) + : MatrixSquare(other) { } + }; + REGISTER_ONE_TEMPLATE_MATRIX_TYPE(Matrix2x2); +}; + +#endif // BASE_INCLUDE_MATRIX2X2_H_ diff --git a/Base/include/Matrix3x3.h b/Base/include/Matrix3x3.h index b5d226b..60edf73 100644 --- a/Base/include/Matrix3x3.h +++ b/Base/include/Matrix3x3.h @@ -31,17 +31,17 @@ namespace FasTC { template class Matrix3x3 : public MatrixSquare { - public: - // Constructors Matrix3x3() { } - Matrix3x3(const MatrixSquare &other) { - for(int i = 0; i < kNumElements; i++) { - mat[i] = other[i]; - } - } + Matrix3x3(const Matrix3x3 &other) + : MatrixSquare(other) { } + Matrix3x3(const MatrixSquare &other) + : MatrixSquare(other) { } + Matrix3x3(const MatrixBase &other) + : MatrixSquare(other) { } }; + REGISTER_ONE_TEMPLATE_MATRIX_TYPE(Matrix3x3); }; #endif // BASE_INCLUDE_MATRIX3X3_H_ diff --git a/Base/include/Matrix4x4.h b/Base/include/Matrix4x4.h index 4a21bf9..5fe18ee 100644 --- a/Base/include/Matrix4x4.h +++ b/Base/include/Matrix4x4.h @@ -59,17 +59,17 @@ namespace FasTC { template class Matrix4x4 : public MatrixSquare { - public: - // Constructors Matrix4x4() { } + Matrix4x4(const Matrix4x4 &other) + : MatrixSquare(other) { } Matrix4x4(const MatrixSquare &other) : MatrixSquare(other) { } - Matrix4x4(const MatrixBase &other) : MatrixSquare(other) { } }; + REGISTER_ONE_TEMPLATE_MATRIX_TYPE(Matrix4x4); }; #endif // BASE_INCLUDE_MATRIX3X3_H_ diff --git a/Base/include/MatrixBase.h b/Base/include/MatrixBase.h index 5bd4c1e..94d8aa2 100644 --- a/Base/include/MatrixBase.h +++ b/Base/include/MatrixBase.h @@ -158,6 +158,13 @@ namespace FasTC { static const EVectorType kVectorType = eVectorType_Matrix; \ } + #define REGISTER_ONE_TEMPLATE_MATRIX_SIZED_TYPE(TYPE) \ + template \ + class VectorTraits< TYPE > { \ + public: \ + static const EVectorType kVectorType = eVectorType_Matrix; \ + } + // Define matrix multiplication for * operator template class MultSwitch< diff --git a/Base/include/MatrixSquare.h b/Base/include/MatrixSquare.h index 8c20752..df887b0 100644 --- a/Base/include/MatrixSquare.h +++ b/Base/include/MatrixSquare.h @@ -26,6 +26,8 @@ #define BASE_INCLUDE_MATRIXSQUARE_H_ #include "MatrixBase.h" +#include +#include namespace FasTC { @@ -40,54 +42,82 @@ namespace FasTC { MatrixSquare(const MatrixBase &other) : MatrixBase(other) { } + MatrixSquare Transpose() const { + return MatrixBase::Transpose(); + } + // Does power iteration to determine the principal eigenvector and eigenvalue. // Returns them in eigVec and eigVal after kMaxNumIterations - int PowerMethod(VectorBase &eigVec, T *eigVal = NULL, - const int kMaxNumIterations = 200) { + int PowerMethod(VectorBase &eigVec, + T *eigVal = NULL, + const int kMaxNumIterations = 200, + const unsigned int kSeed = time(NULL)) { + + srand(kSeed); int numIterations = 0; - // !SPEED! Find eigenvectors by using the power method. This is good because the - // matrix is only 4x4, which allows us to use SIMD... VectorBase b; for(int i = 0; i < N; i++) - b[i] = T(1.0); - - b /= b.Length(); + b[i] = static_cast(rand()); + b.Normalize(); + bool badEigenValue = false; bool fixed = false; numIterations = 0; while(!fixed && ++numIterations < kMaxNumIterations) { - VectorBase newB = (*this).operator*(b); + VectorBase newB = (*this) * b; - // !HACK! If the principal eigenvector of the covariance matrix - // converges to zero, that means that the points lie equally - // spaced on a sphere in this space. In this (extremely rare) - // situation, just choose a point and use it as the principal - // direction. + // !HACK! If the principal eigenvector of the matrix + // converges to zero, that could mean that there is no + // principal eigenvector. However, that may be due to + // poor initialization of the random vector, so rerandomize + // and try again. const float newBlen = newB.Length(); if(newBlen < 1e-10) { - eigVec = b; - if(eigVal) *eigVal = 0.0; - return numIterations; + if(badEigenValue) { + eigVec = b; + if(eigVal) *eigVal = 0.0; + return numIterations; + } + + VectorBase b; + for(int i = 0; i < N; i++) + b[i] = static_cast(rand()); + + b.Normalize(); + badEigenValue = true; } - T len = newB.Length(); - newB /= len; - if(eigVal) - *eigVal = len; + // Normalize + newB.Normalize(); - if(fabs(1.0f - (b.Dot(newB))) < 1e-5) + // If the new eigenvector is close enough to the old one, + // then we've converged. + if(fabs(1.0f - (b.Dot(newB))) < 1e-8) fixed = true; + // Save and continue. b = newB; } - eigVec = b; + // Store the eigenvector in the proper variable. + eigVec = b; + + // Store eigenvalue if it was requested + if(eigVal) { + VectorBase result = (*this) * b; + *eigVal = result.Length() / b.Length(); + } + return numIterations; } + private: + }; + REGISTER_ONE_TEMPLATE_MATRIX_SIZED_TYPE(MatrixSquare); + }; #endif // BASE_INCLUDE_MATRIXSQUARE_H_ diff --git a/Base/test/TestMatrix.cpp b/Base/test/TestMatrix.cpp index 2e26cf5..55a4aa9 100644 --- a/Base/test/TestMatrix.cpp +++ b/Base/test/TestMatrix.cpp @@ -123,7 +123,13 @@ TEST(MatrixBase, PointerConversion) { } TEST(MatrixBase, CastVector) { + srand(time(NULL)); + FasTC::MatrixBase v3f; + for(int i = 0; i < 6; i++) { + v3f[i] = static_cast(rand()); + } + FasTC::MatrixBase v3d = v3f; FasTC::MatrixBase v3i = v3f; for(int i = 0; i < 3*2; i++) { @@ -198,28 +204,137 @@ TEST(MatrixBase, VectorMultiplication) { EXPECT_EQ(v[4], 0 + (3 * 2) + (5 * 3)); } +//////////////////////////////////////////////////////////////////////////////// + +#include "MatrixSquare.h" + TEST(MatrixSquare, Constructors) { - // Stub - EXPECT_EQ(0, 1); + FasTC::MatrixBase m; + m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; + m(1, 0) = 2; m(1, 1) = 3; m(1, 2) = 4; + m(2, 0) = 3; m(2, 1) = 4; m(2, 2) = 5; + + FasTC::MatrixSquare sqm (m); + for(int i = 0; i < 9; i++) { + EXPECT_EQ(m[i], sqm[i]); + } + + FasTC::MatrixSquare fsqm(m); + for(int i = 0; i < 9; i++) { + EXPECT_NEAR(m[i], fsqm[i], kEpsilon); + } + + FasTC::MatrixSquare fcsqm(sqm); + for(int i = 0; i < 9; i++) { + EXPECT_NEAR(fcsqm[i], sqm[i], kEpsilon); + } } -TEST(MatrixSquare, EigenvalueCalculation) { - // Stub - EXPECT_EQ(0, 1); +TEST(MatrixSquare, PowerMethod) { + FasTC::MatrixSquare A; + A(0, 0) = 0.8f; A(0, 1) = 0.3f; + A(1, 0) = 0.2f; A(1, 1) = 0.7f; + + double e; + FasTC::VectorBase x; + A.PowerMethod(x, &e, 20, 200); + + EXPECT_NEAR(x[0], 0.83205f, 0.0001); + EXPECT_NEAR(x[1], 0.5547f, 0.0001); + + EXPECT_NEAR(e, 1.f, 0.0001); } +//////////////////////////////////////////////////////////////////////////////// + +#include "Matrix2x2.h" + TEST(Matrix2x2, Constructors) { - // Stub - EXPECT_EQ(0, 1); + FasTC::MatrixBase m; + m(0, 0) = 1; m(0, 1) = 2; + m(1, 0) = 2; m(1, 1) = 3; + + FasTC::MatrixSquare sqm (m); + for(int i = 0; i < 4; i++) { + EXPECT_EQ(m[i], sqm[i]); + } + + FasTC::Matrix2x2 tbtm (m); + for(int i = 0; i < 4; i++) { + EXPECT_EQ(m[i], tbtm[i]); + } + + FasTC::Matrix2x2 fsqm(m); + for(int i = 0; i < 4; i++) { + EXPECT_NEAR(m[i], fsqm[i], kEpsilon); + } + + FasTC::Matrix2x2 fcsqm(sqm); + for(int i = 0; i < 4; i++) { + EXPECT_NEAR(fcsqm[i], sqm[i], kEpsilon); + } } +//////////////////////////////////////////////////////////////////////////////// + +#include "Matrix3x3.h" + TEST(Matrix3x3, Constructors) { - // Stub - EXPECT_EQ(0, 1); + FasTC::MatrixBase m; + m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; + m(1, 0) = 2; m(1, 1) = 3; m(1, 2) = 4; + m(2, 0) = 3; m(2, 1) = 4; m(2, 2) = 5; + + FasTC::MatrixSquare sqm (m); + for(int i = 0; i < 9; i++) { + EXPECT_EQ(m[i], sqm[i]); + } + + FasTC::Matrix3x3 tbtm (m); + for(int i = 0; i < 9; i++) { + EXPECT_EQ(m[i], tbtm[i]); + } + + FasTC::Matrix3x3 fsqm(m); + for(int i = 0; i < 9; i++) { + EXPECT_NEAR(m[i], fsqm[i], kEpsilon); + } + + FasTC::Matrix3x3 fcsqm(sqm); + for(int i = 0; i < 9; i++) { + EXPECT_NEAR(fcsqm[i], sqm[i], kEpsilon); + } } +//////////////////////////////////////////////////////////////////////////////// + +#include "Matrix4x4.h" + TEST(Matrix4x4, Constructors) { - // Stub - EXPECT_EQ(0, 1); + FasTC::MatrixBase m; + m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; m(0, 3) = 4; + m(1, 0) = 2; m(1, 1) = 3; m(1, 2) = 4; m(1, 3) = 5; + m(2, 0) = 3; m(2, 1) = 4; m(2, 2) = 5; m(2, 3) = 6; + m(3, 0) = 4; m(3, 1) = 5; m(3, 2) = 6; m(3, 3) = 7; + + FasTC::MatrixSquare sqm (m); + for(int i = 0; i < 9; i++) { + EXPECT_EQ(m[i], sqm[i]); + } + + FasTC::Matrix4x4 tbtm (m); + for(int i = 0; i < 9; i++) { + EXPECT_EQ(m[i], tbtm[i]); + } + + FasTC::Matrix4x4 fsqm(m); + for(int i = 0; i < 9; i++) { + EXPECT_NEAR(m[i], fsqm[i], kEpsilon); + } + + FasTC::Matrix4x4 fcsqm(sqm); + for(int i = 0; i < 9; i++) { + EXPECT_NEAR(fcsqm[i], sqm[i], kEpsilon); + } }