g2o
linear_solver_pcg.hpp
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 // helpers for doing fixed or variable size operations on the matrices
28 
29 namespace internal {
30 
31 #ifdef _MSC_VER
32  // MSVC does not like the template specialization, seems like MSVC applies type conversion
33  // which results in calling a fixed size method (segment<int>) on the dynamically sized matrices
34  template<typename MatrixType>
35  void pcg_axy(const MatrixType& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
36  {
37  y.segment(yoff, A.rows()) = A * x.segment(xoff, A.cols());
38  }
39 #else
40  template<typename MatrixType>
41  inline void pcg_axy(const MatrixType& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
42  {
43  y.segment<MatrixType::RowsAtCompileTime>(yoff) = A * x.segment<MatrixType::ColsAtCompileTime>(xoff);
44  }
45 
46  template<>
47  inline void pcg_axy(const MatrixXD& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
48  {
49  y.segment(yoff, A.rows()) = A * x.segment(xoff, A.cols());
50  }
51 #endif
52 
53  template<typename MatrixType>
54  inline void pcg_axpy(const MatrixType& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
55  {
56  y.segment<MatrixType::RowsAtCompileTime>(yoff) += A * x.segment<MatrixType::ColsAtCompileTime>(xoff);
57  }
58 
59  template<>
60  inline void pcg_axpy(const MatrixXD& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
61  {
62  y.segment(yoff, A.rows()) += A * x.segment(xoff, A.cols());
63  }
64 
65  template<typename MatrixType>
66  inline void pcg_atxpy(const MatrixType& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
67  {
68  y.segment<MatrixType::ColsAtCompileTime>(yoff) += A.transpose() * x.segment<MatrixType::RowsAtCompileTime>(xoff);
69  }
70 
71  template<>
72  inline void pcg_atxpy(const MatrixXD& A, const VectorXD& x, int xoff, VectorXD& y, int yoff)
73  {
74  y.segment(yoff, A.cols()) += A.transpose() * x.segment(xoff, A.rows());
75  }
76 }
77 // helpers end
78 
79 template <typename MatrixType>
80 bool LinearSolverPCG<MatrixType>::solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
81 {
82  const bool indexRequired = _indices.size() == 0;
83  _diag.clear();
84  _J.clear();
85 
86  // put the block matrix once in a linear structure, makes mult faster
87  int colIdx = 0;
88  for (size_t i = 0; i < A.blockCols().size(); ++i){
89  const typename SparseBlockMatrix<MatrixType>::IntBlockMap& col = A.blockCols()[i];
90  if (col.size() > 0) {
91  typename SparseBlockMatrix<MatrixType>::IntBlockMap::const_iterator it;
92  for (it = col.begin(); it != col.end(); ++it) {
93  if (it->first == (int)i) { // only the upper triangular block is needed
94  _diag.push_back(it->second);
95  _J.push_back(it->second->inverse());
96  break;
97  }
98  if (indexRequired) {
99  _indices.push_back(std::make_pair(it->first > 0 ? A.rowBlockIndices()[it->first-1] : 0, colIdx));
100  _sparseMat.push_back(it->second);
101  }
102 
103  }
104  }
105  colIdx = A.colBlockIndices()[i];
106  }
107 
108  int n = A.rows();
109  assert(n > 0 && "Hessian has 0 rows/cols");
110  Eigen::Map<VectorXD> xvec(x, A.cols());
111  const Eigen::Map<VectorXD> bvec(b, n);
112  xvec.setZero();
113 
114  VectorXD r, d, q, s;
115  d.setZero(n);
116  q.setZero(n);
117  s.setZero(n);
118 
119  r = bvec;
120  multDiag(A.colBlockIndices(), _J, r, d);
121  double dn = r.dot(d);
122  double d0 = _tolerance * dn;
123 
124  if (_absoluteTolerance) {
125  if (_residual > 0.0 && _residual > d0)
126  d0 = _residual;
127  }
128 
129  int maxIter = _maxIter < 0 ? A.rows() : _maxIter;
130 
131  int iteration;
132  for (iteration = 0; iteration < maxIter; ++iteration) {
133  if (_verbose)
134  std::cerr << "residual[" << iteration << "]: " << dn << std::endl;
135  if (dn <= d0)
136  break; // done
137  mult(A.colBlockIndices(), d, q);
138  double a = dn / d.dot(q);
139  xvec += a*d;
140  // TODO: reset residual here every 50 iterations
141  r -= a*q;
142  multDiag(A.colBlockIndices(), _J, r, s);
143  double dold = dn;
144  dn = r.dot(s);
145  double ba = dn / dold;
146  d = s + ba*d;
147  }
148  //std::cerr << "residual[" << iteration << "]: " << dn << std::endl;
149  _residual = 0.5 * dn;
150  G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
151  if (globalStats) {
152  globalStats->iterationsLinearSolver = iteration;
153  }
154 
155  return true;
156 }
157 
158 template <typename MatrixType>
159 void LinearSolverPCG<MatrixType>::multDiag(const std::vector<int>& colBlockIndices, MatrixVector& A, const VectorXD& src, VectorXD& dest)
160 {
161  int row = 0;
162  for (size_t i = 0; i < A.size(); ++i) {
163  internal::pcg_axy(A[i], src, row, dest, row);
164  row = colBlockIndices[i];
165  }
166 }
167 
168 template <typename MatrixType>
169 void LinearSolverPCG<MatrixType>::multDiag(const std::vector<int>& colBlockIndices, MatrixPtrVector& A, const VectorXD& src, VectorXD& dest)
170 {
171  int row = 0;
172  for (size_t i = 0; i < A.size(); ++i) {
173  internal::pcg_axy(*A[i], src, row, dest, row);
174  row = colBlockIndices[i];
175  }
176 }
177 
178 template <typename MatrixType>
179 void LinearSolverPCG<MatrixType>::mult(const std::vector<int>& colBlockIndices, const VectorXD& src, VectorXD& dest)
180 {
181  // first multiply with the diagonal
182  multDiag(colBlockIndices, _diag, src, dest);
183 
184  // now multiply with the upper triangular block
185  for (size_t i = 0; i < _sparseMat.size(); ++i) {
186  const int& srcOffset = _indices[i].second;
187  const int& destOffsetT = srcOffset;
188  const int& destOffset = _indices[i].first;
189  const int& srcOffsetT = destOffset;
190 
191  const typename SparseBlockMatrix<MatrixType>::SparseMatrixBlock* a = _sparseMat[i];
192  // destVec += *a * srcVec (according to the sub-vector parts)
193  internal::pcg_axpy(*a, src, srcOffset, dest, destOffset);
194  // destVec += *a.transpose() * srcVec (according to the sub-vector parts)
195  internal::pcg_atxpy(*a, src, srcOffsetT, dest, destOffsetT);
196  }
197 }
void pcg_axpy(const MatrixType &A, const VectorXD &x, int xoff, VectorXD &y, int yoff)
Eigen::Matrix< double, Eigen::Dynamic, 1, Eigen::ColMajor > VectorXD
Definition: eigen_types.h:48
void pcg_atxpy(const MatrixType &A, const VectorXD &x, int xoff, VectorXD &y, int yoff)
void pcg_axy(const MatrixType &A, const VectorXD &x, int xoff, VectorXD &y, int yoff)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > MatrixXD
Definition: eigen_types.h:63