g2o
Loading...
Searching...
No Matches
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#include <cholmod.h>
22
23#include <cassert>
24
28#include "g2o/stuff/timeutil.h"
29#include "g2o_incremental_api.h"
30
31namespace g2o {
32
37 public:
38 LinearSolverCholmodOnlineInterface() : cmember(0), batchEveryN(100) {}
39 virtual int choleskyUpdate(cholmod_sparse* update) = 0;
40 virtual bool solve(double* x, double* b) = 0;
41 virtual cholmod_factor* L() const = 0;
42 virtual size_t nonZerosInL() const = 0;
43 Eigen::VectorXi* cmember;
45};
46
50template <typename MatrixType>
51class LinearSolverCholmodOnline : public LinearSolver<MatrixType>,
53 public:
57 cholmod_start(&_cholmodCommon);
58
59 // setup ordering strategy
60 _cholmodCommon.nmethods = 1;
61 _cholmodCommon.method[0].ordering = CHOLMOD_AMD; // CHOLMOD_COLAMD
62 //_cholmodCommon.postorder = 0;
63
64 _cholmodCommon.supernodal =
65 CHOLMOD_AUTO; // CHOLMOD_SUPERNODAL; //CHOLMOD_SIMPLICIAL;
66 batchEveryN = 100;
67 }
68
70 delete _cholmodSparse;
71 if (_cholmodFactor) {
72 cholmod_free_factor(&_cholmodFactor, &_cholmodCommon);
74 }
75 cholmod_finish(&_cholmodCommon);
76 }
77
78 virtual bool init() { return true; }
79
80 bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b) {
81 cholmod_free_factor(&_cholmodFactor, &_cholmodCommon);
83 fillCholmodExt(A, false);
84
86 assert(_cholmodFactor && "Symbolic cholesky failed");
87 double t = get_monotonic_time();
88
89 // setting up b for calling cholmod
90 cholmod_dense bcholmod;
91 bcholmod.nrow = bcholmod.d = _cholmodSparse->nrow;
92 bcholmod.ncol = 1;
93 bcholmod.x = b;
94 bcholmod.xtype = CHOLMOD_REAL;
95 bcholmod.dtype = CHOLMOD_DOUBLE;
96
97 cholmod_factorize(_cholmodSparse, _cholmodFactor, &_cholmodCommon);
98 if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
99 std::cerr << "solve(): Cholesky failure, writing debug.txt (Hessian "
100 "loadable by Octave)"
101 << std::endl;
102 writeCCSMatrix("debug.txt", _cholmodSparse->nrow, _cholmodSparse->ncol,
103 (int*)_cholmodSparse->p, (int*)_cholmodSparse->i,
104 (double*)_cholmodSparse->x, true);
105 return false;
106 }
107
108 cholmod_dense* xcholmod =
109 cholmod_solve(CHOLMOD_A, _cholmodFactor, &bcholmod, &_cholmodCommon);
110 memcpy(x, xcholmod->x,
111 sizeof(double) * bcholmod.nrow); // copy back to our array
112 cholmod_free_dense(&xcholmod, &_cholmodCommon);
113
115 if (globalStats) {
116 globalStats->timeNumericDecomposition = get_monotonic_time() - t;
117 globalStats->choleskyNNZ =
118 static_cast<size_t>(_cholmodCommon.method[0].lnz);
119 }
120
121 return true;
122 }
123
124 bool blockOrdering() const { return true; }
125
126 cholmod_factor* L() const { return _cholmodFactor; }
127
131 size_t nonZerosInL() const {
132 size_t nnz = 0;
133 int* nz = (int*)_cholmodFactor->nz;
134 if (!nz) return 0;
135 for (size_t i = 0; i < _cholmodFactor->n; ++i) nnz += nz[i];
136 return nnz;
137 }
138
139 int choleskyUpdate(cholmod_sparse* update) {
140 int result = cholmod_updown(1, update, _cholmodFactor, &_cholmodCommon);
141 // std::cerr << __PRETTY_FUNCTION__ << " " << result << std::endl;
142 if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
143 std::cerr
144 << "Cholesky failure, writing debug.txt (Hessian loadable by Octave)"
145 << std::endl;
146 writeCCSMatrix("debug.txt", _cholmodSparse->nrow, _cholmodSparse->ncol,
147 (int*)_cholmodSparse->p, (int*)_cholmodSparse->i,
148 (double*)_cholmodSparse->x, true);
149 return 0;
150 }
151 return result;
152 }
153
154 bool solve(double* x, double* b) {
155 // setting up b for calling cholmod
156 cholmod_dense bcholmod;
157 bcholmod.nrow = bcholmod.d = _cholmodSparse->nrow;
158 bcholmod.ncol = 1;
159 bcholmod.x = b;
160 bcholmod.xtype = CHOLMOD_REAL;
161 bcholmod.dtype = CHOLMOD_DOUBLE;
162
163 cholmod_dense* xcholmod =
164 cholmod_solve(CHOLMOD_A, _cholmodFactor, &bcholmod, &_cholmodCommon);
165 memcpy(x, xcholmod->x,
166 sizeof(double) * bcholmod.nrow); // copy back to our array
167 cholmod_free_dense(&xcholmod, &_cholmodCommon);
168 return true;
169 }
170
171 protected:
172 // temp used for cholesky with cholmod
173 cholmod_common _cholmodCommon;
175 cholmod_factor* _cholmodFactor;
179
180 public:
182 double t = get_monotonic_time();
183
185
186 // get the ordering for the block matrix
187 if (_blockPermutation.size() <
188 _matrixStructure.n) // double space if resizing
190
191 int amdStatus = camd_order(_matrixStructure.n, _matrixStructure.Ap,
193 NULL, NULL, cmember->data());
194 if (amdStatus != CAMD_OK) {
195 std::cerr << "Error while computing ordering" << std::endl;
196 }
197
198 // blow up the permutation to the scalar matrix and extend to include the
199 // additional blocks
200 if (_scalarPermutation.size() == 0)
202 if (_scalarPermutation.size() < (int)_cholmodSparse->ncol)
203 _scalarPermutation.resize(2 * _cholmodSparse->ncol);
204 size_t scalarIdx = 0;
205 for (int i = 0; i < _matrixStructure.n; ++i) {
206 const int& p = _blockPermutation(i);
207 int base = A.colBaseOfBlock(p);
208 int nCols = A.colsOfBlock(p);
209 for (int j = 0; j < nCols; ++j) _scalarPermutation(scalarIdx++) = base++;
210 }
211
212 for (; scalarIdx < _cholmodSparse->ncol;
213 ++scalarIdx) // extending for the additional blocks
214 _scalarPermutation(scalarIdx) = scalarIdx;
215 assert(scalarIdx == _cholmodSparse->ncol);
216
217 // apply the ordering
218 _cholmodCommon.nmethods = 1;
219 _cholmodCommon.method[0].ordering = CHOLMOD_GIVEN;
220 _cholmodFactor = cholmod_analyze_p(
222
224 if (globalStats)
226 }
227
228 void fillCholmodExt(const SparseBlockMatrix<MatrixType>& A, bool onlyValues) {
229 int blockDimension = MatrixType::RowsAtCompileTime;
230 assert(blockDimension > 0);
231 // size_t origM = A.rows();
232 size_t origN = A.cols();
233 int additionalSpace = blockDimension * batchEveryN;
234 size_t m = A.rows() + additionalSpace;
235 size_t n = A.cols() + additionalSpace;
236
238 // std::cerr << __PRETTY_FUNCTION__ << ": reallocating columns" <<
239 // std::endl;
242 ? n
243 : 2 * n; // pre-allocate more space if re-allocating
244 delete[] (int*)_cholmodSparse->p;
246 }
247 if (!onlyValues) {
248 size_t nzmax = A.nonZeros() + additionalSpace;
249 if (_cholmodSparse->nzmax < nzmax) {
250 // std::cerr << __PRETTY_FUNCTION__ << ": reallocating row + values" <<
251 // std::endl;
252 _cholmodSparse->nzmax =
253 _cholmodSparse->nzmax == 0
254 ? nzmax
255 : 2 * nzmax; // pre-allocate more space if re-allocating
256 delete[] (double*)_cholmodSparse->x;
257 delete[] (int*)_cholmodSparse->i;
258 _cholmodSparse->i = new int[_cholmodSparse->nzmax];
259 _cholmodSparse->x = new double[_cholmodSparse->nzmax];
260 }
261 }
262 _cholmodSparse->ncol = n;
263 _cholmodSparse->nrow = m;
264
265 int nz = 0;
266 if (onlyValues)
267 nz = A.fillCCS((double*)_cholmodSparse->x, true);
268 else
269 nz = A.fillCCS((int*)_cholmodSparse->p, (int*)_cholmodSparse->i,
270 (double*)_cholmodSparse->x, true);
271
272 int* cp = (int*)_cholmodSparse->p;
273 int* ci = (int*)_cholmodSparse->i;
274 double* cx = (double*)_cholmodSparse->x;
275
276 cp = &cp[origN];
277 ci = &ci[nz];
278 cx = &cx[nz];
279
280 // diagonal for the next blocks
281 for (int i = 0; i < additionalSpace; ++i) {
282 *cp++ = nz;
283 *ci++ = origN + i;
284 *cx++ = 1e-6;
285 ++nz;
286 }
287 *cp = nz;
288 }
289};
290
291} // namespace g2o
292
293#endif
generic interface for the online solver
virtual cholmod_factor * L() const =0
virtual bool solve(double *x, double *b)=0
virtual int choleskyUpdate(cholmod_sparse *update)=0
virtual size_t nonZerosInL() const =0
linear solver which allows to update the cholesky factor
void computeSymbolicDecomposition(const SparseBlockMatrix< MatrixType > &A)
void fillCholmodExt(const SparseBlockMatrix< MatrixType > &A, bool onlyValues)
bool solve(const SparseBlockMatrix< MatrixType > &A, double *x, double *b)
basic solver for Ax = b
representing the structure of a matrix in column compressed structure (only the upper triangular part...
int * Aii
row indices of A, of size nz = Ap [n]
int n
A is m-by-n. n must be >= 0.
int * Ap
column pointers for A, of size n+1
Sparse matrix which uses blocks.
size_t nonZeros() const
number of non-zero elements
int colBaseOfBlock(int c) const
where does the col at block-col r starts?
int fillCCS(int *Cp, int *Ci, double *Cx, bool upperTriangle=false) const
int rows() const
rows of the matrix
int cols() const
columns of the matrix
void fillBlockStructure(MatrixStructure &ms) const
exports the non zero blocks in the structure matrix ms
int colsOfBlock(int c) const
how many cols does the block at block-col c has?
#define G2O_INCREMENTAL_API
bool writeCCSMatrix(const string &filename, int rows, int cols, const int *Ap, const int *Ai, const double *Ax, bool upperTriangleSymmetric)
double get_monotonic_time()
Definition timeutil.cpp:43
statistics about the optimization
Definition batch_stats.h:40
static G2OBatchStatistics * globalStats()
Definition batch_stats.h:77
double timeNumericDecomposition
numeric decomposition (0 if not done)
Definition batch_stats.h:58
double timeSymbolicDecomposition
symbolic decomposition (0 if not done)
Definition batch_stats.h:57
size_t choleskyNNZ
number of non-zeros in the cholesky factor
Definition batch_stats.h:75
Our extension of the CHOLMOD matrix struct.
Definition cholmod_ext.h:38
utility functions for handling time related stuff