Merge pull request #40 from borglab/feature/factor_graph_update

Factor graph update and add_factors method
release/4.3a0
Frank Dellaert 2019-06-02 02:03:29 -04:00 committed by GitHub
commit 51fb1b6696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 556 additions and 489 deletions

View File

@ -26,74 +26,105 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <stdio.h> #include <stdio.h>
#include <algorithm>
#include <iostream> // for cout :-(
#include <sstream> #include <sstream>
#include <iostream> // for cout :-( #include <string>
namespace gtsam { namespace gtsam {
/* ************************************************************************* */ /* ************************************************************************* */
template<class FACTOR> template <class FACTOR>
void FactorGraph<FACTOR>::print(const std::string& s, const KeyFormatter& formatter) const { void FactorGraph<FACTOR>::print(const std::string& s,
std::cout << s << std::endl; const KeyFormatter& formatter) const {
std::cout << "size: " << size() << std::endl; std::cout << s << std::endl;
for (size_t i = 0; i < factors_.size(); i++) { std::cout << "size: " << size() << std::endl;
std::stringstream ss; for (size_t i = 0; i < factors_.size(); i++) {
ss << "factor " << i << ": "; std::stringstream ss;
if (factors_[i]) ss << "factor " << i << ": ";
factors_[i]->print(ss.str(), formatter); if (factors_[i]) factors_[i]->print(ss.str(), formatter);
}
}
/* ************************************************************************* */
template <class FACTOR>
bool FactorGraph<FACTOR>::equals(const This& fg, double tol) const {
// check whether the two factor graphs have the same number of factors.
if (factors_.size() != fg.size()) return false;
// check whether the factors are the same, in same order.
for (size_t i = 0; i < factors_.size(); i++) {
sharedFactor f1 = factors_[i], f2 = fg.factors_[i];
if (f1 == NULL && f2 == NULL) continue;
if (f1 == NULL || f2 == NULL) return false;
if (!f1->equals(*f2, tol)) return false;
}
return true;
}
/* ************************************************************************* */
template <class FACTOR>
size_t FactorGraph<FACTOR>::nrFactors() const {
size_t size_ = 0;
for (const sharedFactor& factor : factors_)
if (factor) size_++;
return size_;
}
/* ************************************************************************* */
template <class FACTOR>
KeySet FactorGraph<FACTOR>::keys() const {
KeySet keys;
for (const sharedFactor& factor : this->factors_) {
if (factor) keys.insert(factor->begin(), factor->end());
}
return keys;
}
/* ************************************************************************* */
template <class FACTOR>
KeyVector FactorGraph<FACTOR>::keyVector() const {
KeyVector keys;
keys.reserve(2 * size()); // guess at size
for (const sharedFactor& factor : factors_)
if (factor) keys.insert(keys.end(), factor->begin(), factor->end());
std::sort(keys.begin(), keys.end());
auto last = std::unique(keys.begin(), keys.end());
keys.erase(last, keys.end());
return keys;
}
/* ************************************************************************* */
template <class FACTOR>
template <typename CONTAINER, typename>
FactorIndices FactorGraph<FACTOR>::add_factors(const CONTAINER& factors,
bool useEmptySlots) {
const size_t num_factors = factors.size();
FactorIndices newFactorIndices(num_factors);
if (useEmptySlots) {
size_t i = 0;
for (size_t j = 0; j < num_factors; ++j) {
// Loop to find the next available factor slot
do {
if (i >= size())
// Make room for remaining factors, happens only once.
resize(size() + num_factors - j);
else if (at(i))
++i; // Move on to the next slot or past end.
else
break; // We found an empty slot, break to fill it.
} while (true);
// Use the current slot, updating graph and newFactorSlots.
at(i) = factors[j];
newFactorIndices[j] = i;
} }
} else {
// We're not looking for unused slots, so just add the factors at the end.
for (size_t i = 0; i < num_factors; ++i) newFactorIndices[i] = i + size();
push_back(factors);
} }
return newFactorIndices;
}
/* ************************************************************************* */ } // namespace gtsam
template<class FACTOR>
bool FactorGraph<FACTOR>::equals(const This& fg, double tol) const {
/** check whether the two factor graphs have the same number of factors_ */
if (factors_.size() != fg.size()) return false;
/** check whether the factors_ are the same */
for (size_t i = 0; i < factors_.size(); i++) {
// TODO: Doesn't this force order of factor insertion?
sharedFactor f1 = factors_[i], f2 = fg.factors_[i];
if (f1 == NULL && f2 == NULL) continue;
if (f1 == NULL || f2 == NULL) return false;
if (!f1->equals(*f2, tol)) return false;
}
return true;
}
/* ************************************************************************* */
template<class FACTOR>
size_t FactorGraph<FACTOR>::nrFactors() const {
size_t size_ = 0;
for(const sharedFactor& factor: factors_)
if (factor) size_++;
return size_;
}
/* ************************************************************************* */
template<class FACTOR>
KeySet FactorGraph<FACTOR>::keys() const {
KeySet keys;
for(const sharedFactor& factor: this->factors_) {
if(factor)
keys.insert(factor->begin(), factor->end());
}
return keys;
}
/* ************************************************************************* */
template <class FACTOR>
KeyVector FactorGraph<FACTOR>::keyVector() const {
KeyVector keys;
keys.reserve(2 * size()); // guess at size
for (const sharedFactor& factor: factors_)
if (factor)
keys.insert(keys.end(), factor->begin(), factor->end());
std::sort(keys.begin(), keys.end());
auto last = std::unique(keys.begin(), keys.end());
keys.erase(last, keys.end());
return keys;
}
/* ************************************************************************* */
} // namespace gtsam

View File

