/* ---------------------------------------------------------------------------- * GTSAM Copyright 2010, Georgia Tech Research Corporation, * Atlanta, Georgia 30332-0415 * All Rights Reserved * Authors: Frank Dellaert, et al. (see THANKS for the full author list) * See LICENSE for the license information * -------------------------------------------------------------------------- */ /** * @file SymmetricBlockMatrixBlockExpr.h * @brief Matrix expression for a block of a SymmetricBlockMatrix * @author Richard Roberts * @date Nov 20, 2013 */ #pragma once #include namespace gtsam { template class SymmetricBlockMatrixBlockExpr; } namespace gtsam { class SymmetricBlockMatrix; } // traits class for Eigen expressions namespace Eigen { namespace internal { template struct traits > : public traits::type> { }; } } namespace gtsam { /// A matrix expression that references a single block of a SymmetricBlockMatrix. Depending on /// the position of the block, this expression will behave either as a regular matrix block, a /// transposed matrix block, or a symmetric matrix block. The only reason this class is templated /// on SymmetricBlockMatrixType is to allow for both const and non-const references. template class SymmetricBlockMatrixBlockExpr : public Eigen::EigenBase > { protected: SymmetricBlockMatrixType& xpr_; ///< The referenced SymmetricBlockMatrix DenseIndex densei_; ///< The scalar indices of the referenced block DenseIndex densej_; ///< The scalar indices of the referenced block DenseIndex denseRows_; ///< The scalar size of the referenced block DenseIndex denseCols_; ///< The scalar size of the referenced block enum BlockType { Plain, SelfAdjoint, Transposed } blockType_; ///< The type of the referenced block, as determined by the block position typedef SymmetricBlockMatrixBlockExpr This; public: // Typedefs and constants used in Eigen typedef typename const_selector::Scalar&, typename Eigen::internal::traits::Scalar>::type ScalarRef; typedef typename Eigen::internal::traits::Scalar Scalar; typedef typename Eigen::internal::traits::Index Index; static const Index ColsAtCompileTime = Eigen::Dynamic; static const Index RowsAtCompileTime = Eigen::Dynamic; typedef typename const_selector::type DenseMatrixType; typedef Eigen::Map > OffDiagonal; typedef Eigen::SelfAdjointView, Eigen::Upper> SelfAdjointView; typedef Eigen::TriangularView, Eigen::Upper> TriangularView; protected: mutable Eigen::Block myBlock_; template friend class SymmetricBlockMatrixBlockExpr; public: /// Create a SymmetricBlockMatrixBlockExpr from the specified block of a SymmetricBlockMatrix. SymmetricBlockMatrixBlockExpr(SymmetricBlockMatrixType& blockMatrix, Index iBlock, Index jBlock) : xpr_(blockMatrix), myBlock_(blockMatrix.matrix_.block(0, 0, 0, 0)) { initIndices(iBlock, jBlock); } /// Create a SymmetricBlockMatrixBlockExpr from the specified range of blocks of a /// SymmetricBlockMatrix. SymmetricBlockMatrixBlockExpr(SymmetricBlockMatrixType& blockMatrix, Index firstRowBlock, Index firstColBlock, Index rowBlocks, Index colBlocks) : xpr_(blockMatrix), myBlock_(blockMatrix.matrix_.block(0, 0, 0, 0)) { initIndices(firstRowBlock, firstColBlock, rowBlocks, colBlocks); } /// Create a SymmetricBlockMatrixBlockExpr from the specified range of blocks of a /// SymmetricBlockMatrix. SymmetricBlockMatrixBlockExpr(SymmetricBlockMatrixType& blockMatrix, Index firstBlock, Index blocks, char dummy) : xpr_(blockMatrix), myBlock_(blockMatrix.matrix_.block(0, 0, 0, 0)) { initIndices(firstBlock, firstBlock, blocks, blocks); } inline Index rows() const { return blockType_ != Transposed ? denseRows_ : denseCols_; } inline Index cols() const { return blockType_ != Transposed ? denseCols_ : denseRows_; } inline BlockType blockType() const { return blockType_; } inline ScalarRef operator()(Index row, Index col) const { return coeffInternal(row, col); } inline OffDiagonal knownOffDiagonal() const { typedef Eigen::Stride DynamicStride; // We can return a Map if we are either on an off-diagonal block, or a block of size 0 or 1 assert(blockType_ != SelfAdjoint || (denseRows_ <= 1 && denseCols_ <= 1)); if(blockType_ == Transposed) { // Swap the inner and outer stride to produce a transposed Map Eigen::Block block = const_cast(*this).xpr_.matrix_.block(densei_, densej_, denseRows_, denseCols_); return Eigen::Map(block.data(), block.cols(), block.rows(), DynamicStride(block.innerStride(), block.outerStride())); } else { Eigen::Block block = const_cast(*this).xpr_.matrix_.block(densei_, densej_, denseRows_, denseCols_); return Eigen::Map(block.data(), block.rows(), block.cols(), DynamicStride(block.outerStride(), block.innerStride())); } } inline SelfAdjointView selfadjointView() const { assert(blockType_ == SelfAdjoint); return myBlock_; } inline TriangularView triangularView() const { assert(blockType_ == SelfAdjoint); return myBlock_; } template inline void evalTo(Dest& dst) const { // Just try to assign to the object using either a selfadjoint view or a block view if(blockType_ == SelfAdjoint) dst = selfadjointView(); else if(blockType_ == Plain) dst = myBlock_; else dst = myBlock_.transpose(); } //template inline void evalTo(const Eigen::SelfAdjointView& rhs) const //{ // if(blockType_ == SelfAdjoint) // rhs.nestedExpression().triangularView() = triangularView(); // else // throw std::invalid_argument("Cannot assign an off-diagonal block to a self-adjoint matrix"); //} //template inline void evalTo(const Eigen::TriangularView& rhs) const //{ // if(blockType_ == SelfAdjoint) // rhs.nestedExpression().triangularView() = triangularView(); // else // throw std::invalid_argument("Cannot assign an off-diagonal block to a self-adjoint matrix"); //} template This& operator=(const Eigen::MatrixBase& rhs) { // Just try to assign to the object using either a selfadjoint view or a block view if(blockType_ == SelfAdjoint) triangularView() = rhs.derived().template triangularView(); else if(blockType_ == Plain) myBlock_ = rhs.derived(); else myBlock_.transpose() = rhs.derived(); return *this; } template This& operator=(const Eigen::SelfAdjointView& rhs) { if(blockType_ == SelfAdjoint) triangularView() = rhs.nestedExpression().template triangularView(); else throw std::invalid_argument("Cannot assign a self-adjoint matrix to an off-diagonal block"); return *this; } template This& operator=(const SymmetricBlockMatrixBlockExpr& other) { _doAssign(other); return *this; } This& operator=(const This& other) { // This version is required so GCC doesn't synthesize a default operator=. _doAssign(other); return *this; } template This& operator+=(const SymmetricBlockMatrixBlockExpr& other) { if(blockType_ == SelfAdjoint) { assert((BlockType)other.blockType() == SelfAdjoint); triangularView() += other.triangularView().nestedExpression(); } else if(blockType_ == Plain) { assert((BlockType)other.blockType() == Plain || (BlockType)other.blockType() == Transposed); if((BlockType)other.blockType() == Transposed) myBlock_ += other.myBlock_.transpose(); else myBlock_ += other.myBlock_; } else { assert((BlockType)other.blockType() == Plain || (BlockType)other.blockType() == Transposed); if((BlockType)other.blockType() == Transposed) myBlock_.transpose() += other.myBlock_.transpose(); else myBlock_.transpose() += other.myBlock_; } return *this; } private: void initIndices(Index iBlock, Index jBlock, Index blockRows = 1, Index blockCols = 1) { if(iBlock == jBlock && blockRows == blockCols) { densei_ = xpr_.offset(iBlock); densej_ = densei_; if(blockRows > 0) xpr_.checkBlock(iBlock + blockRows - 1); denseRows_ = xpr_.offsetUnchecked(iBlock + blockRows) - densei_; if(blockCols > 0) xpr_.checkBlock(jBlock + blockCols - 1); denseCols_ = xpr_.offsetUnchecked(jBlock + blockCols) - densej_; blockType_ = SelfAdjoint; } else { if(jBlock > iBlock || (iBlock == jBlock && blockCols > blockRows)) { densei_ = xpr_.offset(iBlock); densej_ = xpr_.offset(jBlock); if(blockRows > 0) xpr_.checkBlock(iBlock + blockRows - 1); denseRows_ = xpr_.offsetUnchecked(iBlock + blockRows) - densei_; if(blockCols > 0) xpr_.checkBlock(jBlock + blockCols - 1); denseCols_ = xpr_.offsetUnchecked(jBlock + blockCols) - densej_; blockType_ = Plain; } else { densei_ = xpr_.offset(jBlock); densej_ = xpr_.offset(iBlock); if(blockCols > 0) xpr_.checkBlock(jBlock + blockCols - 1); denseRows_ = xpr_.offsetUnchecked(jBlock + blockCols) - densei_; if(blockRows > 0) xpr_.checkBlock(iBlock + blockRows - 1); denseCols_ = xpr_.offsetUnchecked(iBlock + blockRows) - densej_; blockType_ = Transposed; } // Validate that the block does not cross below the diagonal (the indices have already been // flipped above the diagonal for ranges starting below the diagonal). if(densei_ + denseRows_ > densej_ + 1) throw std::invalid_argument("Off-diagonal block ranges may not cross the diagonal"); } new (&myBlock_) Eigen::Block(xpr_.matrix_.block(densei_, densej_, denseRows_, denseCols_)); } template inline ScalarType coeffInternal(Index row, Index col) const { // We leave index checking up to the Block class if(blockType_ == Plain) { return myBlock_(row, col); } else if(blockType_ == SelfAdjoint) { if(row <= col) return myBlock_(row, col); else return myBlock_.transpose()(row, col); } else { return myBlock_.transpose()(row, col); } } template void _doAssign(const SymmetricBlockMatrixBlockExpr& other) { if(blockType_ == SelfAdjoint) { assert((BlockType)other.blockType() == SelfAdjoint); triangularView() = other.triangularView().nestedExpression(); } else if(blockType_ == Plain) { assert((BlockType)other.blockType() == Plain || (BlockType)other.blockType() == Transposed); if((BlockType)other.blockType() == Transposed) myBlock_ = other.myBlock_.transpose(); else myBlock_ = other.myBlock_; } else { assert((BlockType)other.blockType() == Plain || (BlockType)other.blockType() == Transposed); if((BlockType)other.blockType() == Transposed) myBlock_.transpose() = other.myBlock_.transpose(); else myBlock_.transpose() = other.myBlock_; } } }; }