g2o
linear_solver_eigen.h
Go to the documentation of this file.
1 // g2o - General Graph Optimization
2 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 #ifndef G2O_LINEAR_SOLVER_EIGEN_H
28 #define G2O_LINEAR_SOLVER_EIGEN_H
29 
30 #include <Eigen/Sparse>
31 #include <Eigen/SparseCholesky>
32 
33 #include "g2o/core/linear_solver.h"
34 #include "g2o/core/batch_stats.h"
35 #include "g2o/stuff/timeutil.h"
36 
37 #include <iostream>
38 #include <vector>
39 
40 namespace g2o {
41 
48 template <typename MatrixType>
49 class LinearSolverEigen: public LinearSolver<MatrixType>
50 {
51  public:
52  typedef Eigen::SparseMatrix<double, Eigen::ColMajor> SparseMatrix;
53  typedef Eigen::Triplet<double> Triplet;
54  typedef Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> PermutationMatrix;
58  class CholeskyDecomposition : public Eigen::SimplicialLDLT<SparseMatrix, Eigen::Upper>
59  {
60  public:
61  CholeskyDecomposition() : Eigen::SimplicialLDLT<SparseMatrix, Eigen::Upper>() {}
62  using Eigen::SimplicialLDLT< SparseMatrix, Eigen::Upper>::analyzePattern_preordered;
63 
64  void analyzePatternWithPermutation(SparseMatrix& a, const PermutationMatrix& permutation)
65  {
66  m_Pinv = permutation;
67  m_P = permutation.inverse();
68  int size = a.cols();
69  SparseMatrix ap(size, size);
70  ap.selfadjointView<Eigen::Upper>() = a.selfadjointView<UpLo>().twistedBy(m_P);
71  analyzePattern_preordered(ap, true);
72  }
73  };
74 
75  public:
77  LinearSolver<MatrixType>(),
78  _init(true), _blockOrdering(false), _writeDebug(false)
79  {
80  }
81 
83  {
84  }
85 
86  virtual bool init()
87  {
88  _init = true;
89  return true;
90  }
91 
92  bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
93  {
94  if (_init)
95  _sparseMatrix.resize(A.rows(), A.cols());
97  if (_init) // compute the symbolic composition once
99  _init = false;
100 
101  double t=get_monotonic_time();
102  _cholesky.factorize(_sparseMatrix);
103  if (_cholesky.info() != Eigen::Success) { // the matrix is not positive definite
104  if (_writeDebug) {
105  std::cerr << "Cholesky failure, writing debug.txt (Hessian loadable by Octave)" << std::endl;
106  A.writeOctave("debug.txt");
107  }
108  return false;
109  }
110 
111  // Solving the system
112  VectorXD::MapType xx(x, _sparseMatrix.cols());
113  VectorXD::ConstMapType bb(b, _sparseMatrix.cols());
114  xx = _cholesky.solve(bb);
116  if (globalStats) {
117  globalStats->timeNumericDecomposition = get_monotonic_time() - t;
118  globalStats->choleskyNNZ = _cholesky.matrixL().nestedExpression().nonZeros() + _sparseMatrix.cols(); // the elements of D
119  }
120 
121  return true;
122  }
123 
125  bool blockOrdering() const { return _blockOrdering;}
127 
129  virtual bool writeDebug() const { return _writeDebug;}
130  virtual void setWriteDebug(bool b) { _writeDebug = b;}
131 
132  protected:
133  bool _init;
136  SparseMatrix _sparseMatrix;
138 
146  {
147  double t=get_monotonic_time();
148  if (! _blockOrdering) {
149  _cholesky.analyzePattern(_sparseMatrix);
150  } else {
151  // block ordering with the Eigen Interface
152  // This is really ugly currently, as it calls internal functions from Eigen
153  // and modifies the SparseMatrix class
154  Eigen::PermutationMatrix<Eigen::Dynamic,Eigen::Dynamic> blockP;
155  {
156  // prepare a block structure matrix for calling AMD
157  std::vector<Triplet> triplets;
158  for (size_t c = 0; c < A.blockCols().size(); ++c){
159  const typename SparseBlockMatrix<MatrixType>::IntBlockMap& column = A.blockCols()[c];
160  for (typename SparseBlockMatrix<MatrixType>::IntBlockMap::const_iterator it = column.begin(); it != column.end(); ++it) {
161  const int& r = it->first;
162  if (r > static_cast<int>(c)) // only upper triangle
163  break;
164  triplets.push_back(Triplet(r, c, 0.));
165  }
166  }
167 
168  // call the AMD ordering on the block matrix.
169  // Relies on Eigen's internal stuff, probably bad idea
170  SparseMatrix auxBlockMatrix(A.blockCols().size(), A.blockCols().size());
171  auxBlockMatrix.setFromTriplets(triplets.begin(), triplets.end());
172  typename CholeskyDecomposition::CholMatrixType C;
173  C = auxBlockMatrix.selfadjointView<Eigen::Upper>();
174  Eigen::internal::minimum_degree_ordering(C, blockP);
175  }
176 
177  int rows = A.rows();
178  assert(rows == A.cols() && "Matrix A is not square");
179 
180  // Adapt the block permutation to the scalar matrix
181  PermutationMatrix scalarP;
182  scalarP.resize(rows);
183  int scalarIdx = 0;
184  for (int i = 0; i < blockP.size(); ++i) {
185  const int& p = blockP.indices()(i);
186  int base = A.colBaseOfBlock(p);
187  int nCols = A.colsOfBlock(p);
188  for (int j = 0; j < nCols; ++j)
189  scalarP.indices()(scalarIdx++) = base++;
190  }
191  assert(scalarIdx == rows && "did not completely fill the permutation matrix");
192  // analyze with the scalar permutation
193  _cholesky.analyzePatternWithPermutation(_sparseMatrix, scalarP);
194 
195  }
197  if (globalStats)
198  globalStats->timeSymbolicDecomposition = get_monotonic_time() - t;
199  }
200 
201  void fillSparseMatrix(const SparseBlockMatrix<MatrixType>& A, bool onlyValues)
202  {
203  if (onlyValues) {
204  A.fillCCS(_sparseMatrix.valuePtr(), true);
205  } else {
206 
207  // create from triplet structure
208  std::vector<Triplet> triplets;
209  triplets.reserve(A.nonZeros());
210  for (size_t c = 0; c < A.blockCols().size(); ++c) {
211  int colBaseOfBlock = A.colBaseOfBlock(c);
212  const typename SparseBlockMatrix<MatrixType>::IntBlockMap& column = A.blockCols()[c];
213  for (typename SparseBlockMatrix<MatrixType>::IntBlockMap::const_iterator it = column.begin(); it != column.end(); ++it) {
214  int rowBaseOfBlock = A.rowBaseOfBlock(it->first);
215  const MatrixType& m = *(it->second);
216  for (int cc = 0; cc < m.cols(); ++cc) {
217  int aux_c = colBaseOfBlock + cc;
218  for (int rr = 0; rr < m.rows(); ++rr) {
219  int aux_r = rowBaseOfBlock + rr;
220  if (aux_r > aux_c)
221  break;
222  triplets.push_back(Triplet(aux_r, aux_c, m(rr, cc)));
223  }
224  }
225  }
226  }
227  _sparseMatrix.setFromTriplets(triplets.begin(), triplets.end());
228 
229  }
230  }
231 };
232 
233 } // end namespace
234 
235 #endif
double get_monotonic_time()
Definition: timeutil.cpp:113
statistics about the optimization
Definition: batch_stats.h:40
int cols() const
columns of the matrix
Eigen::SparseMatrix< double, Eigen::ColMajor > SparseMatrix
bool writeOctave(const char *filename, bool upperTriangle=true) const
int rows() const
rows of the matrix
Sub-classing Eigen&#39;s SimplicialLDLT to perform ordering with a given ordering.
void computeSymbolicDecomposition(const SparseBlockMatrix< MatrixType > &A)
int colsOfBlock(int c) const
how many cols does the block at block-col c has?
Eigen::Triplet< double > Triplet
size_t nonZeros() const
number of non-zero elements
Eigen::PermutationMatrix< Eigen::Dynamic, Eigen::Dynamic > PermutationMatrix
size_t choleskyNNZ
number of non-zeros in the cholesky factor
Definition: batch_stats.h:71
utility functions for handling time related stuff
bool blockOrdering() const
do the AMD ordering on the blocks or on the scalar matrix
basic solver for Ax = b
Definition: linear_solver.h:41
linear solver which uses the sparse Cholesky solver from Eigen
static G2OBatchStatistics * globalStats()
Definition: batch_stats.h:73
double timeNumericDecomposition
numeric decomposition (0 if not done)
Definition: batch_stats.h:58
std::map< int, SparseMatrixBlock * > IntBlockMap
int colBaseOfBlock(int c) const
where does the col at block-col r starts?
const std::vector< IntBlockMap > & blockCols() const
the block matrices per block-column
void fillSparseMatrix(const SparseBlockMatrix< MatrixType > &A, bool onlyValues)
bool solve(const SparseBlockMatrix< MatrixType > &A, double *x, double *b)
double timeSymbolicDecomposition
symbolic decomposition (0 if not done)
Definition: batch_stats.h:57
virtual bool writeDebug() const
write a debug dump of the system matrix if it is not SPD in solve
void setBlockOrdering(bool blockOrdering)
CholeskyDecomposition _cholesky
int rowBaseOfBlock(int r) const
where does the row at block-row r starts?
Sparse matrix which uses blocks.
int fillCCS(int *Cp, int *Ci, double *Cx, bool upperTriangle=false) const
void analyzePatternWithPermutation(SparseMatrix &a, const PermutationMatrix &permutation)
virtual void setWriteDebug(bool b)