@ -22,350 +22,382 @@
#pragma once #pragma once
#include <gtsam/base/Testable.h>
#include <gtsam/base/FastVector.h> #include <gtsam/base/FastVector.h>
#include <gtsam/base/Testable.h>
#include <gtsam/inference/Key.h> #include <gtsam/inference/Key.h>
#include <Eigen/Core> // for Eigen::aligned_allocator #include <Eigen/Core> // for Eigen::aligned_allocator
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/assign/list_inserter.hpp> #include <boost/assign/list_inserter.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/vector.hpp>
#include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
namespace gtsam { namespace gtsam {
/// Define collection type:
typedef FastVector<FactorIndex> FactorIndices;
// Forward declarations // Forward declarations
template<class CLIQUE> class BayesTree; template <class CLIQUE>
class BayesTree;
/** Helper */ /** Helper */
template<class C> template <class C>
class CRefCallPushBack class CRefCallPushBack {
{ C& obj;
C& obj;
public:
CRefCallPushBack(C& obj) : obj(obj) {}
template<typename A>
void operator()(const A& a) { obj.push_back(a); }
};
/** Helper */ public:
template<class C> explicit CRefCallPushBack(C& obj) : obj(obj) {}
class RefCallPushBack template <typename A>
{ void operator()(const A& a) {
C& obj; obj.push_back(a);
public: }
RefCallPushBack(C& obj) : obj(obj) {} };
template<typename A>
void operator()(A& a) { obj.push_back(a); }
};
/** Helper */ /** Helper */
template<class C> template <class C>
class CRefCallAddCopy class RefCallPushBack {
{ C& obj;
C& obj;
public: public:
CRefCallAddCopy(C& obj) : obj(obj) {} explicit RefCallPushBack(C& obj) : obj(obj) {}
template<typename A> template <typename A>
void operator()(const A& a) { obj.addCopy(a); } void operator()(A& a) {
}; obj.push_back(a);
}
};
/** Helper */
template <class C>
class CRefCallAddCopy {
C& obj;
public:
explicit CRefCallAddCopy(C& obj) : obj(obj) {}
template <typename A>
void operator()(const A& a) {
obj.addCopy(a);
}
};
/**
* A factor graph is a bipartite graph with factor nodes connected to variable
* nodes. In this class, however, only factor nodes are kept around.
* \nosubgrouping
*/
template <class FACTOR>
class FactorGraph {
public:
typedef FACTOR FactorType; ///< factor type
typedef boost::shared_ptr<FACTOR>
sharedFactor; ///< Shared pointer to a factor
typedef sharedFactor value_type;
typedef typename FastVector<sharedFactor>::iterator iterator;
typedef typename FastVector<sharedFactor>::const_iterator const_iterator;
private:
typedef FactorGraph<FACTOR> This; ///< Typedef for this class
typedef boost::shared_ptr<This>
shared_ptr; ///< Shared pointer for this class
/// Check if a DERIVEDFACTOR is in fact derived from FactorType.
template <typename DERIVEDFACTOR>
using IsDerived = typename std::enable_if<
std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type;
/// Check if T has a value_type derived from FactorType.
template <typename T>
using HasDerivedValueType = typename std::enable_if<
std::is_base_of<FactorType, typename T::value_type>::value>::type;
/// Check if T has a value_type derived from FactorType.
template <typename T>
using HasDerivedElementType = typename std::enable_if<std::is_base_of<
FactorType, typename T::value_type::element_type>::value>::type;
protected:
/** concept check, makes sure FACTOR defines print and equals */
GTSAM_CONCEPT_TESTABLE_TYPE(FACTOR)
/** Collection of factors */
FastVector<sharedFactor> factors_;
/// @name Standard Constructors
/// @{
/** Default constructor */
FactorGraph() {}
/** Constructor from iterator over factors (shared_ptr or plain objects) */
template <typename ITERATOR>
FactorGraph(ITERATOR firstFactor, ITERATOR lastFactor) {
push_back(firstFactor, lastFactor);
}
/** Construct from container of factors (shared_ptr or plain objects) */
template <class CONTAINER>
explicit FactorGraph(const CONTAINER& factors) {
push_back(factors);
}
/// @}
public:
/// @name Adding Single Factors
/// @{
/** /**
* A factor graph is a bipartite graph with factor nodes connected to variable nodes. * Reserve space for the specified number of factors if you know in
* In this class, however, only factor nodes are kept around. * advance how many there will be (works like FastVector::reserve).
* \nosubgrouping
*/ */
template<class FACTOR> void reserve(size_t size) { factors_.reserve(size); }
class FactorGraph {
public: /// Add a factor directly using a shared_ptr.
typedef FACTOR FactorType; ///< factor type template <class DERIVEDFACTOR>
typedef boost::shared_ptr<FACTOR> sharedFactor; ///< Shared pointer to a factor IsDerived<DERIVEDFACTOR> push_back(boost::shared_ptr<DERIVEDFACTOR> factor) {
typedef sharedFactor value_type; factors_.push_back(boost::shared_ptr<FACTOR>(factor));
typedef typename FastVector<sharedFactor>::iterator iterator; }
typedef typename FastVector<sharedFactor>::const_iterator const_iterator;
private: /// Emplace a shared pointer to factor of given type.
typedef FactorGraph<FACTOR> This; ///< Typedef for this class template <class DERIVEDFACTOR, class... Args>
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer for this class IsDerived<DERIVEDFACTOR> emplace_shared(Args&&... args) {
factors_.push_back(boost::allocate_shared<DERIVEDFACTOR>(
Eigen::aligned_allocator<DERIVEDFACTOR>(),
std::forward<Args>(args)...));
}
protected: /**
/** concept check, makes sure FACTOR defines print and equals */ * Add a factor by value, will be copy-constructed (use push_back with a
GTSAM_CONCEPT_TESTABLE_TYPE(FACTOR) * shared_ptr to avoid the copy).
*/
template <class DERIVEDFACTOR>
IsDerived<DERIVEDFACTOR> push_back(const DERIVEDFACTOR& factor) {
factors_.push_back(boost::allocate_shared<DERIVEDFACTOR>(
Eigen::aligned_allocator<DERIVEDFACTOR>(), factor));
}
/** Collection of factors */ /// `add` is a synonym for push_back.
FastVector<sharedFactor> factors_; template <class DERIVEDFACTOR>
IsDerived<DERIVEDFACTOR> add(boost::shared_ptr<DERIVEDFACTOR> factor) {
push_back(factor);
}
/// @name Standard Constructors /// `+=` works well with boost::assign list inserter.
/// @{ template <class DERIVEDFACTOR>
typename std::enable_if<
std::is_base_of<FactorType, DERIVEDFACTOR>::value,
boost::assign::list_inserter<RefCallPushBack<This>>>::type
operator+=(boost::shared_ptr<DERIVEDFACTOR> factor) {
return boost::assign::make_list_inserter(RefCallPushBack<This>(*this))(
factor);
}
/** Default constructor */ /// @}
FactorGraph() {} /// @name Adding via iterators
/// @{
/** Constructor from iterator over factors (shared_ptr or plain objects) */ /**
template<typename ITERATOR> * Push back many factors with an iterator over shared_ptr (factors are not
FactorGraph(ITERATOR firstFactor, ITERATOR lastFactor) { push_back(firstFactor, lastFactor); } * copied)
*/
template <typename ITERATOR>
HasDerivedElementType<ITERATOR> push_back(ITERATOR firstFactor,
ITERATOR lastFactor) {
factors_.insert(end(), firstFactor, lastFactor);
}
/** Construct from container of factors (shared_ptr or plain objects) */ /// Push back many factors with an iterator (factors are copied)
template<class CONTAINER> template <typename ITERATOR>
explicit FactorGraph(const CONTAINER& factors) { push_back(factors); } HasDerivedValueType<ITERATOR> push_back(ITERATOR firstFactor,
ITERATOR lastFactor) {
for (ITERATOR f = firstFactor; f != lastFactor; ++f) push_back(*f);
}
/// @} /// @}
/// @name Advanced Constructors /// @name Adding via container
/// @{ /// @{
// TODO: are these needed? /**
* Push back many factors as shared_ptr's in a container (factors are not
* copied)
*/
template <typename CONTAINER>
HasDerivedElementType<CONTAINER> push_back(const CONTAINER& container) {
push_back(container.begin(), container.end());
}
///** /// Push back non-pointer objects in a container (factors are copied).
// * @brief Constructor from a Bayes net template <typename CONTAINER>
// * @param bayesNet the Bayes net to convert, type CONDITIONAL must yield compatible factor HasDerivedValueType<CONTAINER> push_back(const CONTAINER& container) {
// * @return a factor graph with all the conditionals, as factors push_back(container.begin(), container.end());
// */ }
//template<class CONDITIONAL>
//FactorGraph(const BayesNet<CONDITIONAL>& bayesNet);
///** convert from Bayes tree */ /**
//template<class CONDITIONAL, class CLIQUE> * Add a factor or container of factors, including STL collections,
//FactorGraph(const BayesTree<CONDITIONAL, CLIQUE>& bayesTree); * BayesTrees, etc.
*/
template <class FACTOR_OR_CONTAINER>
void add(const FACTOR_OR_CONTAINER& factorOrContainer) {
push_back(factorOrContainer);
}
///** convert from a derived type */ /**
//template<class DERIVEDFACTOR> * Add a factor or container of factors, including STL collections,
//FactorGraph(const FactorGraph<DERIVEDFACTOR>& factors) { * BayesTrees, etc.
// factors_.assign(factors.begin(), factors.end()); */
//} template <class FACTOR_OR_CONTAINER>
boost::assign::list_inserter<CRefCallPushBack<This>> operator+=(
const FACTOR_OR_CONTAINER& factorOrContainer) {
return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this))(
factorOrContainer);
}
/// @} /// @}
/// @name Specialized versions
/// @{
public: /**
/// @name Adding Factors * Push back a BayesTree as a collection of factors.
/// @{ * NOTE: This should be hidden in derived classes in favor of a
* type-specialized version that calls this templated function.
*/
template <class CLIQUE>
typename std::enable_if<
std::is_base_of<This, typename CLIQUE::FactorGraphType>::value>::type
push_back(const BayesTree<CLIQUE>& bayesTree) {
bayesTree.addFactorsToGraph(*this);
}
/** /**
* Reserve space for the specified number of factors if you know in * Add new factors to a factor graph and returns a list of new factor indices,
* advance how many there will be (works like FastVector::reserve). * optionally finding and reusing empty factor slots.
*/ */
void reserve(size_t size) { factors_.reserve(size); } template <typename CONTAINER, typename = HasDerivedElementType<CONTAINER>>
FactorIndices add_factors(const CONTAINER& factors,
bool useEmptySlots = false);
// TODO: are these needed? /// @}
/// @name Testable
/// @{
/** Add a factor directly using a shared_ptr */ /** print out graph */
template<class DERIVEDFACTOR> void print(const std::string& s = "FactorGraph",
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type const KeyFormatter& formatter = DefaultKeyFormatter) const;
push_back(boost::shared_ptr<DERIVEDFACTOR> factor) {
factors_.push_back(boost::shared_ptr<FACTOR>(factor)); }
/** Add a factor directly using a shared_ptr */ /** Check equality */
void push_back(const sharedFactor& factor) { bool equals(const This& fg, double tol = 1e-9) const;
factors_.push_back(factor); } /// @}
/** Emplace a factor */ public:
template<class DERIVEDFACTOR, class... Args> /// @name Standard Interface
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type /// @{
emplace_shared(Args&&... args) {
factors_.push_back(boost::allocate_shared<DERIVEDFACTOR>(Eigen::aligned_allocator<DERIVEDFACTOR>(), std::forward<Args>(args)...));
}
/** push back many factors with an iterator over shared_ptr (factors are not copied) */ /** return the number of factors (including any null factors set by remove()
template<typename ITERATOR> * ). */
typename std::enable_if<std::is_base_of<FactorType, typename ITERATOR::value_type::element_type>::value>::type size_t size() const { return factors_.size(); }
push_back(ITERATOR firstFactor, ITERATOR lastFactor) {
factors_.insert(end(), firstFactor, lastFactor); }
/** push back many factors as shared_ptr's in a container (factors are not copied) */ /** Check if the graph is empty (null factors set by remove() will cause
template<typename CONTAINER> * this to return false). */
typename std::enable_if<std::is_base_of<FactorType, typename CONTAINER::value_type::element_type>::value>::type bool empty() const { return factors_.empty(); }
push_back(const CONTAINER& container) {
push_back(container.begin(), container.end());
}
/** push back a BayesTree as a collection of factors. NOTE: This should be hidden in derived /** Get a specific factor by index (this checks array bounds and may throw
* classes in favor of a type-specialized version that calls this templated function. */ * an exception, as opposed to operator[] which does not).
template<class CLIQUE> */
typename std::enable_if<std::is_base_of<This, typename CLIQUE::FactorGraphType>::value>::type const sharedFactor at(size_t i) const { return factors_.at(i); }
push_back(const BayesTree<CLIQUE>& bayesTree) {
bayesTree.addFactorsToGraph(*this);
}
//#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V4 /** Get a specific factor by index (this checks array bounds and may throw
/** Add a factor by value, will be copy-constructed (use push_back with a shared_ptr to avoid * an exception, as opposed to operator[] which does not).
* the copy). */ */
template<class DERIVEDFACTOR> sharedFactor& at(size_t i) { return factors_.at(i); }
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type
push_back(const DERIVEDFACTOR& factor) {
factors_.push_back(boost::allocate_shared<DERIVEDFACTOR>(Eigen::aligned_allocator<DERIVEDFACTOR>(), factor));
}
//#endif
/** push back many factors with an iterator over plain factors (factors are copied) */ /** Get a specific factor by index (this does not check array bounds, as
template<typename ITERATOR> * opposed to at() which does).
typename std::enable_if<std::is_base_of<FactorType, typename ITERATOR::value_type>::value>::type */
push_back(ITERATOR firstFactor, ITERATOR lastFactor) { const sharedFactor operator[](size_t i) const { return at(i); }
for (ITERATOR f = firstFactor; f != lastFactor; ++f)
push_back(*f);
}
/** push back many factors as non-pointer objects in a container (factors are copied) */ /** Get a specific factor by index (this does not check array bounds, as
template<typename CONTAINER> * opposed to at() which does).
typename std::enable_if<std::is_base_of<FactorType, typename CONTAINER::value_type>::value>::type */
push_back(const CONTAINER& container) { sharedFactor& operator[](size_t i) { return at(i); }
push_back(container.begin(), container.end());
}
/** Add a factor directly using a shared_ptr */ /** Iterator to beginning of factors. */
template<class DERIVEDFACTOR> const_iterator begin() const { return factors_.begin(); }
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value,
boost::assign::list_inserter<RefCallPushBack<This> > >::type
operator+=(boost::shared_ptr<DERIVEDFACTOR> factor) {
return boost::assign::make_list_inserter(RefCallPushBack<This>(*this))(factor);
}
/** Add a factor directly using a shared_ptr */ /** Iterator to end of factors. */
boost::assign::list_inserter<CRefCallPushBack<This> > const_iterator end() const { return factors_.end(); }
operator+=(const sharedFactor& factor) {
return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this))(factor);
}
/** Add a factor or container of factors, including STL collections, BayesTrees, etc. */ /** Get the first factor */
template<class FACTOR_OR_CONTAINER> sharedFactor front() const { return factors_.front(); }
boost::assign::list_inserter<CRefCallPushBack<This> >
operator+=(const FACTOR_OR_CONTAINER& factorOrContainer) {
return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this))(factorOrContainer);
}
/** Add a factor directly using a shared_ptr */ /** Get the last factor */
template<class DERIVEDFACTOR> sharedFactor back() const { return factors_.back(); }
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type
add(boost::shared_ptr<DERIVEDFACTOR> factor) {
push_back(factor);
}
/** Add a factor directly using a shared_ptr */ /// @}
void add(const sharedFactor& factor) { /// @name Modifying Factor Graphs (imperative, discouraged)
push_back(factor); /// @{
}
/** Add a factor or container of factors, including STL collections, BayesTrees, etc. */ /** non-const STL-style begin() */
template<class FACTOR_OR_CONTAINER> iterator begin() { return factors_.begin(); }
void add(const FACTOR_OR_CONTAINER& factorOrContainer) {
push_back(factorOrContainer);
}
/// @} /** non-const STL-style end() */
/// @name Testable iterator end() { return factors_.end(); }
/// @{
/** print out graph */ /** Directly resize the number of factors in the graph. If the new size is
void print(const std::string& s = "FactorGraph", * less than the original, factors at the end will be removed. If the new
const KeyFormatter& formatter = DefaultKeyFormatter) const; * size is larger than the original, null factors will be appended.
*/
void resize(size_t size) { factors_.resize(size); }
/** Check equality */ /** delete factor without re-arranging indexes by inserting a NULL pointer
bool equals(const This& fg, double tol = 1e-9) const; */
/// @} void remove(size_t i) { factors_[i].reset(); }
public: /** replace a factor by index */
/// @name Standard Interface void replace(size_t index, sharedFactor factor) { at(index) = factor; }
/// @{
/** return the number of factors (including any null factors set by remove() ). */ /** Erase factor and rearrange other factors to take up the empty space */
size_t size() const { return factors_.size(); } iterator erase(iterator item) { return factors_.erase(item); }
/** Check if the graph is empty (null factors set by remove() will cause this to return false). */ /** Erase factors and rearrange other factors to take up the empty space */
bool empty() const { return factors_.empty(); } iterator erase(iterator first, iterator last) {
return factors_.erase(first, last);
}
/** Get a specific factor by index (this checks array bounds and may throw an exception, as /// @}
* opposed to operator[] which does not). /// @name Advanced Interface
*/ /// @{
const sharedFactor at(size_t i) const { return factors_.at(i); }
/** Get a specific factor by index (this checks array bounds and may throw an exception, as /** return the number of non-null factors */
* opposed to operator[] which does not). size_t nrFactors() const;
*/
sharedFactor& at(size_t i) { return factors_.at(i); }
/** Get a specific factor by index (this does not check array bounds, as opposed to at() which /** Potentially slow function to return all keys involved, sorted, as a set
* does). */
*/ KeySet keys() const;
const sharedFactor operator[](size_t i) const { return at(i); }
/** Get a specific factor by index (this does not check array bounds, as opposed to at() which /** Potentially slow function to return all keys involved, sorted, as a
* does). * vector
*/ */
sharedFactor& operator[](size_t i) { return at(i); } KeyVector keyVector() const;
/** Iterator to beginning of factors. */ /** MATLAB interface utility: Checks whether a factor index idx exists in
const_iterator begin() const { return factors_.begin();} * the graph and is a live pointer */
inline bool exists(size_t idx) const { return idx < size() && at(idx); }
/** Iterator to end of factors. */ private:
const_iterator end() const { return factors_.end(); } /** Serialization function */
friend class boost::serialization::access;
template <class ARCHIVE>
void serialize(ARCHIVE& ar, const unsigned int /*version*/) {
ar& BOOST_SERIALIZATION_NVP(factors_);
}
/** Get the first factor */ /// @}
sharedFactor front() const { return factors_.front(); } }; // FactorGraph
} // namespace gtsam
/** Get the last factor */
sharedFactor back() const { return factors_.back(); }
/// @}
/// @name Modifying Factor Graphs (imperative, discouraged)
/// @{
/** non-const STL-style begin() */
iterator begin() { return factors_.begin();}
/** non-const STL-style end() */
iterator end() { return factors_.end(); }
/** Directly resize the number of factors in the graph. If the new size is less than the
* original, factors at the end will be removed. If the new size is larger than the original,
* null factors will be appended.
*/
void resize(size_t size) { factors_.resize(size); }
/** delete factor without re-arranging indexes by inserting a NULL pointer */
void remove(size_t i) { factors_[i].reset();}
/** replace a factor by index */
void replace(size_t index, sharedFactor factor) { at(index) = factor; }
/** Erase factor and rearrange other factors to take up the empty space */
iterator erase(iterator item) { return factors_.erase(item); }
/** Erase factors and rearrange other factors to take up the empty space */
iterator erase(iterator first, iterator last) { return factors_.erase(first, last); }
/// @}
/// @name Advanced Interface
/// @{
/** return the number of non-null factors */
size_t nrFactors() const;
/** Potentially slow function to return all keys involved, sorted, as a set */
KeySet keys() const;
/** Potentially slow function to return all keys involved, sorted, as a vector */
KeyVector keyVector() const;
/** MATLAB interface utility: Checks whether a factor index idx exists in the graph and is a live pointer */
inline bool exists(size_t idx) const { return idx < size() && at(idx); }
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
ar & BOOST_SERIALIZATION_NVP(factors_);
}
/// @}
}; // FactorGraph
} // namespace gtsam
#include <gtsam/inference/FactorGraph-inst.h> #include <gtsam/inference/FactorGraph-inst.h>

