2011-10-12 15:51:37 +08:00
/* ----------------------------------------------------------------------------
* 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
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2011-08-19 02:06:35 +08:00
/**
2012-03-17 04:55:21 +08:00
* @ file ISAM2 - impl . cpp
2011-08-19 02:06:35 +08:00
* @ brief Incremental update functionality ( ISAM2 ) for BayesTree , with fluid relinearization .
* @ author Michael Kaess , Richard Roberts
*/
2012-03-17 04:55:21 +08:00
# include <gtsam/nonlinear/ISAM2-impl.h>
2012-03-18 07:57:42 +08:00
# include <gtsam/base/debug.h>
2012-03-14 03:41:03 +08:00
2011-08-19 02:06:35 +08:00
namespace gtsam {
/* ************************************************************************* */
2012-03-17 04:55:21 +08:00
void ISAM2 : : Impl : : AddVariables (
2012-03-19 22:32:37 +08:00
const Values & newTheta , Values & theta , Permuted < VectorValues > & delta ,
Permuted < VectorValues > & deltaNewton , Permuted < VectorValues > & deltaGradSearch , vector < bool > & replacedKeys ,
2012-03-18 07:57:42 +08:00
Ordering & ordering , Base : : Nodes & nodes , const KeyFormatter & keyFormatter ) {
2011-08-19 02:06:35 +08:00
const bool debug = ISDEBUG ( " ISAM2 AddVariables " ) ;
theta . insert ( newTheta ) ;
if ( debug ) newTheta . print ( " The new variables are: " ) ;
// Add the new keys onto the ordering, add zeros to the delta for the new variables
2012-02-14 04:27:54 +08:00
std : : vector < Index > dims ( newTheta . dims ( * newTheta . orderingArbitrary ( ) ) ) ;
2011-08-19 02:06:35 +08:00
if ( debug ) cout < < " New variables have total dimensionality " < < accumulate ( dims . begin ( ) , dims . end ( ) , 0 ) < < endl ;
2011-10-26 10:04:06 +08:00
const size_t newDim = accumulate ( dims . begin ( ) , dims . end ( ) , 0 ) ;
const size_t originalDim = delta - > dim ( ) ;
const size_t originalnVars = delta - > size ( ) ;
delta . container ( ) . append ( dims ) ;
2011-12-13 07:19:31 +08:00
delta . container ( ) . vector ( ) . segment ( originalDim , newDim ) . operator = ( Vector : : Zero ( newDim ) ) ;
2011-10-26 10:04:06 +08:00
delta . permutation ( ) . resize ( originalnVars + newTheta . size ( ) ) ;
2012-03-18 07:57:44 +08:00
deltaNewton . container ( ) . append ( dims ) ;
deltaNewton . container ( ) . vector ( ) . segment ( originalDim , newDim ) . operator = ( Vector : : Zero ( newDim ) ) ;
deltaNewton . permutation ( ) . resize ( originalnVars + newTheta . size ( ) ) ;
deltaGradSearch . container ( ) . append ( dims ) ;
deltaGradSearch . container ( ) . vector ( ) . segment ( originalDim , newDim ) . operator = ( Vector : : Zero ( newDim ) ) ;
deltaGradSearch . permutation ( ) . resize ( originalnVars + newTheta . size ( ) ) ;
2011-08-19 02:06:35 +08:00
{
2012-02-03 06:06:33 +08:00
Index nextVar = originalnVars ;
BOOST_FOREACH ( const Values : : ConstKeyValuePair & key_value , newTheta ) {
delta . permutation ( ) [ nextVar ] = nextVar ;
2012-03-19 22:32:37 +08:00
deltaNewton . permutation ( ) [ nextVar ] = nextVar ;
deltaGradSearch . permutation ( ) [ nextVar ] = nextVar ;
2012-02-25 07:38:51 +08:00
ordering . insert ( key_value . key , nextVar ) ;
if ( debug ) cout < < " Adding variable " < < keyFormatter ( key_value . key ) < < " with order " < < nextVar < < endl ;
2012-02-03 06:06:33 +08:00
+ + nextVar ;
}
2011-08-19 02:06:35 +08:00
assert ( delta . permutation ( ) . size ( ) = = delta . container ( ) . size ( ) ) ;
assert ( ordering . nVars ( ) = = delta . size ( ) ) ;
assert ( ordering . size ( ) = = delta . size ( ) ) ;
}
assert ( ordering . nVars ( ) > = nodes . size ( ) ) ;
2012-03-12 06:10:51 +08:00
replacedKeys . resize ( ordering . nVars ( ) , false ) ;
2011-08-19 02:06:35 +08:00
nodes . resize ( ordering . nVars ( ) ) ;
}
2011-09-02 05:53:57 +08:00
/* ************************************************************************* */
2012-03-18 07:57:42 +08:00
FastSet < Index > ISAM2 : : Impl : : IndicesFromFactors ( const Ordering & ordering , const NonlinearFactorGraph & factors ) {
2011-09-02 05:53:57 +08:00
FastSet < Index > indices ;
2012-03-18 07:57:42 +08:00
BOOST_FOREACH ( const NonlinearFactor : : shared_ptr & factor , factors ) {
2012-02-19 09:02:07 +08:00
BOOST_FOREACH ( Key key , factor - > keys ( ) ) {
2011-09-02 05:53:57 +08:00
indices . insert ( ordering [ key ] ) ;
}
}
return indices ;
}
2011-09-07 23:42:49 +08:00
/* ************************************************************************* */
2012-03-17 04:55:21 +08:00
FastSet < Index > ISAM2 : : Impl : : CheckRelinearization ( const Permuted < VectorValues > & delta , const Ordering & ordering ,
2012-02-21 08:53:35 +08:00
const ISAM2Params : : RelinearizationThreshold & relinearizeThreshold , const KeyFormatter & keyFormatter ) {
2011-09-07 23:42:49 +08:00
FastSet < Index > relinKeys ;
2012-01-07 00:53:16 +08:00
if ( relinearizeThreshold . type ( ) = = typeid ( double ) ) {
double threshold = boost : : get < double > ( relinearizeThreshold ) ;
for ( Index var = 0 ; var < delta . size ( ) ; + + var ) {
double maxDelta = delta [ var ] . lpNorm < Eigen : : Infinity > ( ) ;
if ( maxDelta > = threshold ) {
relinKeys . insert ( var ) ;
}
}
} else if ( relinearizeThreshold . type ( ) = = typeid ( FastMap < char , Vector > ) ) {
const FastMap < char , Vector > & thresholds = boost : : get < FastMap < char , Vector > > ( relinearizeThreshold ) ;
BOOST_FOREACH ( const Ordering : : value_type & key_index , ordering ) {
2012-02-21 08:53:35 +08:00
const Vector & threshold = thresholds . find ( Symbol ( key_index . first ) . chr ( ) ) - > second ;
2012-01-07 00:53:16 +08:00
Index j = key_index . second ;
if ( threshold . rows ( ) ! = delta [ j ] . rows ( ) )
throw std : : invalid_argument ( " Relinearization threshold vector dimensionality passed into iSAM2 parameters does not match actual variable dimensionality " ) ;
if ( ( delta [ j ] . array ( ) . abs ( ) > threshold . array ( ) ) . any ( ) )
relinKeys . insert ( j ) ;
2011-09-07 23:42:49 +08:00
}
}
2012-01-07 00:53:16 +08:00
2011-09-11 05:32:39 +08:00
return relinKeys ;
2011-09-07 23:42:49 +08:00
}
2011-09-28 04:24:39 +08:00
/* ************************************************************************* */
2012-03-18 07:57:42 +08:00
void ISAM2 : : Impl : : FindAll ( ISAM2Clique : : shared_ptr clique , FastSet < Index > & keys , const vector < bool > & markedMask ) {
2011-09-28 04:24:39 +08:00
static const bool debug = false ;
// does the separator contain any of the variables?
bool found = false ;
BOOST_FOREACH ( const Index & key , ( * clique ) - > parents ( ) ) {
if ( markedMask [ key ] )
found = true ;
}
if ( found ) {
// then add this clique
keys . insert ( ( * clique ) - > beginFrontals ( ) , ( * clique ) - > endFrontals ( ) ) ;
if ( debug ) clique - > print ( " Key(s) marked in clique " ) ;
if ( debug ) cout < < " so marking key " < < ( * clique ) - > keys ( ) . front ( ) < < endl ;
}
2012-03-18 07:57:42 +08:00
BOOST_FOREACH ( const ISAM2Clique : : shared_ptr & child , clique - > children_ ) {
2011-09-28 04:24:39 +08:00
FindAll ( child , keys , markedMask ) ;
}
}
/* ************************************************************************* */
2012-03-17 04:55:21 +08:00
void ISAM2 : : Impl : : ExpmapMasked ( Values & values , const Permuted < VectorValues > & delta , const Ordering & ordering ,
2012-02-21 08:53:35 +08:00
const vector < bool > & mask , boost : : optional < Permuted < VectorValues > & > invalidateIfDebug , const KeyFormatter & keyFormatter ) {
2011-09-28 04:24:39 +08:00
// If debugging, invalidate if requested, otherwise do not invalidate.
// Invalidating means setting expmapped entries to Inf, to trigger assertions
// if we try to re-use them.
# ifdef NDEBUG
invalidateIfDebug = boost : : optional < Permuted < VectorValues > & > ( ) ;
# endif
2012-03-12 06:10:51 +08:00
assert ( values . size ( ) = = ordering . nVars ( ) ) ;
assert ( delta . size ( ) = = ordering . nVars ( ) ) ;
2012-02-03 06:06:33 +08:00
Values : : iterator key_value ;
Ordering : : const_iterator key_index ;
for ( key_value = values . begin ( ) , key_index = ordering . begin ( ) ;
key_value ! = values . end ( ) & & key_index ! = ordering . end ( ) ; + + key_value , + + key_index ) {
2012-02-25 07:38:51 +08:00
assert ( key_value - > key = = key_index - > first ) ;
2012-02-03 06:06:33 +08:00
const Index var = key_index - > second ;
if ( ISDEBUG ( " ISAM2 update verbose " ) ) {
if ( mask [ var ] )
2012-02-25 07:38:51 +08:00
cout < < " expmap " < < keyFormatter ( key_value - > key ) < < " (j = " < < var < < " ), delta = " < < delta [ var ] . transpose ( ) < < endl ;
2012-02-03 06:06:33 +08:00
else
2012-02-25 07:38:51 +08:00
cout < < " " < < keyFormatter ( key_value - > key ) < < " (j = " < < var < < " ), delta = " < < delta [ var ] . transpose ( ) < < endl ;
2012-02-03 06:06:33 +08:00
}
2012-02-25 07:38:51 +08:00
assert ( delta [ var ] . size ( ) = = ( int ) key_value - > value . dim ( ) ) ;
2012-02-03 06:06:33 +08:00
assert ( delta [ var ] . unaryExpr ( & isfinite < double > ) . all ( ) ) ;
if ( mask [ var ] ) {
2012-02-25 07:38:51 +08:00
Value * retracted = key_value - > value . retract_ ( delta [ var ] ) ;
key_value - > value = * retracted ;
2012-02-03 06:06:33 +08:00
retracted - > deallocate_ ( ) ;
if ( invalidateIfDebug )
( * invalidateIfDebug ) [ var ] . operator = ( Vector : : Constant ( delta [ var ] . rows ( ) , numeric_limits < double > : : infinity ( ) ) ) ; // Strange syntax to work with clang++ (bug in clang?)
}
}
2011-09-28 04:24:39 +08:00
}
/* ************************************************************************* */
2012-03-17 04:55:21 +08:00
ISAM2 : : Impl : : PartialSolveResult
ISAM2 : : Impl : : PartialSolve ( GaussianFactorGraph & factors ,
2011-09-28 04:24:39 +08:00
const FastSet < Index > & keys , const ReorderingMode & reorderingMode ) {
static const bool debug = ISDEBUG ( " ISAM2 recalculate " ) ;
PartialSolveResult result ;
tic ( 1 , " select affected variables " ) ;
# ifndef NDEBUG
// Debug check that all variables involved in the factors to be re-eliminated
// are in affectedKeys, since we will use it to select a subset of variables.
BOOST_FOREACH ( const GaussianFactor : : shared_ptr & factor , factors ) {
BOOST_FOREACH ( Index key , factor - > keys ( ) ) {
assert ( find ( keys . begin ( ) , keys . end ( ) , key ) ! = keys . end ( ) ) ;
}
}
# endif
Permutation affectedKeysSelector ( keys . size ( ) ) ; // Create a permutation that pulls the affected keys to the front
Permutation affectedKeysSelectorInverse ( keys . size ( ) > 0 ? * keys . rbegin ( ) + 1 : 0 /*ordering_.nVars()*/ ) ; // And its inverse
# ifndef NDEBUG
// If debugging, fill with invalid values that will trip asserts if dereferenced
std : : fill ( affectedKeysSelectorInverse . begin ( ) , affectedKeysSelectorInverse . end ( ) , numeric_limits < Index > : : max ( ) ) ;
# endif
{ Index position = 0 ; BOOST_FOREACH ( Index var , keys ) {
affectedKeysSelector [ position ] = var ;
affectedKeysSelectorInverse [ var ] = position ;
+ + position ; } }
if ( debug ) affectedKeysSelector . print ( " affectedKeysSelector: " ) ;
if ( debug ) affectedKeysSelectorInverse . print ( " affectedKeysSelectorInverse: " ) ;
factors . permuteWithInverse ( affectedKeysSelectorInverse ) ;
if ( debug ) factors . print ( " Factors to reorder/re-eliminate: " ) ;
toc ( 1 , " select affected variables " ) ;
tic ( 2 , " variable index " ) ;
VariableIndex affectedFactorsIndex ( factors ) ; // Create a variable index for the factors to be re-eliminated
if ( debug ) affectedFactorsIndex . print ( " affectedFactorsIndex: " ) ;
toc ( 2 , " variable index " ) ;
tic ( 3 , " ccolamd " ) ;
2012-02-14 04:27:54 +08:00
vector < int > cmember ( affectedKeysSelector . size ( ) , 0 ) ;
2012-01-04 03:14:00 +08:00
if ( reorderingMode . constrain = = ReorderingMode : : CONSTRAIN_LAST ) {
assert ( reorderingMode . constrainedKeys ) ;
if ( keys . size ( ) > reorderingMode . constrainedKeys - > size ( ) ) {
BOOST_FOREACH ( Index var , * reorderingMode . constrainedKeys ) {
2011-09-28 04:24:39 +08:00
cmember [ affectedKeysSelectorInverse [ var ] ] = 1 ;
}
}
}
2012-03-04 04:23:03 +08:00
Permutation : : shared_ptr affectedColamd ( inference : : PermutationCOLAMD_ ( affectedFactorsIndex , cmember ) ) ;
2011-09-28 04:24:39 +08:00
toc ( 3 , " ccolamd " ) ;
tic ( 4 , " ccolamd permutations " ) ;
Permutation : : shared_ptr affectedColamdInverse ( affectedColamd - > inverse ( ) ) ;
if ( debug ) affectedColamd - > print ( " affectedColamd: " ) ;
if ( debug ) affectedColamdInverse - > print ( " affectedColamdInverse: " ) ;
result . fullReordering =
* Permutation : : Identity ( reorderingMode . nFullSystemVars ) . partialPermutation ( affectedKeysSelector , * affectedColamd ) ;
result . fullReorderingInverse =
* Permutation : : Identity ( reorderingMode . nFullSystemVars ) . partialPermutation ( affectedKeysSelector , * affectedColamdInverse ) ;
if ( debug ) result . fullReordering . print ( " partialReordering: " ) ;
toc ( 4 , " ccolamd permutations " ) ;
tic ( 5 , " permute affected variable index " ) ;
affectedFactorsIndex . permute ( * affectedColamd ) ;
toc ( 5 , " permute affected variable index " ) ;
tic ( 6 , " permute affected factors " ) ;
factors . permuteWithInverse ( * affectedColamdInverse ) ;
toc ( 6 , " permute affected factors " ) ;
if ( debug ) factors . print ( " Colamd-ordered affected factors: " ) ;
# ifndef NDEBUG
VariableIndex fromScratchIndex ( factors ) ;
assert ( assert_equal ( fromScratchIndex , affectedFactorsIndex ) ) ;
# endif
// eliminate into a Bayes net
tic ( 7 , " eliminate " ) ;
2012-03-18 07:57:42 +08:00
JunctionTree < GaussianFactorGraph , ISAM2 : : Clique > jt ( factors , affectedFactorsIndex ) ;
2011-11-13 05:19:46 +08:00
result . bayesTree = jt . eliminate ( EliminatePreferLDL ) ;
2011-09-28 04:24:39 +08:00
toc ( 7 , " eliminate " ) ;
tic ( 8 , " permute eliminated " ) ;
if ( result . bayesTree ) result . bayesTree - > permuteWithInverse ( affectedKeysSelector ) ;
if ( debug & & result . bayesTree ) {
cout < < " Full var-ordered eliminated BT: \n " ;
result . bayesTree - > printTree ( " " ) ;
}
toc ( 8 , " permute eliminated " ) ;
return result ;
}
2012-03-17 00:16:13 +08:00
/* ************************************************************************* */
namespace internal {
2012-03-17 04:55:21 +08:00
inline static void optimizeInPlace ( const boost : : shared_ptr < ISAM2Clique > & clique , VectorValues & result ) {
2012-03-17 00:16:13 +08:00
// parents are assumed to already be solved and available in result
clique - > conditional ( ) - > solveInPlace ( result ) ;
// starting from the root, call optimize on each conditional
2012-03-17 04:55:21 +08:00
BOOST_FOREACH ( const boost : : shared_ptr < ISAM2Clique > & child , clique - > children_ )
2012-03-17 00:16:13 +08:00
optimizeInPlace ( child , result ) ;
}
}
2012-03-12 06:10:51 +08:00
/* ************************************************************************* */
2012-03-17 04:55:21 +08:00
size_t ISAM2 : : Impl : : UpdateDelta ( const boost : : shared_ptr < ISAM2Clique > & root , std : : vector < bool > & replacedKeys , Permuted < VectorValues > & delta , double wildfireThreshold ) {
2012-03-12 06:10:51 +08:00
size_t lastBacksubVariableCount ;
if ( wildfireThreshold < = 0.0 ) {
// Threshold is zero or less, so do a full recalculation
// Collect dimensions and allocate new VectorValues
vector < size_t > dims ( delta . size ( ) ) ;
for ( size_t j = 0 ; j < delta . size ( ) ; + + j )
dims [ j ] = delta - > dim ( j ) ;
VectorValues newDelta ( dims ) ;
// Optimize full solution delta
2012-03-14 03:41:03 +08:00
internal : : optimizeInPlace ( root , newDelta ) ;
2012-03-12 06:10:51 +08:00
// Copy solution into delta
delta . permutation ( ) = Permutation : : Identity ( delta . size ( ) ) ;
delta . container ( ) = newDelta ;
lastBacksubVariableCount = delta . size ( ) ;
} else {
// Optimize with wildfire
2012-03-14 03:41:03 +08:00
lastBacksubVariableCount = optimizeWildfire ( root , wildfireThreshold , replacedKeys , delta ) ; // modifies delta_
2012-03-12 06:10:51 +08:00
# ifndef NDEBUG
for ( size_t j = 0 ; j < delta . container ( ) . size ( ) ; + + j )
assert ( delta . container ( ) [ j ] . unaryExpr ( & isfinite < double > ) . all ( ) ) ;
# endif
}
// Clear replacedKeys
replacedKeys . assign ( replacedKeys . size ( ) , false ) ;
return lastBacksubVariableCount ;
}
2012-03-18 13:13:40 +08:00
/* ************************************************************************* */
namespace internal {
2012-03-19 22:32:37 +08:00
void updateDoglegDeltas ( const boost : : shared_ptr < ISAM2Clique > & clique , std : : vector < bool > & replacedKeys ,
const VectorValues & grad , Permuted < VectorValues > & deltaNewton , Permuted < VectorValues > & RgProd , size_t & varsUpdated ) {
// Check if any frontal or separator keys were recalculated, if so, we need
// update deltas and recurse to children, but if not, we do not need to
// recurse further because of the running separator property.
bool anyReplaced = false ;
BOOST_FOREACH ( Index j , * clique - > conditional ( ) ) {
if ( replacedKeys [ j ] ) {
anyReplaced = true ;
break ;
}
}
2012-03-18 13:13:40 +08:00
2012-03-19 22:32:37 +08:00
if ( anyReplaced ) {
// Update the current variable
// Get VectorValues slice corresponding to current variables
Vector gR = internal : : extractVectorValuesSlices ( grad , ( * clique ) - > beginFrontals ( ) , ( * clique ) - > endFrontals ( ) ) ;
Vector gS = internal : : extractVectorValuesSlices ( grad , ( * clique ) - > beginParents ( ) , ( * clique ) - > endParents ( ) ) ;
2012-03-18 13:13:40 +08:00
2012-03-19 22:32:37 +08:00
// Compute R*g and S*g for this clique
Vector RSgProd = ( ( * clique ) - > get_R ( ) * ( * clique ) - > permutation ( ) . transpose ( ) ) * gR + ( * clique ) - > get_S ( ) * gS ;
2012-03-18 13:13:40 +08:00
2012-03-19 22:32:37 +08:00
// Write into RgProd vector
internal : : writeVectorValuesSlices ( RSgProd , RgProd , ( * clique ) - > beginFrontals ( ) , ( * clique ) - > endFrontals ( ) ) ;
2012-03-18 13:13:40 +08:00
2012-03-19 22:32:37 +08:00
// Now solve the part of the Newton's method point for this clique (back-substitution)
2012-03-20 00:25:03 +08:00
//(*clique)->solveInPlace(deltaNewton);
2012-03-19 22:32:37 +08:00
varsUpdated + = ( * clique ) - > nrFrontals ( ) ;
// Recurse to children
BOOST_FOREACH ( const ISAM2Clique : : shared_ptr & child , clique - > children ( ) ) {
updateDoglegDeltas ( child , replacedKeys , grad , deltaNewton , RgProd , varsUpdated ) ; }
}
2012-03-18 13:13:40 +08:00
}
}
/* ************************************************************************* */
2012-03-20 00:25:03 +08:00
size_t ISAM2 : : Impl : : UpdateDoglegDeltas ( const ISAM2 & isam , double wildfireThreshold , std : : vector < bool > & replacedKeys ,
2012-03-18 13:13:40 +08:00
Permuted < VectorValues > & deltaNewton , Permuted < VectorValues > & RgProd ) {
// Get gradient
VectorValues grad = * allocateVectorValues ( isam ) ;
gradientAtZero ( isam , grad ) ;
// Update variables
2012-03-19 22:32:37 +08:00
size_t varsUpdated = 0 ;
internal : : updateDoglegDeltas ( isam . root ( ) , replacedKeys , grad , deltaNewton , RgProd , varsUpdated ) ;
2012-03-20 00:25:03 +08:00
optimizeWildfire ( isam . root ( ) , wildfireThreshold , replacedKeys , deltaNewton ) ;
#if 0
VectorValues expected = * allocateVectorValues ( isam ) ;
internal : : optimizeInPlace < ISAM2 > ( isam . root ( ) , expected ) ;
for ( size_t j = 0 ; j < expected . size ( ) ; + + j )
assert ( equal_with_abs_tol ( expected [ j ] , deltaNewton [ j ] , 1e-2 ) ) ;
FactorGraph < JacobianFactor > Rd_jfg ( isam ) ;
Errors Rg = Rd_jfg * grad ;
double RgMagExpected = dot ( Rg , Rg ) ;
double RgMagActual = RgProd . container ( ) . vector ( ) . squaredNorm ( ) ;
cout < < fabs ( RgMagExpected - RgMagActual ) < < endl ;
assert ( fabs ( RgMagExpected - RgMagActual ) < ( 1e-8 * RgMagActual + 1e-4 ) ) ;
# endif
2012-03-19 22:32:37 +08:00
replacedKeys . assign ( replacedKeys . size ( ) , false ) ;
return varsUpdated ;
2012-03-18 13:13:40 +08:00
}
2011-08-19 02:06:35 +08:00
}