g2o
linear_solver_cholmod_online.h
Go to the documentation of this file.
1 // g2o - General Graph Optimization
2 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
3 //
4 // g2o is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published
6 // by the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // g2o is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef G2O_LINEAR_SOLVER_CHOLMOD_ONLINE
18 #define G2O_LINEAR_SOLVER_CHOLMOD_ONLINE
19 
20 #include <camd.h>
21 
22 #include "g2o_incremental_api.h"
24 
25 namespace g2o {
26 
31 {
32  public:
33  LinearSolverCholmodOnlineInterface() : cmember(0), batchEveryN(100) {}
34  virtual int choleskyUpdate(cholmod_sparse* update) = 0;
35  virtual bool solve(double* x, double* b) = 0;
36  virtual cholmod_factor* L() const = 0;
37  virtual size_t nonZerosInL() const = 0;
38  Eigen::VectorXi* cmember;
40 };
41 
45 template <typename MatrixType>
47 {
48  public:
50  LinearSolver<MatrixType>()
51  {
52  _cholmodSparse = new CholmodExt();
53  _cholmodFactor = 0;
54  cholmod_start(&_cholmodCommon);
55 
56  // setup ordering strategy
57  _cholmodCommon.nmethods = 1 ;
58  _cholmodCommon.method[0].ordering = CHOLMOD_AMD; //CHOLMOD_COLAMD
59  //_cholmodCommon.postorder = 0;
60 
61  _cholmodCommon.supernodal = CHOLMOD_AUTO; //CHOLMOD_SUPERNODAL; //CHOLMOD_SIMPLICIAL;
62  batchEveryN = 100;
63  }
64 
66  {
67  delete _cholmodSparse;
68  if (_cholmodFactor) {
69  cholmod_free_factor(&_cholmodFactor, &_cholmodCommon);
70  _cholmodFactor = 0;
71  }
72  cholmod_finish(&_cholmodCommon);
73  }
74 
75  virtual bool init()
76  {
77  return true;
78  }
79 
80  bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
81  {
82  cholmod_free_factor(&_cholmodFactor, &_cholmodCommon);
83  _cholmodFactor = 0;
84  fillCholmodExt(A, false);
85 
86  computeSymbolicDecomposition(A);
87  assert(_cholmodFactor && "Symbolic cholesky failed");
88  double t=get_monotonic_time();
89 
90  // setting up b for calling cholmod
91  cholmod_dense bcholmod;
92  bcholmod.nrow = bcholmod.d = _cholmodSparse->nrow;
93  bcholmod.ncol = 1;
94  bcholmod.x = b;
95  bcholmod.xtype = CHOLMOD_REAL;
96  bcholmod.dtype = CHOLMOD_DOUBLE;
97 
98  cholmod_factorize(_cholmodSparse, _cholmodFactor, &_cholmodCommon);
99  if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
100  std::cerr << "solve(): Cholesky failure, writing debug.txt (Hessian loadable by Octave)" << std::endl;
101  writeCCSMatrix("debug.txt", _cholmodSparse->nrow, _cholmodSparse->ncol, (int*)_cholmodSparse->p, (int*)_cholmodSparse->i, (double*)_cholmodSparse->x, true);
102  return false;
103  }
104 
105  cholmod_dense* xcholmod = cholmod_solve(CHOLMOD_A, _cholmodFactor, &bcholmod, &_cholmodCommon);
106  memcpy(x, xcholmod->x, sizeof(double) * bcholmod.nrow); // copy back to our array
107  cholmod_free_dense(&xcholmod, &_cholmodCommon);
108 
110  if (globalStats){
111  globalStats->timeNumericDecomposition = get_monotonic_time() - t;
112  globalStats->choleskyNNZ = static_cast<size_t>(_cholmodCommon.method[0].lnz);
113  }
114 
115  return true;
116  }
117 
118  bool blockOrdering() const { return true;}
119 
120  cholmod_factor* L() const { return _cholmodFactor;}
121 
125  size_t nonZerosInL() const {
126  size_t nnz= 0;
127  int* nz = (int*)_cholmodFactor->nz;
128  if (! nz)
129  return 0;
130  for (size_t i = 0; i < _cholmodFactor->n; ++i)
131  nnz += nz[i];
132  return nnz;
133  }
134 
135  int choleskyUpdate(cholmod_sparse* update)
136  {
137  int result = cholmod_updown(1, update, _cholmodFactor, &_cholmodCommon);
138  //std::cerr << __PRETTY_FUNCTION__ << " " << result << std::endl;
139  if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
140  std::cerr << "Cholesky failure, writing debug.txt (Hessian loadable by Octave)" << std::endl;
141  writeCCSMatrix("debug.txt", _cholmodSparse->nrow, _cholmodSparse->ncol, (int*)_cholmodSparse->p, (int*)_cholmodSparse->i, (double*)_cholmodSparse->x, true);
142  return 0;
143  }
144  return result;
145  }
146 
147  bool solve(double* x, double* b)
148  {
149  // setting up b for calling cholmod
150  cholmod_dense bcholmod;
151  bcholmod.nrow = bcholmod.d = _cholmodSparse->nrow;
152  bcholmod.ncol = 1;
153  bcholmod.x = b;
154  bcholmod.xtype = CHOLMOD_REAL;
155  bcholmod.dtype = CHOLMOD_DOUBLE;
156 
157  cholmod_dense* xcholmod = cholmod_solve(CHOLMOD_A, _cholmodFactor, &bcholmod, &_cholmodCommon);
158  memcpy(x, xcholmod->x, sizeof(double) * bcholmod.nrow); // copy back to our array
159  cholmod_free_dense(&xcholmod, &_cholmodCommon);
160  return true;
161  }
162 
163  protected:
164  // temp used for cholesky with cholmod
165  cholmod_common _cholmodCommon;
167  cholmod_factor* _cholmodFactor;
170  Eigen::VectorXi _scalarPermutation, _blockPermutation;
171 
172  public:
174  {
175  double t = get_monotonic_time();
176 
177  A.fillBlockStructure(_matrixStructure);
178 
179  // get the ordering for the block matrix
180  if (_blockPermutation.size() < _matrixStructure.n) // double space if resizing
181  _blockPermutation.resize(2*_matrixStructure.n);
182 
183  int amdStatus = camd_order(_matrixStructure.n, _matrixStructure.Ap, _matrixStructure.Aii, _blockPermutation.data(), NULL, NULL, cmember->data());
184  if (amdStatus != CAMD_OK) {
185  std::cerr << "Error while computing ordering" << std::endl;
186  }
187 
188  // blow up the permutation to the scalar matrix and extend to include the additional blocks
189  if (_scalarPermutation.size() == 0)
190  _scalarPermutation.resize(_cholmodSparse->ncol);
191  if (_scalarPermutation.size() < (int)_cholmodSparse->ncol)
192  _scalarPermutation.resize(2*_cholmodSparse->ncol);
193  size_t scalarIdx = 0;
194  for (int i = 0; i < _matrixStructure.n; ++i) {
195  const int& p = _blockPermutation(i);
196  int base = A.colBaseOfBlock(p);
197  int nCols = A.colsOfBlock(p);
198  for (int j = 0; j < nCols; ++j)
199  _scalarPermutation(scalarIdx++) = base++;
200  }
201 
202  for (; scalarIdx < _cholmodSparse->ncol; ++scalarIdx) // extending for the additional blocks
203  _scalarPermutation(scalarIdx) = scalarIdx;
204  assert(scalarIdx == _cholmodSparse->ncol);
205 
206  // apply the ordering
207  _cholmodCommon.nmethods = 1;
208  _cholmodCommon.method[0].ordering = CHOLMOD_GIVEN;
209  _cholmodFactor = cholmod_analyze_p(_cholmodSparse, _scalarPermutation.data(), NULL, 0, &_cholmodCommon);
210 
212  if (globalStats)
213  globalStats->timeSymbolicDecomposition = get_monotonic_time() - t;
214 
215  }
216 
217  void fillCholmodExt(const SparseBlockMatrix<MatrixType>& A, bool onlyValues)
218  {
219  int blockDimension = MatrixType::RowsAtCompileTime;
220  assert(blockDimension > 0);
221  //size_t origM = A.rows();
222  size_t origN = A.cols();
223  int additionalSpace = blockDimension * batchEveryN;
224  size_t m = A.rows() + additionalSpace;
225  size_t n = A.cols() + additionalSpace;
226 
227  if (_cholmodSparse->columnsAllocated < n) {
228  //std::cerr << __PRETTY_FUNCTION__ << ": reallocating columns" << std::endl;
229  _cholmodSparse->columnsAllocated = _cholmodSparse->columnsAllocated == 0 ? n : 2 * n; // pre-allocate more space if re-allocating
230  delete[] (int*)_cholmodSparse->p;
231  _cholmodSparse->p = new int[_cholmodSparse->columnsAllocated+1];
232  }
233  if (! onlyValues) {
234  size_t nzmax = A.nonZeros() + additionalSpace;
235  if (_cholmodSparse->nzmax < nzmax) {
236  //std::cerr << __PRETTY_FUNCTION__ << ": reallocating row + values" << std::endl;
237  _cholmodSparse->nzmax = _cholmodSparse->nzmax == 0 ? nzmax : 2 * nzmax; // pre-allocate more space if re-allocating
238  delete[] (double*)_cholmodSparse->x;
239  delete[] (int*)_cholmodSparse->i;
240  _cholmodSparse->i = new int[_cholmodSparse->nzmax];
241  _cholmodSparse->x = new double[_cholmodSparse->nzmax];
242  }
243  }
244  _cholmodSparse->ncol = n;
245  _cholmodSparse->nrow = m;
246 
247  int nz = 0;
248  if (onlyValues)
249  nz = A.fillCCS((double*)_cholmodSparse->x, true);
250  else
251  nz = A.fillCCS((int*)_cholmodSparse->p, (int*)_cholmodSparse->i, (double*)_cholmodSparse->x, true);
252 
253  int* cp = (int*)_cholmodSparse->p;
254  int* ci = (int*)_cholmodSparse->i;
255  double* cx = (double*)_cholmodSparse->x;
256 
257  cp = &cp[origN];
258  ci = &ci[nz];
259  cx = &cx[nz];
260 
261  // diagonal for the next blocks
262  for (int i = 0; i < additionalSpace; ++i) {
263  *cp++ = nz;
264  *ci++ = origN + i;
265  *cx++ = 1e-6;
266  ++nz;
267  }
268  *cp = nz;
269 
270  }
271 
272 };
273 
274 } // end namespace
275 
276 #endif
double get_monotonic_time()
Definition: timeutil.cpp:113
statistics about the optimization
Definition: batch_stats.h:40
int choleskyUpdate(cholmod_sparse *update)
int cols() const
columns of the matrix
int * Ap
column pointers for A, of size n+1
int n
A is m-by-n. n must be >= 0.
bool solve(const SparseBlockMatrix< MatrixType > &A, double *x, double *b)
int rows() const
rows of the matrix
representing the structure of a matrix in column compressed structure (only the upper triangular part...
bool writeCCSMatrix(const string &filename, int rows, int cols, const int *Ap, const int *Ai, const double *Ax, bool upperTriangleSymmetric)
void fillCholmodExt(const SparseBlockMatrix< MatrixType > &A, bool onlyValues)
int colsOfBlock(int c) const
how many cols does the block at block-col c has?
size_t nonZeros() const
number of non-zero elements
Our extension of the CHOLMOD matrix struct.
void fillBlockStructure(MatrixStructure &ms) const
exports the non zero blocks in the structure matrix ms
size_t choleskyNNZ
number of non-zeros in the cholesky factor
Definition: batch_stats.h:71
#define G2O_INCREMENTAL_API
basic solver for Ax = b
Definition: linear_solver.h:41
if(POLICY CMP0020) cmake_policy(SET CMP0020 OLD) endif() if(Qt4_FOUND) endif() if(Qt5_FOUND) endif() ADD_LIBRARY(viewer_library $
Definition: CMakeLists.txt:2
generic interface for the online solver
static G2OBatchStatistics * globalStats()
Definition: batch_stats.h:73
double timeNumericDecomposition
numeric decomposition (0 if not done)
Definition: batch_stats.h:58
int colBaseOfBlock(int c) const
where does the col at block-col r starts?
int * Aii
row indices of A, of size nz = Ap [n]
void computeSymbolicDecomposition(const SparseBlockMatrix< MatrixType > &A)
linear solver which allows to update the cholesky factor
Sparse matrix which uses blocks.
int fillCCS(int *Cp, int *Ci, double *Cx, bool upperTriangle=false) const