NDEVR
API Documentation
block_solver.h
1#pragma once
2#include <Eigen/Core>
3#include "solver.h"
4#include "linear_solver.h"
5#include "sparse_block_matrix.h"
6#include "sparse_block_matrix_diagonal.h"
7#include "openmp_mutex.h"
8
9
10
11namespace NDEVR
12{
15 {
18 public:
19 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
20 };
21 using namespace Eigen;
22
26 template <class t_solver, int _PoseDim, int _LandmarkDim>
28 {
29 static const int PoseDim = _PoseDim;
30 static const int LandmarkDim = _LandmarkDim;
31 typedef Eigen::Matrix<g_type, PoseDim, PoseDim> PoseMatrixType;
32 typedef Eigen::Matrix<g_type, LandmarkDim, LandmarkDim> LandmarkMatrixType;
33 typedef Eigen::Matrix<g_type, PoseDim, LandmarkDim> PoseLandmarkMatrixType;
34 typedef Eigen::Matrix<g_type, PoseDim, 1> PoseVectorType;
35 typedef Eigen::Matrix<g_type, LandmarkDim, 1> LandmarkVectorType;
36
37 typedef SparseBlockMatrix<PoseMatrixType> PoseHessianType;
38 typedef SparseBlockMatrix<LandmarkMatrixType> LandmarkHessianType;
39 typedef SparseBlockMatrix<PoseLandmarkMatrixType> PoseLandmarkHessianType;
40 typedef t_solver LinearSolverType;
41 };
42
46 template <class t_type>
47 struct BlockSolverTraits<t_type, Eigen::Dynamic, Eigen::Dynamic>
48 {
49 static const int PoseDim = Eigen::Dynamic;
50 static const int LandmarkDim = Eigen::Dynamic;
51 typedef MatrixX<g_type> PoseMatrixType;
52 typedef MatrixX<g_type> LandmarkMatrixType;
53 typedef MatrixX<g_type> PoseLandmarkMatrixType;
54 typedef VectorX<g_type> PoseVectorType;
55 typedef VectorX<g_type> LandmarkVectorType;
56
57 typedef SparseBlockMatrix<PoseMatrixType> PoseHessianType;
58 typedef SparseBlockMatrix<LandmarkMatrixType> LandmarkHessianType;
59 typedef SparseBlockMatrix<PoseLandmarkMatrixType> PoseLandmarkHessianType;
60 typedef t_type LinearSolverType;
61 };
65 template <typename Traits>
66 class BlockSolver : public Solver
67 {
68 public:
69
70 static const int PoseDim = Traits::PoseDim;
71 static const int LandmarkDim = Traits::LandmarkDim;
72 typedef typename Traits::PoseMatrixType PoseMatrixType;
73 typedef typename Traits::LandmarkMatrixType LandmarkMatrixType;
74 typedef typename Traits::PoseLandmarkMatrixType PoseLandmarkMatrixType;
75 typedef typename Traits::PoseVectorType PoseVectorType;
76 typedef typename Traits::LandmarkVectorType LandmarkVectorType;
77
78 typedef typename Traits::PoseHessianType PoseHessianType;
79 typedef typename Traits::LandmarkHessianType LandmarkHessianType;
80 typedef typename Traits::PoseLandmarkHessianType PoseLandmarkHessianType;
81 typedef typename Traits::LinearSolverType LinearSolverType;
82
83 public:
84 LinearSolverType solver;
88 void clear()
89 {
90 _numPoses = 0;
91 _numLandmarks = 0;
92 _sizePoses = 0;
94 _doSchur = true;
95 _x.clear();
96 _b.clear();
97 }
98
100 {
101 return solver.optimizer;
102 }
103
105 {
106 return solver.optimizer;
107 }
108
111 bool init(bool online = false);
116 bool buildStructure(bool zeroBlocks, IndexScratch& scratch);
125 {
126 //# pragma omp parallel for default (shared) if (solver.optimizer.indexMapping().size() > 1000)
127 for (OptimizableGraph::OGVertex* v : solver.optimizer.indexMapping())
128 v->clearQuadraticForm();
129 _Hpp.clear();
130 if (_doSchur)
131 {
132 _Hll.clear();
133 _Hpl.clear();
134 }
135
136 // resetting the terms for the pairwise constraints
137 // built up the current system by storing the Hessian blocks in the edges and vertices
138 uint04 size = solver.optimizer.activeEdges().size();
139 // no threading, we do not need to copy the workspace
140 JacobianWorkspace& jacobianWorkspace = solver.optimizer.jacobianWorkspace();
141 for (uint04 i = 0; i < size; ++i)
142 {
143 OptimizableGraph::OGEdge* e = solver.optimizer.activeEdges()[i];
144 e->linearizeOplusAndConstructQuadraticForm(jacobianWorkspace); // jacobian of the nodes' oplus (manifold)
145 }
146
147 // flush the current system in a sparse block matrix
148 for (uint04 i = 0; i < solver.optimizer.indexMapping().size(); ++i)
149 {
150 OptimizableGraph::OGVertex* v = solver.optimizer.indexMapping()[i];
151 uint04 iBase = v->colInHessian();
152 if (v->marginalized())
153 iBase += _sizePoses;
154 lib_assert(_b.size() > iBase, "Bad size");
155 v->copyB(_b.begin(iBase));
156 }
157 return true;
158 }
159
162 bool solve()
163 {
164 if (!_doSchur)
165 {
166 bool ok = solver.solve(_Hpp, _x, _b);
167 return ok;
168 }
169 _Hschur.clear();
170 _Hpp.add(_Hschur);
171 _coefficients.setAllToValue(0.0f);
172# ifdef G2O_OPENMP
173 //# pragma omp parallel for default (shared) schedule(dynamic, 10)
174# endif
175 for (uint04 idx = 0; idx < _Hll.blockCols().size(); ++idx)
176 {
177 auto& marginalizeColumn = _Hll.blockCols()[idx];
178
179 assert(marginalizeColumn.size() == 1 && "more than one block in _Hll column");
180
181 // calculate inverse block for the landmark
182 const LandmarkMatrixType& D = marginalizeColumn.begin()->second;
183 //if (!D.array().isFinite().all())
184 // throw "bad ex";
185 assert(D.rows() == D.cols() && "Error in landmark matrix");
186 LandmarkMatrixType& Dinv = _DInvSchur.diagonal()[idx];
187 Dinv = D.inverse();
188 //if (!Dinv.array().isFinite().all())
189 //throw "ex";
190 LandmarkVectorType db(D.rows());
191 for (uint04 j = 0; j < D.rows(); ++j)
192 db[j] = _b[_Hll.rowBaseOfBlock(idx) + _sizePoses + j];
193 db = Dinv * db;
194
195 const auto& landmarkColumn = _HplCCS.columns()[idx];
196
197 for (auto it_outer = landmarkColumn.begin(); it_outer != landmarkColumn.end(); ++it_outer)
198 {
199 int i1 = it_outer->row;
200
201 const PoseLandmarkMatrixType& Bi = it_outer->block;
202
203 PoseLandmarkMatrixType BDinv = Bi * Dinv;
204 assert(_HplCCS.rowBaseOfBlock(i1) < cast<int>(_sizePoses) && "Index out of bounds");
205 typename PoseVectorType::MapType Bb(&_coefficients[_HplCCS.rowBaseOfBlock(i1)], Bi.rows());
206# ifdef G2O_OPENMP
207 //ScopedOpenMPMutex mutexLock(&_coefficientsMutex[i1]);
208# endif
209 Bb.noalias() += Bi * db;
210 //if (!Bb.array().isFinite().any())
211 // throw "ex";
212 assert(i1 >= 0 && i1 < static_cast<int>(_HschurTransposedCCS.columns().size()) && "Index out of bounds");
213 auto targetColumnIt = _HschurTransposedCCS.columns()[i1].begin();
214
216 auto it_inner = std::lower_bound(landmarkColumn.begin(), landmarkColumn.end(), aux);
217 for (; it_inner != landmarkColumn.end(); ++it_inner)
218 {
219 int i2 = it_inner->row;
220 const PoseLandmarkMatrixType& Bj = it_inner->block;
221 while (targetColumnIt->row < i2 /*&& targetColumnIt != _HschurTransposedCCS.blockCols()[i1].end()*/)
222 ++targetColumnIt;
223 assert(targetColumnIt != _HschurTransposedCCS.columns()[i1].end() && targetColumnIt->row == i2 && "invalid iterator, something wrong with the matrix structure");
224 PoseMatrixType& Hi1i2 = targetColumnIt->block;//_Hschur.block(i1,i2);
225 Hi1i2.noalias() -= BDinv * Bj.transpose();
226 }
227 }
228 }
229 //cerr << "Solve [marginalize] = " << get_monotonic_time()-t << endl;
230
231 // _bschur = _b for calling solver, and not touching _b
232 memcpy(_bschur, _b, _sizePoses * sizeof(g_type));
233 //for (uint04 i = 0; i < _sizePoses; ++i)
234 //if (IsInvalid(_coefficients[i]))
235 //throw "ex";
236 for (uint04 i = 0; i < _sizePoses; ++i)
237 _bschur[i] -= _coefficients[i];
238
239
240 bool solvedPoses = solver.solve(_Hschur, _x, _bschur);
241
242 //cerr << "Solve [decompose and solve] = " << get_monotonic_time()-t << endl;
243 if (!solvedPoses)
244 return false;
245
246 // _x contains the solution for the poses, now applying it to the landmarks to get the new part of the
247 // solution;
248 g_type* xp = _x.begin();
249 g_type* cp = _coefficients;
250 lib_assert(_x.size() > _sizePoses, "Bad size");
251 g_type* xl = _x.begin(_sizePoses);
252 g_type* cl = _coefficients.begin() + _sizePoses;
253 g_type* bl = _b.begin(_sizePoses);
254
255 // cp = -xp
256 for (uint04 i = 0; i < _sizePoses; ++i)
257 cp[i] = -xp[i];
258
259 // cl = bl
260 memcpy(cl, bl, _sizeLandmarks * sizeof(g_type));
261
262 // cl = bl - Bt * xp
263 //Bt.multiply(cl, cp);
264 _HplCCS.rightMultiply(cl, cp);
265
266 // xl = Dinv * cl
267 memset(xl, 0, _sizeLandmarks * sizeof(g_type));
268 _DInvSchur.multiply(xl, cl);
269 //_DInvSchur.rightMultiply(xl,cl);
270 //cerr << "Solve [landmark delta] = " << get_monotonic_time()-t << endl;
271
272 return true;
273 }
274 //bool computeMarginals(SparseBlockMatrix<MatrixXd>& spinv, const Buffer<std::pair<int, int> >& blockIndices);
279 bool setLambda(g_type lambda, bool backup = false);
283 bool supportsSchur() { return true; }
285 bool schur() { return _doSchur; }
288 void setSchur(bool s) { _doSchur = s; }
292 void multiplyHessian(g_type* dest, const g_type* src) const { _Hpp.multiplySymmetricUpperTriangle(dest, src); }
293
294 protected:
298 void resize(IndexScratch& scratch, int totalDim);
299
303
306
309
312
313 bool _doSchur;
314
317
319 uint04 _sizePoses, _sizeLandmarks;
320 public:
321 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
322 };
323
326 template<class t_type>
335
336} // end namespace
337
338#include "block_solver.hpp"
Convenience typedefs for commonly used BlockSolver configurations.
BlockSolver< BlockSolverTraits< t_type, 7, 3 > > BlockSolver_7_3
Solver for BA with scale (7-DoF poses, 3D landmarks).
BlockSolver< BlockSolverTraits< t_type, 3, 2 > > BlockSolver_3_2
Solver for 3-DoF poses and 2D landmarks.
BlockSolver< BlockSolverTraits< t_type, 6, 3 > > BlockSolver_6_3
Solver for BA / 3D SLAM (6-DoF poses, 3D landmarks).
BlockSolver< BlockSolverTraits< t_type, Eigen::Dynamic, Eigen::Dynamic > > BlockSolverX
Variable-size solver.
Implementation of a solver operating on the blocks of the Hessian.
const SparseOptimizer & optimizer() const
Returns a const reference to the underlying optimizer.
void resize(IndexScratch &scratch, int totalDim)
Resizes all internal matrices and vectors for the given problem size.
SparseBlockMatrixCCS< PoseLandmarkMatrixType > _HplCCS
SparseBlockMatrix< PoseMatrixType > _Hpp
SparseBlockMatrix< LandmarkMatrixType > _Hll
void multiplyHessian(g_type *dest, const g_type *src) const
Multiplies the pose Hessian by a vector: dest = Hpp * src.
bool buildSystem()
Builds the linear system (Hessian blocks and gradient) from the current graph.
SparseBlockMatrixCCS< PoseMatrixType > _HschurTransposedCCS
void setSchur(bool s)
Enables or disables the Schur complement.
bool buildStructure(bool zeroBlocks, IndexScratch &scratch)
Builds the sparse block matrix structure from the graph.
void clear()
Clears all solver state, resetting sizes to zero.
bool schur()
Returns whether the Schur complement is currently enabled.
SparseBlockMatrix< PoseLandmarkMatrixType > _Hpl
bool updateStructure(const Buffer< HyperGraph::HGVertex * > &vset, const Set< HyperGraph::HGVertex * > &edges)
Updates the structure after vertices or edges have changed.
SparseBlockMatrix< PoseMatrixType > _Hschur
bool solve()
Solves the linear system, optionally using the Schur complement.
bool supportsSchur()
Returns true; this solver supports the Schur complement.
bool setLambda(g_type lambda, bool backup=false)
Adds a damping factor to the diagonal of the Hessian.
SparseOptimizer & optimizer()
Returns a mutable reference to the underlying optimizer.
void restoreDiagonal()
Restores the Hessian diagonal from the backup.
BlockSolver()
Default constructor.
bool init(bool online=false)
Initializes the block solver.
SparseBlockMatrixDiagonal< LandmarkMatrixType > _DInvSchur
Buffer< LandmarkVectorType > _diagonalBackupLandmark
The equivelent of std::vector but with a bit more control.
Definition Buffer.hpp:58
provide memory workspace for computing the Jacobians
Base edge class for the optimizable graph, adding error computation and robust kernels.
virtual void linearizeOplusAndConstructQuadraticForm(JacobianWorkspace &jacobianWorkspace)=0
Linearizes the constraint in the edge in the manifold space, and store the result in the given worksp...
A general case Vertex for optimization.
virtual sint04 copyB(g_type *b_) const =0
copies the b vector in the array b_
bool marginalized() const
true => this node is marginalized out during the optimization
int colInHessian() const
get the row of this vertex in the Hessian
Container that stores unique elements in no particular order, and which allow for fast retrieval or i...
Definition Set.h:59
Solver()
Default constructor.
Definition solver.h:19
Buffer< g_type > _b
The right-hand side vector.
Definition solver.h:41
Buffer< g_type > _x
The solution vector.
Definition solver.h:40
Sparse matrix which uses blocks.
Sparse matrix which uses blocks on the diagonal.
Sparse matrix which uses blocks.
Sparse optimizer that manages active vertices and edges for graph-based optimization.
The primary namespace for the NDEVR SDK.
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408
traits to summarize the properties of the fixed size optimization problem
Scratch buffers for block index assignments during structure building.
Buffer< int > block_pose_indices
Block indices for pose vertices.
Buffer< int > block_landmark_indices
Block indices for landmark vertices.