NDEVR
API Documentation
optimizable_graph.h
1#pragma once
2#include <NDEVR/Set.h>
3#include <limits>
4#include <cmath>
5#include <typeinfo>
6
7#include "openmp_mutex.h"
8#include "hyper_graph.h"
9#include "jacobian_workspace.h"
10
11
12#include "optimization_algorithm_property.h"
13#include "robust_kernel.h"
14
15#include <Eigen/Dense>
16
17
18#include "macros.h"
19#ifdef GRAPH_PARALLEL
20#include <mutex>
21#endif
22namespace NDEVR
23{
24
25 class HyperGraphAction;
27 class RobustKernel;
37 {
38
45
46 typedef Set<HyperGraphAction*> HyperGraphActionSet;
47
48 // forward declarations
49 class OGVertex;
50 class OGEdge;
51
56 class Data
57 {
58 friend struct OptimizableGraph;
59 public:
60 ~Data() { delete _next; };
61 Data() {};
62 const Data* next() const { return _next; }
63 Data* next() { return _next; }
64 void setNext(Data* next_) { _next = next_; }
65 protected:
66 Data* _next = nullptr; // linked list of multiple data;
67 };
68
73 bool operator() (const OGVertex* v1, const OGVertex* v2) const
74 {
75 return v1->id() < v2->id();
76 }
77 };
78
83 bool operator() (const OGEdge* e1, const OGEdge* e2) const
84 {
85 return e1->internalId() < e2->internalId();
86 }
87 };
88
93
97 class OGVertex : public HyperGraph::HGVertex {
98 private:
99 friend struct OptimizableGraph;
100 public:
101 OGVertex()
103 , _hessianIndex(-1)
104 , _fixed(false)
105 , _marginalized(false)
106 , _colInHessian(-1)
107 {}
108
109
110 virtual ~OGVertex()
111 {}
112
114 virtual const g_type& hessian(int i, int j) const = 0;
115 virtual g_type& hessian(int i, int j) = 0;
116 virtual g_type hessianDeterminant() const = 0;
117 virtual g_type* hessianData() = 0;
118
120 virtual void mapHessianMemory(g_type* d) = 0;
121
126 virtual sint04 copyB(g_type* b_) const = 0;
127
129 virtual const g_type& b(int i) const = 0;
130 virtual g_type& b(int i) = 0;
132 virtual g_type* bData() = 0;
133
137 virtual void clearQuadraticForm() = 0;
138
143 virtual g_type solveDirect(g_type lambda = 0) = 0;
144
150 bool setEstimateData(const g_type* estimate)
151 {
152 return setEstimateDataImpl(estimate);
153 }
154
160 bool setEstimateData(const Buffer<g_type>& estimate) {
161#ifndef NDEBUG
162 int dim = estimateDimension();
163 assert((dim == -1) || (estimate.size() == std::size_t(dim)));
164#endif
165 return setEstimateData(&estimate[0]);
166 };
167
172 virtual bool getEstimateData(g_type*) const
173 {
174 return false;
175 }
176
181 virtual bool getEstimateData(Buffer<g_type>& estimate) const {
182 int dim = estimateDimension();
183 if (dim < 0)
184 return false;
185 estimate.resize(dim);
186 return getEstimateData(&estimate[0]);
187 };
188
193 virtual sint04 estimateDimension() const { return -1; };
194
200 bool setMinimalEstimateData(const g_type* estimate)
201 {
202 return setMinimalEstimateDataImpl(estimate);
203 }
204
211#ifndef NDEBUG
212 int dim = minimalEstimateDimension();
213 assert((dim == -1) || (estimate.size() == std::size_t(dim)));
214#endif
215 return setMinimalEstimateData(&estimate[0]);
216 };
217
222 virtual bool getMinimalEstimateData(g_type*) const
223 {
224 return false;
225 }
226
231 virtual bool getMinimalEstimateData(Buffer<g_type>& estimate) const {
232 int dim = minimalEstimateDimension();
233 if (dim < 0)
234 return false;
235 estimate.resize(dim);
236 return getMinimalEstimateData(&estimate[0]);
237 };
238
243 virtual sint04 minimalEstimateDimension() const { return -1; }
244
246 virtual void push() = 0;
247
249 virtual void pop() = 0;
250
252 virtual void discardTop() = 0;
253
255 virtual int stackSize() const = 0;
256
263 void oplus(const g_type* v)
264 {
265 oplusImpl(v);
266 }
267
269 int hessianIndex() const { return _hessianIndex; }
270 int G2O_ATTRIBUTE_DEPRECATED(tempIndex() const) { return hessianIndex(); }
272 void setHessianIndex(int ti) { _hessianIndex = ti; }
273 void G2O_ATTRIBUTE_DEPRECATED(setTempIndex(int ti)) { setHessianIndex(ti); }
274
276 bool fixed() const { return _fixed; }
278 void setFixed(bool fixed) { _fixed = fixed; }
279
281 bool marginalized() const { return _marginalized; }
284
286 virtual sint04 dimension() const = 0;
287
289 virtual void setId(int id) { _id = id; }
290
292 void setColInHessian(int c) { _colInHessian = c; }
294 int colInHessian() const { return _colInHessian; }
295
296
297 protected:
299 bool _fixed = false;
300 bool _marginalized = false;
306 virtual void oplusImpl(const g_type* v) = 0;
307
312 virtual bool setEstimateDataImpl(const g_type*) { return false; }
313
318 virtual bool setMinimalEstimateDataImpl(const g_type*) { return false; }
319
320 };
321
324 {
325 private:
326 friend struct OptimizableGraph;
327 public:
330 {}
331
333 virtual bool allVerticesFixed() const = 0;
334
336 virtual void computeError() = 0;
337
344 {
345 _robustKernel = &ptr;
346 }
347
349 {
350 _robustKernel = nullptr;
351 }
352
353 virtual const g_type* informationData() const = 0;
354 virtual g_type* informationData() = 0;
355
357 virtual g_type chi2() const = 0;
358
359
368 virtual void mapHessianMemory(g_type* d, int i, int j, bool rowMajor) = 0;
369
380
382 int level() const { return _level; }
384 void setLevel(int l) { _level = l; }
385
387 virtual sint04 dimension() const = 0;
388
390 long long internalId() const { return _internalId; }
391
392
393
394 protected:
396 int _internalId = 0;
397 int _level = 0;
398
399
400 };
401
404 {
405 _nextEdgeId = 0;
406 }
407 virtual ~OptimizableGraph()
408 {
409 clear();
410 }
411
416 virtual bool addVertex(HyperGraph::HGVertex& v) final override
417 {
418 lib_assert(!vertex(v.id()), "Vertex with this ID already contained in the graph");
419 lib_assert(dynamic_cast<OptimizableGraph::OGVertex*>(&v), "Vertex does not inherit from OGVertex");
420 return HyperGraph::addVertex(v);
421 }
422
428 virtual bool addEdge(HyperGraph::HGEdge* e_) final override
429 {
431 lib_assert(e, "Edge does not inherit from OptimizableGraph::OGEdge");
432 bool eresult = HyperGraph::addEdge(e);
433 if (!eresult)
434 return false;
436 _jacobianWorkspace.updateSize(e);
437
438 return true;
439 }
440
442 g_type chi2() const
443 {
444 g_type chi = 0.0;
445 for (auto it = this->edges().begin(); it != this->edges().end(); ++it)
446 {
447 const OptimizableGraph::OGEdge* e = static_cast<const OptimizableGraph::OGEdge*>(*it);
448 chi += e->chi2();
449 }
450 return chi;
451 }
452
455 {
456 sint04 maxDim = 0;
457 for (auto it = vertices().begin(); it != vertices().end(); ++it)
458 {
459 const OptimizableGraph::OGVertex* v = static_cast<const OptimizableGraph::OGVertex*>(it->second);
460 maxDim = getMax(maxDim, v->dimension());
461 }
462 return maxDim;
463 }
464
469 {
470 Set<int> auxDims;
471 for (auto it = vertices().begin(); it != vertices().end(); ++it)
472 {
473 OGVertex* v = static_cast<OGVertex*>(it->second);
474 auxDims.insert(v->dimension());
475 }
476 return auxDims;
477 }
478
479
481 virtual void push()
482 {
483 for (auto it = _vertices.begin(); it != _vertices.end(); ++it)
484 {
485 OGVertex* v = static_cast<OGVertex*>(it->second);
486 v->push();
487 }
488 }
489
490 virtual void pop()
491 {
492 for (auto it = _vertices.begin(); it != _vertices.end(); ++it)
493 {
494 OGVertex* v = static_cast<OGVertex*>(it->second);
495 v->pop();
496 }
497 }
498
499 virtual void discardTop()
500 {
501 for (auto it = _vertices.begin(); it != _vertices.end(); ++it) {
502 OGVertex* v = static_cast<OGVertex*>(it->second);
503 v->discardTop();
504 }
505 }
506
509 {
510 for (auto it = vset.begin(); it != vset.end(); ++it) {
511 OGVertex* v = static_cast<OGVertex*>(*it);
512 v->push();
513 }
514 }
515
516 virtual void pop(Set<HyperGraph::HGVertex*>& vset)
517 {
518 for (auto it = vset.begin(); it != vset.end(); ++it) {
519 OGVertex* v = static_cast<OGVertex*>(*it);
520 v->pop();
521 }
522 }
523
524
526 virtual void setFixed(Set<HyperGraph::HGVertex*>& vset, bool fixed)
527 {
528 for (auto it = vset.begin(); it != vset.end(); ++it)
529 {
530 OGVertex* v = static_cast<OGVertex*>(*it);
531 v->setFixed(fixed);
532 }
533 }
534
540 bool isSolverSuitable(const OptimizationAlgorithmProperty& solverProperty, const Set<sint04>& vertDims_ = Set<int>()) const
541 {
542 Set<sint04> auxDims;
543 if (vertDims_.size() == 0)
544 auxDims = dimensions();
545 const Set<sint04>& vertDims = vertDims_.size() == 0 ? auxDims : vertDims_;
546 bool suitableSolver = true;
547 if (vertDims.size() == 2)
548 {
549 if (solverProperty.requiresMarginalize)
550 suitableSolver = vertDims.count(solverProperty.poseDim) == 1 && vertDims.count(solverProperty.landmarkDim) == 1;
551 else
552 suitableSolver = solverProperty.poseDim == -1;
553 }
554 else if (vertDims.size() == 1)
555 {
556 suitableSolver = vertDims.count(solverProperty.poseDim) == 1 || solverProperty.poseDim == -1;
557 }
558 else
559 {
560 suitableSolver = solverProperty.poseDim == -1 && !solverProperty.requiresMarginalize;
561 }
562 return suitableSolver;
563 }
564
571 /*bool verifyInformationMatrices(bool verbose = false) const
572 {
573 bool allEdgeOk = true;
574 SelfAdjointEigenSolver<MatrixXd> eigenSolver;
575 for (OptimizableGraph::EdgeSet::const_iterator it = edges().begin(); it != edges().end(); ++it) {
576 OptimizableGraph::OGEdge* e = static_cast<OptimizableGraph::OGEdge*>(*it);
577 Eigen::MatrixXd::MapType information(e->informationData(), e->dimension(), e->dimension());
578 // test on symmetry
579 bool isSymmetric = information.transpose() == information;
580 bool okay = isSymmetric;
581 if (isSymmetric) {
582 // compute the eigenvalues
583 eigenSolver.compute(information, Eigen::EigenvaluesOnly);
584 bool isSPD = eigenSolver.eigenvalues()(0) >= 0.;
585 okay = okay && isSPD;
586 }
587 allEdgeOk = allEdgeOk && okay;
588 if (!okay) {
589 if (verbose) {
590 if (!isSymmetric)
591 std::cerr << "Information Matrix for an edge is not symmetric:";
592 else
593 std::cerr << "Information Matrix for an edge is not SPD:";
594 for (uint04 i = 0; i < e->vertices().size(); ++i)
595 std::cerr << " " << e->vertex(i)->id();
596 if (isSymmetric)
597 std::cerr << "\teigenvalues: " << eigenSolver.eigenvalues().transpose();
598 std::cerr << std::endl;
599 }
600 }
601 }
602 return allEdgeOk;
603 }*/
604
609
610 protected:
612 int _nextEdgeId = 0;
613
614
615 };
616
617}
The equivelent of std::vector but with a bit more control.
Definition Buffer.hpp:58
Abstract Edge class.
Definition hyper_graph.h:54
abstract Vertex, your types must derive from that one
Definition hyper_graph.h:28
int _id
Unique identifier for this vertex.
Definition hyper_graph.h:46
int id() const
returns the id
Definition hyper_graph.h:37
HyperGraph()
constructs an empty hyper graph
Definition hyper_graph.h:86
virtual void clear()
removes a vertex from the graph. Returns true on success (vertex was present)
virtual bool addEdge(HGEdge *e)
Adds an edge to the graph.
const Dictionary< int, HGVertex * > & vertices() const
const Buffer< HGEdge * > & edges() const
Dictionary< int, HGVertex * > _vertices
Map from vertex id to vertex pointer.
virtual bool addVertex(HGVertex &v)
adds a vertex to the graph.
HGVertex * vertex(int id)
returns a vertex id in the hyper-graph, or 0 if the vertex id is not present
Definition hyper_graph.h:92
provide memory workspace for computing the Jacobians
Base edge class for the optimizable graph, adding error computation and robust kernels.
virtual void mapHessianMemory(g_type *d, int i, int j, bool rowMajor)=0
maps the internal matrix to some external memory location, you need to provide the memory before call...
RobustKernel * _robustKernel
Optional robust kernel for this edge.
virtual void linearizeOplusAndConstructQuadraticForm(JacobianWorkspace &jacobianWorkspace)=0
Linearizes the constraint in the edge in the manifold space, and store the result in the given worksp...
virtual const g_type * informationData() const =0
returns the memory of the information matrix, usable for example with a Eigen::Map<MatrixXd>
virtual void computeError()=0
Computes the error of the edge and stores it internally.
void clearRobustKernel()
Removes the robust kernel from this edge.
OGEdge()
Default constructor.
int level() const
returns the level of the edge
virtual sint04 dimension() const =0
returns the dimensions of the error function
virtual g_type chi2() const =0
computes the chi2 based on the cached error value, only valid after computeError has been called.
void setLevel(int l)
sets the level of the edge
int _level
Optimization level for multi-level optimization.
int _internalId
Internal sequential id assigned on insertion.
virtual bool allVerticesFixed() const =0
Returns true if all vertices connected to this edge are fixed.
RobustKernel * robustKernel() const
if NOT NULL, error of this edge will be robustifed with the kernel
long long internalId() const
the internal ID of the edge
void setRobustKernel(RobustKernel &ptr)
specify the robust kernel to be used in this edge
A general case Vertex for optimization.
virtual const g_type & b(int i) const =0
get the b vector element
int _hessianIndex
Index of this vertex in the Hessian.
virtual bool getEstimateData(Buffer< g_type > &estimate) const
writes the estimater to an array of g_type
bool setEstimateData(const Buffer< g_type > &estimate)
sets the initial estimate from an array of g_type Implement setEstimateDataImpl()
bool _fixed
Whether this vertex is fixed during optimization.
bool fixed() const
true => this node is fixed during the optimization
virtual bool getMinimalEstimateData(Buffer< g_type > &estimate) const
writes the estimate to an array of g_type
virtual bool setMinimalEstimateDataImpl(const g_type *)
sets the initial estimate from an array of g_type
virtual void mapHessianMemory(g_type *d)=0
maps the internal matrix to some external memory location
virtual g_type solveDirect(g_type lambda=0)=0
updates the current vertex with the direct solution x += H_ii\b_ii
void setColInHessian(int c)
set the row of this vertex in the Hessian
virtual sint04 copyB(g_type *b_) const =0
copies the b vector in the array b_
virtual sint04 dimension() const =0
dimension of the estimated state belonging to this node
virtual sint04 estimateDimension() const
returns the dimension of the extended representation used by get/setEstimate(g_type*) -1 if it is not...
virtual void discardTop()=0
pop the last element from the stack, without restoring the current estimate
virtual g_type * bData()=0
return a pointer to the b vector associated with this vertex
virtual void setId(int id)
sets the id of the node in the graph be sure that the graph keeps consistent after changing the id
virtual int stackSize() const =0
return the stack size
bool setEstimateData(const g_type *estimate)
sets the initial estimate from an array of g_type Implement setEstimateDataImpl()
virtual void clearQuadraticForm()=0
set the b vector part of this vertex to zero
int hessianIndex() const
temporary index of this node in the parameter vector obtained from linearization
virtual bool getMinimalEstimateData(g_type *) const
writes the estimate to an array of g_type
void setFixed(bool fixed)
true => this node should be considered fixed during the optimization
int _colInHessian
Column of this vertex in the Hessian matrix.
virtual void oplusImpl(const g_type *v)=0
update the position of the node from the parameters in v.
virtual bool getEstimateData(g_type *) const
writes the estimater to an array of g_type
void setMarginalized(bool marginalized)
true => this node should be marginalized out during the optimization
virtual sint04 minimalEstimateDimension() const
returns the dimension of the extended representation used by get/setEstimate(g_type*) -1 if it is not...
bool _marginalized
Whether this vertex is marginalized (Schur complement).
virtual const g_type & hessian(int i, int j) const =0
get the element from the hessian matrix
bool marginalized() const
true => this node is marginalized out during the optimization
virtual void push()=0
backup the position of the vertex to a stack
bool setMinimalEstimateData(const Buffer< g_type > &estimate)
sets the initial estimate from an array of g_type.
void oplus(const g_type *v)
Update the position of the node from the parameters in v.
int colInHessian() const
get the row of this vertex in the Hessian
virtual void pop()=0
restore the position of the vertex by retrieving the position from the stack
bool setMinimalEstimateData(const g_type *estimate)
sets the initial estimate from an array of g_type.
void setHessianIndex(int ti)
set the temporary index of the vertex in the parameter blocks
virtual bool setEstimateDataImpl(const g_type *)
writes the estimater to an array of g_type
base for all robust cost functions
Container that stores unique elements in no particular order, and which allow for fast retrieval or i...
Definition Set.h:59
uint04 size() const
Returns the number of elements in the Set.
Definition Set.h:158
The primary namespace for the NDEVR SDK.
constexpr t_type getMax(const t_type &left, const t_type &right)
Finds the max of the given arguments using the > operator The only requirement is that t_type have > ...
int32_t sint04
-Defines an alias representing a 4 byte, signed integer.
order edges based on the internal ID, which is assigned to the edge in addEdge()
order vertices based on their ID
This is an abstract class that represents one optimization problem.
OptimizableGraph()
empty constructor
virtual bool addEdge(HyperGraph::HGEdge *e_) final override
adds a new edge.
sint04 maxDimension() const
return the maximum dimension of all vertices in the graph
JacobianWorkspace _jacobianWorkspace
Workspace for computing Jacobians.
virtual void push()
push the estimate of all variables onto a stack
Buffer< OptimizableGraph::OGVertex * > VertexContainer
vector container for vertices
virtual bool addVertex(HyperGraph::HGVertex &v) final override
adds a new vertex.
int _nextEdgeId
Counter for assigning internal edge IDs.
JacobianWorkspace & jacobianWorkspace()
verify that all the information of the edges are semi positive definite, i.e., all Eigenvalues are >=...
Set< sint04 > dimensions() const
iterates over all vertices and returns a set of all the vertex dimensions in the graph
virtual void discardTop()
discard the last backup of the estimate for all variables by removing it from the stack
virtual void setFixed(Set< HyperGraph::HGVertex * > &vset, bool fixed)
fixes/releases a set of vertices
Buffer< OptimizableGraph::OGEdge * > EdgeContainer
vector container for edges
ActionType
Types of actions that can be registered for optimization callbacks.
@ AT_POSTITERATION
Action invoked after each iteration.
@ AT_NUM_ELEMENTS
Sentinel value; keep as last element.
@ AT_PREITERATION
Action invoked before each iteration.
virtual void push(Set< HyperGraph::HGVertex * > &vset)
push the estimate of a subset of the variables onto a stack
const JacobianWorkspace & jacobianWorkspace() const
Returns a const reference to the Jacobian workspace.
bool isSolverSuitable(const OptimizationAlgorithmProperty &solverProperty, const Set< sint04 > &vertDims_=Set< int >()) const
test whether a solver is suitable for optimizing this graph.
g_type chi2() const
returns the chi2 of the current configuration
virtual void pop(Set< HyperGraph::HGVertex * > &vset)
pop (restore) the estimate a subset of the variables from the stack
virtual void pop()
pop (restore) the estimate of all variables from the stack
describe the properties of a solver
bool requiresMarginalize
whether the solver requires marginalization of landmarks
sint04 landmarkDim
dimension of the landmar vertices (-1 if variable)
sint04 poseDim
dimension of the pose vertices (-1 if variable)