/* ---------------------------------------------------------------------------- * GTSAM Copyright 2010, Georgia Tech Research Corporation, * Atlanta, Georgia 30332-0415 * All Rights Reserved * Authors: Frank Dellaert, et al. (see THANKS for the full author list) * See LICENSE for the license information * -------------------------------------------------------------------------- */ /** * @file FactorGraph.h * @brief Factor Graph Base Class * @author Carlos Nieto * @author Christian Potthast * @author Michael Kaess * @author Richard Roberts */ // \callgraph #pragma once #include #include #include #include #include // for Eigen::aligned_allocator #include #include #include #include #include #include #include #include namespace gtsam { /// Define collection type: typedef FastVector FactorIndices; // Forward declarations template class BayesTree; /** Helper */ template class CRefCallPushBack { C& obj; public: explicit CRefCallPushBack(C& obj) : obj(obj) {} template void operator()(const A& a) { obj.push_back(a); } }; /** Helper */ template class RefCallPushBack { C& obj; public: explicit RefCallPushBack(C& obj) : obj(obj) {} template void operator()(A& a) { obj.push_back(a); } }; /** Helper */ template class CRefCallAddCopy { C& obj; public: explicit CRefCallAddCopy(C& obj) : obj(obj) {} template 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 FactorGraph { public: typedef FACTOR FactorType; ///< factor type typedef boost::shared_ptr sharedFactor; ///< Shared pointer to a factor typedef sharedFactor value_type; typedef typename FastVector::iterator iterator; typedef typename FastVector::const_iterator const_iterator; private: typedef FactorGraph This; ///< Typedef for this class typedef boost::shared_ptr shared_ptr; ///< Shared pointer for this class /// Check if a DERIVEDFACTOR is in fact derived from FactorType. template using IsDerived = typename std::enable_if< std::is_base_of::value>::type; /// Check if T has a value_type derived from FactorType. template using HasDerivedValueType = typename std::enable_if< std::is_base_of::value>::type; /// Check if T has a value_type derived from FactorType. template using HasDerivedElementType = typename std::enable_if::value>::type; protected: /** concept check, makes sure FACTOR defines print and equals */ GTSAM_CONCEPT_TESTABLE_TYPE(FACTOR) /** Collection of factors */ FastVector factors_; /// Check exact equality of the factor pointers. Useful for derived ==. bool isEqual(const FactorGraph& other) const { return factors_ == other.factors_; } /// @name Standard Constructors /// @{ /** Default constructor */ FactorGraph() {} /** Constructor from iterator over factors (shared_ptr or plain objects) */ template FactorGraph(ITERATOR firstFactor, ITERATOR lastFactor) { push_back(firstFactor, lastFactor); } /** Construct from container of factors (shared_ptr or plain objects) */ template explicit FactorGraph(const CONTAINER& factors) { push_back(factors); } /// @} public: /// Default destructor // Public and virtual so boost serialization can call it. virtual ~FactorGraph() = default; /// @name Adding Single Factors /// @{ /** * Reserve space for the specified number of factors if you know in * advance how many there will be (works like FastVector::reserve). */ void reserve(size_t size) { factors_.reserve(size); } /// Add a factor directly using a shared_ptr. template IsDerived push_back(boost::shared_ptr factor) { factors_.push_back(boost::shared_ptr(factor)); } /// Emplace a shared pointer to factor of given type. template IsDerived emplace_shared(Args&&... args) { factors_.push_back(boost::allocate_shared( Eigen::aligned_allocator(), std::forward(args)...)); } /** * Add a factor by value, will be copy-constructed (use push_back with a * shared_ptr to avoid the copy). */ template IsDerived push_back(const DERIVEDFACTOR& factor) { factors_.push_back(boost::allocate_shared( Eigen::aligned_allocator(), factor)); } /// `add` is a synonym for push_back. template IsDerived add(boost::shared_ptr factor) { push_back(factor); } /// `+=` works well with boost::assign list inserter. template typename std::enable_if< std::is_base_of::value, boost::assign::list_inserter>>::type operator+=(boost::shared_ptr factor) { return boost::assign::make_list_inserter(RefCallPushBack(*this))( factor); } /// @} /// @name Adding via iterators /// @{ /** * Push back many factors with an iterator over shared_ptr (factors are not * copied) */ template HasDerivedElementType push_back(ITERATOR firstFactor, ITERATOR lastFactor) { factors_.insert(end(), firstFactor, lastFactor); } /// Push back many factors with an iterator (factors are copied) template HasDerivedValueType push_back(ITERATOR firstFactor, ITERATOR lastFactor) { for (ITERATOR f = firstFactor; f != lastFactor; ++f) push_back(*f); } /// @} /// @name Adding via container /// @{ /** * Push back many factors as shared_ptr's in a container (factors are not * copied) */ template HasDerivedElementType push_back(const CONTAINER& container) { push_back(container.begin(), container.end()); } /// Push back non-pointer objects in a container (factors are copied). template HasDerivedValueType push_back(const CONTAINER& container) { push_back(container.begin(), container.end()); } /** * Add a factor or container of factors, including STL collections, * BayesTrees, etc. */ template void add(const FACTOR_OR_CONTAINER& factorOrContainer) { push_back(factorOrContainer); } /** * Add a factor or container of factors, including STL collections, * BayesTrees, etc. */ template boost::assign::list_inserter> operator+=( const FACTOR_OR_CONTAINER& factorOrContainer) { return boost::assign::make_list_inserter(CRefCallPushBack(*this))( factorOrContainer); } /// @} /// @name Specialized versions /// @{ /** * 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 typename std::enable_if< std::is_base_of::value>::type push_back(const BayesTree& bayesTree) { bayesTree.addFactorsToGraph(this); } /** * Add new factors to a factor graph and returns a list of new factor indices, * optionally finding and reusing empty factor slots. */ template > FactorIndices add_factors(const CONTAINER& factors, bool useEmptySlots = false); /// @} /// @name Testable /// @{ /// Print out graph to std::cout, with optional key formatter. virtual void print(const std::string& s = "FactorGraph", const KeyFormatter& formatter = DefaultKeyFormatter) const; /// Check equality up to tolerance. bool equals(const This& fg, double tol = 1e-9) const; /// @} public: /// @name Standard Interface /// @{ /** return the number of factors (including any null factors set by remove() * ). */ size_t size() const { return factors_.size(); } /** Check if the graph is empty (null factors set by remove() will cause * this to return false). */ bool empty() const { return factors_.empty(); } /** Get a specific factor by index (this checks array bounds and may throw * an exception, as opposed to operator[] which does not). */ 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 opposed to operator[] which does not). */ 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 does). */ 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 does). */ sharedFactor& operator[](size_t i) { return at(i); } /** Iterator to beginning of factors. */ const_iterator begin() const { return factors_.begin(); } /** Iterator to end of factors. */ const_iterator end() const { return factors_.end(); } /** Get the first factor */ sharedFactor front() const { return factors_.front(); } /** 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. */ virtual void resize(size_t size) { factors_.resize(size); } /** delete factor without re-arranging indexes by inserting a nullptr pointer */ void remove(size_t i) { factors_.at(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 Graph Display /// @{ /// Output to graphviz format, stream version. void dot(std::ostream& os, const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DotWriter& writer = DotWriter()) const; /// Output to graphviz format string. std::string dot(const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DotWriter& writer = DotWriter()) const; /// output to file with graphviz format. void saveGraph(const std::string& filename, const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DotWriter& writer = DotWriter()) const; /// @} /// @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 void serialize(ARCHIVE& ar, const unsigned int /*version*/) { ar& BOOST_SERIALIZATION_NVP(factors_); } /// @} }; // FactorGraph } // namespace gtsam #include