g2o
linear_solver_csparse.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_SOLVERCSPARSE_H
28 #define G2O_LINEAR_SOLVERCSPARSE_H
29 
30 #include "csparse_helper.h"
31 
32 #include "g2o/core/linear_solver.h"
33 #include "g2o/core/batch_stats.h"
35 #include "g2o/stuff/timeutil.h"
36 #include "g2o_csparse_api.h"
37 
38 #include <iostream>
39 
40 namespace g2o {
41 
46 {
48  {
49  nzmax = 0;
50  m = 0;
51  n = 0;
52  p = 0;
53  i = 0;
54  x = 0;
55  nz = 0;
56  columnsAllocated = 0;
57  }
59  {
60  delete[] p;
61  delete[] i;
62  delete[] x;
63  }
65 };
66 
70 template <typename MatrixType>
71 class LinearSolverCSparse : public LinearSolverCCS<MatrixType>
72 {
73  public:
75  LinearSolverCCS<MatrixType>()
76  {
77  _symbolicDecomposition = 0;
78  _csWorkspaceSize = -1;
79  _csWorkspace = 0;
80  _csIntWorkspace = 0;
81  _ccsA = new CSparseExt;
82  _blockOrdering = true;
83  _writeDebug = true;
84  }
85 
87  {
88  if (_symbolicDecomposition) {
89  cs_sfree(_symbolicDecomposition);
90  _symbolicDecomposition = 0;
91  }
92  delete[] _csWorkspace; _csWorkspace = 0;
93  delete[] _csIntWorkspace; _csIntWorkspace = 0;
94  delete _ccsA;
95  }
96 
97  virtual bool init()
98  {
99  if (_symbolicDecomposition) {
100  cs_sfree(_symbolicDecomposition);
101  _symbolicDecomposition = 0;
102  }
103  return true;
104  }
105 
106  bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
107  {
108  fillCSparse(A, _symbolicDecomposition != 0);
109  // perform symbolic cholesky once
110  if (_symbolicDecomposition == 0) {
111  computeSymbolicDecomposition(A);
112  }
113  // re-allocate the temporary workspace for cholesky
114  if (_csWorkspaceSize < _ccsA->n) {
115  _csWorkspaceSize = 2 * _ccsA->n;
116  delete[] _csWorkspace;
117  _csWorkspace = new double[_csWorkspaceSize];
118  delete[] _csIntWorkspace;
119  _csIntWorkspace = new int[2*_csWorkspaceSize];
120  }
121 
122  double t=get_monotonic_time();
123  // _x = _b for calling csparse
124  if (x != b)
125  memcpy(x, b, _ccsA->n * sizeof(double));
126  int ok = csparse_extension::cs_cholsolsymb(_ccsA, x, _symbolicDecomposition, _csWorkspace, _csIntWorkspace);
127  if (! ok) {
128  if (_writeDebug) {
129  std::cerr << "Cholesky failure, writing debug.txt (Hessian loadable by Octave)" << std::endl;
130  csparse_extension::writeCs2Octave("debug.txt", _ccsA, true);
131  }
132  return false;
133  }
134 
136  if (globalStats){
137  globalStats->timeNumericDecomposition = get_monotonic_time() - t;
138  globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
139  }
140 
141  return ok != 0;
142  }
143 
144  bool solveBlocks(double**& blocks, const SparseBlockMatrix<MatrixType>& A) {
145  fillCSparse(A, _symbolicDecomposition != 0);
146  // perform symbolic cholesky once
147  if (_symbolicDecomposition == 0) {
148  computeSymbolicDecomposition(A);
149  assert(_symbolicDecomposition && "Symbolic cholesky failed");
150  }
151  // re-allocate the temporary workspace for cholesky
152  if (_csWorkspaceSize < _ccsA->n) {
153  _csWorkspaceSize = 2 * _ccsA->n;
154  delete[] _csWorkspace;
155  _csWorkspace = new double[_csWorkspaceSize];
156  delete[] _csIntWorkspace;
157  _csIntWorkspace = new int[2*_csWorkspaceSize];
158  }
159 
160  if (! blocks){
161  blocks=new double*[A.rows()];
162  double **block=blocks;
163  for (size_t i=0; i < A.rowBlockIndices().size(); ++i){
164  int dim = A.rowsOfBlock(i) * A.colsOfBlock(i);
165  *block = new double [dim];
166  block++;
167  }
168  }
169 
170  int ok = 1;
171  csn* numericCholesky = csparse_extension::cs_chol_workspace(_ccsA, _symbolicDecomposition, _csIntWorkspace, _csWorkspace);
172  if (numericCholesky) {
174  mcc.setCholeskyFactor(_ccsA->n, numericCholesky->L->p, numericCholesky->L->i, numericCholesky->L->x, _symbolicDecomposition->pinv);
175  mcc.computeCovariance(blocks, A.rowBlockIndices());
176  cs_nfree(numericCholesky);
177  } else {
178  ok = 0;
179  std::cerr << "inverse fail (numeric decomposition)" << std::endl;
180  }
181 
183  if (globalStats){
184  globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
185  }
186 
187  return ok != 0;
188  }
189 
190  virtual bool solvePattern(SparseBlockMatrix<MatrixXD>& spinv, const std::vector<std::pair<int, int> >& blockIndices, const SparseBlockMatrix<MatrixType>& A) {
191  fillCSparse(A, _symbolicDecomposition != 0);
192  // perform symbolic cholesky once
193  if (_symbolicDecomposition == 0) {
194  computeSymbolicDecomposition(A);
195  assert(_symbolicDecomposition && "Symbolic cholesky failed");
196  }
197  // re-allocate the temporary workspace for cholesky
198  if (_csWorkspaceSize < _ccsA->n) {
199  _csWorkspaceSize = 2 * _ccsA->n;
200  delete[] _csWorkspace;
201  _csWorkspace = new double[_csWorkspaceSize];
202  delete[] _csIntWorkspace;
203  _csIntWorkspace = new int[2*_csWorkspaceSize];
204  }
205 
206 
207  int ok = 1;
208  csn* numericCholesky = csparse_extension::cs_chol_workspace(_ccsA, _symbolicDecomposition, _csIntWorkspace, _csWorkspace);
209  if (numericCholesky) {
211  mcc.setCholeskyFactor(_ccsA->n, numericCholesky->L->p, numericCholesky->L->i, numericCholesky->L->x, _symbolicDecomposition->pinv);
212  mcc.computeCovariance(spinv, A.rowBlockIndices(), blockIndices);
213  cs_nfree(numericCholesky);
214  } else {
215  ok = 0;
216  std::cerr << "inverse fail (numeric decomposition)" << std::endl;
217  }
218 
220  if (globalStats){
221  globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
222  }
223 
224  return ok != 0;
225  }
226 
228  bool blockOrdering() const { return _blockOrdering;}
229  void setBlockOrdering(bool blockOrdering) { _blockOrdering = blockOrdering;}
230 
232  virtual bool writeDebug() const { return _writeDebug;}
233  virtual void setWriteDebug(bool b) { _writeDebug = b;}
234 
235  protected:
238  double* _csWorkspace;
245 
247  {
248  double t=get_monotonic_time();
249  if (! _blockOrdering) {
250  _symbolicDecomposition = cs_schol (1, _ccsA) ;
251  } else {
252  A.fillBlockStructure(_matrixStructure);
253 
254  // prepare block structure for the CSparse call
255  cs auxBlock;
256  auxBlock.nzmax = _matrixStructure.nzMax();
257  auxBlock.m = auxBlock.n = _matrixStructure.n;
258  auxBlock.p = _matrixStructure.Ap;
259  auxBlock.i = _matrixStructure.Aii;
260  auxBlock.x = NULL; // no values
261  auxBlock.nz = -1; // CCS format
262 
263  // AMD ordering on the block structure
264  const int& n = _ccsA->n;
265  int* P = cs_amd(1, &auxBlock);
266 
267  // blow up the permutation to the scalar matrix
268  if (_scalarPermutation.size() == 0)
269  _scalarPermutation.resize(n);
270  if (_scalarPermutation.size() < n)
271  _scalarPermutation.resize(2*n);
272  size_t scalarIdx = 0;
273  for (int i = 0; i < _matrixStructure.n; ++i) {
274  const int& p = P[i];
275  int base = A.colBaseOfBlock(p);
276  int nCols = A.colsOfBlock(p);
277  for (int j = 0; j < nCols; ++j)
278  _scalarPermutation(scalarIdx++) = base++;
279  }
280  assert((int)scalarIdx == n);
281  cs_free(P);
282 
283  // apply the scalar permutation to finish symbolic decomposition
284  _symbolicDecomposition = (css*) cs_calloc(1, sizeof(css)); /* allocate result S */
285  _symbolicDecomposition->pinv = cs_pinv(_scalarPermutation.data(), n);
286  cs* C = cs_symperm(_ccsA, _symbolicDecomposition->pinv, 0);
287  _symbolicDecomposition->parent = cs_etree(C, 0);
288  int* post = cs_post(_symbolicDecomposition->parent, n);
289  int* c = cs_counts(C, _symbolicDecomposition->parent, post, 0);
290  cs_free(post);
291  cs_spfree(C);
292  _symbolicDecomposition->cp = (int*) cs_malloc(n+1, sizeof(int));
293  _symbolicDecomposition->unz = _symbolicDecomposition->lnz = cs_cumsum(_symbolicDecomposition->cp, c, n);
294  cs_free(c);
295  if (_symbolicDecomposition->lnz < 0) {
296  cs_sfree(_symbolicDecomposition);
297  _symbolicDecomposition = 0;
298  }
299 
300  }
302  if (globalStats){
303  globalStats->timeSymbolicDecomposition = get_monotonic_time() - t;
304  }
305 
306  /* std::cerr << "# Number of nonzeros in L: " << (int)_symbolicDecomposition->lnz << " by " */
307  /* << (_blockOrdering ? "block" : "scalar") << " AMD ordering " << std::endl; */
308  }
309 
310  void fillCSparse(const SparseBlockMatrix<MatrixType>& A, bool onlyValues)
311  {
312  if (! onlyValues)
313  this->initMatrixStructure(A);
314  int m = A.rows();
315  int n = A.cols();
316  assert(m > 0 && n > 0 && "Hessian has 0 rows/cols");
317 
318  if (_ccsA->columnsAllocated < n) {
319  _ccsA->columnsAllocated = _ccsA->columnsAllocated == 0 ? n : 2 * n; // pre-allocate more space if re-allocating
320  delete[] _ccsA->p;
321  _ccsA->p = new int[_ccsA->columnsAllocated+1];
322  }
323 
324  if (! onlyValues) {
325  int nzmax = A.nonZeros();
326  if (_ccsA->nzmax < nzmax) {
327  _ccsA->nzmax = _ccsA->nzmax == 0 ? nzmax : 2 * nzmax; // pre-allocate more space if re-allocating
328  delete[] _ccsA->x;
329  delete[] _ccsA->i;
330  _ccsA->i = new int[_ccsA->nzmax];
331  _ccsA->x = new double[_ccsA->nzmax];
332  }
333  }
334  _ccsA->m = m;
335  _ccsA->n = n;
336 
337  if (onlyValues) {
338  this->_ccsMatrix->fillCCS(_ccsA->x, true);
339  } else {
340  int nz = this->_ccsMatrix->fillCCS(_ccsA->p, _ccsA->i, _ccsA->x, true); (void) nz;
341  assert(nz <= _ccsA->nzmax);
342  }
343  _ccsA->nz=-1; // tag as CCS formatted matrix
344  }
345 };
346 
347 } // end namespace
348 
349 #endif
const std::vector< int > & rowBlockIndices() const
indices of the row blocks
double get_monotonic_time()
Definition: timeutil.cpp:113
virtual void setWriteDebug(bool b)
statistics about the optimization
Definition: batch_stats.h:40
int rowsOfBlock(int r) const
how many rows does the block at block-row r has?
void setCholeskyFactor(int n, int *Lp, int *Li, double *Lx, int *permInv)
int cols() const
columns of the matrix
int * Ap
column pointers for A, of size n+1
computing the marginal covariance given a cholesky factor (lower triangle of the factor) ...
int n
A is m-by-n. n must be >= 0.
int rows() const
rows of the matrix
bool blockOrdering() const
do the AMD ordering on the blocks or on the scalar matrix
representing the structure of a matrix in column compressed structure (only the upper triangular part...
void fillCSparse(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
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
utility functions for handling time related stuff
int cs_cholsolsymb(const cs *A, double *b, const css *S, double *x, int *work)
void setBlockOrdering(bool blockOrdering)
#define G2O_SOLVER_CSPARSE_API
linear solver which uses CSparse
Our C++ version of the csparse struct.
void computeCovariance(double **covBlocks, const std::vector< int > &blockIndices)
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]
bool solve(const SparseBlockMatrix< MatrixType > &A, double *x, double *b)
void computeSymbolicDecomposition(const SparseBlockMatrix< MatrixType > &A)
Solver with faster iterating structure for the linear matrix.
Definition: linear_solver.h:87
Eigen::Matrix< int, Eigen::Dynamic, 1, Eigen::ColMajor > VectorXI
Definition: eigen_types.h:38
double timeSymbolicDecomposition
symbolic decomposition (0 if not done)
Definition: batch_stats.h:57
csn * cs_chol_workspace(const cs *A, const css *S, int *cin, double *xin)
virtual bool solvePattern(SparseBlockMatrix< MatrixXD > &spinv, const std::vector< std::pair< int, int > > &blockIndices, const SparseBlockMatrix< MatrixType > &A)
int nzMax() const
max number of non-zeros blocks
virtual bool writeDebug() const
write a debug dump of the system matrix if it is not SPD in solve
bool solveBlocks(double **&blocks, const SparseBlockMatrix< MatrixType > &A)
bool writeCs2Octave(const char *filename, const cs *A, bool upperTriangular)
Sparse matrix which uses blocks.