From b0ea3b715b191de629a4f59b47d8f20a86c947bd Mon Sep 17 00:00:00 2001 From: Luca Date: Mon, 20 Oct 2014 10:48:50 -0400 Subject: [PATCH] fixed unit test for implicitSchur factor: Point covariance was not invertible, causing eigen weirdness --- .cproject | 106 ++++---- gtsam/slam/ImplicitSchurFactor.h | 43 +-- gtsam/slam/tests/testImplicitSchurFactor.cpp | 259 +++++++++++++++++++ 3 files changed, 343 insertions(+), 65 deletions(-) create mode 100644 gtsam/slam/tests/testImplicitSchurFactor.cpp diff --git a/.cproject b/.cproject index 46b623bb9..e08e9bc62 100644 --- a/.cproject +++ b/.cproject @@ -582,6 +582,7 @@ make + tests/testBayesTree.run true false @@ -589,6 +590,7 @@ make + testBinaryBayesNet.run true false @@ -636,6 +638,7 @@ make + testSymbolicBayesNet.run true false @@ -643,6 +646,7 @@ make + tests/testSymbolicFactor.run true false @@ -650,6 +654,7 @@ make + testSymbolicFactorGraph.run true false @@ -665,6 +670,7 @@ make + tests/testBayesTree true false @@ -1000,6 +1006,7 @@ make + testErrors.run true false @@ -1229,6 +1236,46 @@ true true + + make + -j5 + testBTree.run + true + true + true + + + make + -j5 + testDSF.run + true + true + true + + + make + -j5 + testDSFMap.run + true + true + true + + + make + -j5 + testDSFVector.run + true + true + true + + + make + -j5 + testFixedVector.run + true + true + true + make -j2 @@ -1311,7 +1358,6 @@ make - testSimulated2DOriented.run true false @@ -1351,7 +1397,6 @@ make - testSimulated2D.run true false @@ -1359,7 +1404,6 @@ make - testSimulated3D.run true false @@ -1373,46 +1417,6 @@ true true - - make - -j5 - testBTree.run - true - true - true - - - make - -j5 - testDSF.run - true - true - true - - - make - -j5 - testDSFMap.run - true - true - true - - - make - -j5 - testDSFVector.run - true - true - true - - - make - -j5 - testFixedVector.run - true - true - true - make -j5 @@ -1670,6 +1674,7 @@ cpack + -G DEB true false @@ -1677,6 +1682,7 @@ cpack + -G RPM true false @@ -1684,6 +1690,7 @@ cpack + -G TGZ true false @@ -1691,6 +1698,7 @@ cpack + --config CPackSourceConfig.cmake true false @@ -2417,6 +2425,7 @@ make + testGraph.run true false @@ -2424,6 +2433,7 @@ make + testJunctionTree.run true false @@ -2431,6 +2441,7 @@ make + testSymbolicBayesNetB.run true false @@ -2636,6 +2647,14 @@ true true + + make + -j5 + testImplicitSchurFactor.run + true + true + true + make -j2 @@ -2910,7 +2929,6 @@ make - tests/testGaussianISAM2 true false diff --git a/gtsam/slam/ImplicitSchurFactor.h b/gtsam/slam/ImplicitSchurFactor.h index c0f339b30..8c6d8f1ff 100644 --- a/gtsam/slam/ImplicitSchurFactor.h +++ b/gtsam/slam/ImplicitSchurFactor.h @@ -81,7 +81,7 @@ public: } /// Get matrix P - inline const Matrix& getPointCovariance() const { + inline const Matrix3& getPointCovariance() const { return PointCovariance_; } @@ -286,26 +286,27 @@ public: return 0.5 * (result + f); } - /// needed to be GaussianFactor - (I - E*P*E')*(F*x - b) - // This is wrong and does not match the definition in Hessian - // virtual double error(const VectorValues& x) const { - // - // // resize does not do malloc if correct size - // e1.resize(size()); - // e2.resize(size()); - // - // // e1 = F * x - b = (2m*dm)*dm - // for (size_t k = 0; k < size(); ++k) - // e1[k] = Fblocks_[k].second * x.at(keys_[k]) - b_.segment < 2 > (k * 2); - // projectError(e1, e2); - // - // double result = 0; - // for (size_t k = 0; k < size(); ++k) - // result += dot(e2[k], e2[k]); - // - // std::cout << "implicitFactor::error result " << result << std::endl; - // return 0.5 * result; - // } + // needed to be GaussianFactor - (I - E*P*E')*(F*x - b) + // This is wrong and does not match the definition in Hessian, + // but it matches the definition of the Jacobian factor (JF) + double errorJF(const VectorValues& x) const { + + // resize does not do malloc if correct size + e1.resize(size()); + e2.resize(size()); + + // e1 = F * x - b = (2m*dm)*dm + for (size_t k = 0; k < size(); ++k) + e1[k] = Fblocks_[k].second * x.at(keys_[k]) - b_.segment < 2 > (k * 2); + projectError(e1, e2); + + double result = 0; + for (size_t k = 0; k < size(); ++k) + result += dot(e2[k], e2[k]); + + // std::cout << "implicitFactor::error result " << result << std::endl; + return 0.5 * result; + } /** * @brief Calculate corrected error Q*e = (I - E*P*E')*e */ diff --git a/gtsam/slam/tests/testImplicitSchurFactor.cpp b/gtsam/slam/tests/testImplicitSchurFactor.cpp new file mode 100644 index 000000000..77faaacc1 --- /dev/null +++ b/gtsam/slam/tests/testImplicitSchurFactor.cpp @@ -0,0 +1,259 @@ +/** + * @file testImplicitSchurFactor.cpp + * @brief unit test implicit jacobian factors + * @author Frank Dellaert + * @date Oct 20, 2013 + */ + +//#include +#include +//#include +#include +//#include "gtsam_unstable/slam/JacobianFactorQR.h" +#include "gtsam/slam/JacobianFactorQR.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost::assign; +using namespace gtsam; + +// F +typedef Eigen::Matrix Matrix26; +const Matrix26 F0 = Matrix26::Ones(); +const Matrix26 F1 = 2 * Matrix26::Ones(); +const Matrix26 F3 = 3 * Matrix26::Ones(); +const vector > Fblocks = list_of > // + (make_pair(0, F0))(make_pair(1, F1))(make_pair(3, F3)); +// RHS and sigmas +const Vector b = (Vector(6) << 1., 2., 3., 4., 5., 6.); + +//************************************************************************************* +TEST( implicitSchurFactor, creation ) { + // Matrix E = Matrix::Ones(6,3); + Matrix E = zeros(6, 3); + E.block<2,2>(0, 0) = eye(2); + E.block<2,3>(2, 0) = 2 * ones(2, 3); + Matrix3 P = (E.transpose() * E).inverse(); + ImplicitSchurFactor<6> expected(Fblocks, E, P, b); + Matrix expectedP = expected.getPointCovariance(); + EXPECT(assert_equal(expectedP, P)); +} + +/* ************************************************************************* */ +TEST( implicitSchurFactor, addHessianMultiply ) { + + Matrix E = zeros(6, 3); + E.block<2,2>(0, 0) = eye(2); + E.block<2,3>(2, 0) = 2 * ones(2, 3); + E.block<2,2>(4, 1) = eye(2); + Matrix3 P = (E.transpose() * E).inverse(); + + double alpha = 0.5; + VectorValues xvalues = map_list_of // + (0, gtsam::repeat(6, 2))// + (1, gtsam::repeat(6, 4))// + (2, gtsam::repeat(6, 0))// distractor + (3, gtsam::repeat(6, 8)); + + VectorValues yExpected = map_list_of// + (0, gtsam::repeat(6, 27))// + (1, gtsam::repeat(6, -40))// + (2, gtsam::repeat(6, 0))// distractor + (3, gtsam::repeat(6, 279)); + + // Create full F + size_t M=4, m = 3, d = 6; + Matrix F(2 * m, d * M); + F << F0, zeros(2, d * 3), zeros(2, d), F1, zeros(2, d*2), zeros(2, d * 3), F3; + + // Calculate expected result F'*alpha*(I - E*P*E')*F*x + FastVector keys; + keys += 0,1,2,3; + Vector x = xvalues.vector(keys); + Vector expected = zero(24); + ImplicitSchurFactor<6>::multiplyHessianAdd(F, E, P, alpha, x, expected); + EXPECT(assert_equal(expected, yExpected.vector(keys), 1e-8)); + + // Create ImplicitSchurFactor + ImplicitSchurFactor<6> implicitFactor(Fblocks, E, P, b); + + VectorValues zero = 0 * yExpected;// quick way to get zero w right structure + { // First Version + VectorValues yActual = zero; + implicitFactor.multiplyHessianAdd(alpha, xvalues, yActual); + EXPECT(assert_equal(yExpected, yActual, 1e-8)); + implicitFactor.multiplyHessianAdd(alpha, xvalues, yActual); + EXPECT(assert_equal(2 * yExpected, yActual, 1e-8)); + implicitFactor.multiplyHessianAdd(-1, xvalues, yActual); + EXPECT(assert_equal(zero, yActual, 1e-8)); + } + + typedef Eigen::Matrix DeltaX; + typedef Eigen::Map XMap; + double* y = new double[24]; + double* xdata = x.data(); + + { // Raw memory Version + std::fill(y, y + 24, 0);// zero y ! + implicitFactor.multiplyHessianAdd(alpha, xdata, y); + EXPECT(assert_equal(expected, XMap(y), 1e-8)); + implicitFactor.multiplyHessianAdd(alpha, xdata, y); + EXPECT(assert_equal(Vector(2 * expected), XMap(y), 1e-8)); + implicitFactor.multiplyHessianAdd(-1, xdata, y); + EXPECT(assert_equal(Vector(0 * expected), XMap(y), 1e-8)); + } + + // Create JacobianFactor with same error + const SharedDiagonal model; + JacobianFactorQ<6> jf(Fblocks, E, P, b, model); + + { // error + double expectedError = jf.error(xvalues); + double actualError = implicitFactor.errorJF(xvalues); + DOUBLES_EQUAL(expectedError,actualError,1e-7) + } + + { // JacobianFactor with same error + VectorValues yActual = zero; + jf.multiplyHessianAdd(alpha, xvalues, yActual); + EXPECT(assert_equal(yExpected, yActual, 1e-8)); + jf.multiplyHessianAdd(alpha, xvalues, yActual); + EXPECT(assert_equal(2 * yExpected, yActual, 1e-8)); + jf.multiplyHessianAdd(-1, xvalues, yActual); + EXPECT(assert_equal(zero, yActual, 1e-8)); + } + + { // check hessian Diagonal + VectorValues diagExpected = jf.hessianDiagonal(); + VectorValues diagActual = implicitFactor.hessianDiagonal(); + EXPECT(assert_equal(diagExpected, diagActual, 1e-8)); + } + + { // check hessian Block Diagonal + map BD = jf.hessianBlockDiagonal(); + map actualBD = implicitFactor.hessianBlockDiagonal(); + LONGS_EQUAL(3,actualBD.size()); + EXPECT(assert_equal(BD[0],actualBD[0])); + EXPECT(assert_equal(BD[1],actualBD[1])); + EXPECT(assert_equal(BD[3],actualBD[3])); + } + + { // Raw memory Version + std::fill(y, y + 24, 0);// zero y ! + jf.multiplyHessianAdd(alpha, xdata, y); + EXPECT(assert_equal(expected, XMap(y), 1e-8)); + jf.multiplyHessianAdd(alpha, xdata, y); + EXPECT(assert_equal(Vector(2 * expected), XMap(y), 1e-8)); + jf.multiplyHessianAdd(-1, xdata, y); + EXPECT(assert_equal(Vector(0 * expected), XMap(y), 1e-8)); + } + + { // Check gradientAtZero + VectorValues expected = jf.gradientAtZero(); + VectorValues actual = implicitFactor.gradientAtZero(); + EXPECT(assert_equal(expected, actual, 1e-8)); + } + + // Create JacobianFactorQR + JacobianFactorQR<6> jfq(Fblocks, E, P, b, model); + { + const SharedDiagonal model; + VectorValues yActual = zero; + jfq.multiplyHessianAdd(alpha, xvalues, yActual); + EXPECT(assert_equal(yExpected, yActual, 1e-8)); + jfq.multiplyHessianAdd(alpha, xvalues, yActual); + EXPECT(assert_equal(2 * yExpected, yActual, 1e-8)); + jfq.multiplyHessianAdd(-1, xvalues, yActual); + EXPECT(assert_equal(zero, yActual, 1e-8)); + } + + { // Raw memory Version + std::fill(y, y + 24, 0);// zero y ! + jfq.multiplyHessianAdd(alpha, xdata, y); + EXPECT(assert_equal(expected, XMap(y), 1e-8)); + jfq.multiplyHessianAdd(alpha, xdata, y); + EXPECT(assert_equal(Vector(2 * expected), XMap(y), 1e-8)); + jfq.multiplyHessianAdd(-1, xdata, y); + EXPECT(assert_equal(Vector(0 * expected), XMap(y), 1e-8)); + } + delete [] y; +} + +/* ************************************************************************* */ +TEST(implicitSchurFactor, hessianDiagonal) +{ + /* TESTED AGAINST MATLAB + * F = [ones(2,6) zeros(2,6) zeros(2,6) + zeros(2,6) 2*ones(2,6) zeros(2,6) + zeros(2,6) zeros(2,6) 3*ones(2,6)] + E = [[1:6] [1:6] [0.5 1:5]]; + E = reshape(E',3,6)' + P = inv(E' * E) + H = F' * (eye(6) - E * P * E') * F + diag(H) + */ + Matrix E(6,3); + E.block<2,3>(0, 0) << 1,2,3,4,5,6; + E.block<2,3>(2, 0) << 1,2,3,4,5,6; + E.block<2,3>(4, 0) << 0.5,1,2,3,4,5; + Matrix3 P = (E.transpose() * E).inverse(); + ImplicitSchurFactor<6> factor(Fblocks, E, P, b); + + // hessianDiagonal + VectorValues expected; + expected.insert(0, 1.195652*ones(6)); + expected.insert(1, 4.782608*ones(6)); + expected.insert(3, 7.043478*ones(6)); + EXPECT(assert_equal(expected, factor.hessianDiagonal(),1e-5)); + + // hessianBlockDiagonal + map actualBD = factor.hessianBlockDiagonal(); + LONGS_EQUAL(3,actualBD.size()); + Matrix FtE0 = F0.transpose() * E.block<2,3>(0, 0); + Matrix FtE1 = F1.transpose() * E.block<2,3>(2, 0); + Matrix FtE3 = F3.transpose() * E.block<2,3>(4, 0); + + // variant one + EXPECT(assert_equal(F0.transpose()*F0-FtE0*P*FtE0.transpose(),actualBD[0])); + EXPECT(assert_equal(F1.transpose()*F1-FtE1*P*FtE1.transpose(),actualBD[1])); + EXPECT(assert_equal(F3.transpose()*F3-FtE3*P*FtE3.transpose(),actualBD[3])); + + // variant two + Matrix I2 = eye(2); + Matrix E0 = E.block<2,3>(0, 0); + Matrix F0t = F0.transpose(); + EXPECT(assert_equal(F0t*F0-F0t*E0*P*E0.transpose()*F0,actualBD[0])); + EXPECT(assert_equal(F0t*(F0-E0*P*E0.transpose()*F0),actualBD[0])); + + Matrix M1 = F0t*(F0-E0*P*E0.transpose()*F0); + Matrix M2 = F0t*F0-F0t*E0*P*E0.transpose()*F0; + + EXPECT(assert_equal( M1 , actualBD[0] )); + EXPECT(assert_equal( M1 , M2 )); + + Matrix M1b = F0t*(E0*P*E0.transpose()*F0); + Matrix M2b = F0t*E0*P*E0.transpose()*F0; + EXPECT(assert_equal( M1b , M2b )); + + EXPECT(assert_equal(F0t*(I2-E0*P*E0.transpose())*F0,actualBD[0])); + EXPECT(assert_equal(F1.transpose()*F1-FtE1*P*FtE1.transpose(),actualBD[1])); + EXPECT(assert_equal(F3.transpose()*F3-FtE3*P*FtE3.transpose(),actualBD[3])); +} + +/* ************************************************************************* */ +int main(void) { + TestResult tr; + int result = TestRegistry::runAllTests(tr); + return result; +} +//*************************************************************************************