/* * @file JacobianFactorSVD.h * @date Oct 27, 2013 * @uthor Frank Dellaert */ #pragma once #include namespace gtsam { /** * JacobianFactor for Schur complement that uses the "Nullspace Trick" by * Mourikis et al. * * This trick is equivalent to the Schur complement, but can be faster. * In essence, the linear factor |E*dp + F*dX - b|, where p is point and X are * poses, is multiplied by Enull, a matrix that spans the left nullspace of E, * i.e., The mx3 matrix is analyzed with SVD as E = [Erange Enull]*S*V (mxm * * mx3 * 3x3) where Enull is an m x (m-3) matrix Then Enull'*E*dp = 0, and * |Enull'*E*dp + Enull'*F*dX - Enull'*b| == |Enull'*F*dX - Enull'*b| * Normally F is m x 6*numKeys, and Enull'*F yields an (m-3) x 6*numKeys matrix. * * The code below assumes that F is block diagonal and is given as a vector of * ZDim*D blocks. Example: m = 4 (2 measurements), Enull = 4*1, F = 4*12 (for * D=6) Then Enull'*F = 1*4 * 4*12 = 1*12, but each 1*6 piece can be computed as * a 1x2 * 2x6 multiplication. */ template class JacobianFactorSVD: public RegularJacobianFactor { typedef RegularJacobianFactor Base; typedef Eigen::Matrix MatrixZD; // e.g 2 x 6 with Z=Point2 typedef std::pair KeyMatrix; public: /// Default constructor JacobianFactorSVD() { } /// Empty constructor with keys. JacobianFactorSVD(const KeyVector& keys, const SharedDiagonal& model = SharedDiagonal()) : Base() { Matrix zeroMatrix = Matrix::Zero(0, D); Vector zeroVector = Vector::Zero(0); std::vector QF; QF.reserve(keys.size()); for(const Key& key: keys) QF.push_back(KeyMatrix(key, zeroMatrix)); JacobianFactor::fillTerms(QF, zeroVector, model); } /** * @brief Construct a new JacobianFactorSVD object, createing a reduced-rank * Jacobian factor on the CameraSet. * * @param keys keys associated with F blocks. * @param Fblocks CameraSet derivatives, ZDim*D blocks of block-diagonal F * @param Enull a reduced point derivative * @param b right-hand side * @param model noise model */ JacobianFactorSVD( const KeyVector& keys, const std::vector >& Fblocks, const Matrix& Enull, const Vector& b, const SharedDiagonal& model = SharedDiagonal()) : Base() { size_t numKeys = Enull.rows() / ZDim; size_t m2 = ZDim * numKeys - 3; // TODO: is this not just Enull.rows()? // PLAIN nullptr SPACE TRICK // Matrix Q = Enull * Enull.transpose(); // for(const KeyMatrixZD& it: Fblocks) // QF.push_back(KeyMatrix(it.first, Q.block(0, 2 * j++, m2, 2) * it.second)); // JacobianFactor factor(QF, Q * b); std::vector QF; QF.reserve(numKeys); for (size_t k = 0; k < Fblocks.size(); ++k) { Key key = keys[k]; QF.emplace_back( key, (Enull.transpose()).block(0, ZDim * k, m2, ZDim) * Fblocks[k]); } JacobianFactor::fillTerms(QF, Enull.transpose() * b, model); } }; }