Maintain reverse-lookup inside of Ordering, to allow fast partial permutations, and modified iSAM2 to use partial permutations on Ordering. Removed InvertedOrdering and updated other GTSAM code and unit tests to use the Ordering reverse-lookup function "key" instead of calculating and inverse ordering.

release/4.3a0
Richard Roberts 2013-01-08 23:31:06 +00:00
parent 6f9601f5e0
commit 013705232c
11 changed files with 189 additions and 239 deletions

View File

@ -55,10 +55,9 @@ void ISAM2::Impl::AddVariables(
if(debug) cout << "Adding variable " << keyFormatter(key_value.key) << " with order " << nextVar << endl;
++ nextVar;
}
assert(ordering.nVars() == delta.size());
assert(ordering.size() == delta.size());
}
replacedKeys.resize(ordering.nVars(), false);
replacedKeys.resize(ordering.size(), false);
}
/* ************************************************************************* */
@ -115,9 +114,10 @@ void ISAM2::Impl::RemoveVariables(const FastSet<Key>& unusedKeys, const ISAM2Cli
}
// Reorder and remove from ordering and solution
ordering.permuteWithInverse(unusedToEndInverse);
ordering.permuteInPlace(unusedToEnd);
BOOST_REVERSE_FOREACH(Key key, unusedKeys) {
ordering.pop_back(key);
Ordering::value_type removed = ordering.pop_back();
assert(removed.first == key);
theta.erase(key);
}
@ -188,14 +188,14 @@ void CheckRelinearizationRecursiveDouble(FastSet<Index>& relinKeys, double thres
}
/* ************************************************************************* */
void CheckRelinearizationRecursiveMap(FastSet<Index>& relinKeys, const FastMap<char,Vector>& thresholds, const VectorValues& delta, const Ordering::InvertedMap& decoder, const ISAM2Clique::shared_ptr& clique) {
void CheckRelinearizationRecursiveMap(FastSet<Index>& relinKeys, const FastMap<char,Vector>& thresholds, const VectorValues& delta, const Ordering& ordering, const ISAM2Clique::shared_ptr& clique) {
// Check the current clique for relinearization
bool relinearize = false;
BOOST_FOREACH(Index var, clique->conditional()->keys()) {
// Lookup the key associated with this index
Key key = decoder.at(var);
Key key = ordering.key(var);
// Find the threshold for this variable type
const Vector& threshold = thresholds.find(Symbol(key).chr())->second;
@ -214,7 +214,7 @@ void CheckRelinearizationRecursiveMap(FastSet<Index>& relinKeys, const FastMap<c
// If this node was relinearized, also check its children
if(relinearize) {
BOOST_FOREACH(const ISAM2Clique::shared_ptr& child, clique->children()) {
CheckRelinearizationRecursiveMap(relinKeys, thresholds, delta, decoder, child);
CheckRelinearizationRecursiveMap(relinKeys, thresholds, delta, ordering, child);
}
}
}
@ -229,8 +229,7 @@ FastSet<Index> ISAM2::Impl::CheckRelinearizationPartial(const ISAM2Clique::share
if(relinearizeThreshold.type() == typeid(double)) {
CheckRelinearizationRecursiveDouble(relinKeys, boost::get<double>(relinearizeThreshold), delta, root);
} else if(relinearizeThreshold.type() == typeid(FastMap<char,Vector>)) {
Ordering::InvertedMap decoder = ordering.invert();
CheckRelinearizationRecursiveMap(relinKeys, boost::get<FastMap<char,Vector> >(relinearizeThreshold), delta, decoder, root);
CheckRelinearizationRecursiveMap(relinKeys, boost::get<FastMap<char,Vector> >(relinearizeThreshold), delta, ordering, root);
}
}
@ -267,8 +266,8 @@ void ISAM2::Impl::ExpmapMasked(Values& values, const VectorValues& delta, const
invalidateIfDebug = boost::none;
#endif
assert(values.size() == ordering.nVars());
assert(delta.size() == ordering.nVars());
assert(values.size() == ordering.size());
assert(delta.size() == ordering.size());
Values::iterator key_value;
Ordering::const_iterator key_index;
for(key_value = values.begin(), key_index = ordering.begin();

View File

@ -353,7 +353,7 @@ boost::shared_ptr<FastSet<Index> > ISAM2::recalculate(const FastSet<Index>& mark
RgProd_.permuteInPlace(*colamd);
gttoc(permute_delta);
gttic(permute_ordering);
ordering_.permuteWithInverse(*colamdInverse);
ordering_.permuteInPlace(*colamd);
gttoc(permute_ordering);
gttoc(reorder);
@ -412,7 +412,7 @@ boost::shared_ptr<FastSet<Index> > ISAM2::recalculate(const FastSet<Index>& mark
// Reeliminated keys for detailed results
if(params_.enableDetailedResults) {
BOOST_FOREACH(Index index, affectedAndNewKeys) {
result.detail->variableStatus[inverseOrdering_->at(index)].isReeliminated = true;
result.detail->variableStatus[ordering_.key(index)].isReeliminated = true;
}
}
@ -450,7 +450,7 @@ boost::shared_ptr<FastSet<Index> > ISAM2::recalculate(const FastSet<Index>& mark
gttic(PartialSolve);
Impl::ReorderingMode reorderingMode;
reorderingMode.nFullSystemVars = ordering_.nVars();
reorderingMode.nFullSystemVars = ordering_.size();
reorderingMode.algorithm = Impl::ReorderingMode::COLAMD;
reorderingMode.constrain = Impl::ReorderingMode::CONSTRAIN_LAST;
if(constrainKeys) {
@ -489,7 +489,7 @@ boost::shared_ptr<FastSet<Index> > ISAM2::recalculate(const FastSet<Index>& mark
RgProd_.permuteInPlace(partialSolveResult.reorderingSelector, partialSolveResult.reorderingPermutation);
gttoc(permute_delta);
gttic(permute_ordering);
ordering_.reduceWithInverse(partialSolveResult.reorderingInverse);
ordering_.permuteInPlace(partialSolveResult.reorderingSelector, partialSolveResult.reorderingPermutation);
gttoc(permute_ordering);
if(params_.cacheLinearizedFactors) {
gttic(permute_cached_linear);
@ -538,7 +538,7 @@ boost::shared_ptr<FastSet<Index> > ISAM2::recalculate(const FastSet<Index>& mark
// Root clique variables for detailed results
if(params_.enableDetailedResults) {
BOOST_FOREACH(Index index, this->root()->conditional()->frontals()) {
result.detail->variableStatus[inverseOrdering_->at(index)].inRootClique = true;
result.detail->variableStatus[ordering_.key(index)].inRootClique = true;
}
}
@ -628,7 +628,6 @@ ISAM2Result ISAM2::update(
Impl::AddVariables(newTheta, theta_, delta_, deltaNewton_, RgProd_, deltaReplacedMask_, ordering_);
// New keys for detailed results
if(params_.enableDetailedResults) {
inverseOrdering_ = ordering_.invert();
BOOST_FOREACH(Key key, newTheta.keys()) { result.detail->variableStatus[key].isNew = true; } }
gttoc(add_new_variables);
@ -649,7 +648,7 @@ ISAM2Result ISAM2::update(
// Observed keys for detailed results
if(params_.enableDetailedResults) {
BOOST_FOREACH(Index index, markedKeys) {
result.detail->variableStatus[inverseOrdering_->at(index)].isObserved = true;
result.detail->variableStatus[ordering_.key(index)].isObserved = true;
}
}
// NOTE: we use assign instead of the iterator constructor here because this
@ -666,7 +665,7 @@ ISAM2Result ISAM2::update(
FastSet<Index> relinKeys;
if (relinearizeThisStep) {
gttic(gather_relinearize_keys);
vector<bool> markedRelinMask(ordering_.nVars(), false);
vector<bool> markedRelinMask(ordering_.size(), false);
// 4. Mark keys in \Delta above threshold \beta: J=\{\Delta_{j}\in\Delta|\Delta_{j}\geq\beta\}.
if(params_.enablePartialRelinearizationCheck)
relinKeys = Impl::CheckRelinearizationPartial(root_, delta_, ordering_, params_.relinearizeThreshold);
@ -677,8 +676,8 @@ ISAM2Result ISAM2::update(
// Above relin threshold keys for detailed results
if(params_.enableDetailedResults) {
BOOST_FOREACH(Index index, relinKeys) {
result.detail->variableStatus[inverseOrdering_->at(index)].isAboveRelinThreshold = true;
result.detail->variableStatus[inverseOrdering_->at(index)].isRelinearized = true; } }
result.detail->variableStatus[ordering_.key(index)].isAboveRelinThreshold = true;
result.detail->variableStatus[ordering_.key(index)].isRelinearized = true; } }
// Add the variables being relinearized to the marked keys
BOOST_FOREACH(const Index j, relinKeys) { markedRelinMask[j] = true; }
@ -696,9 +695,9 @@ ISAM2Result ISAM2::update(
FastSet<Index> involvedRelinKeys;
Impl::FindAll(this->root(), involvedRelinKeys, markedRelinMask);
BOOST_FOREACH(Index index, involvedRelinKeys) {
if(!result.detail->variableStatus[inverseOrdering_->at(index)].isAboveRelinThreshold) {
result.detail->variableStatus[inverseOrdering_->at(index)].isRelinearizeInvolved = true;
result.detail->variableStatus[inverseOrdering_->at(index)].isRelinearized = true; } }
if(!result.detail->variableStatus[ordering_.key(index)].isAboveRelinThreshold) {
result.detail->variableStatus[ordering_.key(index)].isRelinearizeInvolved = true;
result.detail->variableStatus[ordering_.key(index)].isRelinearized = true; } }
}
}
gttoc(fluid_find_all);
@ -818,7 +817,7 @@ Values ISAM2::calculateEstimate() const {
const VectorValues& delta(getDelta());
gttoc(getDelta);
gttic(Expmap);
vector<bool> mask(ordering_.nVars(), true);
vector<bool> mask(ordering_.size(), true);
Impl::ExpmapMasked(ret, delta, ordering_, mask);
gttoc(Expmap);
return ret;

View File

@ -502,9 +502,6 @@ protected:
/** The current Dogleg Delta (trust region radius) */
mutable boost::optional<double> doglegDelta_;
/** The inverse ordering, only used for creating ISAM2Result::DetailedResults */
boost::optional<Ordering::InvertedMap> inverseOrdering_;
public:
typedef ISAM2 This; ///< This class

View File

@ -13,14 +13,8 @@ namespace gtsam {
/* ************************************************************************* */
void LinearContainerFactor::rekeyFactor(const Ordering& ordering) {
Ordering::InvertedMap invOrdering = ordering.invert(); // TODO: inefficient - make more selective ordering invert
rekeyFactor(invOrdering);
}
/* ************************************************************************* */
void LinearContainerFactor::rekeyFactor(const Ordering::InvertedMap& invOrdering) {
BOOST_FOREACH(Index& idx, factor_->keys()) {
Key fullKey = invOrdering.find(idx)->second;
Key fullKey = ordering.key(idx);
idx = fullKey;
keys_.push_back(fullKey);
}
@ -86,34 +80,6 @@ LinearContainerFactor::LinearContainerFactor(
initializeLinearizationPoint(linearizationPoint);
}
/* ************************************************************************* */
LinearContainerFactor::LinearContainerFactor(const JacobianFactor& factor,
const Ordering::InvertedMap& inverted_ordering,
const Values& linearizationPoint)
: factor_(factor.clone()) {
rekeyFactor(inverted_ordering);
initializeLinearizationPoint(linearizationPoint);
}
/* ************************************************************************* */
LinearContainerFactor::LinearContainerFactor(const HessianFactor& factor,
const Ordering::InvertedMap& inverted_ordering,
const Values& linearizationPoint)
: factor_(factor.clone()) {
rekeyFactor(inverted_ordering);
initializeLinearizationPoint(linearizationPoint);
}
/* ************************************************************************* */
LinearContainerFactor::LinearContainerFactor(
const GaussianFactor::shared_ptr& factor,
const Ordering::InvertedMap& ordering,
const Values& linearizationPoint)
: factor_(factor->clone()) {
rekeyFactor(ordering);
initializeLinearizationPoint(linearizationPoint);
}
/* ************************************************************************* */
void LinearContainerFactor::print(const std::string& s, const KeyFormatter& keyFormatter) const {
Base::print(s+"LinearContainerFactor", keyFormatter);
@ -215,9 +181,8 @@ GaussianFactor::shared_ptr LinearContainerFactor::linearize(
}
// reset ordering
Ordering::InvertedMap invLocalOrdering = localOrdering.invert();
BOOST_FOREACH(Index& idx, linFactor->keys())
idx = ordering[invLocalOrdering[idx] ];
idx = ordering[localOrdering.key(idx)];
return linFactor;
}
@ -259,18 +224,11 @@ NonlinearFactor::shared_ptr LinearContainerFactor::negate() const {
NonlinearFactorGraph LinearContainerFactor::convertLinearGraph(
const GaussianFactorGraph& linear_graph, const Ordering& ordering,
const Values& linearizationPoint) {
return convertLinearGraph(linear_graph, ordering.invert());
}
/* ************************************************************************* */
NonlinearFactorGraph LinearContainerFactor::convertLinearGraph(
const GaussianFactorGraph& linear_graph, const InvertedOrdering& invOrdering,
const Values& linearizationPoint) {
NonlinearFactorGraph result;
BOOST_FOREACH(const GaussianFactor::shared_ptr& f, linear_graph)
if (f)
result.push_back(NonlinearFactorGraph::sharedFactor(
new LinearContainerFactor(f, invOrdering, linearizationPoint)));
new LinearContainerFactor(f, ordering, linearizationPoint)));
return result;
}

View File

@ -45,21 +45,6 @@ public:
LinearContainerFactor(const GaussianFactor::shared_ptr& factor,
const Values& linearizationPoint = Values());
/** Alternate constructor: store a linear factor and decode keys with inverted ordering*/
LinearContainerFactor(const JacobianFactor& factor,
const Ordering::InvertedMap& inverted_ordering,
const Values& linearizationPoint = Values());
/** Alternate constructor: store a linear factor and decode keys with inverted ordering*/
LinearContainerFactor(const HessianFactor& factor,
const Ordering::InvertedMap& inverted_ordering,
const Values& linearizationPoint = Values());
/** Constructor from shared_ptr with inverted ordering*/
LinearContainerFactor(const GaussianFactor::shared_ptr& factor,
const Ordering::InvertedMap& ordering,
const Values& linearizationPoint = Values());
// Access
const GaussianFactor::shared_ptr& factor() const { return factor_; }
@ -130,19 +115,13 @@ public:
/**
* Utility function for converting linear graphs to nonlinear graphs
* consisting of LinearContainerFactors. Two versions are available, using
* either the ordering the linear graph was linearized around, or the inverse ordering.
* If the inverse ordering is present, it will be faster.
* consisting of LinearContainerFactors.
*/
static NonlinearFactorGraph convertLinearGraph(const GaussianFactorGraph& linear_graph,
const Ordering& ordering, const Values& linearizationPoint = Values());
static NonlinearFactorGraph convertLinearGraph(const GaussianFactorGraph& linear_graph,
const InvertedOrdering& invOrdering, const Values& linearizationPoint = Values());
protected:
void rekeyFactor(const Ordering& ordering);
void rekeyFactor(const Ordering::InvertedMap& invOrdering);
void initializeLinearizationPoint(const Values& linearizationPoint);
}; // \class LinearContainerFactor

View File

@ -223,7 +223,7 @@ Ordering::shared_ptr NonlinearFactorGraph::orderingCOLAMD(
variableIndex));
// Permute the Ordering with the COLAMD ordering
ordering->permuteWithInverse(*colamdPerm->inverse());
ordering->permuteInPlace(*colamdPerm);
return ordering;
}
@ -254,7 +254,7 @@ Ordering::shared_ptr NonlinearFactorGraph::orderingCOLAMDConstrained(const Value
variableIndex, index_constraints));
// Permute the Ordering with the COLAMD ordering
ordering->permuteWithInverse(*colamdPerm->inverse());
ordering->permuteInPlace(*colamdPerm);
return ordering;
}

View File

@ -55,10 +55,8 @@ void NonlinearISAM::update(const NonlinearFactorGraph& newFactors,
// Augment ordering
// TODO: allow for ordering constraints within the new variables
// FIXME: should just loop over new values
BOOST_FOREACH(const NonlinearFactorGraph::sharedFactor& factor, newFactors)
BOOST_FOREACH(Key key, factor->keys())
ordering_.tryInsert(key, ordering_.nVars()); // will do nothing if already present
BOOST_FOREACH(const Values::ConstKeyValuePair& key_value, initialValues)
ordering_.insert(key_value.key, ordering_.size());
boost::shared_ptr<GaussianFactorGraph> linearizedNewFactors = newFactors.linearize(linPoint_, ordering_);

View File

@ -26,24 +26,53 @@ using namespace std;
namespace gtsam {
/* ************************************************************************* */
Ordering::Ordering(const std::list<Key> & L):nVars_(0) {
Ordering::Ordering(const std::list<Key> & L) {
int i = 0;
BOOST_FOREACH( Key s, L )
insert(s, i++) ;
}
/* ************************************************************************* */
void Ordering::permuteWithInverse(const Permutation& inversePermutation) {
gttic(Ordering_permuteWithInverse);
BOOST_FOREACH(Ordering::value_type& key_order, *this) {
key_order.second = inversePermutation[key_order.second];
}
Ordering::Ordering(const Ordering& other) : order_(other.order_), orderingIndex_(other.size()) {
for(iterator item = order_.begin(); item != order_.end(); ++item)
orderingIndex_[item->second] = item;
}
/* ************************************************************************* */
void Ordering::reduceWithInverse(const internal::Reduction& inverseReduction) {
BOOST_FOREACH(Ordering::value_type& key_order, *this) {
key_order.second = inverseReduction[key_order.second];
Ordering& Ordering::operator=(const Ordering& rhs) {
order_ = rhs.order_;
orderingIndex_.resize(rhs.size());
for(iterator item = order_.begin(); item != order_.end(); ++item)
orderingIndex_[item->second] = item;
return *this;
}
/* ************************************************************************* */
void Ordering::permuteInPlace(const Permutation& permutation) {
gttic(Ordering_permuteInPlace);
// Allocate new index and permute in map iterators
OrderingIndex newIndex(permutation.size());
for(size_t j = 0; j < newIndex.size(); ++j) {
newIndex[j] = orderingIndex_[permutation[j]]; // Assign the iterator
newIndex[j]->second = j; // Change the stored index to its permuted value
}
// Swap the new index into this Ordering class
orderingIndex_.swap(newIndex);
}
/* ************************************************************************* */
void Ordering::permuteInPlace(const Permutation& selector, const Permutation& permutation) {
if(selector.size() != permutation.size())
throw invalid_argument("Ordering::permuteInPlace (partial permutation version) called with selector and permutation of different sizes.");
// Create new index the size of the permuted entries
OrderingIndex newIndex(selector.size());
// Permute the affected entries into the new index
for(size_t dstSlot = 0; dstSlot < selector.size(); ++dstSlot)
newIndex[dstSlot] = orderingIndex_[selector[permutation[dstSlot]]];
// Put the affected entries back in the new order and fix the indices
for(size_t slot = 0; slot < selector.size(); ++slot) {
orderingIndex_[selector[slot]] = newIndex[slot];
orderingIndex_[selector[slot]]->second = selector[slot];
}
}
@ -51,16 +80,15 @@ void Ordering::reduceWithInverse(const internal::Reduction& inverseReduction) {
void Ordering::print(const string& str, const KeyFormatter& keyFormatter) const {
cout << str;
// Print ordering in index order
Ordering::InvertedMap inverted = this->invert();
// Print the ordering with varsPerLine ordering entries printed on each line,
// for compactness.
static const size_t varsPerLine = 10;
bool endedOnNewline = false;
BOOST_FOREACH(const Ordering::InvertedMap::value_type& index_key, inverted) {
if(index_key.first % varsPerLine != 0)
BOOST_FOREACH(const Map::iterator& index_key, orderingIndex_) {
if(index_key->second % varsPerLine != 0)
cout << ", ";
cout << index_key.first << ":" << keyFormatter(index_key.second);
if(index_key.first % varsPerLine == varsPerLine - 1) {
cout << index_key->second<< ":" << keyFormatter(index_key->first);
if(index_key->second % varsPerLine == varsPerLine - 1) {
cout << "\n";
endedOnNewline = true;
} else {
@ -79,40 +107,11 @@ bool Ordering::equals(const Ordering& rhs, double tol) const {
/* ************************************************************************* */
Ordering::value_type Ordering::pop_back() {
// FIXME: is there a way of doing this without searching over the entire structure?
for (iterator it=begin(); it!=end(); ++it) {
if (it->second == nVars_ - 1) {
value_type result = *it;
order_.erase(it);
--nVars_;
return result;
}
}
return value_type();
}
/* ************************************************************************* */
Index Ordering::pop_back(Key key) {
Map::iterator item = order_.find(key);
if(item == order_.end()) {
throw invalid_argument("Attempting to remove a key from an ordering that does not contain that key");
} else {
if(item->second != nVars_ - 1) {
throw invalid_argument("Attempting to remove a key from an ordering in which that key is not last");
} else {
order_.erase(item);
-- nVars_;
return nVars_;
}
}
}
/* ************************************************************************* */
Ordering::InvertedMap Ordering::invert() const {
InvertedMap result;
BOOST_FOREACH(const value_type& p, *this)
result.insert(make_pair(p.second, p.first));
return result;
iterator lastItem = orderingIndex_.back(); // Get the map iterator to the highest-index entry
value_type removed = *lastItem; // Save the key-index pair to return
order_.erase(lastItem); // Erase the entry from the map
orderingIndex_.pop_back(); // Erase the entry from the index
return removed; // Return the removed item
}
/* ************************************************************************* */

View File

@ -24,7 +24,7 @@
#include <boost/foreach.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <set>
#include <vector>
namespace gtsam {
@ -35,8 +35,9 @@ namespace gtsam {
class Ordering {
protected:
typedef FastMap<Key, Index> Map;
typedef std::vector<Map::iterator> OrderingIndex;
Map order_;
Index nVars_;
OrderingIndex orderingIndex_;
public:
@ -51,25 +52,33 @@ public:
/// @{
/// Default constructor for empty ordering
Ordering() : nVars_(0) {}
Ordering() {}
/// Copy constructor
Ordering(const Ordering& other);
/// Construct from list, assigns order indices sequentially to list items.
Ordering(const std::list<Key> & L) ;
Ordering(const std::list<Key> & L);
/// Assignment operator
Ordering& operator=(const Ordering& rhs);
/// @}
/// @name Standard Interface
/// @{
/** One greater than the maximum ordering index, i.e. including missing indices in the count. See also size(). */
Index nVars() const { return nVars_; }
/** The actual number of variables in this ordering, i.e. not including missing indices in the count. See also nVars(). */
Index size() const { return order_.size(); }
Index size() const { return orderingIndex_.size(); }
const_iterator begin() const { return order_.begin(); } /**< Iterator in order of sorted symbols, not in elimination/index order! */
const_iterator end() const { return order_.end(); } /**< Iterator in order of sorted symbols, not in elimination/index order! */
Index at(Key key) const { return operator[](key); } ///< Synonym for operator[](Key) const
Key key(Index index) const {
if(index >= orderingIndex_.size())
throw std::out_of_range("Attempting to access out-of-range index in Ordering");
else
return orderingIndex_[index]->first; }
/** Assigns the ordering index of the requested \c key into \c index if the symbol
* is present in the ordering, otherwise does not modify \c index. The
@ -85,8 +94,7 @@ public:
index = i->second;
return true;
} else
return false;
}
return false; }
/// Access the index for the requested key, throws std::out_of_range if the
/// key is not present in the ordering (note that this differs from the
@ -122,23 +130,25 @@ public:
*/
const_iterator find(Key key) const { return order_.find(key); }
/**
* Attempts to insert a symbol/order pair with same semantics as stl::Map::insert(),
* i.e., returns a pair of iterator and success (false if already present)
*/
std::pair<iterator,bool> tryInsert(const value_type& key_order) {
std::pair<iterator,bool> it_ok(order_.insert(key_order));
if(it_ok.second == true && key_order.second+1 > nVars_)
nVars_ = key_order.second+1;
return it_ok;
}
/** Try insert, but will fail if the key is already present */
/** Insert a key-index pair, but will fail if the key is already present */
iterator insert(const value_type& key_order) {
std::pair<iterator,bool> it_ok(tryInsert(key_order));
if(!it_ok.second) throw std::invalid_argument(std::string("Attempting to insert a key into an ordering that already contains that key"));
else return it_ok.first;
}
std::pair<iterator,bool> it_ok(order_.insert(key_order));
if(it_ok.second) {
if(key_order.second >= orderingIndex_.size())
orderingIndex_.resize(key_order.second + 1);
orderingIndex_[key_order.second] = it_ok.first;
return it_ok.first;
} else
throw std::invalid_argument(std::string("Attempting to insert a key into an ordering that already contains that key")); }
/// Test if the key exists in the ordering.
bool exists(Key key) const { return order_.count(key) > 0; }
/** Insert a key-index pair, but will fail if the key is already present */
iterator insert(Key key, Index order) { return insert(std::make_pair(key,order)); }
/// Adds a new key to the ordering with an index of one greater than the current highest index.
Index push_back(Key key) { return insert(std::make_pair(key, orderingIndex_.size()))->second; }
/// @}
/// @name Advanced Interface
@ -154,18 +164,6 @@ public:
*/
iterator end() { return order_.end(); }
/// Test if the key exists in the ordering.
bool exists(Key key) const { return order_.count(key) > 0; }
///TODO: comment
std::pair<iterator,bool> tryInsert(Key key, Index order) { return tryInsert(std::make_pair(key,order)); }
///TODO: comment
iterator insert(Key key, Index order) { return insert(std::make_pair(key,order)); }
/// Adds a new key to the ordering with an index of one greater than the current highest index.
Index push_back(Key key) { return insert(std::make_pair(key, nVars_))->second; }
/** Remove the last (last-ordered, not highest-sorting key) symbol/index pair
* from the ordering (this version is \f$ O(n) \f$, use it when you do not
* know the last-ordered key).
@ -177,16 +175,6 @@ public:
*/
value_type pop_back();
/** Remove the last-ordered symbol from the ordering (this version is
* \f$ O(\log n) \f$, use it if you already know the last-ordered key).
*
* Throws std::invalid_argument if the requested key is not actually the
* last-ordered.
*
* @return The index of the symbol that was removed.
*/
Index pop_back(Key key);
/**
* += operator allows statements like 'ordering += x0,x1,x2,x3;', which are
* very useful for unit tests. This functionality is courtesy of
@ -201,24 +189,13 @@ public:
* internally, permuting an initial key-sorted ordering into a fill-reducing
* ordering.
*/
void permuteWithInverse(const Permutation& inversePermutation);
void permuteInPlace(const Permutation& permutation);
/**
* Reorder the variables with a permutation. This is typically used
* internally, permuting an initial key-sorted ordering into a fill-reducing
* ordering.
*/
void reduceWithInverse(const internal::Reduction& inverseReduction);
void permuteInPlace(const Permutation& selector, const Permutation& permutation);
/// Synonym for operator[](Key)
Index& at(Key key) { return operator[](key); }
/**
* Create an inverse mapping from Index->Key, useful for decoding linear systems
* @return inverse mapping structure
*/
InvertedMap invert() const;
/// @}
/// @name Testable
/// @{
@ -235,16 +212,26 @@ private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
template<class ARCHIVE>
void save(ARCHIVE & ar, const unsigned int version) const
{
ar & BOOST_SERIALIZATION_NVP(order_);
ar & BOOST_SERIALIZATION_NVP(nVars_);
}
size_t size_ = orderingIndex_.size(); // Save only the size but not the iterators
ar & BOOST_SERIALIZATION_NVP(size_);
}
template<class ARCHIVE>
void load(ARCHIVE & ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(order_);
size_t size_;
ar & BOOST_SERIALIZATION_NVP(size_);
orderingIndex_.resize(size_);
for(iterator item = order_.begin(); item != order_.end(); ++item)
orderingIndex_[item->second] = item; // Assign the iterators
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
}; // \class Ordering
// typedef for use with matlab
typedef Ordering::InvertedMap InvertedOrdering;
/**
* @class Unordered
* @brief a set of unordered indices
@ -270,14 +257,15 @@ public:
// Create an index formatter that looks up the Key in an inverse ordering, then
// formats the key using the provided key formatter, used in saveGraph.
struct OrderingIndexFormatter {
Ordering::InvertedMap inverseOrdering;
const KeyFormatter& keyFormatter;
class OrderingIndexFormatter {
private:
Ordering ordering_;
KeyFormatter keyFormatter_;
public:
OrderingIndexFormatter(const Ordering& ordering, const KeyFormatter& keyFormatter) :
inverseOrdering(ordering.invert()), keyFormatter(keyFormatter) {}
ordering_(ordering), keyFormatter_(keyFormatter) {}
std::string operator()(Index index) {
return keyFormatter(inverseOrdering.at(index));
}
return keyFormatter_(ordering_.key(index)); }
};
} // \namespace gtsam

View File

@ -23,26 +23,31 @@ using namespace std;
using namespace gtsam;
/* ************************************************************************* */
TEST( testOrdering, simple_modifications ) {
TEST( Ordering, simple_modifications ) {
Ordering ordering;
// create an ordering
Symbol x1('x', 1), x2('x', 2), x3('x', 3), x4('x', 4);
ordering += x1, x2, x3, x4;
EXPECT_LONGS_EQUAL(0, ordering[x1]);
EXPECT_LONGS_EQUAL(1, ordering[x2]);
EXPECT_LONGS_EQUAL(2, ordering[x3]);
EXPECT_LONGS_EQUAL(3, ordering[x4]);
EXPECT_LONGS_EQUAL(Key(x1), ordering.key(0));
EXPECT_LONGS_EQUAL(Key(x2), ordering.key(1));
EXPECT_LONGS_EQUAL(Key(x3), ordering.key(2));
EXPECT_LONGS_EQUAL(Key(x4), ordering.key(3));
// pop the last two elements
Ordering::value_type x4p = ordering.pop_back();
EXPECT_LONGS_EQUAL(3, ordering.size());
EXPECT(assert_equal(x4, x4p.first));
Index x3p = ordering.pop_back(x3);
Index x3p = ordering.pop_back().second;
EXPECT_LONGS_EQUAL(2, ordering.size());
EXPECT_LONGS_EQUAL(2, (int)x3p);
// try to pop an element that doesn't exist and isn't last
CHECK_EXCEPTION(ordering.pop_back(x4), std::invalid_argument);
CHECK_EXCEPTION(ordering.pop_back(x1), std::invalid_argument);
// reassemble back make the ordering 1, 2, 4, 3
EXPECT_LONGS_EQUAL(2, ordering.push_back(x4));
EXPECT_LONGS_EQUAL(3, ordering.push_back(x3));
@ -50,6 +55,9 @@ TEST( testOrdering, simple_modifications ) {
EXPECT_LONGS_EQUAL(2, ordering[x4]);
EXPECT_LONGS_EQUAL(3, ordering[x3]);
EXPECT_LONGS_EQUAL(Key(x4), ordering.key(2));
EXPECT_LONGS_EQUAL(Key(x3), ordering.key(3));
// verify
Ordering expectedFinal;
expectedFinal += x1, x2, x4, x3;
@ -57,7 +65,36 @@ TEST( testOrdering, simple_modifications ) {
}
/* ************************************************************************* */
TEST( testOrdering, invert ) {
TEST(Ordering, permute) {
Ordering ordering;
ordering += 2, 4, 6, 8;
Ordering expected;
expected += 2, 8, 6, 4;
Permutation permutation(4);
permutation[0] = 0;
permutation[1] = 3;
permutation[2] = 2;
permutation[3] = 1;
Ordering actual = ordering;
actual.permuteInPlace(permutation);
EXPECT(assert_equal(expected, actual));
EXPECT_LONGS_EQUAL(0, actual[2]);
EXPECT_LONGS_EQUAL(1, actual[8]);
EXPECT_LONGS_EQUAL(2, actual[6]);
EXPECT_LONGS_EQUAL(3, actual[4]);
EXPECT_LONGS_EQUAL(2, actual.key(0));
EXPECT_LONGS_EQUAL(8, actual.key(1));
EXPECT_LONGS_EQUAL(6, actual.key(2));
EXPECT_LONGS_EQUAL(4, actual.key(3));
}
/* ************************************************************************* */
TEST( Ordering, invert ) {
// creates a map with the opposite mapping: Index->Key
Ordering ordering;
@ -65,14 +102,10 @@ TEST( testOrdering, invert ) {
Symbol x1('x', 1), x2('x', 2), x3('x', 3), x4('x', 4);
ordering += x1, x2, x3, x4;
Ordering::InvertedMap actual = ordering.invert();
Ordering::InvertedMap expected;
expected.insert(make_pair(0, x1));
expected.insert(make_pair(1, x2));
expected.insert(make_pair(2, x3));
expected.insert(make_pair(3, x4));
EXPECT(assert_container_equality(expected, actual));
EXPECT_LONGS_EQUAL(Key(x1), ordering.key(0));
EXPECT_LONGS_EQUAL(Key(x2), ordering.key(1));
EXPECT_LONGS_EQUAL(Key(x3), ordering.key(2));
EXPECT_LONGS_EQUAL(Key(x4), ordering.key(3));
}
/* ************************************************************************* */

View File

@ -252,7 +252,7 @@ TEST( GaussianFactorGraph, getOrdering)
Ordering original; original += L(1),X(1),X(2);
FactorGraph<IndexFactor> symbolic(createGaussianFactorGraph(original));
Permutation perm(*inference::PermutationCOLAMD(VariableIndex(symbolic)));
Ordering actual = original; actual.permuteWithInverse((*perm.inverse()));
Ordering actual = original; actual.permuteInPlace(perm);
Ordering expected; expected += L(1),X(2),X(1);
EXPECT(assert_equal(expected,actual));
}