gtsam  4.0.0
gtsam
SymmetricBlockMatrix.h
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2 
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7 
8 * See LICENSE for the license information
9 
10 * -------------------------------------------------------------------------- */
11 
18 #pragma once
19 
20 #include <gtsam/base/FastVector.h>
21 #include <gtsam/base/Matrix.h>
22 #include <gtsam/base/types.h>
23 #include <gtsam/dllexport.h>
24 #include <boost/serialization/nvp.hpp>
25 #include <cassert>
26 #include <stdexcept>
27 #include <array>
28 
29 namespace boost {
30 namespace serialization {
31 class access;
32 } /* namespace serialization */
33 } /* namespace boost */
34 
35 namespace gtsam {
36 
37  // Forward declarations
38  class VerticalBlockMatrix;
39 
51  class GTSAM_EXPORT SymmetricBlockMatrix
52  {
53  public:
54  typedef SymmetricBlockMatrix This;
55  typedef Eigen::Block<Matrix> Block;
56  typedef Eigen::Block<const Matrix> constBlock;
57 
58  protected:
59  Matrix matrix_;
60  FastVector<DenseIndex> variableColOffsets_;
61 
63 
64  public:
67  blockStart_(0)
68  {
69  variableColOffsets_.push_back(0);
70  assertInvariants();
71  }
72 
74  template<typename CONTAINER>
75  SymmetricBlockMatrix(const CONTAINER& dimensions, bool appendOneDimension = false) :
76  blockStart_(0)
77  {
78  fillOffsets(dimensions.begin(), dimensions.end(), appendOneDimension);
79  matrix_.resize(variableColOffsets_.back(), variableColOffsets_.back());
80  assertInvariants();
81  }
82 
84  template<typename ITERATOR>
85  SymmetricBlockMatrix(ITERATOR firstBlockDim, ITERATOR lastBlockDim, bool appendOneDimension = false) :
86  blockStart_(0)
87  {
88  fillOffsets(firstBlockDim, lastBlockDim, appendOneDimension);
89  matrix_.resize(variableColOffsets_.back(), variableColOffsets_.back());
90  assertInvariants();
91  }
92 
94  template<typename CONTAINER>
95  SymmetricBlockMatrix(const CONTAINER& dimensions, const Matrix& matrix, bool appendOneDimension = false) :
96  blockStart_(0)
97  {
98  matrix_.resize(matrix.rows(), matrix.cols());
99  matrix_.triangularView<Eigen::Upper>() = matrix.triangularView<Eigen::Upper>();
100  fillOffsets(dimensions.begin(), dimensions.end(), appendOneDimension);
101  if(matrix_.rows() != matrix_.cols())
102  throw std::invalid_argument("Requested to create a SymmetricBlockMatrix from a non-square matrix.");
103  if(variableColOffsets_.back() != matrix_.cols())
104  throw std::invalid_argument("Requested to create a SymmetricBlockMatrix with dimensions that do not sum to the total size of the provided matrix.");
105  assertInvariants();
106  }
107 
111  static SymmetricBlockMatrix LikeActiveViewOf(const SymmetricBlockMatrix& other);
112 
116  static SymmetricBlockMatrix LikeActiveViewOf(const VerticalBlockMatrix& other);
117 
119  DenseIndex rows() const { assertInvariants(); return variableColOffsets_.back() - variableColOffsets_[blockStart_]; }
120 
122  DenseIndex cols() const { return rows(); }
123 
125  DenseIndex nBlocks() const { return nActualBlocks() - blockStart_; }
126 
128  DenseIndex getDim(DenseIndex block) const {
129  return calcIndices(block, block, 1, 1)[2];
130  }
131 
134 
137  Matrix block(DenseIndex I, DenseIndex J) const;
138 
140  Eigen::SelfAdjointView<Block, Eigen::Upper> diagonalBlock(DenseIndex J) {
141  return block_(J, J).selfadjointView<Eigen::Upper>();
142  }
143 
145  Eigen::SelfAdjointView<constBlock, Eigen::Upper> diagonalBlock(DenseIndex J) const {
146  return block_(J, J).selfadjointView<Eigen::Upper>();
147  }
148 
150  Vector diagonal(DenseIndex J) const {
151  return block_(J, J).diagonal();
152  }
153 
155  constBlock aboveDiagonalBlock(DenseIndex I, DenseIndex J) const {
156  assert(I < J);
157  return block_(I, J);
158  }
159 
161  Eigen::SelfAdjointView<constBlock, Eigen::Upper> selfadjointView(
162  DenseIndex I, DenseIndex J) const {
163  assert(J > I);
164  return block_(I, I, J - I, J - I).selfadjointView<Eigen::Upper>();
165  }
166 
168  Eigen::TriangularView<constBlock, Eigen::Upper> triangularView(DenseIndex I,
169  DenseIndex J) const {
170  assert(J > I);
171  return block_(I, I, J - I, J - I).triangularView<Eigen::Upper>();
172  }
173 
175  constBlock aboveDiagonalRange(DenseIndex i_startBlock,
176  DenseIndex i_endBlock,
177  DenseIndex j_startBlock,
178  DenseIndex j_endBlock) const {
179  assert(i_startBlock < j_startBlock);
180  assert(i_endBlock <= j_startBlock);
181  return block_(i_startBlock, j_startBlock, i_endBlock - i_startBlock,
182  j_endBlock - j_startBlock);
183  }
184 
186  Block aboveDiagonalRange(DenseIndex i_startBlock, DenseIndex i_endBlock,
187  DenseIndex j_startBlock, DenseIndex j_endBlock) {
188  assert(i_startBlock < j_startBlock);
189  assert(i_endBlock <= j_startBlock);
190  return block_(i_startBlock, j_startBlock, i_endBlock - i_startBlock,
191  j_endBlock - j_startBlock);
192  }
193 
197 
199  template <typename XprType>
200  void setDiagonalBlock(DenseIndex I, const XprType& xpr) {
201  block_(I, I).triangularView<Eigen::Upper>() = xpr.template triangularView<Eigen::Upper>();
202  }
203 
205  template <typename XprType>
206  void setOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType& xpr) {
207  assert(I != J);
208  if (I < J) {
209  block_(I, J) = xpr;
210  } else {
211  block_(J, I) = xpr.transpose();
212  }
213  }
214 
216  template <typename XprType>
217  void updateDiagonalBlock(DenseIndex I, const XprType& xpr) {
218  // TODO(gareth): Eigen won't let us add triangular or self-adjoint views
219  // here, so we do it manually.
220  auto dest = block_(I, I);
221  assert(dest.rows() == xpr.rows());
222  assert(dest.cols() == xpr.cols());
223  for (DenseIndex col = 0; col < dest.cols(); ++col) {
224  for (DenseIndex row = 0; row <= col; ++row) {
225  dest(row, col) += xpr(row, col);
226  }
227  }
228  }
229 
232  template <typename XprType>
233  void updateOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType& xpr) {
234  assert(I != J);
235  if (I < J) {
236  block_(I, J).noalias() += xpr;
237  } else {
238  block_(J, I).noalias() += xpr.transpose();
239  }
240  }
241 
245 
247  Eigen::SelfAdjointView<Block, Eigen::Upper> selfadjointView() {
248  return full().selfadjointView<Eigen::Upper>();
249  }
250 
252  Eigen::SelfAdjointView<constBlock, Eigen::Upper> selfadjointView() const {
253  return full().selfadjointView<Eigen::Upper>();
254  }
255 
257  template <typename XprType>
258  void setFullMatrix(const XprType& xpr) {
259  full().triangularView<Eigen::Upper>() = xpr.template triangularView<Eigen::Upper>();
260  }
261 
263  void setZero() {
264  full().triangularView<Eigen::Upper>().setZero();
265  }
266 
268  void negate() {
269  full().triangularView<Eigen::Upper>() *= -1.0;
270  }
271 
273  void invertInPlace() {
274  const auto identity = Matrix::Identity(rows(), rows());
275  full().triangularView<Eigen::Upper>() =
276  selfadjointView()
277  .llt()
278  .solve(identity)
279  .triangularView<Eigen::Upper>();
280  }
281 
283 
287  DenseIndex& blockStart() { return blockStart_; }
288 
291  DenseIndex blockStart() const { return blockStart_; }
292 
303  void choleskyPartial(DenseIndex nFrontals);
304 
311 
312  protected:
313 
316  return variableColOffsets_.size();
317  }
318 
321  return nOffsets() - 1;
322  }
323 
325  DenseIndex offset(DenseIndex block) const {
326  assert(block >= 0);
327  const DenseIndex actual_index = block + blockStart();
328  assert(actual_index < nOffsets());
329  return variableColOffsets_[actual_index];
330  }
331 
333  constBlock block_(DenseIndex iBlock, DenseIndex jBlock,
334  DenseIndex blockRows = 1, DenseIndex blockCols = 1) const {
335  const std::array<DenseIndex, 4> indices =
336  calcIndices(iBlock, jBlock, blockRows, blockCols);
337  return matrix_.block(indices[0], indices[1], indices[2], indices[3]);
338  }
339 
341  Block block_(DenseIndex iBlock, DenseIndex jBlock, DenseIndex blockRows = 1,
342  DenseIndex blockCols = 1) {
343  const std::array<DenseIndex, 4> indices =
344  calcIndices(iBlock, jBlock, blockRows, blockCols);
345  return matrix_.block(indices[0], indices[1], indices[2], indices[3]);
346  }
347 
349  constBlock full() const {
350  return block_(0, 0, nBlocks(), nBlocks());
351  }
352 
354  Block full() {
355  return block_(0, 0, nBlocks(), nBlocks());
356  }
357 
359  std::array<DenseIndex, 4> calcIndices(DenseIndex iBlock, DenseIndex jBlock,
360  DenseIndex blockRows,
361  DenseIndex blockCols) const {
362  assert(blockRows >= 0);
363  assert(blockCols >= 0);
364 
365  // adjust indices to account for start and size of blocks
366  const DenseIndex denseI = offset(iBlock);
367  const DenseIndex denseJ = offset(jBlock);
368  const DenseIndex denseRows = offset(iBlock + blockRows) - denseI;
369  const DenseIndex denseCols = offset(jBlock + blockCols) - denseJ;
370  return {{denseI, denseJ, denseRows, denseCols}};
371  }
372 
373  void assertInvariants() const
374  {
375  assert(matrix_.rows() == matrix_.cols());
376  assert(matrix_.cols() == variableColOffsets_.back());
377  assert(blockStart_ < (DenseIndex)variableColOffsets_.size());
378  }
379 
380  template<typename ITERATOR>
381  void fillOffsets(ITERATOR firstBlockDim, ITERATOR lastBlockDim, bool appendOneDimension)
382  {
383  variableColOffsets_.resize((lastBlockDim-firstBlockDim) + 1 + (appendOneDimension ? 1 : 0));
384  variableColOffsets_[0] = 0;
385  DenseIndex j=0;
386  for(ITERATOR dim=firstBlockDim; dim!=lastBlockDim; ++dim) {
387  variableColOffsets_[j+1] = variableColOffsets_[j] + *dim;
388  ++ j;
389  }
390  if(appendOneDimension)
391  {
392  variableColOffsets_[j+1] = variableColOffsets_[j] + 1;
393  ++ j;
394  }
395  }
396 
397  friend class VerticalBlockMatrix;
398  template<typename SymmetricBlockMatrixType> friend class SymmetricBlockMatrixBlockExpr;
399 
400  private:
402  friend class boost::serialization::access;
403  template<class ARCHIVE>
404  void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
405  // Fill in the lower triangle part of the matrix, so boost::serialization won't
406  // complain about uninitialized data with an input_stream_error exception
407  // http://www.boost.org/doc/libs/1_37_0/libs/serialization/doc/exceptions.html#stream_error
408  matrix_.triangularView<Eigen::Lower>() = matrix_.triangularView<Eigen::Upper>().transpose();
409  ar & BOOST_SERIALIZATION_NVP(matrix_);
410  ar & BOOST_SERIALIZATION_NVP(variableColOffsets_);
411  ar & BOOST_SERIALIZATION_NVP(blockStart_);
412  }
413  };
414 
416  class CholeskyFailed;
417 
418 }
419 
DenseIndex getDim(DenseIndex block) const
Number of dimensions for variable on this diagonal block.
Definition: SymmetricBlockMatrix.h:128
constBlock aboveDiagonalBlock(DenseIndex I, DenseIndex J) const
Get block above the diagonal (I, J).
Definition: SymmetricBlockMatrix.h:155
ptrdiff_t DenseIndex
The index type for Eigen objects.
Definition: types.h:63
A thin wrapper around std::vector that uses a custom allocator.
DenseIndex cols() const
Column size.
Definition: SymmetricBlockMatrix.h:122
FastVector< DenseIndex > variableColOffsets_
the starting columns of each block (0-based)
Definition: SymmetricBlockMatrix.h:60
Block full()
Get the full matrix as a block.
Definition: SymmetricBlockMatrix.h:354
SymmetricBlockMatrix(const CONTAINER &dimensions, bool appendOneDimension=false)
Construct from a container of the sizes of each block.
Definition: SymmetricBlockMatrix.h:75
DenseIndex rows() const
Row size.
Definition: SymmetricBlockMatrix.h:119
void split(const G &g, const PredecessorMap< KEY > &tree, G &Ab1, G &Ab2)
Split the graph into two parts: one corresponds to the given spanning tree, and the other corresponds...
Definition: graph-inl.h:255
Eigen::SelfAdjointView< constBlock, Eigen::Upper > selfadjointView() const
Get self adjoint view.
Definition: SymmetricBlockMatrix.h:252
void setOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType &xpr)
Set an off-diagonal block. Only the upper triangular portion of xpr is evaluated.
Definition: SymmetricBlockMatrix.h:206
constBlock block_(DenseIndex iBlock, DenseIndex jBlock, DenseIndex blockRows=1, DenseIndex blockCols=1) const
Get an arbitrary block from the matrix. Indices are in block units.
Definition: SymmetricBlockMatrix.h:333
DenseIndex offset(DenseIndex block) const
Get an offset for a block index (in the active view).
Definition: SymmetricBlockMatrix.h:325
Eigen::TriangularView< constBlock, Eigen::Upper > triangularView(DenseIndex I, DenseIndex J) const
Return the square sub-matrix that contains blocks(i:j, i:j) as a triangular view.
Definition: SymmetricBlockMatrix.h:168
Definition: VerticalBlockMatrix.h:41
Eigen::SelfAdjointView< constBlock, Eigen::Upper > selfadjointView(DenseIndex I, DenseIndex J) const
Return the square sub-matrix that contains blocks(i:j, i:j).
Definition: SymmetricBlockMatrix.h:161
void setDiagonalBlock(DenseIndex I, const XprType &xpr)
Set a diagonal block. Only the upper triangular portion of xpr is evaluated.
Definition: SymmetricBlockMatrix.h:200
SymmetricBlockMatrix()
Construct from an empty matrix (asserts that the matrix is empty)
Definition: SymmetricBlockMatrix.h:66
constBlock aboveDiagonalRange(DenseIndex i_startBlock, DenseIndex i_endBlock, DenseIndex j_startBlock, DenseIndex j_endBlock) const
Get a range [i,j) from the matrix. Indices are in block units.
Definition: SymmetricBlockMatrix.h:175
std::array< DenseIndex, 4 > calcIndices(DenseIndex iBlock, DenseIndex jBlock, DenseIndex blockRows, DenseIndex blockCols) const
Compute the indices into the underlying matrix for a given block.
Definition: SymmetricBlockMatrix.h:359
void negate()
Negate the entire active matrix.
Definition: SymmetricBlockMatrix.h:268
Eigen::SelfAdjointView< Block, Eigen::Upper > diagonalBlock(DenseIndex J)
Return the J'th diagonal block as a self adjoint view.
Definition: SymmetricBlockMatrix.h:140
Eigen::SelfAdjointView< constBlock, Eigen::Upper > diagonalBlock(DenseIndex J) const
Return the J'th diagonal block as a self adjoint view.
Definition: SymmetricBlockMatrix.h:145
DenseIndex nOffsets() const
Number of offsets in the full matrix.
Definition: SymmetricBlockMatrix.h:315
void setZero()
Set the entire active matrix zero.
Definition: SymmetricBlockMatrix.h:263
Block block_(DenseIndex iBlock, DenseIndex jBlock, DenseIndex blockRows=1, DenseIndex blockCols=1)
Get an arbitrary block from the matrix. Indices are in block units.
Definition: SymmetricBlockMatrix.h:341
Matrix matrix_
The full matrix.
Definition: SymmetricBlockMatrix.h:59
DenseIndex blockStart() const
Retrieve the first logical block, i.e.
Definition: SymmetricBlockMatrix.h:291
constBlock full() const
Get the full matrix as a block.
Definition: SymmetricBlockMatrix.h:349
Vector diagonal(DenseIndex J) const
Get the diagonal of the J'th diagonal block.
Definition: SymmetricBlockMatrix.h:150
DenseIndex nActualBlocks() const
Number of actual blocks in the full matrix.
Definition: SymmetricBlockMatrix.h:320
Definition: SymmetricBlockMatrix.h:51
Global functions in a separate testing namespace.
Definition: chartTesting.h:28
DenseIndex & blockStart()
Retrieve or modify the first logical block, i.e.
Definition: SymmetricBlockMatrix.h:287
Block aboveDiagonalRange(DenseIndex i_startBlock, DenseIndex i_endBlock, DenseIndex j_startBlock, DenseIndex j_endBlock)
Get a range [i,j) from the matrix. Indices are in block units.
Definition: SymmetricBlockMatrix.h:186
DenseIndex blockStart_
Changes apparent matrix view, see main class comment.
Definition: SymmetricBlockMatrix.h:62
typedef and functions to augment Eigen's MatrixXd
void updateOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType &xpr)
Update an off diagonal block.
Definition: SymmetricBlockMatrix.h:233
const MATRIX::ConstRowXpr row(const MATRIX &A, size_t j)
Extracts a row view from a matrix that avoids a copy.
Definition: Matrix.h:224
void setFullMatrix(const XprType &xpr)
Set the entire active matrix. Only reads the upper triangular part of xpr.
Definition: SymmetricBlockMatrix.h:258
DenseIndex nBlocks() const
Block count.
Definition: SymmetricBlockMatrix.h:125
void invertInPlace()
Invert the entire active matrix in place.
Definition: SymmetricBlockMatrix.h:273
Eigen::SelfAdjointView< Block, Eigen::Upper > selfadjointView()
Get self adjoint view.
Definition: SymmetricBlockMatrix.h:247
void updateDiagonalBlock(DenseIndex I, const XprType &xpr)
Increment the diagonal block by the values in xpr. Only reads the upper triangular part of xpr.
Definition: SymmetricBlockMatrix.h:217
SymmetricBlockMatrix(ITERATOR firstBlockDim, ITERATOR lastBlockDim, bool appendOneDimension=false)
Construct from iterator over the sizes of each vertical block.
Definition: SymmetricBlockMatrix.h:85
bool choleskyPartial(Matrix &ABC, size_t nFrontal, size_t topleft)
Partial Cholesky computes a factor [R S such that [R' 0 [R S = [A B 0 L] S' I] 0 L] B' C].
Definition: cholesky.cpp:108
Typedefs for easier changing of types.
SymmetricBlockMatrix(const CONTAINER &dimensions, const Matrix &matrix, bool appendOneDimension=false)
Construct from a container of the sizes of each vertical block and a pre-prepared matrix.
Definition: SymmetricBlockMatrix.h:95