/** * @file Conditional.h * @brief Base class for conditional densities * @author Frank Dellaert */ // \callgraph #pragma once #include #include // for noncopyable #include #include #include #include #include #include #include namespace gtsam { /** * Base class for conditional densities * * We make it noncopyable so we enforce the fact that factors are * kept in pointer containers. To be safe, you should make them * immutable, i.e., practicing functional programming. */ class Conditional: public Factor, boost::noncopyable, public Testable { protected: /** The first nFrontal variables are frontal and the rest are parents. */ size_t nrFrontals_; public: /** convenience typename for a shared pointer to this class */ typedef gtsam::Factor FactorType; typedef boost::shared_ptr shared_ptr; typedef Factor::iterator iterator; typedef Factor::const_iterator const_iterator; typedef boost::iterator_range Frontals; typedef boost::iterator_range Parents; /** Empty Constructor to make serialization possible */ Conditional() : nrFrontals_(0) {} /** No parents */ Conditional(Index key) : Factor(key), nrFrontals_(1) {} /** Single parent */ Conditional(Index key, Index parent) : Factor(key, parent), nrFrontals_(1) {} /** Two parents */ Conditional(Index key, Index parent1, Index parent2) : Factor(key, parent1, parent2), nrFrontals_(1) {} /** Three parents */ Conditional(Index key, Index parent1, Index parent2, Index parent3) : Factor(key, parent1, parent2, parent3), nrFrontals_(1) {} /** Constructor from a frontal variable and a vector of parents */ Conditional(Index key, const std::vector& parents) : nrFrontals_(1) { keys_.resize(1 + parents.size()); *(beginFrontals()) = key; std::copy(parents.begin(), parents.end(), beginParents()); } /** Named constructor from any number of frontal variables and parents */ template static shared_ptr fromRange(Iterator firstKey, Iterator lastKey, size_t nrFrontals) { shared_ptr conditional(new Conditional); conditional->nrFrontals_ = nrFrontals; std::copy(firstKey, lastKey, back_inserter(conditional->keys_)); return conditional; } /** check equality */ bool equals(const Conditional& c, double tol = 1e-9) const { return nrFrontals_ == c.nrFrontals_ && Factor::equals(c, tol); } /** return the number of frontals */ size_t nrFrontals() const { return nrFrontals_; } /** return the number of parents */ size_t nrParents() const { return keys_.size() - nrFrontals_; } /** Special accessor when there is only one frontal variable. */ Index key() const { assert(nrFrontals_==1); return keys_[0]; } /** Iterators over frontal and parent variables. */ const_iterator beginFrontals() const { return keys_.begin(); } const_iterator endFrontals() const { return keys_.begin()+nrFrontals_; } const_iterator beginParents() const { return keys_.begin()+nrFrontals_; } const_iterator endParents() const { return keys_.end(); } /** Mutable iterators and accessors */ iterator beginFrontals() { return keys_.begin(); } iterator endFrontals() { return keys_.begin()+nrFrontals_; } iterator beginParents() { return keys_.begin()+nrFrontals_; } iterator endParents() { return keys_.end(); } boost::iterator_range frontals() { return boost::make_iterator_range(beginFrontals(), endFrontals()); } boost::iterator_range parents() { return boost::make_iterator_range(beginParents(), endParents()); } /** return a view of the frontal keys */ Frontals frontals() const { return boost::make_iterator_range(beginFrontals(), endFrontals()); } /** return a view of the parent keys */ Parents parents() const { return boost::make_iterator_range(beginParents(), endParents()); } /** print */ void print(const std::string& s = "Conditional") const { std::cout << s << " P("; BOOST_FOREACH(Index key, frontals()) std::cout << " " << key; if (nrParents()>0) std::cout << " |"; BOOST_FOREACH(Index parent, parents()) std::cout << " " << parent; std::cout << ")" << std::endl; } /** Permute the variables when only separator variables need to be permuted. * Returns true if any reordered variables appeared in the separator and * false if not. */ bool permuteSeparatorWithInverse(const Permutation& inversePermutation) { #ifndef NDEBUG BOOST_FOREACH(Index key, frontals()) { assert(key == inversePermutation[key]); } #endif bool parentChanged = false; BOOST_FOREACH(Index& parent, parents()) { Index newParent = inversePermutation[parent]; if(parent != newParent) { parentChanged = true; parent = newParent; } } return parentChanged; } /** * Permutes the Conditional, but for efficiency requires the permutation * to already be inverted. */ void permuteWithInverse(const Permutation& inversePermutation) { // The permutation may not move the separators into the frontals #ifndef NDEBUG BOOST_FOREACH(const Index frontal, this->frontals()) { BOOST_FOREACH(const Index separator, this->parents()) { assert(inversePermutation[frontal] < inversePermutation[separator]); } } #endif Factor::permuteWithInverse(inversePermutation); } protected: /** Debugging invariant that the keys should be in order, including that the * conditioned variable is numbered lower than the parents. */ void assertInvariants() const; friend class Factor; private: /** Serialization function */ friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_NVP(nrFrontals_); } }; }