View File

@ -35,12 +35,15 @@ namespace gtsam {
namespace internal namespace internal
{ {
/* ************************************************************************* */ /* ************************************************************************* */
double logDeterminant(const GaussianBayesTreeClique::shared_ptr& clique, double& parentSum) double logDeterminant(const GaussianBayesTreeClique::shared_ptr& clique,
{ double& parentSum) {
parentSum += clique->conditional()->R().diagonal().unaryExpr(std::ptr_fun<double,double>(log)).sum(); parentSum += clique->conditional()
assert(false); ->R()
return 0; .diagonal()
} .unaryExpr(std::ptr_fun<double, double>(log))
.sum();
return 0;
}
} }
/* ************************************************************************* */ /* ************************************************************************* */

View File

@ -15,14 +15,16 @@
* @author Christian Potthast * @author Christian Potthast
**/ **/
#include <CppUnitLite/TestHarness.h>
#include <gtsam/symbolic/SymbolicFactorGraph.h> #include <gtsam/symbolic/SymbolicFactorGraph.h>
#include <gtsam/base/TestableAssertions.h>
#include <gtsam/symbolic/SymbolicBayesNet.h> #include <gtsam/symbolic/SymbolicBayesNet.h>
#include <gtsam/symbolic/SymbolicBayesTree.h> #include <gtsam/symbolic/SymbolicBayesTree.h>
#include <gtsam/symbolic/SymbolicConditional.h> #include <gtsam/symbolic/SymbolicConditional.h>
#include <gtsam/symbolic/tests/symbolicExampleGraphs.h> #include <gtsam/symbolic/tests/symbolicExampleGraphs.h>
#include <CppUnitLite/TestHarness.h>
#include <boost/assign/std/set.hpp> #include <boost/assign/std/set.hpp>
using namespace std; using namespace std;
@ -46,11 +48,10 @@ TEST(SymbolicFactorGraph, keys2) {
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminateFullSequential) TEST(SymbolicFactorGraph, eliminateFullSequential) {
{
// Test with simpleTestGraph1 // Test with simpleTestGraph1
Ordering order; Ordering order;
order += 0,1,2,3,4; order += 0, 1, 2, 3, 4;
SymbolicBayesNet actual1 = *simpleTestGraph1.eliminateSequential(order); SymbolicBayesNet actual1 = *simpleTestGraph1.eliminateSequential(order);
EXPECT(assert_equal(simpleTestGraph1BayesNet, actual1)); EXPECT(assert_equal(simpleTestGraph1BayesNet, actual1));
@ -60,24 +61,20 @@ TEST(SymbolicFactorGraph, eliminateFullSequential)
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminatePartialSequential) TEST(SymbolicFactorGraph, eliminatePartialSequential) {
{
// Eliminate 0 and 1 // Eliminate 0 and 1
const Ordering order = list_of(0)(1); const Ordering order = list_of(0)(1);
const SymbolicBayesNet expectedBayesNet = list_of const SymbolicBayesNet expectedBayesNet =
(SymbolicConditional(0,1,2)) list_of(SymbolicConditional(0, 1, 2))(SymbolicConditional(1, 2, 3, 4));
(SymbolicConditional(1,2,3,4));
const SymbolicFactorGraph expectedSfg = list_of const SymbolicFactorGraph expectedSfg = list_of(SymbolicFactor(2, 3))(
(SymbolicFactor(2,3)) SymbolicFactor(4, 5))(SymbolicFactor(2, 3, 4));
(SymbolicFactor(4,5))
(SymbolicFactor(2,3,4));
SymbolicBayesNet::shared_ptr actualBayesNet; SymbolicBayesNet::shared_ptr actualBayesNet;
SymbolicFactorGraph::shared_ptr actualSfg; SymbolicFactorGraph::shared_ptr actualSfg;
boost::tie(actualBayesNet, actualSfg) = boost::tie(actualBayesNet, actualSfg) =
simpleTestGraph2.eliminatePartialSequential(Ordering(list_of(0)(1))); simpleTestGraph2.eliminatePartialSequential(Ordering(list_of(0)(1)));
EXPECT(assert_equal(expectedSfg, *actualSfg)); EXPECT(assert_equal(expectedSfg, *actualSfg));
EXPECT(assert_equal(expectedBayesNet, *actualBayesNet)); EXPECT(assert_equal(expectedBayesNet, *actualBayesNet));
@ -85,75 +82,71 @@ TEST(SymbolicFactorGraph, eliminatePartialSequential)
SymbolicBayesNet::shared_ptr actualBayesNet2; SymbolicBayesNet::shared_ptr actualBayesNet2;
SymbolicFactorGraph::shared_ptr actualSfg2; SymbolicFactorGraph::shared_ptr actualSfg2;
boost::tie(actualBayesNet2, actualSfg2) = boost::tie(actualBayesNet2, actualSfg2) =
simpleTestGraph2.eliminatePartialSequential(list_of(0)(1).convert_to_container<KeyVector >()); simpleTestGraph2.eliminatePartialSequential(
list_of(0)(1).convert_to_container<KeyVector>());
EXPECT(assert_equal(expectedSfg, *actualSfg2)); EXPECT(assert_equal(expectedSfg, *actualSfg2));
EXPECT(assert_equal(expectedBayesNet, *actualBayesNet2)); EXPECT(assert_equal(expectedBayesNet, *actualBayesNet2));
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminateFullMultifrontal) TEST(SymbolicFactorGraph, eliminateFullMultifrontal) {
{ Ordering ordering;
Ordering ordering; ordering += 0,1,2,3; ordering += 0, 1, 2, 3;
SymbolicBayesTree actual1 = SymbolicBayesTree actual1 = *simpleChain.eliminateMultifrontal(ordering);
*simpleChain.eliminateMultifrontal(ordering);
EXPECT(assert_equal(simpleChainBayesTree, actual1)); EXPECT(assert_equal(simpleChainBayesTree, actual1));
SymbolicBayesTree actual2 = SymbolicBayesTree actual2 = *asiaGraph.eliminateMultifrontal(asiaOrdering);
*asiaGraph.eliminateMultifrontal(asiaOrdering);
EXPECT(assert_equal(asiaBayesTree, actual2)); EXPECT(assert_equal(asiaBayesTree, actual2));
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminatePartialMultifrontal) TEST(SymbolicFactorGraph, eliminatePartialMultifrontal) {
{
SymbolicBayesTree expectedBayesTree; SymbolicBayesTree expectedBayesTree;
SymbolicConditional::shared_ptr root = boost::make_shared<SymbolicConditional>( SymbolicConditional::shared_ptr root =
SymbolicConditional::FromKeys(list_of(4)(5)(1), 2)); boost::make_shared<SymbolicConditional>(
expectedBayesTree.insertRoot(boost::make_shared<SymbolicBayesTreeClique>(root)); SymbolicConditional::FromKeys(list_of(4)(5)(1), 2));
expectedBayesTree.insertRoot(
boost::make_shared<SymbolicBayesTreeClique>(root));
SymbolicFactorGraph expectedFactorGraph = list_of SymbolicFactorGraph expectedFactorGraph =
(SymbolicFactor(0,1)) list_of(SymbolicFactor(0, 1))(SymbolicFactor(0, 2))(SymbolicFactor(1, 3))(
(SymbolicFactor(0,2)) SymbolicFactor(2, 3))(SymbolicFactor(1));
(SymbolicFactor(1,3))
(SymbolicFactor(2,3))
(SymbolicFactor(1));
SymbolicBayesTree::shared_ptr actualBayesTree; SymbolicBayesTree::shared_ptr actualBayesTree;
SymbolicFactorGraph::shared_ptr actualFactorGraph; SymbolicFactorGraph::shared_ptr actualFactorGraph;
boost::tie(actualBayesTree, actualFactorGraph) = boost::tie(actualBayesTree, actualFactorGraph) =
simpleTestGraph2.eliminatePartialMultifrontal(Ordering(list_of(4)(5))); simpleTestGraph2.eliminatePartialMultifrontal(Ordering(list_of(4)(5)));
EXPECT(assert_equal(expectedFactorGraph, *actualFactorGraph)); EXPECT(assert_equal(expectedFactorGraph, *actualFactorGraph));
EXPECT(assert_equal(expectedBayesTree, *actualBayesTree)); EXPECT(assert_equal(expectedBayesTree, *actualBayesTree));
SymbolicBayesTree expectedBayesTree2; SymbolicBayesTree expectedBayesTree2;
SymbolicBayesTreeClique::shared_ptr root2 = boost::make_shared<SymbolicBayesTreeClique>( SymbolicBayesTreeClique::shared_ptr root2 =
boost::make_shared<SymbolicConditional>(4,1)); boost::make_shared<SymbolicBayesTreeClique>(
boost::make_shared<SymbolicConditional>(4, 1));
root2->children.push_back(boost::make_shared<SymbolicBayesTreeClique>( root2->children.push_back(boost::make_shared<SymbolicBayesTreeClique>(
boost::make_shared<SymbolicConditional>(5,4))); boost::make_shared<SymbolicConditional>(5, 4)));
expectedBayesTree2.insertRoot(root2); expectedBayesTree2.insertRoot(root2);
SymbolicBayesTree::shared_ptr actualBayesTree2; SymbolicBayesTree::shared_ptr actualBayesTree2;
SymbolicFactorGraph::shared_ptr actualFactorGraph2; SymbolicFactorGraph::shared_ptr actualFactorGraph2;
boost::tie(actualBayesTree2, actualFactorGraph2) = boost::tie(actualBayesTree2, actualFactorGraph2) =
simpleTestGraph2.eliminatePartialMultifrontal(list_of<Key>(4)(5).convert_to_container<KeyVector >()); simpleTestGraph2.eliminatePartialMultifrontal(
list_of<Key>(4)(5).convert_to_container<KeyVector>());
EXPECT(assert_equal(expectedFactorGraph, *actualFactorGraph2)); EXPECT(assert_equal(expectedFactorGraph, *actualFactorGraph2));
EXPECT(assert_equal(expectedBayesTree2, *actualBayesTree2)); EXPECT(assert_equal(expectedBayesTree2, *actualBayesTree2));
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST(SymbolicFactorGraph, marginalMultifrontalBayesNet) TEST(SymbolicFactorGraph, marginalMultifrontalBayesNet) {
{ SymbolicBayesNet expectedBayesNet =
SymbolicBayesNet expectedBayesNet = list_of list_of(SymbolicConditional(0, 1, 2))(SymbolicConditional(1, 2, 3))(
(SymbolicConditional(0, 1, 2)) SymbolicConditional(2, 3))(SymbolicConditional(3));
(SymbolicConditional(1, 2, 3))
(SymbolicConditional(2, 3))
(SymbolicConditional(3));
SymbolicBayesNet actual1 = *simpleTestGraph2.marginalMultifrontalBayesNet( SymbolicBayesNet actual1 = *simpleTestGraph2.marginalMultifrontalBayesNet(
Ordering(list_of(0)(1)(2)(3))); Ordering(list_of(0)(1)(2)(3)));
EXPECT(assert_equal(expectedBayesNet, actual1)); EXPECT(assert_equal(expectedBayesNet, actual1));
} }
@ -167,104 +160,75 @@ TEST(SymbolicFactorGraph, eliminate_disconnected_graph) {
// create expected Chordal bayes Net // create expected Chordal bayes Net
SymbolicBayesNet expected; SymbolicBayesNet expected;
expected.push_back(boost::make_shared<SymbolicConditional>(0,1,2)); expected.push_back(boost::make_shared<SymbolicConditional>(0, 1, 2));
expected.push_back(boost::make_shared<SymbolicConditional>(1,2)); expected.push_back(boost::make_shared<SymbolicConditional>(1, 2));
expected.push_back(boost::make_shared<SymbolicConditional>(2)); expected.push_back(boost::make_shared<SymbolicConditional>(2));
expected.push_back(boost::make_shared<SymbolicConditional>(3,4)); expected.push_back(boost::make_shared<SymbolicConditional>(3, 4));
expected.push_back(boost::make_shared<SymbolicConditional>(4)); expected.push_back(boost::make_shared<SymbolicConditional>(4));
Ordering order; Ordering order;
order += 0,1,2,3,4; order += 0, 1, 2, 3, 4;
SymbolicBayesNet actual = *fg.eliminateSequential(order); SymbolicBayesNet actual = *fg.eliminateSequential(order);
EXPECT(assert_equal(expected,actual)); EXPECT(assert_equal(expected, actual));
} }
/* ************************************************************************* */ /* ************************************************************************* */
//TEST(SymbolicFactorGraph, marginals) TEST(SymbolicFactorGraph, marginals) {
//{ // Create factor graph
// // Create factor graph SymbolicFactorGraph fg;
// SymbolicFactorGraph fg; fg.push_factor(0, 1);
// fg.push_factor(0, 1); fg.push_factor(0, 2);
// fg.push_factor(0, 2); fg.push_factor(1, 4);
// fg.push_factor(1, 4); fg.push_factor(2, 4);
// fg.push_factor(2, 4); fg.push_factor(3, 4);
// fg.push_factor(3, 4);
// // eliminate
// // eliminate Ordering ord(list_of(3)(4)(2)(1)(0));
// SymbolicSequentialSolver solver(fg); auto actual = fg.eliminateSequential(ord);
// SymbolicBayesNet::shared_ptr actual = solver.eliminate(); SymbolicBayesNet expected;
// SymbolicBayesNet expected; expected.emplace_shared<SymbolicConditional>(3, 4);
// expected.push_front(boost::make_shared<IndexConditional>(4)); expected.emplace_shared<SymbolicConditional>(4, 1, 2);
// expected.push_front(boost::make_shared<IndexConditional>(3, 4)); expected.emplace_shared<SymbolicConditional>(2, 0, 1);
// expected.push_front(boost::make_shared<IndexConditional>(2, 4)); expected.emplace_shared<SymbolicConditional>(1, 0);
// expected.push_front(boost::make_shared<IndexConditional>(1, 2, 4)); expected.emplace_shared<SymbolicConditional>(0);
// expected.push_front(boost::make_shared<IndexConditional>(0, 1, 2)); EXPECT(assert_equal(expected, *actual));
// EXPECT(assert_equal(expected,*actual));
// {
// { // jointBayesNet
// // jointBayesNet Ordering ord(list_of(0)(4)(3));
// vector<Index> js; auto actual = fg.eliminatePartialSequential(ord);
// js.push_back(0); SymbolicBayesNet expectedBN;
// js.push_back(4); expectedBN.emplace_shared<SymbolicConditional>(0, 1, 2);
// js.push_back(3); expectedBN.emplace_shared<SymbolicConditional>(4, 1, 2, 3);
// SymbolicBayesNet::shared_ptr actualBN = solver.jointBayesNet(js); expectedBN.emplace_shared<SymbolicConditional>(3, 1, 2);
// SymbolicBayesNet expectedBN; EXPECT(assert_equal(expectedBN, *(actual.first)));
// expectedBN.push_front(boost::make_shared<IndexConditional>(3)); }
// expectedBN.push_front(boost::make_shared<IndexConditional>(4, 3));
// expectedBN.push_front(boost::make_shared<IndexConditional>(0, 4)); {
// EXPECT( assert_equal(expectedBN,*actualBN)); // jointBayesNet
// Ordering ord(list_of(0)(2)(3));
// // jointFactorGraph auto actual = fg.eliminatePartialSequential(ord);
// SymbolicFactorGraph::shared_ptr actualFG = solver.jointFactorGraph(js); SymbolicBayesNet expectedBN;
// SymbolicFactorGraph expectedFG; expectedBN.emplace_shared<SymbolicConditional>(0, 1, 2);
// expectedFG.push_factor(0, 4); expectedBN.emplace_shared<SymbolicConditional>(2, 1, 4);
// expectedFG.push_factor(4, 3); expectedBN.emplace_shared<SymbolicConditional>(3, 4);
// expectedFG.push_factor(3); EXPECT(assert_equal(expectedBN, *(actual.first)));
// EXPECT( assert_equal(expectedFG,(SymbolicFactorGraph)(*actualFG))); }
// }
// {
// { // conditionalBayesNet
// // jointBayesNet Ordering ord(list_of(0)(2));
// vector<Index> js; auto actual = fg.eliminatePartialSequential(ord);
// js.push_back(0); SymbolicBayesNet expectedBN;
// js.push_back(2); expectedBN.emplace_shared<SymbolicConditional>(0, 1, 2);
// js.push_back(3); expectedBN.emplace_shared<SymbolicConditional>(2, 1, 4);
// SymbolicBayesNet::shared_ptr actualBN = solver.jointBayesNet(js); EXPECT(assert_equal(expectedBN, *(actual.first)));
// SymbolicBayesNet expectedBN; }
// expectedBN.push_front(boost::make_shared<IndexConditional>(2)); }
// expectedBN.push_front(boost::make_shared<IndexConditional>(3, 2));
// expectedBN.push_front(boost::make_shared<IndexConditional>(0, 3, 2));
// EXPECT( assert_equal(expectedBN,*actualBN));
//
// // jointFactorGraph
// SymbolicFactorGraph::shared_ptr actualFG = solver.jointFactorGraph(js);
// SymbolicFactorGraph expectedFG;
// expectedFG.push_factor(0, 3, 2);
// expectedFG.push_factor(3, 2);
// expectedFG.push_factor(2);
// EXPECT( assert_equal(expectedFG,(SymbolicFactorGraph)(*actualFG)));
// }
//
// {
// // conditionalBayesNet
// vector<Index> js;
// js.push_back(0);
// js.push_back(2);
// js.push_back(3);
// size_t nrFrontals = 2;
// SymbolicBayesNet::shared_ptr actualBN = //
// solver.conditionalBayesNet(js, nrFrontals);
// SymbolicBayesNet expectedBN;
// expectedBN.push_front(boost::make_shared<IndexConditional>(2, 3));
// expectedBN.push_front(boost::make_shared<IndexConditional>(0, 2, 3));
// EXPECT( assert_equal(expectedBN,*actualBN));
// }
//}
/* ************************************************************************* */ /* ************************************************************************* */
TEST( SymbolicFactorGraph, constructFromBayesNet ) TEST(SymbolicFactorGraph, constructFromBayesNet) {
{
// create expected factor graph // create expected factor graph
SymbolicFactorGraph expected; SymbolicFactorGraph expected;
expected.push_factor(0, 1, 2); expected.push_factor(0, 1, 2);
@ -284,8 +248,7 @@ TEST( SymbolicFactorGraph, constructFromBayesNet )
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST( SymbolicFactorGraph, constructFromBayesTree ) TEST(SymbolicFactorGraph, constructFromBayesTree) {
{
// create expected factor graph // create expected factor graph
SymbolicFactorGraph expected; SymbolicFactorGraph expected;
expected.push_factor(_E_, _L_, _B_); expected.push_factor(_E_, _L_, _B_);
@ -300,8 +263,7 @@ TEST( SymbolicFactorGraph, constructFromBayesTree )
} }
/* ************************************************************************* */ /* ************************************************************************* */
TEST( SymbolicFactorGraph, push_back ) TEST(SymbolicFactorGraph, push_back) {
{
// Create two factor graphs and expected combined graph // Create two factor graphs and expected combined graph
SymbolicFactorGraph fg1, fg2, expected; SymbolicFactorGraph fg1, fg2, expected;
@ -321,8 +283,47 @@ TEST( SymbolicFactorGraph, push_back )
actual.push_back(fg1); actual.push_back(fg1);
actual.push_back(fg2); actual.push_back(fg2);
CHECK(assert_equal(expected, actual)); CHECK(assert_equal(expected, actual));
// combine in second way
SymbolicFactorGraph actual2 = fg1;
actual2.push_back(fg2);
CHECK(assert_equal(expected, actual2));
} }
/* ************************************************************************* */ /* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr); } TEST(SymbolicFactorGraph, add_factors) {
SymbolicFactorGraph fg1;
fg1.push_factor(10);
fg1 += SymbolicFactor::shared_ptr(); // empty slot!
fg1.push_factor(11);
SymbolicFactorGraph fg2;
fg2.push_factor(1);
fg2.push_factor(2);
SymbolicFactorGraph expected;
expected.push_factor(10);
expected.push_factor(1);
expected.push_factor(11);
expected.push_factor(2);
const FactorIndices expectedIndices = list_of(1)(3);
const FactorIndices actualIndices = fg1.add_factors(fg2, true);
EXPECT(assert_equal(expected, fg1));
EXPECT(assert_container_equality(expectedIndices, actualIndices));
expected.push_factor(1);
expected.push_factor(2);
const FactorIndices expectedIndices2 = list_of(4)(5);
const FactorIndices actualIndices2 = fg1.add_factors(fg2, false);
EXPECT(assert_equal(expected, fg1));
EXPECT(assert_container_equality(expectedIndices2, actualIndices2));
}
/* ************************************************************************* */
int main() {
TestResult tr;
return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */ /* ************************************************************************* */