1 // g2o - General Graph Optimization
2 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, H. Strasdat, 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 #include "optimizable_graph.h"
28
29 #include <cassert>
30 #include <iostream>
31 #include <iomanip>
32 #include <fstream>
33 #include <algorithm>
34
35 #include <Eigen/Dense>
36
37 #include "estimate_propagator.h"
38 #include "factory.h"
39 #include "optimization_algorithm_property.h"
40 #include "hyper_graph_action.h"
41 #include "cache.h"
42 #include "robust_kernel.h"
43 #include "ownership.h"
44
45 #include "g2o/stuff/macros.h"
46 #include "g2o/stuff/color_macros.h"
47 #include "g2o/stuff/string_tools.h"
48 #include "g2o/stuff/misc.h"
49
50 namespace g2o {
51
52 using namespace std;
53
54 namespace {
printIdChain(std::ostream & os,const std::vector<int> & ids)55 std::ostream& printIdChain(std::ostream& os, const std::vector<int>& ids) {
56 for (size_t l = 0; l < ids.size(); ++l) {
57 if (l > 0) cerr << " <->";
58 cerr << " " << ids[l];
59 }
60 return os;
61 }
62 } // namespace
63
Vertex()64 OptimizableGraph::Vertex::Vertex() :
65 HyperGraph::Vertex(),
66 _graph(0), _userData(0), _hessianIndex(-1), _fixed(false), _marginalized(false),
67 _colInHessian(-1), _cacheContainer(0)
68 {
69 }
70
cacheContainer()71 CacheContainer* OptimizableGraph::Vertex::cacheContainer(){
72 if (! _cacheContainer)
73 _cacheContainer = new CacheContainer(this);
74 return _cacheContainer;
75 }
76
77
updateCache()78 void OptimizableGraph::Vertex::updateCache(){
79 if (_cacheContainer){
80 _cacheContainer->setUpdateNeeded();
81 _cacheContainer->update();
82 }
83 }
84
~Vertex()85 OptimizableGraph::Vertex::~Vertex()
86 {
87 delete _cacheContainer;
88 delete _userData;
89 }
90
setEstimateData(const number_t * v)91 bool OptimizableGraph::Vertex::setEstimateData(const number_t* v)
92 {
93 bool ret = setEstimateDataImpl(v);
94 updateCache();
95 return ret;
96 }
97
getEstimateData(number_t *) const98 bool OptimizableGraph::Vertex::getEstimateData(number_t *) const
99 {
100 return false;
101 }
102
estimateDimension() const103 int OptimizableGraph::Vertex::estimateDimension() const
104 {
105 return -1;
106 }
107
setMinimalEstimateData(const number_t * v)108 bool OptimizableGraph::Vertex::setMinimalEstimateData(const number_t* v)
109 {
110 bool ret = setMinimalEstimateDataImpl(v);
111 updateCache();
112 return ret;
113 }
114
getMinimalEstimateData(number_t *) const115 bool OptimizableGraph::Vertex::getMinimalEstimateData(number_t *) const
116 {
117 return false;
118 }
119
minimalEstimateDimension() const120 int OptimizableGraph::Vertex::minimalEstimateDimension() const
121 {
122 return -1;
123 }
124
125
Edge()126 OptimizableGraph::Edge::Edge() :
127 HyperGraph::Edge(),
128 _dimension(-1), _level(0), _robustKernel(nullptr)
129 {
130 }
131
~Edge()132 OptimizableGraph::Edge::~Edge()
133 {
134 release(_robustKernel);
135 }
136
graph()137 OptimizableGraph* OptimizableGraph::Edge::graph(){
138 if (! _vertices.size())
139 return nullptr;
140 OptimizableGraph::Vertex* v=(OptimizableGraph::Vertex*)_vertices[0];
141 if (!v)
142 return nullptr;
143 return v->graph();
144 }
145
graph() const146 const OptimizableGraph* OptimizableGraph::Edge::graph() const{
147 if (! _vertices.size())
148 return nullptr;
149 const OptimizableGraph::Vertex* v=(const OptimizableGraph::Vertex*) _vertices[0];
150 if (!v)
151 return nullptr;
152 return v->graph();
153 }
154
setParameterId(int argNum,int paramId)155 bool OptimizableGraph::Edge::setParameterId(int argNum, int paramId){
156 if ((int)_parameters.size()<=argNum)
157 return false;
158 if (argNum<0)
159 return false;
160 *_parameters[argNum] = 0;
161 _parameterIds[argNum] = paramId;
162 return true;
163 }
164
resolveParameters()165 bool OptimizableGraph::Edge::resolveParameters() {
166 if (!graph()) {
167 cerr << __PRETTY_FUNCTION__ << ": edge not registered with a graph" << endl;
168 return false;
169 }
170
171 assert (_parameters.size() == _parameterIds.size());
172 //cerr << __PRETTY_FUNCTION__ << ": encountered " << _parameters.size() << " parameters" << endl;
173 for (size_t i=0; i<_parameters.size(); i++){
174 int index = _parameterIds[i];
175 *_parameters[i] = graph()->parameter(index);
176 auto& aux = **_parameters[i];
177 if (typeid(aux).name()!=_parameterTypes[i]){
178 cerr << __PRETTY_FUNCTION__ << ": FATAL, parameter type mismatch - encountered " << typeid(aux).name() << "; should be " << _parameterTypes[i] << endl;
179 }
180 if (!*_parameters[i]) {
181 cerr << __PRETTY_FUNCTION__ << ": FATAL, *_parameters[i] == 0" << endl;
182 return false;
183 }
184 }
185 return true;
186 }
187
setRobustKernel(RobustKernel * ptr)188 void OptimizableGraph::Edge::setRobustKernel(RobustKernel* ptr)
189 {
190 if (_robustKernel)
191 release(_robustKernel);
192
193 _robustKernel = ptr;
194 }
195
resolveCaches()196 bool OptimizableGraph::Edge::resolveCaches() {
197 return true;
198 }
199
setMeasurementData(const number_t *)200 bool OptimizableGraph::Edge::setMeasurementData(const number_t *)
201 {
202 return false;
203 }
204
getMeasurementData(number_t *) const205 bool OptimizableGraph::Edge::getMeasurementData(number_t *) const
206 {
207 return false;
208 }
209
measurementDimension() const210 int OptimizableGraph::Edge::measurementDimension() const
211 {
212 return -1;
213 }
214
setMeasurementFromState()215 bool OptimizableGraph::Edge::setMeasurementFromState(){
216 return false;
217 }
218
OptimizableGraph()219 OptimizableGraph::OptimizableGraph()
220 {
221 _nextEdgeId = 0;
222 _graphActions.resize(AT_NUM_ELEMENTS);
223 }
224
~OptimizableGraph()225 OptimizableGraph::~OptimizableGraph()
226 {
227 clear();
228 clearParameters();
229 }
230
addVertex(OptimizableGraph::Vertex * ov,Data * userData)231 bool OptimizableGraph::addVertex(OptimizableGraph::Vertex* ov, Data* userData)
232 {
233 if (ov->id() <0){
234 cerr << __FUNCTION__ << ": FATAL, a vertex with (negative) ID " << ov->id() << " cannot be inserted in the graph" << endl;
235 assert(0 && "Invalid vertex id");
236 return false;
237 }
238 Vertex* inserted = vertex(ov->id());
239 if (inserted) {
240 cerr << __FUNCTION__ << ": FATAL, a vertex with ID " << ov->id() << " has already been registered with this graph" << endl;
241 assert(0 && "Vertex with this ID already contained in the graph");
242 return false;
243 }
244 if (ov->_graph != nullptr && ov->_graph != this) {
245 cerr << __FUNCTION__ << ": FATAL, vertex with ID " << ov->id() << " has already registered with another graph " << ov->_graph << endl;
246 assert(0 && "Vertex already registered with another graph");
247 return false;
248 }
249 if (userData)
250 ov->setUserData(userData);
251 ov->_graph=this;
252 return HyperGraph::addVertex(ov);
253 }
254
addVertex(HyperGraph::Vertex * v,Data * userData)255 bool OptimizableGraph::addVertex(HyperGraph::Vertex* v, Data* userData) {
256 OptimizableGraph::Vertex* ov = dynamic_cast<OptimizableGraph::Vertex*>(v);
257 assert(ov && "Vertex does not inherit from OptimizableGraph::Vertex");
258 if (!ov) return false;
259
260 return addVertex(ov, userData);
261 }
262
addEdge(OptimizableGraph::Edge * e)263 bool OptimizableGraph::addEdge(OptimizableGraph::Edge* e) {
264 OptimizableGraph* g = e->graph();
265
266 if (g != nullptr && g != this) {
267 cerr << __FUNCTION__ << ": FATAL, edge with ID " << e->id()
268 << " has already registered with another graph " << g << endl;
269 assert(0 && "Edge already registered with another graph");
270 return false;
271 }
272
273 bool eresult = HyperGraph::addEdge(e);
274 if (!eresult) return false;
275
276 e->_internalId = _nextEdgeId++;
277 if (e->numUndefinedVertices()) return true;
278 if (!e->resolveParameters()) {
279 cerr << __FUNCTION__ << ": FATAL, cannot resolve parameters for edge " << e << endl;
280 return false;
281 }
282 if (!e->resolveCaches()) {
283 cerr << __FUNCTION__ << ": FATAL, cannot resolve caches for edge " << e << endl;
284 return false;
285 }
286
287 _jacobianWorkspace.updateSize(e);
288
289 return true;
290 }
291
addEdge(HyperGraph::Edge * e_)292 bool OptimizableGraph::addEdge(HyperGraph::Edge* e_)
293 {
294 OptimizableGraph::Edge* e = dynamic_cast<OptimizableGraph::Edge*>(e_);
295 assert(e && "Edge does not inherit from OptimizableGraph::Edge");
296 if (!e)
297 return false;
298 return addEdge(e);
299 }
300
setEdgeVertex(HyperGraph::Edge * e,int pos,HyperGraph::Vertex * v)301 bool OptimizableGraph::setEdgeVertex(HyperGraph::Edge* e, int pos, HyperGraph::Vertex* v) {
302 if (!HyperGraph::setEdgeVertex(e, pos, v)) {
303 return false;
304 }
305 if (!e->numUndefinedVertices()) {
306 #ifndef NDEBUG
307 OptimizableGraph::Edge* ee = dynamic_cast<OptimizableGraph::Edge*>(e);
308 assert(ee && "Edge is not a OptimizableGraph::Edge");
309 #else
310 OptimizableGraph::Edge* ee = static_cast<OptimizableGraph::Edge*>(e);
311 #endif
312 if (!ee->resolveParameters()) {
313 cerr << __FUNCTION__ << ": FATAL, cannot resolve parameters for edge " << e << endl;
314 return false;
315 }
316 if (!ee->resolveCaches()) {
317 cerr << __FUNCTION__ << ": FATAL, cannot resolve caches for edge " << e << endl;
318 return false;
319 }
320 _jacobianWorkspace.updateSize(e);
321 }
322 return true;
323 }
324
optimize(int,bool)325 int OptimizableGraph::optimize(int /*iterations*/, bool /*online*/) {return 0;}
326
chi2() const327 number_t OptimizableGraph::chi2() const
328 {
329 number_t chi = 0.0;
330 for (OptimizableGraph::EdgeSet::const_iterator it = this->edges().begin(); it != this->edges().end(); ++it) {
331 const OptimizableGraph::Edge* e = static_cast<const OptimizableGraph::Edge*>(*it);
332 chi += e->chi2();
333 }
334 return chi;
335 }
336
push()337 void OptimizableGraph::push() {
338 forEachVertex([](OptimizableGraph::Vertex* v) { v->push(); });
339 }
340
pop()341 void OptimizableGraph::pop() {
342 forEachVertex([](OptimizableGraph::Vertex* v) { v->pop(); });
343 }
344
discardTop()345 void OptimizableGraph::discardTop() {
346 forEachVertex([](OptimizableGraph::Vertex* v) { v->discardTop(); });
347 }
348
push(HyperGraph::VertexSet & vset)349 void OptimizableGraph::push(HyperGraph::VertexSet& vset) {
350 forEachVertex(vset, [](OptimizableGraph::Vertex* v) { v->push(); });
351 }
352
pop(HyperGraph::VertexSet & vset)353 void OptimizableGraph::pop(HyperGraph::VertexSet& vset) {
354 forEachVertex(vset, [](OptimizableGraph::Vertex* v) { v->pop(); });
355 }
356
discardTop(HyperGraph::VertexSet & vset)357 void OptimizableGraph::discardTop(HyperGraph::VertexSet& vset) {
358 forEachVertex(vset, [](OptimizableGraph::Vertex* v) { v->discardTop(); });
359 }
360
setFixed(HyperGraph::VertexSet & vset,bool fixed)361 void OptimizableGraph::setFixed(HyperGraph::VertexSet& vset, bool fixed) {
362 forEachVertex(vset, [fixed](OptimizableGraph::Vertex* v) { v->setFixed(fixed); });
363 }
364
forEachVertex(std::function<void (OptimizableGraph::Vertex *)> fn)365 void OptimizableGraph::forEachVertex(std::function<void(OptimizableGraph::Vertex*)> fn) {
366 for (auto it = _vertices.begin(); it != _vertices.end(); ++it) {
367 OptimizableGraph::Vertex* v = static_cast<OptimizableGraph::Vertex*>(it->second);
368 fn(v);
369 }
370 }
371
forEachVertex(HyperGraph::VertexSet & vset,std::function<void (OptimizableGraph::Vertex *)> fn)372 void OptimizableGraph::forEachVertex(HyperGraph::VertexSet& vset,
373 std::function<void(OptimizableGraph::Vertex*)> fn) {
374 for (auto it = vset.begin(); it != vset.end(); ++it) {
375 OptimizableGraph::Vertex* v = static_cast<OptimizableGraph::Vertex*>(*it);
376 fn(v);
377 }
378 }
379
load(istream & is)380 bool OptimizableGraph::load(istream& is)
381 {
382 set<string> warnedUnknownTypes;
383 stringstream currentLine;
384 string token;
385
386 Factory* factory = Factory::instance();
387 HyperGraph::GraphElemBitset elemBitset;
388 elemBitset[HyperGraph::HGET_PARAMETER] = 1;
389 elemBitset.flip();
390
391 HyperGraph::GraphElemBitset elemParamBitset;
392 elemParamBitset[HyperGraph::HGET_PARAMETER] = 1;
393
394 HyperGraph::DataContainer* previousDataContainer = 0;
395 Data* previousData = 0;
396
397 int lineNumber = 0;
398 while (1) {
399 int bytesRead = readLine(is, currentLine);
400 lineNumber++;
401 if (bytesRead == -1) break;
402 currentLine >> token;
403 // cerr << "Token=" << token << endl;
404 if (bytesRead == 0 || token.size() == 0 || token[0] == '#') continue;
405
406 // handle commands encoded in the file
407 if (token == "FIX") {
408 int id;
409 while (currentLine >> id) {
410 OptimizableGraph::Vertex* v = static_cast<OptimizableGraph::Vertex*>(vertex(id));
411 if (v) {
412 #ifndef NDEBUG
413 cerr << "Fixing vertex " << v->id() << endl;
414 #endif
415 v->setFixed(true);
416 } else {
417 cerr << "Warning: Unable to fix vertex with id " << id << ". Not found in the graph."
418 << endl;
419 }
420 }
421 continue;
422 }
423
424 // do the mapping to an internal type if it matches
425 if (_renamedTypesLookup.size() > 0) {
426 map<string, string>::const_iterator foundIt = _renamedTypesLookup.find(token);
427 if (foundIt != _renamedTypesLookup.end()) {
428 token = foundIt->second;
429 }
430 }
431
432 if (!factory->knowsTag(token)) {
433 if (warnedUnknownTypes.count(token) != 1) {
434 warnedUnknownTypes.insert(token);
435 cerr << CL_RED(__PRETTY_FUNCTION__ << " unknown type: " << token) << endl;
436 }
437 continue;
438 }
439
440 // first handle the parameters
441 HyperGraph::HyperGraphElement* pelement = factory->construct(token, elemParamBitset);
442 if (pelement) { // not a parameter or otherwise unknown tag
443 assert(pelement->elementType() == HyperGraph::HGET_PARAMETER && "Should be a param");
444 Parameter* p = static_cast<Parameter*>(pelement);
445 int pid;
446 currentLine >> pid;
447 p->setId(pid);
448 bool r = p->read(currentLine);
449 if (!r) {
450 cerr << __PRETTY_FUNCTION__ << ": Error reading data " << token << " for parameter " << pid
451 << " at line " << lineNumber << endl;
452 delete p;
453 } else {
454 if (!_parameters.addParameter(p)) {
455 cerr << __PRETTY_FUNCTION__ << ": Parameter of type:" << token << " id:" << pid
456 << " already defined"
457 << " at line " << lineNumber << endl;
458 }
459 }
460 continue;
461 }
462
463 HyperGraph::HyperGraphElement* element = factory->construct(token, elemBitset);
464 if (dynamic_cast<Vertex*>(element)) { // it's a vertex type
465 previousData = 0;
466 Vertex* v = static_cast<Vertex*>(element);
467 int id;
468 currentLine >> id;
469 bool r = v->read(currentLine);
470 if (!r)
471 cerr << __PRETTY_FUNCTION__ << ": Error reading vertex " << token << " " << id
472 << " at line " << lineNumber << endl;
473 v->setId(id);
474 if (!addVertex(v)) {
475 cerr << __PRETTY_FUNCTION__ << ": Failure adding Vertex, " << token << " " << id
476 << " at line " << lineNumber << endl;
477 delete v;
478 } else {
479 previousDataContainer = v;
480 }
481 } else if (dynamic_cast<Edge*>(element)) {
482 // cerr << "it is an edge" << endl;
483 previousData = 0;
484 Edge* e = static_cast<Edge*>(element);
485 int numV = e->vertices().size();
486
487 vector<int> ids;
488 if (e->vertices().size() != 0) {
489 ids.resize(e->vertices().size());
490 for (int l = 0; l < numV; ++l) currentLine >> ids[l];
491 } else {
492 string buff; // reading the IDs of a dynamically sized edge
493 while (currentLine >> buff) {
494 if (buff == "||") break;
495 ids.push_back(atoi(buff.c_str()));
496 currentLine >> buff;
497 }
498 e->resize(numV);
499 }
500 bool vertsOkay = true;
501 for (size_t l = 0; l < ids.size(); ++l) {
502 int vertexId = ids[l];
503 if (vertexId != HyperGraph::UnassignedId) {
504 HyperGraph::Vertex* v = vertex(vertexId);
505 if (!v) {
506 vertsOkay = false;
507 break;
508 }
509 e->setVertex(l, v);
510 }
511 }
512 if (!vertsOkay) {
513 cerr << __PRETTY_FUNCTION__ << ": Unable to find vertices for edge " << token << " at line "
514 << lineNumber << " IDs: ";
515 printIdChain(cerr, ids) << std::endl;
516 delete e;
517 e = nullptr;
518 } else {
519 bool r = e->read(currentLine);
520 if (!r || !addEdge(e)) {
521 cerr << __PRETTY_FUNCTION__ << ": Unable to add edge " << token << " at line "
522 << lineNumber << " IDs: ";
523 printIdChain(cerr, ids) << std::endl;
524 delete e;
525 e = nullptr;
526 }
527 }
528
529 previousDataContainer = e;
530 } else if (dynamic_cast<Data*>(element)) { // reading in the data packet for the vertex
531 // cerr << "read data packet " << token << " vertex " << previousVertex->id() << endl;
532 Data* d = static_cast<Data*>(element);
533 bool r = d->read(currentLine);
534 if (!r) {
535 cerr << __PRETTY_FUNCTION__ << ": Error reading data " << token << " at line " << lineNumber
536 << " IDs: " << endl;
537 delete d;
538 previousData = 0;
539 } else if (previousData) {
540 previousData->setNext(d);
541 d->setDataContainer(previousData->dataContainer());
542 previousData = d;
543 } else if (previousDataContainer) {
544 previousDataContainer->setUserData(d);
545 d->setDataContainer(previousDataContainer);
546 previousData = d;
547 previousDataContainer = 0;
548 } else {
549 cerr << __PRETTY_FUNCTION__ << ": got data element, but no data container available"
550 << endl;
551 delete d;
552 previousData = 0;
553 }
554 }
555 } // while read line
556
557 #ifndef NDEBUG
558 cerr << "Loaded " << _parameters.size() << " parameters" << endl;
559 #endif
560
561 return true;
562 }
563
load(const char * filename)564 bool OptimizableGraph::load(const char* filename)
565 {
566 ifstream ifs(filename);
567 if (!ifs) {
568 cerr << __PRETTY_FUNCTION__ << " unable to open file " << filename << endl;
569 return false;
570 }
571 return load(ifs);
572 }
573
save(const char * filename,int level) const574 bool OptimizableGraph::save(const char* filename, int level) const
575 {
576 ofstream ofs(filename);
577 if (!ofs)
578 return false;
579 return save(ofs, level);
580 }
581
save(ostream & os,int level) const582 bool OptimizableGraph::save(ostream& os, int level) const
583 {
584 // write the parameters to the top of the file
585 if (!_parameters.write(os)) return false;
586 set<Vertex*, VertexIDCompare> verticesToSave; // set sorted by ID
587 for (HyperGraph::EdgeSet::const_iterator it = edges().begin(); it != edges().end(); ++it) {
588 OptimizableGraph::Edge* e = static_cast<OptimizableGraph::Edge*>(*it);
589 if (e->level() == level) {
590 for (auto it = e->vertices().begin(); it != e->vertices().end(); ++it) {
591 if (*it) verticesToSave.insert(static_cast<OptimizableGraph::Vertex*>(*it));
592 }
593 }
594 }
595
596 for (auto v : verticesToSave) saveVertex(os, v);
597
598 std::vector<HyperGraph::Edge*> edgesToSave;
599 std::copy_if(edges().begin(), edges().end(), std::back_inserter(edgesToSave),
600 [level](const HyperGraph::Edge* ee) {
601 const OptimizableGraph::Edge* e = dynamic_cast<const OptimizableGraph::Edge*>(ee);
602 return (e->level() == level);
603 });
604 sort(edgesToSave.begin(), edgesToSave.end(), EdgeIDCompare());
605 for (auto e : edgesToSave) saveEdge(os, static_cast<Edge*>(e));
606
607 return os.good();
608 }
609
saveSubset(ostream & os,HyperGraph::VertexSet & vset,int level)610 bool OptimizableGraph::saveSubset(ostream& os, HyperGraph::VertexSet& vset, int level) {
611 if (!_parameters.write(os)) return false;
612
613 for (auto v : vset) saveVertex(os, static_cast<Vertex*>(v));
614
615 for (HyperGraph::EdgeSet::const_iterator it = edges().begin(); it != edges().end(); ++it) {
616 OptimizableGraph::Edge* e = dynamic_cast<OptimizableGraph::Edge*>(*it);
617 if (e->level() != level) continue;
618
619 bool verticesInEdge = true;
620 for (vector<HyperGraph::Vertex*>::const_iterator it = e->vertices().begin(); it != e->vertices().end(); ++it) {
621 if (vset.find(*it) == vset.end()) {
622 verticesInEdge = false;
623 break;
624 }
625 }
626 if (!verticesInEdge) continue;
627
628 saveEdge(os, e);
629 }
630
631 return os.good();
632 }
633
saveSubset(ostream & os,HyperGraph::EdgeSet & eset)634 bool OptimizableGraph::saveSubset(ostream& os, HyperGraph::EdgeSet& eset) {
635 if (!_parameters.write(os)) return false;
636 HyperGraph::VertexSet vset;
637 for (auto e : eset)
638 for (auto v : e->vertices())
639 if (v) vset.insert(v);
640
641 for (auto v : vset) saveVertex(os, static_cast<Vertex*>(v));
642 for (auto e : eset) saveEdge(os, static_cast<Edge*>(e));
643 return os.good();
644 }
645
maxDimension() const646 int OptimizableGraph::maxDimension() const{
647 int maxDim=0;
648 for (HyperGraph::VertexIDMap::const_iterator it=vertices().begin(); it!=vertices().end(); ++it){
649 const OptimizableGraph::Vertex* v= static_cast< const OptimizableGraph::Vertex*>(it->second);
650 maxDim = (std::max)(maxDim, v->dimension());
651 }
652 return maxDim;
653 }
654
setRenamedTypesFromString(const std::string & types)655 void OptimizableGraph::setRenamedTypesFromString(const std::string& types)
656 {
657 Factory* factory = Factory::instance();
658 vector<string> typesMap = strSplit(types, ",");
659 for (size_t i = 0; i < typesMap.size(); ++i) {
660 vector<string> m = strSplit(typesMap[i], "=");
661 if (m.size() != 2) {
662 cerr << __PRETTY_FUNCTION__ << ": unable to extract type map from " << typesMap[i] << endl;
663 continue;
664 }
665 string typeInFile = trim(m[0]);
666 string loadedType = trim(m[1]);
667 if (! factory->knowsTag(loadedType)) {
668 cerr << __PRETTY_FUNCTION__ << ": unknown type " << loadedType << endl;
669 continue;
670 }
671
672 _renamedTypesLookup[typeInFile] = loadedType;
673 }
674
675 cerr << "# load look up table" << endl;
676 for (std::map<std::string, std::string>::const_iterator it = _renamedTypesLookup.begin(); it != _renamedTypesLookup.end(); ++it) {
677 cerr << "#\t" << it->first << " -> " << it->second << endl;
678 }
679 }
680
isSolverSuitable(const OptimizationAlgorithmProperty & solverProperty,const std::set<int> & vertDims_) const681 bool OptimizableGraph::isSolverSuitable(const OptimizationAlgorithmProperty& solverProperty, const std::set<int>& vertDims_) const
682 {
683 std::set<int> auxDims;
684 if (vertDims_.size() == 0) {
685 auxDims = dimensions();
686 }
687 const set<int>& vertDims = vertDims_.size() == 0 ? auxDims : vertDims_;
688 bool suitableSolver = true;
689 if (vertDims.size() == 2) {
690 if (solverProperty.requiresMarginalize) {
691 suitableSolver = vertDims.count(solverProperty.poseDim) == 1 && vertDims.count(solverProperty.landmarkDim) == 1;
692 }
693 else {
694 suitableSolver = solverProperty.poseDim == -1;
695 }
696 } else if (vertDims.size() == 1) {
697 suitableSolver = vertDims.count(solverProperty.poseDim) == 1 || solverProperty.poseDim == -1;
698 } else {
699 suitableSolver = solverProperty.poseDim == -1 && !solverProperty.requiresMarginalize;
700 }
701 return suitableSolver;
702 }
703
dimensions() const704 std::set<int> OptimizableGraph::dimensions() const
705 {
706 std::set<int> auxDims;
707 for (VertexIDMap::const_iterator it = vertices().begin(); it != vertices().end(); ++it) {
708 OptimizableGraph::Vertex* v = static_cast<OptimizableGraph::Vertex*>(it->second);
709 auxDims.insert(v->dimension());
710 }
711 return auxDims;
712 }
713
performActions(int iter,HyperGraphActionSet & actions)714 void OptimizableGraph::performActions(int iter, HyperGraphActionSet& actions)
715 {
716 if (actions.size() > 0) {
717 HyperGraphAction::ParametersIteration params(iter);
718 for (HyperGraphActionSet::iterator it = actions.begin(); it != actions.end(); ++it) {
719 (*(*it))(this, ¶ms);
720 }
721 }
722 }
723
preIteration(int iter)724 void OptimizableGraph::preIteration(int iter)
725 {
726 performActions(iter, _graphActions[AT_PREITERATION]);
727 }
728
postIteration(int iter)729 void OptimizableGraph::postIteration(int iter)
730 {
731 performActions(iter, _graphActions[AT_POSTITERATION]);
732 }
733
addPostIterationAction(HyperGraphAction * action)734 bool OptimizableGraph::addPostIterationAction(HyperGraphAction* action)
735 {
736 std::pair<HyperGraphActionSet::iterator, bool> insertResult = _graphActions[AT_POSTITERATION].insert(action);
737 return insertResult.second;
738 }
739
addPreIterationAction(HyperGraphAction * action)740 bool OptimizableGraph::addPreIterationAction(HyperGraphAction* action)
741 {
742 std::pair<HyperGraphActionSet::iterator, bool> insertResult = _graphActions[AT_PREITERATION].insert(action);
743 return insertResult.second;
744 }
745
removePreIterationAction(HyperGraphAction * action)746 bool OptimizableGraph::removePreIterationAction(HyperGraphAction* action)
747 {
748 return _graphActions[AT_PREITERATION].erase(action) > 0;
749 }
750
removePostIterationAction(HyperGraphAction * action)751 bool OptimizableGraph::removePostIterationAction(HyperGraphAction* action)
752 {
753 return _graphActions[AT_POSTITERATION].erase(action) > 0;
754 }
755
saveUserData(std::ostream & os,HyperGraph::Data * d) const756 bool OptimizableGraph::saveUserData(std::ostream& os, HyperGraph::Data* d) const {
757 Factory* factory = Factory::instance();
758 while (d) { // write the data packet for the vertex
759 string tag = factory->tag(d);
760 if (tag.size() > 0) {
761 os << tag << " ";
762 d->write(os);
763 os << endl;
764 }
765 d = d->next();
766 }
767 return os.good();
768 }
769
saveVertex(std::ostream & os,OptimizableGraph::Vertex * v) const770 bool OptimizableGraph::saveVertex(std::ostream& os, OptimizableGraph::Vertex* v) const
771 {
772 Factory* factory = Factory::instance();
773 string tag = factory->tag(v);
774 if (tag.size() > 0) {
775 os << tag << " " << v->id() << " ";
776 v->write(os);
777 os << endl;
778 saveUserData(os, v->userData());
779 if (v->fixed()) {
780 os << "FIX " << v->id() << endl;
781 }
782 return os.good();
783 }
784 return false;
785 }
786
saveParameter(std::ostream & os,Parameter * p) const787 bool OptimizableGraph::saveParameter(std::ostream& os, Parameter* p) const
788 {
789 Factory* factory = Factory::instance();
790 string tag = factory->tag(p);
791 if (tag.size() > 0) {
792 os << tag << " " << p->id() << " ";
793 p->write(os);
794 os << endl;
795 }
796 return os.good();
797 }
798
saveEdge(std::ostream & os,OptimizableGraph::Edge * e) const799 bool OptimizableGraph::saveEdge(std::ostream& os, OptimizableGraph::Edge* e) const
800 {
801 Factory* factory = Factory::instance();
802 string tag = factory->tag(e);
803 if (tag.size() > 0) {
804 os << tag << " ";
805 for (vector<HyperGraph::Vertex*>::const_iterator it = e->vertices().begin(); it != e->vertices().end(); ++it) {
806 int vertexId = (*it) ? (*it)->id() : HyperGraph::UnassignedId;
807 os << vertexId << " ";
808 }
809 e->write(os);
810 os << endl;
811 saveUserData(os, e->userData());
812 return os.good();
813 }
814 return false;
815 }
816
clearParameters()817 void OptimizableGraph::clearParameters()
818 {
819 HyperGraph::clear();
820 _parameters.clear();
821 }
822
verifyInformationMatrices(bool verbose) const823 bool OptimizableGraph::verifyInformationMatrices(bool verbose) const
824 {
825 bool allEdgeOk = true;
826 Eigen::SelfAdjointEigenSolver<MatrixX> eigenSolver;
827 for (OptimizableGraph::EdgeSet::const_iterator it = edges().begin(); it != edges().end(); ++it) {
828 OptimizableGraph::Edge* e = static_cast<OptimizableGraph::Edge*>(*it);
829 MatrixX::MapType information(e->informationData(), e->dimension(), e->dimension());
830 // test on symmetry
831 bool isSymmetric = information.transpose() == information;
832 bool okay = isSymmetric;
833 if (isSymmetric) {
834 // compute the eigenvalues
835 eigenSolver.compute(information, Eigen::EigenvaluesOnly);
836 bool isSPD = eigenSolver.eigenvalues()(0) >= 0.;
837 okay = okay && isSPD;
838 }
839 allEdgeOk = allEdgeOk && okay;
840 if (! okay) {
841 if (verbose) {
842 if (! isSymmetric)
843 cerr << "Information Matrix for an edge is not symmetric:";
844 else
845 cerr << "Information Matrix for an edge is not SPD:";
846 for (size_t i = 0; i < e->vertices().size(); ++i)
847 cerr << " " << e->vertex(i)->id();
848 if (isSymmetric)
849 cerr << "\teigenvalues: " << eigenSolver.eigenvalues().transpose();
850 cerr << endl;
851 }
852 }
853 }
854 return allEdgeOk;
855 }
856
initMultiThreading()857 bool OptimizableGraph::initMultiThreading()
858 {
859 # if (defined G2O_OPENMP) && EIGEN_VERSION_AT_LEAST(3,1,0)
860 Eigen::initParallel();
861 # endif
862 return true;
863 }
864
865 } // end namespace
866
867