Skip to content

Commit

Permalink
Merge pull request #25 from jlblancoc/fix_issue_10_iSAM2
Browse files Browse the repository at this point in the history
Fix iSAM2 support for smart factors
  • Loading branch information
dellaert authored May 29, 2019
2 parents 630d2c1 + d6b2429 commit 103223f
Show file tree
Hide file tree
Showing 6 changed files with 535 additions and 20 deletions.
13 changes: 13 additions & 0 deletions gtsam/inference/VariableIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,17 @@ void VariableIndex::outputMetisFormat(ostream& os) const {
os << flush;
}

/* ************************************************************************* */
void VariableIndex::augmentExistingFactor(const FactorIndex factorIndex, const KeySet & newKeys)
{
gttic(VariableIndex_augmentExistingFactor);

for(const Key key: newKeys) {
index_[key].push_back(factorIndex);
++nEntries_;
}

gttoc(VariableIndex_augmentExistingFactor);
}

}
7 changes: 7 additions & 0 deletions gtsam/inference/VariableIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ class GTSAM_EXPORT VariableIndex {
template<class FG>
void augment(const FG& factors, boost::optional<const FactorIndices&> newFactorIndices = boost::none);

/**
* Augment the variable index after an existing factor now affects to more
* variable Keys. This can be used when solving problems incrementally, with
* smart factors or in general with factors with a dynamic number of Keys.
*/
void augmentExistingFactor(const FactorIndex factorIndex, const KeySet & newKeys);

/**
* Remove entries corresponding to the specified factors. NOTE: We intentionally do not decrement
* nFactors_ because the factor indices need to remain consistent. Removing factors from a factor
Expand Down
72 changes: 52 additions & 20 deletions gtsam/nonlinear/ISAM2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ FactorIndexSet ISAM2::getAffectedFactors(const KeyList& keys) const {
GaussianFactorGraph::shared_ptr ISAM2::relinearizeAffectedFactors(
const FastList<Key>& affectedKeys, const KeySet& relinKeys) const {
gttic(getAffectedFactors);
KeySet candidates = getAffectedFactors(affectedKeys);
FactorIndexSet candidates = getAffectedFactors(affectedKeys);
gttoc(getAffectedFactors);

gttic(affectedKeysSet);
Expand All @@ -139,7 +139,7 @@ GaussianFactorGraph::shared_ptr ISAM2::relinearizeAffectedFactors(

gttic(check_candidates_and_linearize);
auto linearized = boost::make_shared<GaussianFactorGraph>();
for (Key idx : candidates) {
for (const FactorIndex idx : candidates) {
bool inside = true;
bool useCachedLinear = params_.cacheLinearizedFactors;
for (Key key : nonlinearFactors_[idx]->keys()) {
Expand Down Expand Up @@ -544,6 +544,21 @@ ISAM2Result ISAM2::update(
const boost::optional<FastList<Key> >& noRelinKeys,
const boost::optional<FastList<Key> >& extraReelimKeys,
bool force_relinearize) {

ISAM2UpdateParams params;
params.constrainedKeys = constrainedKeys;
params.extraReelimKeys = extraReelimKeys;
params.force_relinearize = force_relinearize;
params.noRelinKeys = noRelinKeys;
params.removeFactorIndices = removeFactorIndices;

return update(newFactors, newTheta, params);
}

/* ************************************************************************* */
ISAM2Result ISAM2::update(const NonlinearFactorGraph& newFactors,
const Values& newTheta,
const ISAM2UpdateParams& updateParams) {
const bool debug = ISDEBUG("ISAM2 update");
const bool verbose = ISDEBUG("ISAM2 update verbose");

Expand All @@ -561,7 +576,7 @@ ISAM2Result ISAM2::update(
if (params_.enableDetailedResults)
result.detail = ISAM2Result::DetailedResults();
const bool relinearizeThisStep =
force_relinearize || (params_.enableRelinearization &&
updateParams.force_relinearize || (params_.enableRelinearization &&
update_count_ % params_.relinearizeSkip == 0);

if (verbose) {
Expand All @@ -585,16 +600,17 @@ ISAM2Result ISAM2::update(

// Remove the removed factors
NonlinearFactorGraph removeFactors;
removeFactors.reserve(removeFactorIndices.size());
for (const auto index : removeFactorIndices) {
removeFactors.reserve(updateParams.removeFactorIndices.size());
for (const auto index : updateParams.removeFactorIndices) {
removeFactors.push_back(nonlinearFactors_[index]);
nonlinearFactors_.remove(index);
if (params_.cacheLinearizedFactors) linearFactors_.remove(index);
}

// Remove removed factors from the variable index so we do not attempt to
// relinearize them
variableIndex_.remove(removeFactorIndices.begin(), removeFactorIndices.end(),
variableIndex_.remove(updateParams.removeFactorIndices.begin(),
updateParams.removeFactorIndices.end(),
removeFactors);

// Compute unused keys and indices
Expand Down Expand Up @@ -649,28 +665,34 @@ ISAM2Result ISAM2::update(
markedRemoveKeys.end()); // Add to the overall set of marked keys
}
// Also mark any provided extra re-eliminate keys
if (extraReelimKeys) {
for (Key key : *extraReelimKeys) {
if (updateParams.extraReelimKeys) {
for (Key key : *updateParams.extraReelimKeys) {
markedKeys.insert(key);
}
}
// Also, keys that were not observed in existing factors, but whose affected
// keys have been extended now (e.g. smart factors)
if (updateParams.newAffectedKeys) {
for (const auto &factorAddedKeys : *updateParams.newAffectedKeys) {
const auto factorIdx = factorAddedKeys.first;
const auto& affectedKeys = nonlinearFactors_.at(factorIdx)->keys();
markedKeys.insert(affectedKeys.begin(),affectedKeys.end());
}
}

// Observed keys for detailed results
if (params_.enableDetailedResults) {
for (Key key : markedKeys) {
result.detail->variableStatus[key].isObserved = true;
}
}
// NOTE: we use assign instead of the iterator constructor here because this
// is a vector of size_t, so the constructor unintentionally resolves to
// vector(size_t count, Key value) instead of the iterator constructor.

KeyVector observedKeys;
observedKeys.reserve(markedKeys.size());
for (Key index : markedKeys) {
if (unusedIndices.find(index) ==
unusedIndices.end()) // Only add if not unused
observedKeys.push_back(
index); // Make a copy of these, as we'll soon add to them
// Only add if not unused
if (unusedIndices.find(index) == unusedIndices.end())
// Make a copy of these, as we'll soon add to them
observedKeys.push_back(index);
}
gttoc(gather_involved_keys);

Expand All @@ -695,8 +717,8 @@ ISAM2Result ISAM2::update(
for (Key key : fixedVariables_) {
relinKeys.erase(key);
}
if (noRelinKeys) {
for (Key key : *noRelinKeys) {
if (updateParams.noRelinKeys) {
for (Key key : *updateParams.noRelinKeys) {
relinKeys.erase(key);
}
}
Expand Down Expand Up @@ -773,14 +795,24 @@ ISAM2Result ISAM2::update(
variableIndex_.augment(newFactors, result.newFactorsIndices);
else
variableIndex_.augment(newFactors);

// Augment it with existing factors which now affect to more variables:
if (updateParams.newAffectedKeys) {
for (const auto &factorAddedKeys : *updateParams.newAffectedKeys) {
const auto factorIdx = factorAddedKeys.first;
variableIndex_.augmentExistingFactor(
factorIdx, factorAddedKeys.second);
}
}
gttoc(augment_VI);

gttic(recalculate);
// 8. Redo top of Bayes tree
boost::shared_ptr<KeySet> replacedKeys;
if (!markedKeys.empty() || !observedKeys.empty())
replacedKeys = recalculate(markedKeys, relinKeys, observedKeys,
unusedIndices, constrainedKeys, &result);
replacedKeys = recalculate(
markedKeys, relinKeys, observedKeys, unusedIndices,
updateParams.constrainedKeys, &result);

// Update replaced keys mask (accumulates until back-substitution takes place)
if (replacedKeys)
Expand Down
23 changes: 23 additions & 0 deletions gtsam/nonlinear/ISAM2.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <gtsam/nonlinear/ISAM2Params.h>
#include <gtsam/nonlinear/ISAM2Result.h>
#include <gtsam/nonlinear/ISAM2Clique.h>
#include <gtsam/nonlinear/ISAM2UpdateParams.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/linear/GaussianBayesTree.h>

Expand Down Expand Up @@ -156,6 +157,28 @@ class GTSAM_EXPORT ISAM2 : public BayesTree<ISAM2Clique> {
const boost::optional<FastList<Key> >& extraReelimKeys = boost::none,
bool force_relinearize = false);

/**
* Add new factors, updating the solution and relinearizing as needed.
*
* Alternative signature of update() (see its documentation above), with all
* additional parameters in one structure. This form makes easier to keep
* future API/ABI compatibility if parameters change.
*
* @param newFactors The new factors to be added to the system
* @param newTheta Initialization points for new variables to be added to the
* system. You must include here all new variables occuring in newFactors
* (which were not already in the system). There must not be any variables
* here that do not occur in newFactors, and additionally, variables that were
* already in the system must not be included here.
* @param updateParams Additional parameters to control relinearization,
* constrained keys, etc.
* @return An ISAM2Result struct containing information about the update
* @note No default parameters to avoid ambiguous call errors.
*/
virtual ISAM2Result update(
const NonlinearFactorGraph& newFactors, const Values& newTheta,
const ISAM2UpdateParams& updateParams);

/** Marginalize out variables listed in leafKeys. These keys must be leaves
* in the BayesTree. Throws MarginalizeNonleafException if non-leaves are
* requested to be marginalized. Marginalization leaves a linear
Expand Down
70 changes: 70 additions & 0 deletions gtsam/nonlinear/ISAM2UpdateParams.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* ----------------------------------------------------------------------------
* 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 ISAM2UpdateParams.h
* @brief Class that stores extra params for ISAM2::update()
* @author Michael Kaess, Richard Roberts, Frank Dellaert, Jose Luis Blanco
*/

// \callgraph

#pragma once

#include <boost/optional.hpp>
#include <gtsam/base/FastList.h>
#include <gtsam/dllexport.h> // GTSAM_EXPORT
#include <gtsam/inference/Key.h> // Key, KeySet
#include <gtsam/nonlinear/ISAM2Result.h> //FactorIndices

namespace gtsam {

/**
* @addtogroup ISAM2
* This struct is used by ISAM2::update() to pass additional parameters to
* give the user a fine-grained control on how factors and relinearized, etc.
*/
struct GTSAM_EXPORT ISAM2UpdateParams {
ISAM2UpdateParams() = default;

/** Indices of factors to remove from system (default: empty) */
FactorIndices removeFactorIndices;

/** An optional map of keys to group labels, such that a variable can be
* constrained to a particular grouping in the BayesTree */
boost::optional<FastMap<Key, int>> constrainedKeys{boost::none};

/** An optional set of nonlinear keys that iSAM2 will hold at a constant
* linearization point, regardless of the size of the linear delta */
boost::optional<FastList<Key>> noRelinKeys{boost::none};

/** An optional set of nonlinear keys that iSAM2 will re-eliminate, regardless
* of the size of the linear delta. This allows the provided keys to be
* reordered. */
boost::optional<FastList<Key>> extraReelimKeys{boost::none};

/** Relinearize any variables whose delta magnitude is sufficiently large
* (Params::relinearizeThreshold), regardless of the relinearization
* interval (Params::relinearizeSkip). */
bool force_relinearize{false};

/** An optional set of new Keys that are now affected by factors,
* indexed by factor indices (as returned by ISAM2::update()).
* Use when working with smart factors. For example:
* - Timestamp `i`: ISAM2::update() called with a new smart factor depending
* on Keys `X(0)` and `X(1)`. It returns that the factor index for the new
* smart factor (inside ISAM2) is `13`.
* - Timestamp `i+1`: The same smart factor has been augmented to now also
* depend on Keys `X(2)`, `X(3)`. Next call to ISAM2::update() must include
* its `newAffectedKeys` field with the map `13 -> {X(2), X(3)}`.
*/
boost::optional<FastMap<FactorIndex,KeySet>> newAffectedKeys{boost::none};

};

} // namespace gtsam
Loading

0 comments on commit 103223f

Please sign in to comment.