Merged from branch 'branches/unordered'

release/4.3a0
Richard Roberts 2013-08-12 21:52:43 +00:00
commit 175965a6bf
333 changed files with 17200 additions and 22375 deletions

View File

@ -100,10 +100,15 @@ endif()
###############################################################################
# Find boost
# To change the path for boost, you will need to set:
# BOOST_ROOT: path to install prefix for boost
# Boost_NO_SYSTEM_PATHS: set to true to keep the find script from ignoring BOOST_ROOT
# If using Boost shared libs, set up auto linking for shared libs
if(MSVC AND NOT Boost_USE_STATIC_LIBS)
add_definitions(-DBOOST_ALL_DYN_LINK)
endif()
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost 1.43 COMPONENTS serialization system filesystem thread program_options date_time regex timer chrono)
@ -115,7 +120,9 @@ endif()
option(GTSAM_DISABLE_NEW_TIMERS "Disables using Boost.chrono for timing" OFF)
# Allow for not using the timer libraries on boost < 1.48 (GTSAM timing code falls back to old timer library)
set(GTSAM_BOOST_LIBRARIES ${Boost_SERIALIZATION_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_REGEX_LIBRARY})
set(GTSAM_BOOST_LIBRARIES
${Boost_SERIALIZATION_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_REGEX_LIBRARY})
if (GTSAM_DISABLE_NEW_TIMERS)
message("WARNING: GTSAM timing instrumentation manually disabled")
add_definitions(-DGTSAM_DISABLE_NEW_TIMERS)
@ -123,7 +130,7 @@ else()
if(Boost_TIMER_LIBRARY)
list(APPEND GTSAM_BOOST_LIBRARIES ${Boost_TIMER_LIBRARY} ${Boost_CHRONO_LIBRARY})
else()
message("WARNING: Boost older than 1.48 was found, GTSAM timing instrumentation will use the older, less accurate, Boost timer library.")
message("WARNING: GTSAM timing instrumentation will use the older, less accurate, Boost timer library because boost older than 1.48 was found.")
endif()
endif()
@ -156,12 +163,32 @@ configure_file(gtsam/3rdparty/gtsam_eigen_includes.h.in gtsam/3rdparty/gtsam_eig
install(FILES ${CMAKE_BINARY_DIR}/gtsam/3rdparty/gtsam_eigen_includes.h DESTINATION include/gtsam/3rdparty)
###############################################################################
# Find TBB
set(ENV{TBB_ARCH_PLATFORM} "intel64/vc11")
set(ENV{TBB_BIN_DIR} "C:/Program Files/Intel/TBB/bin")
set(ENV{TBB_TARGET_ARCH} intel64)
set(ENV{TBB_TARGET_VS} vc11)
find_package(TBB REQUIRED)
set(TBB_LIBS "")
if(TBB_DEBUG_LIBRARIES)
foreach(lib ${TBB_LIBRARIES})
list(APPEND TBB_LIBS optimized "${lib}")
endforeach()
foreach(lib ${TBB_DEBUG_LIBRARIES})
list(APPEND TBB_LIBS debug "${lib}")
endforeach()
else()
set(TBB_LIBS ${TBB_LIBRARIES})
endif()
###############################################################################
# Global compile options
# Include boost - use 'BEFORE' so that a specific boost specified to CMake
# takes precedence over a system-installed one.
include_directories(BEFORE ${Boost_INCLUDE_DIR})
include_directories(BEFORE ${TBB_INCLUDE_DIRS} ${Boost_INCLUDE_DIR})
# Add includes for source directories 'BEFORE' boost and any system include
# paths so that the compiler uses GTSAM headers in our source directory instead
@ -175,7 +202,9 @@ include_directories(BEFORE
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
add_definitions(/wd4251 /wd4275 /wd4251) # Disable non-DLL-exported base class warnings
add_definitions(/wd4251 /wd4275 /wd4251 /wd4661) # Disable non-DLL-exported base class and other warnings
else()
list(APPEND CMAKE_CXX_FLAGS "-std=c++11") # FIXME: we should probably avoid these extensions
endif()
if(GTSAM_ENABLE_CONSISTENCY_CHECKS)

BIN
doc/CodingGuidelines.docx Normal file

Binary file not shown.

481
doc/CodingGuidelines.lyx Normal file
View File

@ -0,0 +1,481 @@
#LyX 2.0 created this file. For more info see http://www.lyx.org/
\lyxformat 413
\begin_document
\begin_header
\textclass article
\begin_preamble
\usepackage{color}
\definecolor{mygreen}{rgb}{0,0.6,0}
\definecolor{mygray}{rgb}{0.5,0.5,0.5}
\definecolor{mymauve}{rgb}{0.58,0,0.82}
\lstset{ %
backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor}
basicstyle=\footnotesize, % the size of the fonts that are used for the code
breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace
breaklines=true, % sets automatic line breaking
captionpos=b, % sets the caption-position to bottom
commentstyle=\color{mygreen}, % comment style
% deletekeywords={...}, % if you want to delete keywords from the given language
escapeinside={\%*}{*)}, % if you want to add LaTeX within your code
extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8
frame=single, % adds a frame around the code
keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible)
keywordstyle=\color{blue}, % keyword style
language=C++, % the language of the code
morekeywords={*,...}, % if you want to add more keywords to the set
numbers=left, % where to put the line-numbers; possible values are (none, left, right)
numbersep=5pt, % how far the line-numbers are from the code
numberstyle=\tiny\color{mygray}, % the style that is used for the line-numbers
rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here))
showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces'
showstringspaces=false, % underline spaces within strings only
showtabs=false, % show tabs within strings adding particular underscores
stepnumber=2, % the step between two line-numbers. If it's 1, each line will be numbered
stringstyle=\color{mymauve}, % string literal style
tabsize=2, % sets default tabsize to 2 spaces
title=\lstname % show the filename of files included with \lstinputlisting; also try caption instead of title
}
\end_preamble
\use_default_options true
\maintain_unincluded_children false
\language english
\language_package default
\inputencoding auto
\fontencoding global
\font_roman lmodern
\font_sans lmss
\font_typewriter lmtt
\font_default_family default
\use_non_tex_fonts false
\font_sc false
\font_osf false
\font_sf_scale 100
\font_tt_scale 100
\graphics default
\default_output_format default
\output_sync 0
\bibtex_command default
\index_command default
\paperfontsize default
\spacing single
\use_hyperref false
\papersize default
\use_geometry false
\use_amsmath 1
\use_esint 1
\use_mhchem 1
\use_mathdots 1
\cite_engine basic
\use_bibtopic false
\use_indices false
\paperorientation portrait
\suppress_date false
\use_refstyle 1
\index Index
\shortcut idx
\color #008000
\end_index
\secnumdepth 3
\tocdepth 3
\paragraph_separation indent
\paragraph_indentation default
\quotes_language english
\papercolumns 1
\papersides 1
\paperpagestyle default
\tracking_changes false
\output_changes false
\html_math_output 0
\html_css_as_file 0
\html_be_strict false
\end_header
\begin_body
\begin_layout Section
Template Classes
\end_layout
\begin_layout Standard
Templated classes are great for writing generic code for multiple types
(e.g.
the same elimination algorithm code for symbolic, discrete, and Gaussian
elimination) without the drawbacks of virtual inheritance (which include
rigid class interfaces, downcasting from returned base class pointers,
and additional runtime overhead).
Depending on how they're used, though, templates can result in very slow
compile times, large binary files, and hard-to-use code.
This section describes the
\begin_inset Quotes eld
\end_inset
best practices
\begin_inset Quotes erd
\end_inset
we have developed for gaining the benefits of templates without the drawbacks.
\end_layout
\begin_layout Standard
If you need to write generic code or classes, here are several programming
patterns we have found to work very well:
\end_layout
\begin_layout Subsection
The
\begin_inset Quotes eld
\end_inset
Templated Base, Specialized Derived
\begin_inset Quotes erd
\end_inset
Pattern
\end_layout
\begin_layout Standard
This pattern is for when you have a generic class containing algorithm or
data structure code that will be specialized to several types.
The templated base class should never be used directly, instead only the
specializations should be used.
Some specialized types can be pre-compiled into the library, but the option
remains to specialize new types in external libraries or projects.
\end_layout
\begin_layout Subsubsection
Basic Class Structure
\end_layout
\begin_layout Standard
We'll use
\family typewriter
FactorGraph
\family default
as an example.
It is templated on the factor type stored in it and has several specializations.
The templated base class
\family typewriter
FactorGraph<class FACTOR>
\family default
is divided into a header file (
\family typewriter
.h
\family default
) and an
\begin_inset Quotes eld
\end_inset
instantiation
\begin_inset Quotes erd
\end_inset
file (
\family typewriter
-inst.h
\family default
).
The basic class structure is as follows.
\begin_inset listings
lstparams "basicstyle={\scriptsize\ttfamily},language={C++}"
inline false
status open
\begin_layout Plain Layout
// File FactorGraph.h
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
%*
\backslash
bfseries{
\backslash
emph{
\backslash
color{red}{// Include a minimal set of headers.
Do not include any '-inst.h' files (this is the key to fast compiles).}}}*)
\end_layout
\begin_layout Plain Layout
#include <boost/serialization/nvp.hpp>
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
namespace gtsam {
\end_layout
\begin_layout Plain Layout
/** Class description */
\end_layout
\begin_layout Plain Layout
template<class FACTOR>
\end_layout
\begin_layout Plain Layout
class FactorGraph
\end_layout
\begin_layout Plain Layout
{
\end_layout
\begin_layout Plain Layout
%*
\backslash
bfseries{
\backslash
emph{
\backslash
color{red}{// Make 'private' any typedefs that must be redefined in derived
classes.
E.g.
'This' in the context of the derived class should refer to the derived
class.
These typedefs will be used only by the generic code in this base class.}}}*)
\end_layout
\begin_layout Plain Layout
private:
\end_layout
\begin_layout Plain Layout
typedef FactorGraph<FACTOR> This; ///< Typedef for this class
\end_layout
\begin_layout Plain Layout
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer to
this
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
%*
\backslash
bfseries{
\backslash
emph{
\backslash
color{red}{// Make 'public' the typedefs that will be valid in the derived
class.}}}*)
\end_layout
\begin_layout Plain Layout
public:
\end_layout
\begin_layout Plain Layout
typedef FACTOR FactorType; ///< Factor type stored in this graph
\end_layout
\begin_layout Plain Layout
typedef boost::shared_ptr<FACTOR> sharedFactor; ///< Shared pointer
to a factor
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
%*
\backslash
bfseries{
\backslash
emph{
\backslash
color{red}{// Normally, data is 'protected' so the derived class can access
it.}}}*)
\end_layout
\begin_layout Plain Layout
protected:
\end_layout
\begin_layout Plain Layout
/** Collection of factors */
\end_layout
\begin_layout Plain Layout
std::vector<sharedFactor> factors_;
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
%*
\backslash
bfseries{
\backslash
emph{
\backslash
color{red}{// Make 'protected' all constructors, named constructors, or
methods returning the base class type.
These are not public - the derived class will call them and properly convert
returned base classes to the derived class.}}}*)
\end_layout
\begin_layout Plain Layout
/// @name Standard Constructors
\end_layout
\begin_layout Plain Layout
/// @{
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
/** Default constructor */
\end_layout
\begin_layout Plain Layout
FactorGraphUnordered() {}
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
/** Named constructor from iterator over factors */
\end_layout
\begin_layout Plain Layout
template<typename ITERATOR>
\end_layout
\begin_layout Plain Layout
static This FromIterator(ITERATOR firstFactor, ITERATOR lastFactor);
\end_layout
\begin_layout Plain Layout
/// @}
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
%*
\backslash
bfseries{
\backslash
emph{
\backslash
color{red}{// Make 'public' standard methods that will be available in the
derived class's API.}}}*)
\end_layout
\begin_layout Plain Layout
public:
\end_layout
\begin_layout Plain Layout
/// @name Adding Factors
\end_layout
\begin_layout Plain Layout
/// @{
\end_layout
\begin_layout Plain Layout
/** ...
*/
\end_layout
\begin_layout Plain Layout
void reserve(size_t size);
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
/// @}
\end_layout
\begin_layout Plain Layout
};
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\end_body
\end_document

View File

@ -27,7 +27,7 @@
#include <gtsam/geometry/Pose2.h>
// We will use simple integer Keys to refer to the robot poses.
#include <gtsam/nonlinear/Key.h>
#include <gtsam/inference/Key.h>
// As in OdometryExample.cpp, we use a BetweenFactor to model odometry measurements.
#include <gtsam/slam/BetweenFactor.h>

View File

@ -29,7 +29,7 @@
#include <gtsam/geometry/Pose2.h>
// We will use simple integer Keys to refer to the robot poses.
#include <gtsam/nonlinear/Key.h>
#include <gtsam/inference/Key.h>
// In GTSAM, measurement functions are represented as 'factors'. Several common factors
// have been provided with the library for solving robotics/SLAM/Bundle Adjustment problems.

View File

@ -33,7 +33,7 @@
// Each variable in the system (poses) must be identified with a unique key.
// We can either use simple integer keys (1, 2, 3, ...) or symbols (X1, X2, L1).
// Here we will use simple integer keys
#include <gtsam/nonlinear/Key.h>
#include <gtsam/inference/Key.h>
// In GTSAM, measurement functions are represented as 'factors'. Several common factors
// have been provided with the library for solving robotics/SLAM/Bundle Adjustment problems.

View File

@ -26,6 +26,7 @@
#include <gtsam/nonlinear/Marginals.h>
#include <fstream>
#include <iostream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/export.hpp>
@ -489,15 +490,14 @@ void runPerturb()
// Perturb values
VectorValues noise;
Ordering ordering = *initial.orderingArbitrary();
BOOST_FOREACH(const Values::KeyValuePair& key_val, initial)
{
Vector noisev(key_val.value.dim());
for(Vector::Index i = 0; i < noisev.size(); ++i)
noisev(i) = normal(rng);
noise.insert(ordering[key_val.key], noisev);
noise.insert(key_val.key, noisev);
}
Values perturbed = initial.retract(noise, ordering);
Values perturbed = initial.retract(noise);
// Write results
try {

View File

@ -24,7 +24,6 @@
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam/nonlinear/Ordering.h>
#include <gtsam/nonlinear/Symbol.h>
#include <gtsam/linear/GaussianSequentialSolver.h>
#include <gtsam/linear/GaussianBayesNet.h>
#include <gtsam/linear/GaussianFactorGraph.h>
#include <gtsam/linear/NoiseModel.h>

688
gtsam.h

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,8 @@ set (gtsam_subdirs
base
geometry
inference
discrete
symbolic
#discrete
linear
nonlinear
slam
@ -77,13 +78,21 @@ set(gtsam_srcs
${base_srcs}
${geometry_srcs}
${inference_srcs}
${discrete_srcs}
${symbolic_srcs}
#${discrete_srcs}
${linear_srcs}
${nonlinear_srcs}
${slam_srcs}
${navigation_srcs}
${gtsam_core_headers}
)
# Generate and install config and dllexport files
configure_file(config.h.in config.h)
set(library_name GTSAM) # For substitution in dllexport.h.in
configure_file("${PROJECT_SOURCE_DIR}/cmake/dllexport.h.in" "dllexport.h")
list(APPEND gtsam_srcs "${CMAKE_BINARY_DIR}/gtsam/config.h" "${CMAKE_BINARY_DIR}/gtsam/dllexport.h")
install(FILES "${CMAKE_BINARY_DIR}/gtsam/config.h" "${CMAKE_BINARY_DIR}/gtsam/dllexport.h" DESTINATION include/gtsam)
# Versions
set(gtsam_version ${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}.${GTSAM_VERSION_PATCH})
@ -95,7 +104,7 @@ message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}")
if (GTSAM_BUILD_STATIC_LIBRARY)
message(STATUS "Building GTSAM - static")
add_library(gtsam-static STATIC ${gtsam_srcs})
target_link_libraries(gtsam-static ${GTSAM_BOOST_LIBRARIES})
target_link_libraries(gtsam-static ${GTSAM_BOOST_LIBRARIES} ${TBB_LIBS})
set_target_properties(gtsam-static PROPERTIES
OUTPUT_NAME gtsam
CLEAN_DIRECT_OUTPUT 1
@ -114,7 +123,7 @@ endif ()
if (GTSAM_BUILD_SHARED_LIBRARY)
message(STATUS "Building GTSAM - shared")
add_library(gtsam-shared SHARED ${gtsam_srcs})
target_link_libraries(gtsam-shared ${GTSAM_BOOST_LIBRARIES})
target_link_libraries(gtsam-shared ${GTSAM_BOOST_LIBRARIES} ${TBB_LIBS})
set_target_properties(gtsam-shared PROPERTIES
OUTPUT_NAME gtsam
CLEAN_DIRECT_OUTPUT 1
@ -142,10 +151,6 @@ if(MSVC)
set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/slam/serialization.cpp"
APPEND PROPERTY COMPILE_FLAGS "/bigobj")
endif()
# Generate and install config file
configure_file(config.h.in config.h)
install(FILES ${CMAKE_BINARY_DIR}/gtsam/config.h DESTINATION include/gtsam)
# Create the matlab toolbox for the gtsam library
if (GTSAM_INSTALL_MATLAB_TOOLBOX)

View File

@ -58,6 +58,9 @@ public:
return std::map<KEY,VALUE>(this->begin(), this->end());
}
/** Handy 'exists' function */
bool exists(const KEY& e) const { return this->find(e) != this->end(); }
private:
/** Serialization function */
friend class boost::serialization::access;

View File

@ -91,6 +91,9 @@ public:
return std::set<VALUE>(this->begin(), this->end());
}
/** Handy 'exists' function */
bool exists(const VALUE& e) const { return this->find(e) != this->end(); }
/** Print to implement Testable */
void print(const std::string& str = "") const { FastSetTestableHelper<VALUE>::print(*this, str); }

View File

@ -23,6 +23,7 @@
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tokenizer.hpp>
#include <cstdarg>
#include <cstring>
@ -587,7 +588,7 @@ void vector_scale_inplace(const Vector& v, Matrix& A, bool inf_mask) {
if (inf_mask) {
for (size_t i=0; i<m; ++i) {
const double& vi = v(i);
if (!isnan(vi) && !isinf(vi))
if (std::isfinite(vi))
A.row(i) *= vi;
}
} else {
@ -612,7 +613,7 @@ Matrix vector_scale(const Matrix& A, const Vector& v, bool inf_mask) {
if (inf_mask) {
for (size_t j=0; j<n; ++j) {
const double& vj = v(j);
if (!isnan(vj) && !isinf(vj))
if (std::isfinite(vj))
M.col(j) *= vj;
}
} else {
@ -768,6 +769,44 @@ Matrix Cayley(const Matrix& A) {
// inlined to let Eigen do more optimization
return (Matrix::Identity(n, n) - A)*(Matrix::Identity(n, n) + A).inverse();
}
/* ************************************************************************* */
std::string formatMatrixIndented(const std::string& label, const Matrix& matrix, bool makeVectorHorizontal)
{
stringstream ss;
const string firstline = label;
ss << firstline;
const string padding(firstline.size(), ' ');
const bool transposeMatrix = makeVectorHorizontal && matrix.cols() == 1 && matrix.rows() > 1;
const DenseIndex effectiveRows = transposeMatrix ? matrix.cols() : matrix.rows();
if(matrix.rows() > 0 && matrix.cols() > 0)
{
stringstream matrixPrinted;
if(transposeMatrix)
matrixPrinted << matrix.transpose();
else
matrixPrinted << matrix;
const std::string matrixStr = matrixPrinted.str();
boost::tokenizer<boost::char_separator<char> > tok(matrixStr, boost::char_separator<char>("\n"));
DenseIndex row = 0;
BOOST_FOREACH(const std::string& line, tok)
{
assert(row < effectiveRows);
if(row > 0)
ss << padding;
ss << "[ " << line << " ]";
if(row < effectiveRows - 1)
ss << "\n";
++ row;
}
} else {
ss << "Empty (" << matrix.rows() << "x" << matrix.cols() << ")";
}
return ss.str();
}
} // namespace gtsam

View File

@ -497,6 +497,8 @@ Eigen::Matrix<double, N, N> Cayley(const Eigen::Matrix<double, N, N>& A) {
return (FMat::Identity() - A)*(FMat::Identity() + A).inverse();
}
std::string formatMatrixIndented(const std::string& label, const Matrix& matrix, bool makeVectorHorizontal = false);
} // namespace gtsam
#include <boost/serialization/nvp.hpp>

View File

@ -0,0 +1,50 @@
/* ----------------------------------------------------------------------------
* 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 SymmetricBlockMatrix.cpp
* @brief Access to matrices via blocks of pre-defined sizes. Used in GaussianFactor and GaussianConditional.
* @author Richard Roberts
* @date Sep 18, 2010
*/
#include <gtsam/base/SymmetricBlockMatrix.h>
#include <gtsam/base/VerticalBlockMatrix.h>
namespace gtsam {
/* ************************************************************************* */
SymmetricBlockMatrix SymmetricBlockMatrix::LikeActiveViewOf(const SymmetricBlockMatrix& other)
{
SymmetricBlockMatrix result;
result.variableColOffsets_.resize(other.nBlocks() + 1);
for(size_t i = 0; i < result.variableColOffsets_.size(); ++i)
result.variableColOffsets_[i] =
other.variableColOffsets_[other.blockStart_ + i] - other.variableColOffsets_[other.blockStart_];
result.matrix_.resize(other.cols(), other.cols());
result.assertInvariants();
return result;
}
/* ************************************************************************* */
SymmetricBlockMatrix SymmetricBlockMatrix::LikeActiveViewOf(const VerticalBlockMatrix& other)
{
SymmetricBlockMatrix result;
result.variableColOffsets_.resize(other.nBlocks() + 1);
for(size_t i = 0; i < result.variableColOffsets_.size(); ++i)
result.variableColOffsets_[i] =
other.variableColOffsets_[other.blockStart_ + i] - other.variableColOffsets_[other.blockStart_];
result.matrix_.resize(other.cols(), other.cols());
result.assertInvariants();
return result;
}
}

View File

@ -0,0 +1,229 @@
/* ----------------------------------------------------------------------------
* 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 SymmetricBlockMatrix.h
* @brief Access to matrices via blocks of pre-defined sizes. Used in GaussianFactor and GaussianConditional.
* @author Richard Roberts
* @date Sep 18, 2010
*/
#pragma once
#include <gtsam/base/Matrix.h>
namespace gtsam {
// Forward declarations
class VerticalBlockMatrix;
/**
* This class stores a dense matrix and allows it to be accessed as a collection of blocks. When
* constructed, the caller must provide the dimensions of the blocks.
*
* The block structure is symmetric, but the underlying matrix does not necessarily need to be.
*
* This class also has a parameter that can be changed after construction to change the apparent
* matrix view. firstBlock() determines the block that appears to have index 0 for all operations
* (except re-setting firstBlock()).
*
* @addtogroup base */
class SymmetricBlockMatrix
{
public:
typedef SymmetricBlockMatrix This;
typedef Eigen::Block<Matrix> Block;
typedef Eigen::Block<const Matrix> constBlock;
protected:
Matrix matrix_; ///< The full matrix
std::vector<DenseIndex> variableColOffsets_; ///< the starting columns of each block (0-based)
DenseIndex blockStart_; ///< Changes apparent matrix view, see main class comment.
public:
/** Construct from an empty matrix (asserts that the matrix is empty) */
SymmetricBlockMatrix() :
blockStart_(0)
{
variableColOffsets_.push_back(0);
assertInvariants();
}
/** Construct from a container of the sizes of each block. */
template<typename CONTAINER>
SymmetricBlockMatrix(const CONTAINER& dimensions) :
blockStart_(0)
{
fillOffsets(dimensions.begin(), dimensions.end());
matrix_.resize(variableColOffsets_.back(), variableColOffsets_.back());
assertInvariants();
}
/**
* Construct from iterator over the sizes of each vertical block. */
template<typename ITERATOR>
SymmetricBlockMatrix(ITERATOR firstBlockDim, ITERATOR lastBlockDim) :
blockStart_(0)
{
fillOffsets(firstBlockDim, lastBlockDim);
matrix_.resize(variableColOffsets_.back(), variableColOffsets_.back());
assertInvariants();
}
/** Construct from a container of the sizes of each vertical block and a pre-prepared matrix. */
template<typename CONTAINER>
SymmetricBlockMatrix(const CONTAINER& dimensions, const Matrix& matrix) :
matrix_(matrix), blockStart_(0)
{
fillOffsets(dimensions.begin(), dimensions.end());
if(matrix_.rows() != matrix_.cols())
throw std::invalid_argument("Requested to create a SymmetricBlockMatrix from a non-square matrix.");
if(variableColOffsets_.back() != matrix_.cols())
throw std::invalid_argument("Requested to create a SymmetricBlockMatrix with dimensions that do not sum to the total size of the provided matrix.");
assertInvariants();
}
/** Copy the block structure, but do not copy the matrix data. If blockStart() has been
* modified, this copies the structure of the corresponding matrix view. In the destination
* SymmetricBlockMatrix, blockStart() will be 0. */
static SymmetricBlockMatrix LikeActiveViewOf(const SymmetricBlockMatrix& other);
/** Copy the block structure, but do not copy the matrix data. If blockStart() has been
* modified, this copies the structure of the corresponding matrix view. In the destination
* SymmetricBlockMatrix, blockStart() will be 0. */
static SymmetricBlockMatrix LikeActiveViewOf(const VerticalBlockMatrix& other);
/** Row size */
DenseIndex rows() const { assertInvariants(); return variableColOffsets_.back() - variableColOffsets_[blockStart_]; }
/** Column size */
DenseIndex cols() const { return rows(); }
/** Block count */
DenseIndex nBlocks() const { assertInvariants(); return variableColOffsets_.size() - 1 - blockStart_; }
Block operator()(DenseIndex i_block, DenseIndex j_block) {
return range(i_block, i_block+1, j_block, j_block+1);
}
constBlock operator()(DenseIndex i_block, DenseIndex j_block) const {
return range(i_block, i_block+1, j_block, j_block+1);
}
Block range(DenseIndex i_startBlock, DenseIndex i_endBlock, DenseIndex j_startBlock, DenseIndex j_endBlock) {
assertInvariants();
DenseIndex i_actualStartBlock = i_startBlock + blockStart_;
DenseIndex i_actualEndBlock = i_endBlock + blockStart_;
DenseIndex j_actualStartBlock = j_startBlock + blockStart_;
DenseIndex j_actualEndBlock = j_endBlock + blockStart_;
checkBlock(i_actualStartBlock);
checkBlock(j_actualStartBlock);
if(i_startBlock != 0 || i_endBlock != 0) {
checkBlock(i_actualStartBlock);
assert(i_actualEndBlock < (DenseIndex)variableColOffsets_.size());
}
if(j_startBlock != 0 || j_endBlock != 0) {
checkBlock(j_actualStartBlock);
assert(j_actualEndBlock < (DenseIndex)variableColOffsets_.size());
}
return matrix_.block(
variableColOffsets_[i_actualStartBlock], variableColOffsets_[j_actualStartBlock],
variableColOffsets_[i_actualEndBlock] - variableColOffsets_[i_actualStartBlock],
variableColOffsets_[j_actualEndBlock] - variableColOffsets_[j_actualStartBlock]);
}
constBlock range(DenseIndex i_startBlock, DenseIndex i_endBlock, DenseIndex j_startBlock, DenseIndex j_endBlock) const {
assertInvariants();
DenseIndex i_actualStartBlock = i_startBlock + blockStart_;
DenseIndex i_actualEndBlock = i_endBlock + blockStart_;
DenseIndex j_actualStartBlock = j_startBlock + blockStart_;
DenseIndex j_actualEndBlock = j_endBlock + blockStart_;
if(i_startBlock != 0 || i_endBlock != 0) {
checkBlock(i_actualStartBlock);
assert(i_actualEndBlock < (DenseIndex)variableColOffsets_.size());
}
if(j_startBlock != 0 || j_endBlock != 0) {
checkBlock(j_actualStartBlock);
assert(j_actualEndBlock < (DenseIndex)variableColOffsets_.size());
}
return matrix_.block(
variableColOffsets_[i_actualStartBlock], variableColOffsets_[j_actualStartBlock],
variableColOffsets_[i_actualEndBlock] - variableColOffsets_[i_actualStartBlock],
variableColOffsets_[j_actualEndBlock] - variableColOffsets_[j_actualStartBlock]);
}
/** Return the full matrix, *not* including any portions excluded by firstBlock(). */
Block full() {
return range(0,nBlocks(), 0,nBlocks());
}
/** Return the full matrix, *not* including any portions excluded by firstBlock(). */
constBlock full() const {
return range(0,nBlocks(), 0,nBlocks());
}
/** Access to full matrix, including any portions excluded by firstBlock() to other operations. */
const Matrix& matrix() const { return matrix_; }
/** Access to full matrix, including any portions excluded by firstBlock() to other operations. */
Matrix& matrix() { return matrix_; }
DenseIndex offset(DenseIndex block) const {
assertInvariants();
DenseIndex actualBlock = block + blockStart_;
checkBlock(actualBlock);
return variableColOffsets_[actualBlock];
}
DenseIndex& blockStart() { return blockStart_; }
DenseIndex blockStart() const { return blockStart_; }
protected:
void assertInvariants() const {
assert(matrix_.rows() == matrix_.cols());
assert(matrix_.cols() == variableColOffsets_.back());
assert(blockStart_ < (DenseIndex)variableColOffsets_.size());
}
void checkBlock(DenseIndex block) const {
assert(matrix_.rows() == matrix_.cols());
assert(matrix_.cols() == variableColOffsets_.back());
assert(block < (DenseIndex)variableColOffsets_.size()-1);
assert(variableColOffsets_[block] < matrix_.cols() && variableColOffsets_[block+1] <= matrix_.cols());
}
template<typename ITERATOR>
void fillOffsets(ITERATOR firstBlockDim, ITERATOR lastBlockDim) {
variableColOffsets_.resize((lastBlockDim-firstBlockDim)+1);
variableColOffsets_[0] = 0;
DenseIndex j=0;
for(ITERATOR dim=firstBlockDim; dim!=lastBlockDim; ++dim) {
variableColOffsets_[j+1] = variableColOffsets_[j] + *dim;
++ j;
}
}
friend class VerticalBlockMatrix;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(matrix_);
ar & BOOST_SERIALIZATION_NVP(variableColOffsets_);
ar & BOOST_SERIALIZATION_NVP(blockStart_);
}
};
}

View File

@ -231,8 +231,6 @@ bool assert_container_equal(const std::vector<std::pair<V1,V2> >& expected,
template<class V>
bool assert_container_equal(const V& expected, const V& actual, double tol = 1e-9) {
bool match = true;
if (expected.size() != actual.size())
match = false;
typename V::const_iterator
itExp = expected.begin(),
itAct = actual.begin();
@ -243,6 +241,8 @@ bool assert_container_equal(const V& expected, const V& actual, double tol = 1e-
break;
}
}
if(itExp != expected.end() || itAct != actual.end())
match = false;
}
if(!match) {
std::cout << "expected: " << std::endl;

View File

@ -165,7 +165,7 @@ bool equal_with_abs_tol(const Vector& vec1, const Vector& vec2, double tol) {
if (vec1.size()!=vec2.size()) return false;
size_t m = vec1.size();
for(size_t i=0; i<m; ++i) {
if(isnan(vec1[i]) ^ isnan(vec2[i]))
if(std::isnan(vec1[i]) ^ std::isnan(vec2[i]))
return false;
if(fabs(vec1[i] - vec2[i]) > tol)
return false;
@ -178,7 +178,7 @@ bool equal_with_abs_tol(const SubVector& vec1, const SubVector& vec2, double tol
if (vec1.size()!=vec2.size()) return false;
size_t m = vec1.size();
for(size_t i=0; i<m; ++i) {
if(isnan(vec1[i]) ^ isnan(vec2[i]))
if(std::isnan(vec1[i]) ^ std::isnan(vec2[i]))
return false;
if(fabs(vec1[i] - vec2[i]) > tol)
return false;
@ -387,7 +387,7 @@ double weightedPseudoinverse(const Vector& a, const Vector& weights,
// Slow version with error checking
pair<Vector, double>
weightedPseudoinverse(const Vector& a, const Vector& weights) {
int m = weights.size();
DenseIndex m = weights.size();
if (a.size() != m)
throw invalid_argument("a and weights have different sizes!");
Vector pseudo(m);

View File

@ -0,0 +1,52 @@
/* ----------------------------------------------------------------------------
* 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 VerticalBlockMatrix.cpp
* @brief A matrix with column blocks of pre-defined sizes. Used in JacobianFactor and
* GaussianConditional.
* @author Richard Roberts
* @date Sep 18, 2010 */
#include <gtsam/base/VerticalBlockMatrix.h>
#include <gtsam/base/SymmetricBlockMatrix.h>
namespace gtsam {
/* ************************************************************************* */
VerticalBlockMatrix VerticalBlockMatrix::LikeActiveViewOf(const VerticalBlockMatrix& other)
{
VerticalBlockMatrix result;
result.variableColOffsets_.resize(other.nBlocks() + 1);
for(size_t i = 0; i < result.variableColOffsets_.size(); ++i)
result.variableColOffsets_[i] =
other.variableColOffsets_[other.blockStart_ + i] - other.variableColOffsets_[other.blockStart_];
result.matrix_.resize(other.rows(), result.variableColOffsets_.back());
result.rowEnd_ = other.rows();
result.assertInvariants();
return result;
}
/* ************************************************************************* */
VerticalBlockMatrix VerticalBlockMatrix::LikeActiveViewOf(const SymmetricBlockMatrix& other, DenseIndex height)
{
VerticalBlockMatrix result;
result.variableColOffsets_.resize(other.nBlocks() + 1);
for(size_t i = 0; i < result.variableColOffsets_.size(); ++i)
result.variableColOffsets_[i] =
other.variableColOffsets_[other.blockStart_ + i] - other.variableColOffsets_[other.blockStart_];
result.matrix_.resize(height, result.variableColOffsets_.back());
result.rowEnd_ = height;
result.assertInvariants();
return result;
}
}

View File

@ -0,0 +1,229 @@
/* ----------------------------------------------------------------------------
* 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 VerticalBlockMatrix.h
* @brief A matrix with column blocks of pre-defined sizes. Used in JacobianFactor and
* GaussianConditional.
* @author Richard Roberts
* @date Sep 18, 2010 */
#pragma once
#include <gtsam/base/Matrix.h>
namespace gtsam {
// Forward declarations
class SymmetricBlockMatrix;
/**
* This class stores a dense matrix and allows it to be accessed as a collection of vertical
* blocks. The dimensions of the blocks are provided when constructing this class.
*
* This class also has three parameters that can be changed after construction that change the
* apparent view of the matrix without any reallocation or data copying. firstBlock() determines
* the block that has index 0 for all operations (except for re-setting firstBlock()). rowStart()
* determines the apparent first row of the matrix for all operations (except for setting
* rowStart() and rowEnd()). rowEnd() determines the apparent exclusive (one-past-the-last) last
* row for all operations. To include all rows, rowEnd() should be set to the number of rows in
* the matrix (i.e. one after the last true row index).
*
* @addtogroup base */
class GTSAM_EXPORT VerticalBlockMatrix
{
public:
typedef VerticalBlockMatrix This;
typedef Eigen::Block<Matrix> Block;
typedef Eigen::Block<const Matrix> constBlock;
protected:
Matrix matrix_; ///< The full matrix
std::vector<DenseIndex> variableColOffsets_; ///< the starting columns of each block (0-based)
DenseIndex rowStart_; ///< Changes apparent matrix view, see main class comment.
DenseIndex rowEnd_; ///< Changes apparent matrix view, see main class comment.
DenseIndex blockStart_; ///< Changes apparent matrix view, see main class comment.
public:
/** Construct an empty VerticalBlockMatrix */
VerticalBlockMatrix() :
rowStart_(0), rowEnd_(0), blockStart_(0)
{
variableColOffsets_.push_back(0);
assertInvariants();
}
/** Construct from a container of the sizes of each vertical block. */
template<typename CONTAINER>
VerticalBlockMatrix(const CONTAINER& dimensions, DenseIndex height) :
rowStart_(0), rowEnd_(height), blockStart_(0)
{
fillOffsets(dimensions.begin(), dimensions.end());
matrix_.resize(height, variableColOffsets_.back());
assertInvariants();
}
/** Construct from a container of the sizes of each vertical block and a pre-prepared matrix. */
template<typename CONTAINER>
VerticalBlockMatrix(const CONTAINER& dimensions, const Matrix& matrix) :
matrix_(matrix), rowStart_(0), rowEnd_(matrix.rows()), blockStart_(0)
{
fillOffsets(dimensions.begin(), dimensions.end());
if(variableColOffsets_.back() != matrix_.cols())
throw std::invalid_argument("Requested to create a VerticalBlockMatrix with dimensions that do not sum to the total columns of the provided matrix.");
assertInvariants();
}
/**
* Construct from iterator over the sizes of each vertical block. */
template<typename ITERATOR>
VerticalBlockMatrix(ITERATOR firstBlockDim, ITERATOR lastBlockDim, DenseIndex height) :
rowStart_(0), rowEnd_(height), blockStart_(0)
{
fillOffsets(firstBlockDim, lastBlockDim);
matrix_.resize(height, variableColOffsets_.back());
assertInvariants();
}
/** Copy the block structure and resize the underlying matrix, but do not copy the matrix data.
* If blockStart(), rowStart(), and/or rowEnd() have been modified, this copies the structure of
* the corresponding matrix view. In the destination VerticalBlockView, blockStart() and
* rowStart() will thus be 0, rowEnd() will be cols() of the source VerticalBlockView, and the
* underlying matrix will be the size of the view of the source matrix. */
static VerticalBlockMatrix LikeActiveViewOf(const VerticalBlockMatrix& rhs);
/** Copy the block structure, but do not copy the matrix data. If blockStart() has been
* modified, this copies the structure of the corresponding matrix view. In the destination
* VerticalBlockMatrix, blockStart() will be 0. */
static VerticalBlockMatrix LikeActiveViewOf(const SymmetricBlockMatrix& rhs, DenseIndex height);
/// Row size
DenseIndex rows() const { assertInvariants(); return rowEnd_ - rowStart_; }
/// Column size
DenseIndex cols() const { assertInvariants(); return variableColOffsets_.back() - variableColOffsets_[blockStart_]; }
/// Block count
DenseIndex nBlocks() const { assertInvariants(); return variableColOffsets_.size() - 1 - blockStart_; }
/** Access a single block in the underlying matrix with read/write access */
Block operator()(DenseIndex block) { return range(block, block+1); }
/** Access a const block view */
const constBlock operator()(DenseIndex block) const { return range(block, block+1); }
/** access ranges of blocks at a time */
Block range(DenseIndex startBlock, DenseIndex endBlock) {
assertInvariants();
DenseIndex actualStartBlock = startBlock + blockStart_;
DenseIndex actualEndBlock = endBlock + blockStart_;
if(startBlock != 0 || endBlock != 0) {
checkBlock(actualStartBlock);
assert(actualEndBlock < (DenseIndex)variableColOffsets_.size());
}
const DenseIndex startCol = variableColOffsets_[actualStartBlock];
const DenseIndex rangeCols = variableColOffsets_[actualEndBlock] - startCol;
return matrix_.block(rowStart_, startCol, this->rows(), rangeCols);
}
const constBlock range(DenseIndex startBlock, DenseIndex endBlock) const {
assertInvariants();
DenseIndex actualStartBlock = startBlock + blockStart_;
DenseIndex actualEndBlock = endBlock + blockStart_;
if(startBlock != 0 || endBlock != 0) {
checkBlock(actualStartBlock);
assert(actualEndBlock < (DenseIndex)variableColOffsets_.size());
}
const DenseIndex startCol = variableColOffsets_[actualStartBlock];
const DenseIndex rangeCols = variableColOffsets_[actualEndBlock] - startCol;
return ((const Matrix&)matrix_).block(rowStart_, startCol, this->rows(), rangeCols);
}
/** Return the full matrix, *not* including any portions excluded by rowStart(), rowEnd(), and firstBlock() */
Block full() { return range(0, nBlocks()); }
/** Return the full matrix, *not* including any portions excluded by rowStart(), rowEnd(), and firstBlock() */
const constBlock full() const { return range(0, nBlocks()); }
DenseIndex offset(DenseIndex block) const {
assertInvariants();
DenseIndex actualBlock = block + blockStart_;
checkBlock(actualBlock);
return variableColOffsets_[actualBlock];
}
/** Get or set the apparent first row of the underlying matrix for all operations */
DenseIndex& rowStart() { return rowStart_; }
/** Get or set the apparent last row (exclusive, i.e. rows() == rowEnd() - rowStart()) of the underlying matrix for all operations */
DenseIndex& rowEnd() { return rowEnd_; }
/** Get or set the apparent first block for all operations */
DenseIndex& firstBlock() { return blockStart_; }
/** Get the apparent first row of the underlying matrix for all operations */
DenseIndex rowStart() const { return rowStart_; }
/** Get the apparent last row (exclusive, i.e. rows() == rowEnd() - rowStart()) of the underlying matrix for all operations */
DenseIndex rowEnd() const { return rowEnd_; }
/** Get the apparent first block for all operations */
DenseIndex firstBlock() const { return blockStart_; }
/** Access to full matrix (*including* any portions excluded by rowStart(), rowEnd(), and firstBlock()) */
const Matrix& matrix() const { return matrix_; }
/** Non-const access to full matrix (*including* any portions excluded by rowStart(), rowEnd(), and firstBlock()) */
Matrix& matrix() { return matrix_; }
protected:
void assertInvariants() const {
assert(matrix_.cols() == variableColOffsets_.back());
assert(blockStart_ < (DenseIndex)variableColOffsets_.size());
assert(rowStart_ <= matrix_.rows());
assert(rowEnd_ <= matrix_.rows());
assert(rowStart_ <= rowEnd_);
}
void checkBlock(DenseIndex block) const {
assert(matrix_.cols() == variableColOffsets_.back());
assert(block < (DenseIndex)variableColOffsets_.size() - 1);
assert(variableColOffsets_[block] < matrix_.cols() && variableColOffsets_[block+1] <= matrix_.cols());
}
template<typename ITERATOR>
void fillOffsets(ITERATOR firstBlockDim, ITERATOR lastBlockDim) {
variableColOffsets_.resize((lastBlockDim-firstBlockDim)+1);
variableColOffsets_[0] = 0;
DenseIndex j=0;
for(ITERATOR dim=firstBlockDim; dim!=lastBlockDim; ++dim) {
variableColOffsets_[j+1] = variableColOffsets_[j] + *dim;
++ j;
}
}
friend class SymmetricBlockMatrix;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(matrix_);
ar & BOOST_SERIALIZATION_NVP(variableColOffsets_);
ar & BOOST_SERIALIZATION_NVP(rowStart_);
ar & BOOST_SERIALIZATION_NVP(rowEnd_);
ar & BOOST_SERIALIZATION_NVP(blockStart_);
}
};
}

View File

@ -1,627 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 blockMatrices.h
* @brief Access to matrices via blocks of pre-defined sizes. Used in GaussianFactor and GaussianConditional.
* @author Richard Roberts
* @date Sep 18, 2010
*/
#pragma once
#include <gtsam/base/Matrix.h>
namespace gtsam {
template<class MATRIX> class SymmetricBlockView;
/**
* This class stores a *reference* to a matrix and allows it to be accessed as
* a collection of vertical blocks. It also provides for accessing individual
* columns from those blocks. When constructed or resized, the caller must
* provide the dimensions of the blocks, as well as an underlying matrix
* storage object. This class will resize the underlying matrix such that it
* is consistent with the given block dimensions.
*
* This class also has three parameters that can be changed after construction
* that change the apparent view of the matrix. firstBlock() determines the
* block that has index 0 for all operations (except for re-setting
* firstBlock()). rowStart() determines the apparent first row of the matrix
* for all operations (except for setting rowStart() and rowEnd()). rowEnd()
* determines the apparent *exclusive* last row for all operations. To include
* all rows, rowEnd() should be set to the number of rows in the matrix (i.e.
* one after the last true row index).
*
* @addtogroup base
*/
template<class MATRIX>
class VerticalBlockView {
public:
typedef MATRIX FullMatrix;
typedef Eigen::Block<MATRIX> Block;
typedef Eigen::Block<const MATRIX> constBlock;
// columns of blocks
typedef Eigen::VectorBlock<typename MATRIX::ColXpr> Column;
typedef Eigen::VectorBlock<const typename MATRIX::ConstColXpr> constColumn;
protected:
FullMatrix& matrix_; // the reference to the full matrix
std::vector<size_t> variableColOffsets_; // the starting columns of each block (0-based)
// Changes apparent matrix view, see main class comment.
size_t rowStart_; // 0 initially
size_t rowEnd_; // the number of row - 1, initially
size_t blockStart_; // 0 initially
public:
/** Construct from an empty matrix (asserts that the matrix is empty) */
VerticalBlockView(FullMatrix& matrix) :
matrix_(matrix), rowStart_(0), rowEnd_(matrix_.rows()), blockStart_(0) {
fillOffsets((size_t*)0, (size_t*)0);
assertInvariants();
}
/**
* Construct from a non-empty matrix and copy the block structure from
* another block view.
*/
template<class RHS>
VerticalBlockView(FullMatrix& matrix, const RHS& rhs) :
matrix_(matrix) {
if((size_t) matrix_.rows() != rhs.rows() || (size_t) matrix_.cols() != rhs.cols())
throw std::invalid_argument(
"In VerticalBlockView<>(FullMatrix& matrix, const RHS& rhs), matrix and rhs must\n"
"already be of the same size. If not, construct the VerticalBlockView from an\n"
"empty matrix and then use copyStructureFrom(const RHS& rhs) to resize the matrix\n"
"and set up the block structure.");
copyStructureFrom(rhs);
assertInvariants();
}
/** Construct from iterators over the sizes of each vertical block */
template<typename ITERATOR>
VerticalBlockView(FullMatrix& matrix, ITERATOR firstBlockDim, ITERATOR lastBlockDim) :
matrix_(matrix), rowStart_(0), rowEnd_(matrix_.rows()), blockStart_(0) {
fillOffsets(firstBlockDim, lastBlockDim);
assertInvariants();
}
/**
* Construct from a vector of the sizes of each vertical block, resize the
* matrix so that its height is matrixNewHeight and its width fits the given
* block dimensions.
*/
template<typename ITERATOR>
VerticalBlockView(FullMatrix& matrix, ITERATOR firstBlockDim, ITERATOR lastBlockDim, size_t matrixNewHeight) :
matrix_(matrix), rowStart_(0), rowEnd_(matrixNewHeight), blockStart_(0) {
fillOffsets(firstBlockDim, lastBlockDim);
matrix_.resize(matrixNewHeight, variableColOffsets_.back());
assertInvariants();
}
/** Row size
*/
size_t rows() const { assertInvariants(); return rowEnd_ - rowStart_; }
/** Column size
*/
size_t cols() const { assertInvariants(); return variableColOffsets_.back() - variableColOffsets_[blockStart_]; }
/** Block count
*/
size_t nBlocks() const { assertInvariants(); return variableColOffsets_.size() - 1 - blockStart_; }
/** Access a single block in the underlying matrix with read/write access */
inline Block operator()(size_t block) {
return range(block, block+1);
}
/** Access a const block view */
inline const constBlock operator()(size_t block) const {
return range(block, block+1);
}
/** access ranges of blocks at a time */
inline Block range(size_t startBlock, size_t endBlock) {
assertInvariants();
size_t actualStartBlock = startBlock + blockStart_;
size_t actualEndBlock = endBlock + blockStart_;
if(startBlock != 0 && endBlock != 0)
checkBlock(actualStartBlock);
assert(actualEndBlock < variableColOffsets_.size());
const size_t& startCol = variableColOffsets_[actualStartBlock];
const size_t& endCol = variableColOffsets_[actualEndBlock];
return matrix_.block(rowStart_, startCol, rowEnd_-rowStart_, endCol-startCol);
}
inline const constBlock range(size_t startBlock, size_t endBlock) const {
assertInvariants();
size_t actualStartBlock = startBlock + blockStart_;
size_t actualEndBlock = endBlock + blockStart_;
if(startBlock != 0 && endBlock != 0)
checkBlock(actualStartBlock);
assert(actualEndBlock < variableColOffsets_.size());
const size_t& startCol = variableColOffsets_[actualStartBlock];
const size_t& endCol = variableColOffsets_[actualEndBlock];
return ((const FullMatrix&)matrix_).block(rowStart_, startCol, rowEnd_-rowStart_, endCol-startCol);
}
/** Return the full matrix, *not* including any portions excluded by rowStart(), rowEnd(), and firstBlock() */
inline Block full() {
return range(0,nBlocks());
}
/** Return the full matrix, *not* including any portions excluded by rowStart(), rowEnd(), and firstBlock() */
inline const constBlock full() const {
return range(0,nBlocks());
}
/** get a single column out of a block */
Column column(size_t block, size_t columnOffset) {
assertInvariants();
size_t actualBlock = block + blockStart_;
checkBlock(actualBlock);
assert(variableColOffsets_[actualBlock] + columnOffset < variableColOffsets_[actualBlock+1]);
return matrix_.col(variableColOffsets_[actualBlock] + columnOffset).segment(rowStart_, rowEnd_-rowStart_);
}
/** get a single column out of a block */
const constColumn column(size_t block, size_t columnOffset) const {
assertInvariants();
size_t actualBlock = block + blockStart_;
checkBlock(actualBlock);
assert(variableColOffsets_[actualBlock] + columnOffset < (size_t) matrix_.cols());
return ((const FullMatrix&)matrix_).col(variableColOffsets_[actualBlock] + columnOffset).segment(rowStart_, rowEnd_-rowStart_);
}
size_t offset(size_t block) const {
assertInvariants();
size_t actualBlock = block + blockStart_;
checkBlock(actualBlock);
return variableColOffsets_[actualBlock];
}
/** Get or set the apparent first row of the underlying matrix for all operations */
size_t& rowStart() { return rowStart_; }
/** Get or set the apparent last row (exclusive, i.e. rows() == rowEnd() - rowStart()) of the underlying matrix for all operations */
size_t& rowEnd() { return rowEnd_; }
/** Get or set the apparent first block for all operations */
size_t& firstBlock() { return blockStart_; }
/** Get the apparent first row of the underlying matrix for all operations */
size_t rowStart() const { return rowStart_; }
/** Get the apparent last row (exclusive, i.e. rows() == rowEnd() - rowStart()) of the underlying matrix for all operations */
size_t rowEnd() const { return rowEnd_; }
/** Get the apparent first block for all operations */
size_t firstBlock() const { return blockStart_; }
/** access to full matrix (*including* any portions excluded by rowStart(), rowEnd(), and firstBlock()) */
const FullMatrix& fullMatrix() const { return matrix_; }
/**
* Copy the block structure and resize the underlying matrix, but do not
* copy the matrix data. If blockStart(), rowStart(), and/or rowEnd() have
* been modified, this copies the structure of the corresponding matrix view.
* In the destination VerticalBlockView, blockStart() and rowStart() will
* thus be 0, rowEnd() will be cols() of the source VerticalBlockView, and
* the underlying matrix will be the size of the view of the source matrix.
*/
template<class RHS>
void copyStructureFrom(const RHS& rhs) {
if((size_t) matrix_.rows() != (size_t) rhs.rows() || (size_t) matrix_.cols() != (size_t) rhs.cols())
matrix_.resize(rhs.rows(), rhs.cols());
if(rhs.blockStart_ == 0)
variableColOffsets_ = rhs.variableColOffsets_;
else {
variableColOffsets_.resize(rhs.nBlocks() + 1);
variableColOffsets_[0] = 0;
size_t j=0;
assert(rhs.variableColOffsets_.begin()+rhs.blockStart_ < rhs.variableColOffsets_.end()-1);
for(std::vector<size_t>::const_iterator off=rhs.variableColOffsets_.begin()+rhs.blockStart_; off!=rhs.variableColOffsets_.end()-1; ++off) {
variableColOffsets_[j+1] = variableColOffsets_[j] + (*(off+1) - *off);
++ j;
}
}
rowStart_ = 0;
rowEnd_ = matrix_.rows();
blockStart_ = 0;
assertInvariants();
}
/** Copy the block struture and matrix data, resizing the underlying matrix
* in the process. This can deal with assigning between different types of
* underlying matrices, as long as the matrices themselves are assignable.
* To avoid creating a temporary matrix this assumes no aliasing, i.e. that
* no part of the underlying matrices refer to the same memory!
*
* If blockStart(), rowStart(), and/or rowEnd() have been modified, this
* copies the structure of the corresponding matrix view. In the destination
* VerticalBlockView, blockStart() and rowStart() will thus be 0, rowEnd()
* will be cols() of the source VerticalBlockView, and the underlying matrix
* will be the size of the view of the source matrix.
*/
template<class RHS>
VerticalBlockView<MATRIX>& assignNoalias(const RHS& rhs) {
copyStructureFrom(rhs);
matrix_.noalias() = rhs.full();
return *this;
}
/** Swap the contents of the underlying matrix and the block information with
* another VerticalBlockView.
*/
void swap(VerticalBlockView<MATRIX>& other) {
matrix_.swap(other.matrix_);
variableColOffsets_.swap(other.variableColOffsets_);
std::swap(rowStart_, other.rowStart_);
std::swap(rowEnd_, other.rowEnd_);
std::swap(blockStart_, other.blockStart_);
assertInvariants();
other.assertInvariants();
}
protected:
void assertInvariants() const {
assert((size_t) matrix_.cols() == variableColOffsets_.back());
assert(blockStart_ < variableColOffsets_.size());
assert(rowStart_ <= (size_t) matrix_.rows());
assert(rowEnd_ <= (size_t) matrix_.rows());
assert(rowStart_ <= rowEnd_);
}
void checkBlock(size_t block) const {
assert((size_t) matrix_.cols() == variableColOffsets_.back());
assert(block < variableColOffsets_.size()-1);
assert(variableColOffsets_[block] < (size_t) matrix_.cols() && variableColOffsets_[block+1] <= (size_t) matrix_.cols());
}
template<typename ITERATOR>
void fillOffsets(ITERATOR firstBlockDim, ITERATOR lastBlockDim) {
variableColOffsets_.resize((lastBlockDim-firstBlockDim)+1);
variableColOffsets_[0] = 0;
size_t j=0;
for(ITERATOR dim=firstBlockDim; dim!=lastBlockDim; ++dim) {
variableColOffsets_[j+1] = variableColOffsets_[j] + *dim;
++ j;
}
}
template<class OTHER> friend class SymmetricBlockView;
template<class RELATED> friend class VerticalBlockView;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(matrix_);
ar & BOOST_SERIALIZATION_NVP(variableColOffsets_);
ar & BOOST_SERIALIZATION_NVP(rowStart_);
ar & BOOST_SERIALIZATION_NVP(rowEnd_);
ar & BOOST_SERIALIZATION_NVP(blockStart_);
}
};
/**
* This class stores a *reference* to a matrix and allows it to be accessed as
* a collection of blocks. It also provides for accessing individual
* columns from those blocks. When constructed or resized, the caller must
* provide the dimensions of the blocks, as well as an underlying matrix
* storage object. This class will resize the underlying matrix such that it
* is consistent with the given block dimensions.
*
* This class uses a symmetric block structure. The underlying matrix does not
* necessarily need to be symmetric.
*
* This class also has a parameter that can be changed after construction to
* change the apparent matrix view. firstBlock() determines the block that
* appears to have index 0 for all operations (except re-setting firstBlock()).
*
* @addtogroup base
*/
template<class MATRIX>
class SymmetricBlockView {
public:
typedef MATRIX FullMatrix;
typedef Eigen::Block<MATRIX> Block;
typedef Eigen::Block<const MATRIX> constBlock;
typedef typename FullMatrix::ColXpr::SegmentReturnType Column;
typedef typename FullMatrix::ConstColXpr::ConstSegmentReturnType constColumn;
private:
static FullMatrix matrixTemp_; // just for finding types
protected:
FullMatrix& matrix_; // the reference to the full matrix
std::vector<size_t> variableColOffsets_; // the starting columns of each block (0-based)
// Changes apparent matrix view, see main class comment.
size_t blockStart_; // 0 initially
public:
/** Construct from an empty matrix (asserts that the matrix is empty) */
SymmetricBlockView(FullMatrix& matrix) :
matrix_(matrix), blockStart_(0) {
fillOffsets((size_t*)0, (size_t*)0);
assertInvariants();
}
/** Construct from iterators over the sizes of each block */
template<typename ITERATOR>
SymmetricBlockView(FullMatrix& matrix, ITERATOR firstBlockDim, ITERATOR lastBlockDim) :
matrix_(matrix), blockStart_(0) {
fillOffsets(firstBlockDim, lastBlockDim);
assertInvariants();
}
/**
* Modify the size and structure of the underlying matrix and this block
* view. If 'preserve' is true, the underlying matrix data will be copied if
* the matrix size changes, otherwise the new data will be uninitialized.
*/
template<typename ITERATOR>
void resize(ITERATOR firstBlockDim, ITERATOR lastBlockDim, bool preserve) {
blockStart_ = 0;
fillOffsets(firstBlockDim, lastBlockDim);
if (preserve)
matrix_.conservativeResize(variableColOffsets_.back(), variableColOffsets_.back());
else
matrix_.resize(variableColOffsets_.back(), variableColOffsets_.back());
}
/** Row size
*/
size_t rows() const { assertInvariants(); return variableColOffsets_.back() - variableColOffsets_[blockStart_]; }
/** Column size
*/
size_t cols() const { return rows(); }
/** Block count
*/
size_t nBlocks() const { assertInvariants(); return variableColOffsets_.size() - 1 - blockStart_; }
Block operator()(size_t i_block, size_t j_block) {
return range(i_block, i_block+1, j_block, j_block+1);
}
constBlock operator()(size_t i_block, size_t j_block) const {
return range(i_block, i_block+1, j_block, j_block+1);
}
Block range(size_t i_startBlock, size_t i_endBlock, size_t j_startBlock, size_t j_endBlock) {
assertInvariants();
size_t i_actualStartBlock = i_startBlock + blockStart_;
size_t i_actualEndBlock = i_endBlock + blockStart_;
size_t j_actualStartBlock = j_startBlock + blockStart_;
size_t j_actualEndBlock = j_endBlock + blockStart_;
checkBlock(i_actualStartBlock);
checkBlock(j_actualStartBlock);
assert(i_actualEndBlock < variableColOffsets_.size());
assert(j_actualEndBlock < variableColOffsets_.size());
return matrix_.block(
variableColOffsets_[i_actualStartBlock], variableColOffsets_[j_actualStartBlock],
variableColOffsets_[i_actualEndBlock]-variableColOffsets_[i_actualStartBlock],
variableColOffsets_[j_actualEndBlock]-variableColOffsets_[j_actualStartBlock]);
}
constBlock range(size_t i_startBlock, size_t i_endBlock, size_t j_startBlock, size_t j_endBlock) const {
assertInvariants();
size_t i_actualStartBlock = i_startBlock + blockStart_;
size_t i_actualEndBlock = i_endBlock + blockStart_;
size_t j_actualStartBlock = j_startBlock + blockStart_;
size_t j_actualEndBlock = j_endBlock + blockStart_;
checkBlock(i_actualStartBlock);
checkBlock(j_actualStartBlock);
assert(i_actualEndBlock < variableColOffsets_.size());
assert(j_actualEndBlock < variableColOffsets_.size());
return ((const FullMatrix&)matrix_).block(
variableColOffsets_[i_actualStartBlock], variableColOffsets_[j_actualStartBlock],
variableColOffsets_[i_actualEndBlock]-variableColOffsets_[i_actualStartBlock],
variableColOffsets_[j_actualEndBlock]-variableColOffsets_[j_actualStartBlock]);
}
Block full() {
return range(0,nBlocks(), 0,nBlocks());
}
constBlock full() const {
return range(0,nBlocks(), 0,nBlocks());
}
/** access to full matrix */
const FullMatrix& fullMatrix() const { return matrix_; }
Column column(size_t i_block, size_t j_block, size_t columnOffset) {
assertInvariants();
size_t i_actualBlock = i_block + blockStart_;
size_t j_actualBlock = j_block + blockStart_;
checkBlock(i_actualBlock);
checkBlock(j_actualBlock);
assert(i_actualBlock < variableColOffsets_.size());
assert(j_actualBlock < variableColOffsets_.size());
assert(variableColOffsets_[j_actualBlock] + columnOffset < variableColOffsets_[j_actualBlock+1]);
return matrix_.col(
variableColOffsets_[j_actualBlock] + columnOffset).segment(
variableColOffsets_[i_actualBlock],
variableColOffsets_[i_actualBlock+1]-variableColOffsets_[i_actualBlock]);
}
constColumn column(size_t i_block, size_t j_block, size_t columnOffset) const {
assertInvariants();
size_t i_actualBlock = i_block + blockStart_;
size_t j_actualBlock = j_block + blockStart_;
checkBlock(i_actualBlock);
checkBlock(j_actualBlock);
assert(i_actualBlock < variableColOffsets_.size());
assert(j_actualBlock < variableColOffsets_.size());
assert(variableColOffsets_[j_actualBlock] + columnOffset < variableColOffsets_[j_actualBlock+1]);
return ((const FullMatrix&)matrix_).col(
variableColOffsets_[j_actualBlock] + columnOffset).segment(
variableColOffsets_[i_actualBlock],
variableColOffsets_[i_actualBlock+1]-variableColOffsets_[i_actualBlock]);
// assertInvariants();
// size_t j_actualBlock = j_block + blockStart_;
// assert(variableColOffsets_[j_actualBlock] + columnOffset < variableColOffsets_[j_actualBlock+1]);
// constBlock blockMat(operator()(i_block, j_block));
// return constColumn(blockMat, columnOffset);
}
Column rangeColumn(size_t i_startBlock, size_t i_endBlock, size_t j_block, size_t columnOffset) {
assertInvariants();
size_t i_actualStartBlock = i_startBlock + blockStart_;
size_t i_actualEndBlock = i_endBlock + blockStart_;
size_t j_actualStartBlock = j_block + blockStart_;
checkBlock(i_actualStartBlock);
checkBlock(j_actualStartBlock);
assert(i_actualEndBlock < variableColOffsets_.size());
assert(variableColOffsets_[j_actualStartBlock] + columnOffset < variableColOffsets_[j_actualStartBlock+1]);
return matrix_.col(
variableColOffsets_[j_actualStartBlock] + columnOffset).segment(
variableColOffsets_[i_actualStartBlock],
variableColOffsets_[i_actualEndBlock]-variableColOffsets_[i_actualStartBlock]);
}
constColumn rangeColumn(size_t i_startBlock, size_t i_endBlock, size_t j_block, size_t columnOffset) const {
assertInvariants();
size_t i_actualStartBlock = i_startBlock + blockStart_;
size_t i_actualEndBlock = i_endBlock + blockStart_;
size_t j_actualStartBlock = j_block + blockStart_;
checkBlock(i_actualStartBlock);
checkBlock(j_actualStartBlock);
assert(i_actualEndBlock < variableColOffsets_.size());
assert(variableColOffsets_[j_actualStartBlock] + columnOffset < variableColOffsets_[j_actualStartBlock+1]);
return ((const FullMatrix&)matrix_).col(
variableColOffsets_[j_actualStartBlock] + columnOffset).segment(
variableColOffsets_[i_actualStartBlock],
variableColOffsets_[i_actualEndBlock]-variableColOffsets_[i_actualStartBlock]);
}
size_t offset(size_t block) const {
assertInvariants();
size_t actualBlock = block + blockStart_;
checkBlock(actualBlock);
return variableColOffsets_[actualBlock];
}
size_t& blockStart() { return blockStart_; }
size_t blockStart() const { return blockStart_; }
/** Copy the block structure and resize the underlying matrix, but do not
* copy the matrix data. If blockStart() has been modified, this copies the
* structure of the corresponding matrix view. In the destination
* SymmetricBlockView, startBlock() will thus be 0 and the underlying matrix
* will be the size of the view of the source matrix.
*/
template<class RHS>
void copyStructureFrom(const RHS& rhs) {
matrix_.resize(rhs.cols(), rhs.cols());
if(rhs.blockStart_ == 0)
variableColOffsets_ = rhs.variableColOffsets_;
else {
variableColOffsets_.resize(rhs.nBlocks() + 1);
variableColOffsets_[0] = 0;
size_t j=0;
assert(rhs.variableColOffsets_.begin()+rhs.blockStart_ < rhs.variableColOffsets_.end()-1);
for(std::vector<size_t>::const_iterator off=rhs.variableColOffsets_.begin()+rhs.blockStart_; off!=rhs.variableColOffsets_.end()-1; ++off) {
variableColOffsets_[j+1] = variableColOffsets_[j] + (*(off+1) - *off);
++ j;
}
}
blockStart_ = 0;
assertInvariants();
}
/** Copy the block struture and matrix data, resizing the underlying matrix
* in the process. This can deal with assigning between different types of
* underlying matrices, as long as the matrices themselves are assignable.
* To avoid creating a temporary matrix this assumes no aliasing, i.e. that
* no part of the underlying matrices refer to the same memory!
*
* If blockStart() has been modified, this copies the structure of the
* corresponding matrix view. In the destination SymmetricBlockView,
* startBlock() will thus be 0 and the underlying matrix will be the size
* of the view of the source matrix.
*/
template<class RHSMATRIX>
SymmetricBlockView<MATRIX>& assignNoalias(const SymmetricBlockView<RHSMATRIX>& rhs) {
copyStructureFrom(rhs);
matrix_.noalias() = rhs.full();
return *this;
}
/** Swap the contents of the underlying matrix and the block information with
* another VerticalBlockView.
*/
void swap(SymmetricBlockView<MATRIX>& other) {
matrix_.swap(other.matrix_);
variableColOffsets_.swap(other.variableColOffsets_);
std::swap(blockStart_, other.blockStart_);
assertInvariants();
other.assertInvariants();
}
protected:
void assertInvariants() const {
assert(matrix_.rows() == matrix_.cols());
assert((size_t) matrix_.cols() == variableColOffsets_.back());
assert(blockStart_ < variableColOffsets_.size());
}
void checkBlock(size_t block) const {
assert(matrix_.rows() == matrix_.cols());
assert((size_t) matrix_.cols() == variableColOffsets_.back());
assert(block < variableColOffsets_.size()-1);
assert(variableColOffsets_[block] < (size_t) matrix_.cols() && variableColOffsets_[block+1] <= (size_t) matrix_.cols());
}
template<typename ITERATOR>
void fillOffsets(ITERATOR firstBlockDim, ITERATOR lastBlockDim) {
variableColOffsets_.resize((lastBlockDim-firstBlockDim)+1);
variableColOffsets_[0] = 0;
size_t j=0;
for(ITERATOR dim=firstBlockDim; dim!=lastBlockDim; ++dim) {
variableColOffsets_[j+1] = variableColOffsets_[j] + *dim;
++ j;
}
}
template<class RELATED> friend class SymmetricBlockView;
template<class OTHER> friend class VerticalBlockView;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(matrix_);
ar & BOOST_SERIALIZATION_NVP(variableColOffsets_);
ar & BOOST_SERIALIZATION_NVP(blockStart_);
}
};
}

View File

@ -1,133 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 testBlockMatrices
* @author Alex Cunningham
*/
#include <iostream>
#include <CppUnitLite/TestHarness.h>
#include <gtsam/base/blockMatrices.h>
using namespace std;
using namespace gtsam;
/* ************************************************************************* */
TEST(testBlockMatrices, jacobian_factor1) {
typedef Matrix AbMatrix;
typedef VerticalBlockView<AbMatrix> BlockAb;
AbMatrix matrix; // actual matrix - empty to start with
// from JacobianFactor::Constructor - one variable
Matrix A1 = Matrix_(2,3,
1., 2., 3.,
4., 5., 6.);
Vector b = Vector_(2, 7., 8.);
size_t dims[] = { A1.cols(), 1};
// build the structure
BlockAb Ab(matrix, dims, dims+2, b.size());
// add a matrix and get back out
Ab(0) = A1;
EXPECT(assert_equal(A1, Ab(0)));
// add vector to the system
Ab.column(1, 0) = b;
EXPECT(assert_equal(A1, Ab(0)));
EXPECT(assert_equal(b, Ab.column(1,0)));
// examine matrix contents
EXPECT_LONGS_EQUAL(2, Ab.nBlocks());
Matrix expFull = Matrix_(2, 4,
1., 2., 3., 7.,
4., 5., 6., 8.);
Matrix actFull = Ab.full();
EXPECT(assert_equal(expFull, actFull));
}
/* ************************************************************************* */
TEST(testBlockMatrices, jacobian_factor2) {
typedef Matrix AbMatrix;
typedef VerticalBlockView<AbMatrix> BlockAb;
AbMatrix matrix; // actual matrix - empty to start with
// from JacobianFactor::Constructor - two variables
Matrix A1 = Matrix_(2,3,
1., 2., 3.,
4., 5., 6.);
Matrix A2 = Matrix_(2,1,
10.,
11.);
Vector b = Vector_(2, 7., 8.);
size_t dims[] = { A1.cols(), A2.cols(), 1};
// build the structure
BlockAb Ab(matrix, dims, dims+3, b.size());
// add blocks
Ab(0) = A1;
Ab(1) = A2;
EXPECT(assert_equal(A1, Ab(0)));
EXPECT(assert_equal(A2, Ab(1)));
// add vector to the system
Ab.column(2, 0) = b;
EXPECT(assert_equal(A1, Ab(0)));
EXPECT(assert_equal(A2, Ab(1)));
EXPECT(assert_equal(b, Ab.column(2,0)));
// examine matrix contents
EXPECT_LONGS_EQUAL(3, Ab.nBlocks());
Matrix expFull = Matrix_(2, 5,
1., 2., 3., 10., 7.,
4., 5., 6., 11., 8.);
Matrix actFull = Ab.full();
EXPECT(assert_equal(expFull, actFull));
}
/* ************************************************************************* */
TEST(testBlockMatrices, hessian_factor1) {
typedef Matrix InfoMatrix;
typedef SymmetricBlockView<InfoMatrix> BlockInfo;
Matrix expected_full = Matrix_(3, 3,
3.0, 5.0, -8.0,
0.0, 6.0, -9.0,
0.0, 0.0, 10.0);
Matrix G = Matrix_(2,2, 3.0, 5.0, 0.0, 6.0);
Vector g = Vector_(2, -8.0, -9.0);
double f = 10.0;
size_t dims[] = { G.rows(), 1 };
InfoMatrix fullMatrix = zeros(G.rows() + 1, G.rows() + 1);
BlockInfo infoView(fullMatrix, dims, dims+2);
infoView(0,0) = G;
infoView.column(0,1,0) = g;
infoView(1,1)(0,0) = f;
EXPECT_LONGS_EQUAL(0, infoView.blockStart());
EXPECT_LONGS_EQUAL(2, infoView.nBlocks());
EXPECT(assert_equal(InfoMatrix(expected_full), fullMatrix));
EXPECT(assert_equal(InfoMatrix(G), infoView.range(0, 1, 0, 1)))
EXPECT_DOUBLES_EQUAL(f, infoView(1, 1)(0,0), 1e-10);
EXPECT(assert_equal(g, Vector(infoView.rangeColumn(0, 1, 1, 0))));
EXPECT(assert_equal(g, Vector(((const BlockInfo)infoView).rangeColumn(0, 1, 1, 0))));
}
/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
/* ************************************************************************* */

View File

@ -28,7 +28,7 @@ double f(const LieVector& x) {
}
/* ************************************************************************* */
TEST_UNSAFE(testNumericalDerivative, numericalHessian) {
TEST(testNumericalDerivative, numericalHessian) {
LieVector center = ones(2);
Matrix expected = Matrix_(2,2,
@ -47,7 +47,7 @@ double f2(const LieVector& x) {
}
/* ************************************************************************* */
TEST_UNSAFE(testNumericalDerivative, numericalHessian2) {
TEST(testNumericalDerivative, numericalHessian2) {
LieVector center(2, 0.5, 1.0);
Matrix expected = Matrix_(2,2,
@ -66,7 +66,7 @@ double f3(const LieVector& x1, const LieVector& x2) {
}
/* ************************************************************************* */
TEST_UNSAFE(testNumericalDerivative, numericalHessian211) {
TEST(testNumericalDerivative, numericalHessian211) {
LieVector center1(1, 1.0), center2(1, 5.0);
Matrix expected11 = Matrix_(1,1,-sin(center1(0))*cos(center2(0)));
@ -89,7 +89,7 @@ double f4(const LieVector& x, const LieVector& y, const LieVector& z) {
}
/* ************************************************************************* */
TEST_UNSAFE(testNumericalDerivative, numericalHessian311) {
TEST(testNumericalDerivative, numericalHessian311) {
LieVector center1(1, 1.0), center2(1, 2.0), center3(1, 3.0);
double x = center1(0), y = center2(0), z = center3(0);
Matrix expected11 = Matrix_(1,1,-sin(x)*cos(y)*z*z);

View File

@ -0,0 +1,163 @@
/* ----------------------------------------------------------------------------
* 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 testTreeTraversal
* @brief Unit tests for tree traversal, cloning, and printing
* @author Richard Roberts
*/
#include <CppUnitLite/TestHarness.h>
#include <gtsam/base/TestableAssertions.h>
#include <gtsam/base/treeTraversal-inst.h>
#include <vector>
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/assign/std/list.hpp>
using boost::assign::operator+=;
using namespace std;
using namespace gtsam;
struct TestNode {
typedef boost::shared_ptr<TestNode> shared_ptr;
int data;
vector<shared_ptr> children;
TestNode() : data(-1) {}
TestNode(int data) : data(data) {}
};
struct TestForest {
typedef TestNode Node;
typedef Node::shared_ptr sharedNode;
vector<sharedNode> roots_;
const vector<sharedNode>& roots() const { return roots_; }
};
TestForest makeTestForest() {
// 0 1
// / \
// 2 3
// |
// 4
TestForest forest;
forest.roots_.push_back(boost::make_shared<TestNode>(0));
forest.roots_.push_back(boost::make_shared<TestNode>(1));
forest.roots_[0]->children.push_back(boost::make_shared<TestNode>(2));
forest.roots_[0]->children.push_back(boost::make_shared<TestNode>(3));
forest.roots_[0]->children[1]->children.push_back(boost::make_shared<TestNode>(4));
return forest;
}
/* ************************************************************************* */
struct PreOrderVisitor {
// This visitor stores the nodes visited so the visit order can be checked later. It also returns
// the current node index as the data and checks that the parent index properly matches the index
// referenced in the node.
std::list<int> visited;
bool parentsMatched;
PreOrderVisitor() : parentsMatched(true) {}
int operator()(const TestNode::shared_ptr& node, int parentData) {
visited.push_back(node->data);
// Check parent index
const int expectedParentIndex =
node->data == 0 ? -1 :
node->data == 1 ? -1 :
node->data == 2 ? 0 :
node->data == 3 ? 0 :
node->data == 4 ? 3 :
node->data == 10 ? 0 :
(parentsMatched = false, -1);
if(expectedParentIndex != parentData)
parentsMatched = false;
return node->data;
}
};
/* ************************************************************************* */
struct PostOrderVisitor {
// This visitor stores the nodes visited so the visit order can be checked later.
std::list<int> visited;
void operator()(const TestNode::shared_ptr& node, int myData) {
visited.push_back(node->data);
}
};
/* ************************************************************************* */
std::list<int> getPreorder(const TestForest& forest) {
std::list<int> result;
PreOrderVisitor preVisitor;
int rootData = -1;
treeTraversal::DepthFirstForest(forest, rootData, preVisitor);
result = preVisitor.visited;
return result;
}
/* ************************************************************************* */
TEST(treeTraversal, DepthFirst)
{
// Get test forest
TestForest testForest = makeTestForest();
// Expected visit order
std::list<int> preOrderExpected;
preOrderExpected += 0, 2, 3, 4, 1;
std::list<int> postOrderExpected;
postOrderExpected += 2, 4, 3, 0, 1;
// Actual visit order
PreOrderVisitor preVisitor;
PostOrderVisitor postVisitor;
int rootData = -1;
treeTraversal::DepthFirstForest(testForest, rootData, preVisitor, postVisitor);
EXPECT(preVisitor.parentsMatched);
EXPECT(assert_container_equality(preOrderExpected, preVisitor.visited));
EXPECT(assert_container_equality(postOrderExpected, postVisitor.visited));
}
/* ************************************************************************* */
TEST(treeTraversal, CloneForest)
{
// Get test forest
TestForest testForest1 = makeTestForest();
TestForest testForest2;
testForest2.roots_ = treeTraversal::CloneForest(testForest1);
// Check that the original and clone both are expected
std::list<int> preOrder1Expected;
preOrder1Expected += 0, 2, 3, 4, 1;
std::list<int> preOrder1Actual = getPreorder(testForest1);
std::list<int> preOrder2Actual = getPreorder(testForest2);
EXPECT(assert_container_equality(preOrder1Expected, preOrder1Actual));
EXPECT(assert_container_equality(preOrder1Expected, preOrder2Actual));
// Modify clone - should not modify original
testForest2.roots_[0]->children[1]->data = 10;
std::list<int> preOrderModifiedExpected;
preOrderModifiedExpected += 0, 2, 10, 4, 1;
// Check that original is the same and only the clone is modified
std::list<int> preOrder1ModActual = getPreorder(testForest1);
std::list<int> preOrder2ModActual = getPreorder(testForest2);
EXPECT(assert_container_equality(preOrder1Expected, preOrder1ModActual));
EXPECT(assert_container_equality(preOrderModifiedExpected, preOrder2ModActual));
}
/* ************************************************************************* */
int main() {
TestResult tr;
return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */

View File

@ -199,7 +199,7 @@ TEST( TestVector, weightedPseudoinverse_constraint )
// verify
EXPECT(assert_equal(expected,actual));
EXPECT(isinf(precision));
EXPECT(std::isinf(precision));
}
/* ************************************************************************* */

View File

@ -0,0 +1,339 @@
/* ----------------------------------------------------------------------------
* 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 treeTraversal-inst.h
* @author Richard Roberts
* @date April 9, 2013
*/
#pragma once
#include <gtsam/base/FastList.h>
#include <gtsam/inference/Key.h>
#include <stack>
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <tbb/tbb.h>
#undef max // TBB seems to include windows.h and we don't want these macros
#undef min
namespace gtsam {
/** Internal functions used for traversing trees */
namespace treeTraversal {
/* ************************************************************************* */
namespace {
// Internal node used in DFS preorder stack
template<typename NODE, typename DATA>
struct TraversalNode {
bool expanded;
const boost::shared_ptr<NODE>& treeNode;
DATA& parentData;
typename FastList<DATA>::iterator dataPointer;
TraversalNode(const boost::shared_ptr<NODE>& _treeNode, DATA& _parentData) :
expanded(false), treeNode(_treeNode), parentData(_parentData) {}
};
// Do nothing - default argument for post-visitor for tree traversal
template<typename NODE, typename DATA>
void no_op(const boost::shared_ptr<NODE>& node, const DATA& data) {}
// Internal node used in parallel traversal stack
template<typename NODE, typename DATA>
struct ParallelTraversalNode {
const boost::shared_ptr<NODE>& treeNode;
DATA myData;
ParallelTraversalNode(const boost::shared_ptr<NODE>& treeNode, const DATA& myData) :
treeNode(treeNode), myData(myData) {}
};
template<typename NODE, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
class PreOrderTask : public tbb::task
{
public:
const boost::shared_ptr<NODE>& treeNode;
DATA myData;
VISITOR_PRE& visitorPre;
VISITOR_POST& visitorPost;
int problemSizeThreshold;
bool makeNewTasks;
PreOrderTask(const boost::shared_ptr<NODE>& treeNode, const DATA& myData,
VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost, int problemSizeThreshold,
bool makeNewTasks = true) :
treeNode(treeNode), myData(myData), visitorPre(visitorPre), visitorPost(visitorPost),
problemSizeThreshold(problemSizeThreshold), makeNewTasks(makeNewTasks) {}
typedef ParallelTraversalNode<NODE, DATA> ParallelTraversalNodeType;
tbb::task* execute()
{
// Process this node and its children
processNode(treeNode, myData);
// Return NULL
return NULL;
}
void processNode(const boost::shared_ptr<NODE>& node, DATA& myData)
{
if(makeNewTasks)
{
bool overThreshold = (node->problemSize() >= problemSizeThreshold);
tbb::task_list childTasks;
BOOST_FOREACH(const boost::shared_ptr<NODE>& child, node->children)
{
// Process child in a subtask
childTasks.push_back(*new(allocate_child())
PreOrderTask(child, visitorPre(child, myData), visitorPre, visitorPost,
problemSizeThreshold, overThreshold));
}
// If we have child tasks, start subtasks and wait for them to complete
set_ref_count(1 + (int)node->children.size());
spawn(childTasks);
wait_for_all();
}
else
{
BOOST_FOREACH(const boost::shared_ptr<NODE>& child, node->children)
{
processNode(child, visitorPre(child, myData));
}
}
// Run the post-order visitor
(void) visitorPost(node, myData);
}
};
template<typename ROOTS, typename NODE, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
class RootTask : public tbb::task
{
public:
const ROOTS& roots;
DATA& myData;
VISITOR_PRE& visitorPre;
VISITOR_POST& visitorPost;
int problemSizeThreshold;
RootTask(const ROOTS& roots, DATA& myData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost,
int problemSizeThreshold) :
roots(roots), myData(myData), visitorPre(visitorPre), visitorPost(visitorPost),
problemSizeThreshold(problemSizeThreshold) {}
tbb::task* execute()
{
typedef PreOrderTask<NODE, DATA, VISITOR_PRE, VISITOR_POST> PreOrderTask;
// Set TBB ref count
set_ref_count(1 + (int)roots.size());
// Create data and tasks for our children
std::vector<PreOrderTask*> tasks;
tasks.reserve(roots.size());
BOOST_FOREACH(const boost::shared_ptr<NODE>& root, roots)
{
tasks.push_back(new(allocate_child())
PreOrderTask(root, visitorPre(root, myData), visitorPre, visitorPost, problemSizeThreshold));
}
// Spawn tasks
BOOST_FOREACH(PreOrderTask* task, tasks)
spawn(*task);
// Wait for tasks to finish
wait_for_all();
// Return NULL
return NULL;
}
};
template<typename NODE, typename ROOTS, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
RootTask<ROOTS, NODE, DATA, VISITOR_PRE, VISITOR_POST>&
CreateRootTask(const ROOTS& roots, DATA& rootData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost,
int problemSizeThreshold)
{
typedef RootTask<ROOTS, NODE, DATA, VISITOR_PRE, VISITOR_POST> RootTask;
return *new(tbb::task::allocate_root()) RootTask(roots, rootData, visitorPre, visitorPost, problemSizeThreshold);
}
/* ************************************************************************* */
//template<class NODE, typename DATA>
//struct ParallelDFSData {
// DATA myData;
// FastList<ParallelTraversalNode<NODE,DATA> >&
//};
}
/** Traverse a forest depth-first with pre-order and post-order visits.
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
* and return a collection of (shared) pointers to \c FOREST::Node.
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
* visiting its children, and will be passed, by reference, the \c DATA object returned
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
* to pass to the children. The returned \c DATA object will be copy-constructed only
* upon returning to store internally, thus may be modified by visiting the children.
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
* @param visitorPost \c visitorPost(node, data) will be called at every node, after visiting
* its children, and will be passed, by reference, the \c DATA object returned by the
* call to \c visitorPre (the \c DATA object may be modified by visiting the children).
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
* root node. */
template<class FOREST, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
void DepthFirstForest(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost)
{
// Typedefs
typedef typename FOREST::Node Node;
typedef boost::shared_ptr<Node> sharedNode;
// Depth first traversal stack
typedef TraversalNode<typename FOREST::Node, DATA> TraversalNode;
typedef FastList<TraversalNode> Stack;
Stack stack;
FastList<DATA> dataList; // List to store node data as it is returned from the pre-order visitor
// Add roots to stack (insert such that they are visited and processed in order
{
typename Stack::iterator insertLocation = stack.begin();
BOOST_FOREACH(const sharedNode& root, forest.roots())
stack.insert(insertLocation, TraversalNode(root, rootData));
}
// Traverse
while(!stack.empty())
{
// Get next node
TraversalNode& node = stack.front();
if(node.expanded) {
// If already expanded, then the data stored in the node is no longer needed, so visit
// then delete it.
(void) visitorPost(node.treeNode, *node.dataPointer);
dataList.erase(node.dataPointer);
stack.pop_front();
} else {
// If not already visited, visit the node and add its children (use reverse iterators so
// children are processed in the order they appear)
node.dataPointer = dataList.insert(dataList.end(), visitorPre(node.treeNode, node.parentData));
typename Stack::iterator insertLocation = stack.begin();
BOOST_FOREACH(const sharedNode& child, node.treeNode->children)
stack.insert(insertLocation, TraversalNode(child, *node.dataPointer));
node.expanded = true;
}
}
assert(dataList.empty());
}
/** Traverse a forest depth-first, with a pre-order visit but no post-order visit.
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
* and return a collection of (shared) pointers to \c FOREST::Node.
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
* visiting its children, and will be passed, by reference, the \c DATA object returned
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
* to pass to the children. The returned \c DATA object will be copy-constructed only
* upon returning to store internally, thus may be modified by visiting the children.
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
* root node. */
template<class FOREST, typename DATA, typename VISITOR_PRE>
void DepthFirstForest(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre)
{
DepthFirstForest(forest, rootData, visitorPre, no_op<typename FOREST::Node, DATA>);
}
/** Traverse a forest depth-first with pre-order and post-order visits.
* @param forest The forest of trees to traverse. The method \c forest.roots() should exist
* and return a collection of (shared) pointers to \c FOREST::Node.
* @param visitorPre \c visitorPre(node, parentData) will be called at every node, before
* visiting its children, and will be passed, by reference, the \c DATA object returned
* by the visit to its parent. Likewise, \c visitorPre should return the \c DATA object
* to pass to the children. The returned \c DATA object will be copy-constructed only
* upon returning to store internally, thus may be modified by visiting the children.
* Regarding efficiency, this copy-on-return is usually optimized out by the compiler.
* @param visitorPost \c visitorPost(node, data) will be called at every node, after visiting
* its children, and will be passed, by reference, the \c DATA object returned by the
* call to \c visitorPre (the \c DATA object may be modified by visiting the children).
* @param rootData The data to pass by reference to \c visitorPre when it is called on each
* root node. */
template<class FOREST, typename DATA, typename VISITOR_PRE, typename VISITOR_POST>
void DepthFirstForestParallel(FOREST& forest, DATA& rootData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost,
int problemSizeThreshold = 10)
{
// Typedefs
typedef typename FOREST::Node Node;
typedef boost::shared_ptr<Node> sharedNode;
tbb::task::spawn_root_and_wait(CreateRootTask<Node>(
forest.roots(), rootData, visitorPre, visitorPost, problemSizeThreshold));
}
/* ************************************************************************* */
/** Traversal function for CloneForest */
namespace {
template<typename NODE>
boost::shared_ptr<NODE>
CloneForestVisitorPre(const boost::shared_ptr<NODE>& node, const boost::shared_ptr<NODE>& parentPointer)
{
// Clone the current node and add it to its cloned parent
boost::shared_ptr<NODE> clone = boost::make_shared<NODE>(*node);
clone->children.clear();
parentPointer->children.push_back(clone);
return clone;
}
}
/** Clone a tree, copy-constructing new nodes (calling boost::make_shared) and setting up child
* pointers for a clone of the original tree.
* @param forest The forest of trees to clone. The method \c forest.roots() should exist and
* return a collection of shared pointers to \c FOREST::Node.
* @return The new collection of roots. */
template<class FOREST>
std::vector<boost::shared_ptr<typename FOREST::Node> > CloneForest(const FOREST& forest)
{
typedef typename FOREST::Node Node;
boost::shared_ptr<Node> rootContainer = boost::make_shared<Node>();
DepthFirstForest(forest, rootContainer, CloneForestVisitorPre<Node>);
return std::vector<boost::shared_ptr<Node> >(rootContainer->children.begin(), rootContainer->children.end());
}
/* ************************************************************************* */
/** Traversal function for PrintForest */
namespace {
struct PrintForestVisitorPre
{
const KeyFormatter& formatter;
PrintForestVisitorPre(const KeyFormatter& formatter) : formatter(formatter) {}
template<typename NODE> std::string operator()(const boost::shared_ptr<NODE>& node, const std::string& parentString)
{
// Print the current node
node->print(parentString + "-", formatter);
// Increment the indentation
return parentString + "| ";
}
};
}
/** Print a tree, prefixing each line with \c str, and formatting keys using \c keyFormatter.
* To print each node, this function calls the \c print function of the tree nodes. */
template<class FOREST>
void PrintForest(const FOREST& forest, std::string str, const KeyFormatter& keyFormatter) {
typedef typename FOREST::Node Node;
PrintForestVisitorPre visitor(keyFormatter);
DepthFirstForest(forest, str, visitor);
}
}
}

View File

@ -20,11 +20,22 @@
#include <boost/lexical_cast.hpp>
#include <gtsam/base/types.h>
#include <gtsam/nonlinear/Symbol.h>
namespace gtsam {
/* ************************************************************************* */
std::string _defaultIndexFormatter(Index j) {
return boost::lexical_cast<std::string>(j);
}
/* ************************************************************************* */
std::string _defaultKeyFormatter(Key key) {
const Symbol asSymbol(key);
if(asSymbol.chr() > 0)
return (std::string)asSymbol;
else
return boost::lexical_cast<std::string>(key);
}
}

View File

@ -25,6 +25,7 @@
#include <string>
#include <boost/function/function1.hpp>
#include <boost/range/concepts.hpp>
namespace gtsam {
@ -40,6 +41,26 @@ namespace gtsam {
/** The default IndexFormatter outputs the index */
static const IndexFormatter DefaultIndexFormatter = &_defaultIndexFormatter;
/// Integer nonlinear key type
typedef size_t Key;
/// Typedef for a function to format a key, i.e. to convert it to a string
typedef boost::function<std::string(Key)> KeyFormatter;
// Helper function for DefaultKeyFormatter
GTSAM_EXPORT std::string _defaultKeyFormatter(Key key);
/// The default KeyFormatter, which is used if no KeyFormatter is passed to
/// a nonlinear 'print' function. Automatically detects plain integer keys
/// and Symbol keys.
static const KeyFormatter DefaultKeyFormatter = &_defaultKeyFormatter;
/// The index type for Eigen objects
typedef ptrdiff_t DenseIndex;
/**
* Helper class that uses templates to select between two types based on
* whether TEST_TYPE is const or not.
@ -83,6 +104,42 @@ namespace gtsam {
operator T() const { return value; }
};
/** A helper class that behaves as a container with one element, and works with
* boost::range */
template<typename T>
class ListOfOneContainer {
T element_;
public:
typedef T value_type;
typedef const T* const_iterator;
typedef T* iterator;
ListOfOneContainer(const T& element) : element_(element) {}
const T* begin() const { return &element_; }
const T* end() const { return &element_ + 1; }
T* begin() { return &element_; }
T* end() { return &element_ + 1; }
size_t size() const { return 1; }
};
BOOST_CONCEPT_ASSERT((boost::RandomAccessRangeConcept<ListOfOneContainer<int> >));
/** Factory function for ListOfOneContainer to enable ListOfOne(e) syntax. */
template<typename T>
ListOfOneContainer<T> ListOfOne(const T& element) {
return ListOfOneContainer<T>(element);
}
/** An assertion that throws an exception if NDEBUG is not defined and
* evaluates to an empty statement otherwise. */
#ifdef NDEBUG
#define assert_throw(CONDITION, EXCEPTION) ((void)0)
#else
#define assert_throw(CONDITION, EXCEPTION) \
if(!(CONDITION)) { \
throw (EXCEPTION); \
}
#endif
}
#ifdef _MSC_VER

View File

@ -45,14 +45,14 @@ namespace gtsam {
/* ************************************************************************* */
bool DecisionTreeFactor::equals(const This& other, double tol) const {
return IndexFactor::equals(other, tol) && Potentials::equals(other, tol);
return IndexFactorOrdered::equals(other, tol) && Potentials::equals(other, tol);
}
/* ************************************************************************* */
void DecisionTreeFactor::print(const string& s,
const IndexFormatter& formatter) const {
cout << s;
IndexFactor::print("IndexFactor:",formatter);
IndexFactorOrdered::print("IndexFactor:",formatter);
Potentials::print("Potentials:",formatter);
}

View File

@ -18,13 +18,13 @@
#include <gtsam/discrete/DiscreteBayesNet.h>
#include <gtsam/discrete/DiscreteConditional.h>
#include <gtsam/inference/BayesNet-inl.h>
#include <gtsam/inference/BayesNetOrdered-inl.h>
#include <boost/make_shared.hpp>
namespace gtsam {
// Explicitly instantiate so we don't have to include everywhere
template class BayesNet<DiscreteConditional> ;
template class BayesNetOrdered<DiscreteConditional> ;
/* ************************************************************************* */
void add_front(DiscreteBayesNet& bayesNet, const Signature& s) {

View File

@ -20,12 +20,12 @@
#include <vector>
#include <map>
#include <boost/shared_ptr.hpp>
#include <gtsam/inference/BayesNet.h>
#include <gtsam/inference/BayesNetOrdered.h>
#include <gtsam/discrete/DiscreteConditional.h>
namespace gtsam {
typedef BayesNet<DiscreteConditional> DiscreteBayesNet;
typedef BayesNetOrdered<DiscreteConditional> DiscreteBayesNet;
/** Add a DiscreteCondtional */
GTSAM_EXPORT void add(DiscreteBayesNet&, const Signature& s);

View File

@ -37,14 +37,14 @@ namespace gtsam {
/* ******************************************************************************** */
DiscreteConditional::DiscreteConditional(const size_t nrFrontals,
const DecisionTreeFactor& f) :
IndexConditional(f.keys(), nrFrontals), Potentials(
IndexConditionalOrdered(f.keys(), nrFrontals), Potentials(
f / (*f.sum(nrFrontals))) {
}
/* ******************************************************************************** */
DiscreteConditional::DiscreteConditional(const DecisionTreeFactor& joint,
const DecisionTreeFactor& marginal) :
IndexConditional(joint.keys(), joint.size() - marginal.size()), Potentials(
IndexConditionalOrdered(joint.keys(), joint.size() - marginal.size()), Potentials(
ISDEBUG("DiscreteConditional::COUNT") ? joint : joint / marginal) {
if (ISDEBUG("DiscreteConditional::DiscreteConditional"))
cout << (firstFrontalKey()) << endl; //TODO Print all keys
@ -52,20 +52,20 @@ namespace gtsam {
/* ******************************************************************************** */
DiscreteConditional::DiscreteConditional(const Signature& signature) :
IndexConditional(signature.indices(), 1), Potentials(
IndexConditionalOrdered(signature.indices(), 1), Potentials(
signature.discreteKeysParentsFirst(), signature.cpt()) {
}
/* ******************************************************************************** */
void DiscreteConditional::print(const std::string& s, const IndexFormatter& formatter) const {
std::cout << s << std::endl;
IndexConditional::print(s, formatter);
IndexConditionalOrdered::print(s, formatter);
Potentials::print(s);
}
/* ******************************************************************************** */
bool DiscreteConditional::equals(const DiscreteConditional& other, double tol) const {
return IndexConditional::equals(other, tol)
return IndexConditionalOrdered::equals(other, tol)
&& Potentials::equals(other, tol);
}
@ -197,7 +197,7 @@ namespace gtsam {
/* ******************************************************************************** */
void DiscreteConditional::permuteWithInverse(const Permutation& inversePermutation){
IndexConditional::permuteWithInverse(inversePermutation);
IndexConditionalOrdered::permuteWithInverse(inversePermutation);
Potentials::permuteWithInverse(inversePermutation);
}

View File

@ -20,7 +20,7 @@
#include <gtsam/discrete/DecisionTreeFactor.h>
#include <gtsam/discrete/Signature.h>
#include <gtsam/inference/IndexConditional.h>
#include <gtsam/inference/IndexConditionalOrdered.h>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
@ -30,13 +30,13 @@ namespace gtsam {
* Discrete Conditional Density
* Derives from DecisionTreeFactor
*/
class GTSAM_EXPORT DiscreteConditional: public IndexConditional, public Potentials {
class GTSAM_EXPORT DiscreteConditional: public IndexConditionalOrdered, public Potentials {
public:
// typedefs needed to play nice with gtsam
typedef DiscreteFactor FactorType;
typedef boost::shared_ptr<DiscreteConditional> shared_ptr;
typedef IndexConditional Base;
typedef IndexConditionalOrdered Base;
/** A map from keys to values */
typedef Assignment<Index> Values;

View File

@ -19,7 +19,7 @@
#pragma once
#include <gtsam/discrete/Assignment.h>
#include <gtsam/inference/IndexFactor.h>
#include <gtsam/inference/IndexFactorOrdered.h>
namespace gtsam {
@ -30,7 +30,7 @@ namespace gtsam {
* Base class for discrete probabilistic factors
* The most general one is the derived DecisionTreeFactor
*/
class GTSAM_EXPORT DiscreteFactor: public IndexFactor {
class GTSAM_EXPORT DiscreteFactor: public IndexFactorOrdered {
public:
@ -47,23 +47,23 @@ namespace gtsam {
/// Construct n-way factor
DiscreteFactor(const std::vector<Index>& js) :
IndexFactor(js) {
IndexFactorOrdered(js) {
}
/// Construct unary factor
DiscreteFactor(Index j) :
IndexFactor(j) {
IndexFactorOrdered(j) {
}
/// Construct binary factor
DiscreteFactor(Index j1, Index j2) :
IndexFactor(j1, j2) {
IndexFactorOrdered(j1, j2) {
}
/// construct from container
template<class KeyIterator>
DiscreteFactor(KeyIterator beginKey, KeyIterator endKey) :
IndexFactor(beginKey, endKey) {
IndexFactorOrdered(beginKey, endKey) {
}
public:
@ -85,7 +85,7 @@ namespace gtsam {
virtual void print(const std::string& s = "DiscreteFactor\n",
const IndexFormatter& formatter
=DefaultIndexFormatter) const {
IndexFactor::print(s,formatter);
IndexFactorOrdered::print(s,formatter);
}
/// @}
@ -105,14 +105,14 @@ namespace gtsam {
* to already be inverted.
*/
virtual void permuteWithInverse(const Permutation& inversePermutation){
IndexFactor::permuteWithInverse(inversePermutation);
IndexFactorOrdered::permuteWithInverse(inversePermutation);
}
/**
* Apply a reduction, which is a remapping of variable indices.
*/
virtual void reduceWithInverse(const internal::Reduction& inverseReduction) {
IndexFactor::reduceWithInverse(inverseReduction);
IndexFactorOrdered::reduceWithInverse(inverseReduction);
}
/// @}

View File

@ -19,14 +19,14 @@
//#define ENABLE_TIMING
#include <gtsam/discrete/DiscreteFactorGraph.h>
#include <gtsam/discrete/DiscreteConditional.h>
#include <gtsam/inference/EliminationTree-inl.h>
#include <gtsam/inference/EliminationTreeOrdered-inl.h>
#include <boost/make_shared.hpp>
namespace gtsam {
// Explicitly instantiate so we don't have to include everywhere
template class FactorGraph<DiscreteFactor> ;
template class EliminationTree<DiscreteFactor> ;
template class FactorGraphOrdered<DiscreteFactor> ;
template class EliminationTreeOrdered<DiscreteFactor> ;
/* ************************************************************************* */
DiscreteFactorGraph::DiscreteFactorGraph() {
@ -34,8 +34,8 @@ namespace gtsam {
/* ************************************************************************* */
DiscreteFactorGraph::DiscreteFactorGraph(
const BayesNet<DiscreteConditional>& bayesNet) :
FactorGraph<DiscreteFactor>(bayesNet) {
const BayesNetOrdered<DiscreteConditional>& bayesNet) :
FactorGraphOrdered<DiscreteFactor>(bayesNet) {
}
/* ************************************************************************* */
@ -95,7 +95,7 @@ namespace gtsam {
/* ************************************************************************* */
std::pair<DiscreteConditional::shared_ptr, DecisionTreeFactor::shared_ptr> //
EliminateDiscrete(const FactorGraph<DiscreteFactor>& factors, size_t num) {
EliminateDiscrete(const FactorGraphOrdered<DiscreteFactor>& factors, size_t num) {
// PRODUCT: multiply all factors
gttic(product);

View File

@ -20,13 +20,13 @@
#include <gtsam/discrete/DecisionTreeFactor.h>
#include <gtsam/discrete/DiscreteBayesNet.h>
#include <gtsam/inference/FactorGraph.h>
#include <gtsam/inference/FactorGraphOrdered.h>
#include <gtsam/base/FastSet.h>
#include <boost/make_shared.hpp>
namespace gtsam {
class DiscreteFactorGraph: public FactorGraph<DiscreteFactor> {
class DiscreteFactorGraph: public FactorGraphOrdered<DiscreteFactor> {
public:
/** A map from keys to values */
@ -39,12 +39,12 @@ public:
/** Constructor from a factor graph of GaussianFactor or a derived type */
template<class DERIVEDFACTOR>
DiscreteFactorGraph(const FactorGraph<DERIVEDFACTOR>& fg) {
DiscreteFactorGraph(const FactorGraphOrdered<DERIVEDFACTOR>& fg) {
push_back(fg);
}
/** construct from a BayesNet */
GTSAM_EXPORT DiscreteFactorGraph(const BayesNet<DiscreteConditional>& bayesNet);
GTSAM_EXPORT DiscreteFactorGraph(const BayesNetOrdered<DiscreteConditional>& bayesNet);
template<class SOURCE>
void add(const DiscreteKey& j, SOURCE table) {
@ -90,7 +90,7 @@ public:
/** Main elimination function for DiscreteFactorGraph */
GTSAM_EXPORT std::pair<boost::shared_ptr<DiscreteConditional>, DecisionTreeFactor::shared_ptr>
EliminateDiscrete(const FactorGraph<DiscreteFactor>& factors,
EliminateDiscrete(const FactorGraphOrdered<DiscreteFactor>& factors,
size_t nrFrontals = 1);
} // namespace gtsam

View File

@ -32,7 +32,7 @@ namespace gtsam {
protected:
BayesTree<DiscreteConditional> bayesTree_;
BayesTreeOrdered<DiscreteConditional> bayesTree_;
public:
@ -40,7 +40,7 @@ namespace gtsam {
* @param graph The factor graph defining the full joint density on all variables.
*/
DiscreteMarginals(const DiscreteFactorGraph& graph) {
typedef JunctionTree<DiscreteFactorGraph> DiscreteJT;
typedef JunctionTreeOrdered<DiscreteFactorGraph> DiscreteJT;
GenericMultifrontalSolver<DiscreteFactor, DiscreteJT> solver(graph);
bayesTree_ = *solver.eliminate(&EliminateDiscrete);
}

View File

@ -47,7 +47,7 @@ namespace gtsam {
* Construct the solver for a factor graph. This builds the elimination
* tree, which already does some of the work of elimination.
*/
DiscreteSequentialSolver(const FactorGraph<DiscreteFactor>& factorGraph) :
DiscreteSequentialSolver(const FactorGraphOrdered<DiscreteFactor>& factorGraph) :
Base(factorGraph) {
}
@ -57,12 +57,12 @@ namespace gtsam {
* is the fastest.
*/
DiscreteSequentialSolver(
const FactorGraph<DiscreteFactor>::shared_ptr& factorGraph,
const VariableIndex::shared_ptr& variableIndex) :
const FactorGraphOrdered<DiscreteFactor>::shared_ptr& factorGraph,
const VariableIndexOrdered::shared_ptr& variableIndex) :
Base(factorGraph, variableIndex) {
}
const EliminationTree<DiscreteFactor>& eliminationTree() const {
const EliminationTreeOrdered<DiscreteFactor>& eliminationTree() const {
return *eliminationTree_;
}
@ -70,7 +70,7 @@ namespace gtsam {
* Eliminate the factor graph sequentially. Uses a column elimination tree
* to recursively eliminate.
*/
BayesNet<DiscreteConditional>::shared_ptr eliminate() const {
BayesNetOrdered<DiscreteConditional>::shared_ptr eliminate() const {
return Base::eliminate(&EliminateDiscrete);
}

View File

@ -19,7 +19,7 @@
#include <gtsam/discrete/AlgebraicDecisionTree.h>
#include <gtsam/discrete/DiscreteKey.h>
#include <gtsam/inference/Permutation.h>
#include <gtsam/inference/PermutationOrdered.h>
#include <boost/shared_ptr.hpp>
#include <set>

View File

@ -17,7 +17,7 @@
#include <gtsam/discrete/DiscreteBayesNet.h>
#include <gtsam/discrete/DiscreteFactorGraph.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/inference/BayesTreeOrdered.h>
#include <boost/assign/std/vector.hpp>
using namespace boost::assign;
@ -32,13 +32,13 @@ static bool debug = false;
/**
* Custom clique class to debug shortcuts
*/
class Clique: public BayesTreeCliqueBase<Clique, DiscreteConditional> {
class Clique: public BayesTreeCliqueBaseOrdered<Clique, DiscreteConditional> {
protected:
public:
typedef BayesTreeCliqueBase<Clique, DiscreteConditional> Base;
typedef BayesTreeCliqueBaseOrdered<Clique, DiscreteConditional> Base;
typedef boost::shared_ptr<Clique> shared_ptr;
// Constructors
@ -56,7 +56,7 @@ public:
/// print index signature only
void printSignature(const std::string& s = "Clique: ",
const IndexFormatter& indexFormatter = DefaultIndexFormatter) const {
((IndexConditional::shared_ptr) conditional_)->print(s, indexFormatter);
((IndexConditionalOrdered::shared_ptr) conditional_)->print(s, indexFormatter);
}
/// evaluate value of sub-tree
@ -70,7 +70,7 @@ public:
};
typedef BayesTree<DiscreteConditional, Clique> DiscreteBayesTree;
typedef BayesTreeOrdered<DiscreteConditional, Clique> DiscreteBayesTree;
/* ************************************************************************* */
double evaluate(const DiscreteBayesTree& tree,

View File

@ -60,7 +60,7 @@ TEST_UNSAFE( DiscreteFactorGraph, debugScheduler) {
//
DiscreteSequentialSolver solver(graph);
// solver.print("solver:");
EliminationTree<DiscreteFactor> eliminationTree = solver.eliminationTree();
EliminationTreeOrdered<DiscreteFactor> eliminationTree = solver.eliminationTree();
// eliminationTree.print("Elimination tree: ");
eliminationTree.eliminate(EliminateDiscrete);
// solver.optimize();
@ -155,7 +155,7 @@ TEST( DiscreteFactorGraph, test)
// GTSAM_PRINT(expected);
// Test elimination tree
const EliminationTree<DiscreteFactor>& etree = solver.eliminationTree();
const EliminationTreeOrdered<DiscreteFactor>& etree = solver.eliminationTree();
DiscreteBayesNet::shared_ptr actual = etree.eliminate(&EliminateDiscrete);
EXPECT(assert_equal(expected, *actual));
@ -221,16 +221,16 @@ TEST( DiscreteFactorGraph, testMPE_Darwiche09book_p244)
// EXPECT_DOUBLES_EQUAL(0.4, (*root)(*actualMPE), 1e-9);
// Let us create the Bayes tree here, just for fun, because we don't use it now
typedef JunctionTree<DiscreteFactorGraph> JT;
typedef JunctionTreeOrdered<DiscreteFactorGraph> JT;
GenericMultifrontalSolver<DiscreteFactor, JT> solver(graph);
BayesTree<DiscreteConditional>::shared_ptr bayesTree = solver.eliminate(&EliminateDiscrete);
BayesTreeOrdered<DiscreteConditional>::shared_ptr bayesTree = solver.eliminate(&EliminateDiscrete);
// bayesTree->print("Bayes Tree");
EXPECT_LONGS_EQUAL(2,bayesTree->size());
#ifdef OLD
// Create the elimination tree manually
VariableIndex structure(graph);
typedef EliminationTree<DiscreteFactor> ETree;
VariableIndexOrdered structure(graph);
typedef EliminationTreeOrdered<DiscreteFactor> ETree;
ETree::shared_ptr eTree = ETree::Create(graph, structure);
//eTree->print(">>>>>>>>>>> Elimination Tree <<<<<<<<<<<<<<<<<");

View File

@ -119,9 +119,9 @@ TEST_UNSAFE( DiscreteMarginals, truss ) {
graph.add(key[0] & key[2] & key[4],"2 2 2 2 1 1 1 1");
graph.add(key[1] & key[3] & key[4],"1 1 1 1 2 2 2 2");
graph.add(key[2] & key[3] & key[4],"1 1 1 1 1 1 1 1");
typedef JunctionTree<DiscreteFactorGraph> JT;
typedef JunctionTreeOrdered<DiscreteFactorGraph> JT;
GenericMultifrontalSolver<DiscreteFactor, JT> solver(graph);
BayesTree<DiscreteConditional>::shared_ptr bayesTree = solver.eliminate(&EliminateDiscrete);
BayesTreeOrdered<DiscreteConditional>::shared_ptr bayesTree = solver.eliminate(&EliminateDiscrete);
// bayesTree->print("Bayes Tree");
typedef BayesTreeClique<DiscreteConditional> Clique;

View File

@ -1,36 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 dllexport.h
* @brief Symbols for exporting classes and methods from DLLs
* @author Richard Roberts
* @date Mar 9, 2013
*/
#ifdef _WIN32
# ifdef GTSAM_EXPORTS
# define GTSAM_EXPORT __declspec(dllexport)
# define GTSAM_EXTERN_EXPORT __declspec(dllexport) extern
# else
# ifndef GTSAM_IMPORT_STATIC
# define GTSAM_EXPORT __declspec(dllimport)
# define GTSAM_EXTERN_EXPORT __declspec(dllimport)
# else /* GTSAM_IMPORT_STATIC */
# define GTSAM_EXPORT
# define GTSAM_EXTERN_EXPORT extern
# endif /* GTSAM_IMPORT_STATIC */
# endif /* GTSAM_EXPORTS */
#else /* _WIN32 */
# define GTSAM_EXPORT
# define GTSAM_EXTERN_EXPORT extern
#endif

View File

@ -1,43 +0,0 @@
*.lo
testPoint2
testSimulated2D
testCal3_S2
testChordalBayesNet
testNonlinearFactorGraph
testLinearFactor
testConditionalGaussian
testPose3
testFactorgraph
testSimulated3D
testFGConfig
testNonlinearFactor
testPoint3
testMatrix
testVector
testRot3
testLinearFactorGraph
testVSLAMFactor
config.h
html
config.h.in
stamp-h1
Makefile
Makefile.in
libgtsam.la
simple_svd_test
simple_ublas_resize_test
simple_ublas_resize_test.cpp
test_svdcmp.cpp
.deps
.libs
resize_test
testConstrainedChordalBayesNet
testConstrainedLinearFactorGraph
testConstrainedNonlinearFactorGraph
testDeltaFunction
testUnconstrainedLinearFactorGraph
testUnconstrainedNonlinearFactorGraph
testEqualityFactor
.DS_Store
testSimpleCamera
testCalibratedCamera

View File

@ -1,192 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 BayesNet-inl.h
* @brief Bayes net template definitions
* @author Frank Dellaert
*/
#pragma once
#include <gtsam/base/Testable.h>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/format.hpp>
#include <boost/assign/std/vector.hpp> // for +=
using boost::assign::operator+=;
#include <algorithm>
#include <iostream>
#include <fstream>
namespace gtsam {
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::print(const std::string& s,
const IndexFormatter& formatter) const {
std::cout << s;
BOOST_REVERSE_FOREACH(sharedConditional conditional, conditionals_)
conditional->print("Conditional", formatter);
}
/* ************************************************************************* */
template<class CONDITIONAL>
bool BayesNet<CONDITIONAL>::equals(const BayesNet& cbn, double tol) const {
if (size() != cbn.size())
return false;
return std::equal(conditionals_.begin(), conditionals_.end(),
cbn.conditionals_.begin(), equals_star<CONDITIONAL>(tol));
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::printStats(const std::string& s) const {
const size_t n = conditionals_.size();
size_t max_size = 0;
size_t total = 0;
BOOST_REVERSE_FOREACH(sharedConditional conditional, conditionals_) {
max_size = std::max(max_size, conditional->size());
total += conditional->size();
}
std::cout << s
<< "maximum conditional size = " << max_size << std::endl
<< "average conditional size = " << total / n << std::endl
<< "density = " << 100.0 * total / (double) (n*(n+1)/2) << " %" << std::endl;
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::saveGraph(const std::string &s,
const IndexFormatter& indexFormatter) const {
std::ofstream of(s.c_str());
of << "digraph G{\n";
BOOST_REVERSE_FOREACH(typename CONDITIONAL::shared_ptr conditional, conditionals_) {
typename CONDITIONAL::Frontals frontals = conditional->frontals();
Index me = frontals.front();
typename CONDITIONAL::Parents parents = conditional->parents();
BOOST_FOREACH(Index p, parents)
of << p << "->" << me << std::endl;
}
of << "}";
of.close();
}
/* ************************************************************************* */
template<class CONDITIONAL>
typename BayesNet<CONDITIONAL>::const_iterator BayesNet<CONDITIONAL>::find(
Index key) const {
for (const_iterator it = begin(); it != end(); ++it)
if (std::find((*it)->beginFrontals(), (*it)->endFrontals(), key)
!= (*it)->endFrontals())
return it;
return end();
}
/* ************************************************************************* */
template<class CONDITIONAL>
typename BayesNet<CONDITIONAL>::iterator BayesNet<CONDITIONAL>::find(
Index key) {
for (iterator it = begin(); it != end(); ++it)
if (std::find((*it)->beginFrontals(), (*it)->endFrontals(), key)
!= (*it)->endFrontals())
return it;
return end();
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::permuteWithInverse(
const Permutation& inversePermutation) {
BOOST_FOREACH(sharedConditional conditional, conditionals_) {
conditional->permuteWithInverse(inversePermutation);
}
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::push_back(const BayesNet<CONDITIONAL>& bn) {
BOOST_FOREACH(sharedConditional conditional,bn.conditionals_)
push_back(conditional);
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::push_front(const BayesNet<CONDITIONAL>& bn) {
BOOST_FOREACH(sharedConditional conditional,bn.conditionals_)
push_front(conditional);
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::popLeaf(iterator conditional) {
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
BOOST_FOREACH(typename CONDITIONAL::shared_ptr checkConditional, conditionals_) {
BOOST_FOREACH(Index key, (*conditional)->frontals()) {
if(std::find(checkConditional->beginParents(), checkConditional->endParents(), key) != checkConditional->endParents())
throw std::invalid_argument(
"Debug mode exception: in BayesNet::popLeaf, the requested conditional is not a leaf.");
}
}
#endif
conditionals_.erase(conditional);
}
/* ************************************************************************* */
template<class CONDITIONAL>
FastList<Index> BayesNet<CONDITIONAL>::ordering() const {
FastList<Index> ord;
BOOST_FOREACH(sharedConditional conditional,conditionals_)
ord.insert(ord.begin(), conditional->beginFrontals(),
conditional->endFrontals());
return ord;
}
// /* ************************************************************************* */
// template<class CONDITIONAL>
// void BayesNet<CONDITIONAL>::saveGraph(const std::string &s) const {
// ofstream of(s.c_str());
// of<< "digraph G{\n";
// BOOST_FOREACH(const_sharedConditional conditional,conditionals_) {
// Index child = conditional->key();
// BOOST_FOREACH(Index parent, conditional->parents()) {
// of << parent << "->" << child << endl;
// }
// }
// of<<"}";
// of.close();
// }
//
/* ************************************************************************* */
template<class CONDITIONAL>
typename BayesNet<CONDITIONAL>::sharedConditional BayesNet<CONDITIONAL>::operator[](
Index key) const {
BOOST_FOREACH(typename CONDITIONAL::shared_ptr conditional, conditionals_) {
typename CONDITIONAL::const_iterator it = std::find(
conditional->beginFrontals(), conditional->endFrontals(), key);
if (it != conditional->endFrontals()) {
return conditional;
}
}
throw(std::invalid_argument(
(boost::format("BayesNet::operator['%1%']: not found") % key).str()));
}
/* ************************************************************************* */
} // namespace gtsam

View File

@ -0,0 +1,55 @@
/* ----------------------------------------------------------------------------
* 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 BayesNet.h
* @brief Bayes network
* @author Frank Dellaert
* @author Richard Roberts
*/
#pragma once
#include <gtsam/inference/FactorGraph-inst.h>
#include <gtsam/inference/BayesNet.h>
#include <boost/foreach.hpp>
#include <fstream>
namespace gtsam {
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::print(const std::string& s, const KeyFormatter& formatter) const
{
Base::print(s, formatter);
}
/* ************************************************************************* */
template<class CONDITIONAL>
void BayesNet<CONDITIONAL>::saveGraph(const std::string &s, const KeyFormatter& keyFormatter) const
{
std::ofstream of(s.c_str());
of << "digraph G{\n";
BOOST_REVERSE_FOREACH(const sharedConditional& conditional, *this) {
typename CONDITIONAL::Frontals frontals = conditional->frontals();
Key me = frontals.front();
typename CONDITIONAL::Parents parents = conditional->parents();
BOOST_FOREACH(Key p, parents)
of << p << "->" << me << std::endl;
}
of << "}";
of.close();
}
}

View File

@ -1,244 +1,72 @@
/* ----------------------------------------------------------------------------
* 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 BayesNet.h
* @brief Bayes network
* @author Frank Dellaert
*/
// \callgraph
#pragma once
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/lexical_cast.hpp>
#include <gtsam/global_includes.h>
#include <gtsam/base/FastList.h>
#include <gtsam/inference/Permutation.h>
namespace gtsam {
/**
* A BayesNet is a list of conditionals, stored in elimination order, i.e.
* leaves first, parents last. GaussianBayesNet and SymbolicBayesNet are
* defined as typedefs of this class, using GaussianConditional and
* IndexConditional as the CONDITIONAL template argument.
*
* todo: Symbolic using Index is a misnomer.
* todo: how to handle Bayes nets with an optimize function? Currently using global functions.
* \nosubgrouping
*/
template<class CONDITIONAL>
class BayesNet {
public:
typedef typename boost::shared_ptr<BayesNet<CONDITIONAL> > shared_ptr;
/** We store shared pointers to Conditional densities */
typedef typename CONDITIONAL::KeyType KeyType;
typedef typename boost::shared_ptr<CONDITIONAL> sharedConditional;
typedef typename boost::shared_ptr<const CONDITIONAL> const_sharedConditional;
typedef typename std::list<sharedConditional> Conditionals;
typedef typename Conditionals::iterator iterator;
typedef typename Conditionals::reverse_iterator reverse_iterator;
typedef typename Conditionals::const_iterator const_iterator;
typedef typename Conditionals::const_reverse_iterator const_reverse_iterator;
protected:
/**
* Conditional densities are stored in reverse topological sort order (i.e., leaves first,
* parents last), which corresponds to the elimination ordering if so obtained,
* and is consistent with the column (block) ordering of an upper triangular matrix.
*/
Conditionals conditionals_;
public:
/// @name Standard Constructors
/// @{
/** Default constructor as an empty BayesNet */
BayesNet() {};
/** convert from a derived type */
template<class DERIVEDCONDITIONAL>
BayesNet(const BayesNet<DERIVEDCONDITIONAL>& bn) {
conditionals_.assign(bn.begin(), bn.end());
}
/** BayesNet with 1 conditional */
explicit BayesNet(const sharedConditional& conditional) { push_back(conditional); }
/// @}
/// @name Testable
/// @{
/** print */
void print(const std::string& s = "",
const IndexFormatter& formatter = DefaultIndexFormatter) const;
/** check equality */
bool equals(const BayesNet& other, double tol = 1e-9) const;
/// @}
/// @name Standard Interface
/// @{
/** size is the number of nodes */
size_t size() const {
return conditionals_.size();
}
/** @return true if there are no conditionals/nodes */
bool empty() const {
return conditionals_.empty();
}
/** print statistics */
void printStats(const std::string& s = "") const;
/** save dot graph */
void saveGraph(const std::string &s, const IndexFormatter& indexFormatter =
DefaultIndexFormatter) const;
/** return keys in reverse topological sort order, i.e., elimination order */
FastList<Index> ordering() const;
/** SLOW O(n) random access to Conditional by key */
sharedConditional operator[](Index key) const;
/** return last node in ordering */
boost::shared_ptr<const CONDITIONAL> front() const { return conditionals_.front(); }
/** return last node in ordering */
boost::shared_ptr<const CONDITIONAL> back() const { return conditionals_.back(); }
/** return iterators. FD: breaks encapsulation? */
const_iterator begin() const {return conditionals_.begin();} ///<TODO: comment
const_iterator end() const {return conditionals_.end();} ///<TODO: comment
const_reverse_iterator rbegin() const {return conditionals_.rbegin();} ///<TODO: comment
const_reverse_iterator rend() const {return conditionals_.rend();} ///<TODO: comment
/** Find an iterator pointing to the conditional where the specified key
* appears as a frontal variable, or end() if no conditional contains this
* key. Running time is approximately \f$ O(n) \f$ in the number of
* conditionals in the BayesNet.
* @param key The index to find in the frontal variables of a conditional.
*/
const_iterator find(Index key) const;
/// @}
/// @name Advanced Interface
/// @{
/**
* Remove any leaf conditional. The conditional to remove is specified by
* iterator. To find the iterator pointing to the conditional containing a
* particular key, use find(), which has \f$ O(n) \f$ complexity. The
* popLeaf function by itself has \f$ O(1) \f$ complexity.
*
* If gtsam is compiled with GTSAM_EXTRA_CONSISTENCY_CHECKS defined, this
* function will check that the node is indeed a leaf, but otherwise will
* not check, because the check has \f$ O(n^2) \f$ complexity.
*
* Example 1:
\code
// Remove a leaf node with a known conditional
GaussianBayesNet gbn = ...
GaussianBayesNet::iterator leafConditional = ...
gbn.popLeaf(leafConditional);
\endcode
* Example 2:
\code
// Remove the leaf node containing variable index 14
GaussianBayesNet gbn = ...
gbn.popLeaf(gbn.find(14));
\endcode
* @param conditional The iterator pointing to the leaf conditional to remove
*/
void popLeaf(iterator conditional);
/** return last node in ordering */
sharedConditional& front() { return conditionals_.front(); }
/** return last node in ordering */
sharedConditional& back() { return conditionals_.back(); }
/** Find an iterator pointing to the conditional where the specified key
* appears as a frontal variable, or end() if no conditional contains this
* key. Running time is approximately \f$ O(n) \f$ in the number of
* conditionals in the BayesNet.
* @param key The index to find in the frontal variables of a conditional.
*/
iterator find(Index key);
/** push_back: use reverse topological sort (i.e. parents last / elimination order) */
inline void push_back(const sharedConditional& conditional) {
conditionals_.push_back(conditional);
}
/** push_front: use topological sort (i.e. parents first / reverse elimination order) */
inline void push_front(const sharedConditional& conditional) {
conditionals_.push_front(conditional);
}
/// push_back an entire Bayes net
void push_back(const BayesNet<CONDITIONAL>& bn);
/// push_front an entire Bayes net
void push_front(const BayesNet<CONDITIONAL>& bn);
/** += syntax for push_back, e.g. bayesNet += c1, c2, c3
* @param conditional The conditional to add to the back of the BayesNet
*/
boost::assign::list_inserter<boost::assign_detail::call_push_back<BayesNet<CONDITIONAL> >, sharedConditional>
operator+=(const sharedConditional& conditional) {
return boost::assign::make_list_inserter(boost::assign_detail::call_push_back<BayesNet<CONDITIONAL> >(*this))(conditional); }
/**
* pop_front: remove node at the bottom, used in marginalization
* For example P(ABC)=P(A|BC)P(B|C)P(C) becomes P(BC)=P(B|C)P(C)
*/
void pop_front() {conditionals_.pop_front();}
/** Permute the variables in the BayesNet */
void permuteWithInverse(const Permutation& inversePermutation);
iterator begin() {return conditionals_.begin();} ///<TODO: comment
iterator end() {return conditionals_.end();} ///<TODO: comment
reverse_iterator rbegin() {return conditionals_.rbegin();} ///<TODO: comment
reverse_iterator rend() {return conditionals_.rend();} ///<TODO: comment
/** saves the bayes to a text file in GraphViz format */
// void saveGraph(const std::string& s) const;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(conditionals_);
}
/// @}
}; // BayesNet
} // namespace gtsam
#include <gtsam/inference/BayesNet-inl.h>
/* ----------------------------------------------------------------------------
* 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 BayesNet.h
* @brief Bayes network
* @author Frank Dellaert
* @author Richard Roberts
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include <gtsam/inference/FactorGraph.h>
namespace gtsam {
/**
* A BayesNet is a tree of conditionals, stored in elimination order.
*
* todo: how to handle Bayes nets with an optimize function? Currently using global functions.
* \nosubgrouping
*/
template<class CONDITIONAL>
class BayesNet : public FactorGraph<CONDITIONAL> {
private:
typedef FactorGraph<CONDITIONAL> Base;
public:
typedef typename boost::shared_ptr<CONDITIONAL> sharedConditional; ///< A shared pointer to a conditional
protected:
/// @name Standard Constructors
/// @{
/** Default constructor as an empty BayesNet */
BayesNet() {};
/** Construct from iterator over conditionals */
template<typename ITERATOR>
BayesNet(ITERATOR firstConditional, ITERATOR lastConditional) : Base(firstConditional, lastConditional) {}
/// @}
public:
/// @name Testable
/// @{
/** print out graph */
void print(const std::string& s = "BayesNet",
const KeyFormatter& formatter = DefaultKeyFormatter) const;
/// @}
/// @name Standard Interface
/// @{
void saveGraph(const std::string &s, const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
};
}

View File

@ -1,804 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 BayesTree-inl.h
* @brief Bayes Tree is a tree of cliques of a Bayes Chain
* @author Frank Dellaert
* @author Michael Kaess
* @author Viorela Ila
* @author Richard Roberts
*/
#pragma once
#include <gtsam/base/FastList.h>
#include <gtsam/base/FastSet.h>
#include <gtsam/base/FastVector.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/inference/inference.h>
#include <gtsam/inference/GenericSequentialSolver.h>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/assign/std/list.hpp> // for operator +=
using boost::assign::operator+=;
#include <boost/format.hpp>
namespace gtsam {
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesTree<CONDITIONAL,CLIQUE>::CliqueData
BayesTree<CONDITIONAL,CLIQUE>::getCliqueData() const {
CliqueData data;
getCliqueData(data, root_);
return data;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::getCliqueData(CliqueData& data, sharedClique clique) const {
data.conditionalSizes.push_back((*clique)->nrFrontals());
data.separatorSizes.push_back((*clique)->nrParents());
BOOST_FOREACH(sharedClique c, clique->children_) {
getCliqueData(data, c);
}
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
size_t BayesTree<CONDITIONAL,CLIQUE>::numCachedSeparatorMarginals() const {
return (root_) ? root_->numCachedSeparatorMarginals() : 0;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::saveGraph(const std::string &s, const IndexFormatter& indexFormatter) const {
if (!root_.get()) throw std::invalid_argument("the root of Bayes tree has not been initialized!");
std::ofstream of(s.c_str());
of<< "digraph G{\n";
saveGraph(of, root_, indexFormatter);
of<<"}";
of.close();
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::saveGraph(std::ostream &s, sharedClique clique, const IndexFormatter& indexFormatter, int parentnum) const {
static int num = 0;
bool first = true;
std::stringstream out;
out << num;
std::string parent = out.str();
parent += "[label=\"";
BOOST_FOREACH(Index index, clique->conditional_->frontals()) {
if(!first) parent += ","; first = false;
parent += indexFormatter(index);
}
if( clique != root_){
parent += " : ";
s << parentnum << "->" << num << "\n";
}
first = true;
BOOST_FOREACH(Index sep, clique->conditional_->parents()) {
if(!first) parent += ","; first = false;
parent += indexFormatter(sep);
}
parent += "\"];\n";
s << parent;
parentnum = num;
BOOST_FOREACH(sharedClique c, clique->children_) {
num++;
saveGraph(s, c, indexFormatter, parentnum);
}
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::CliqueStats::print(const std::string& s) const {
std::cout << s
<<"avg Conditional Size: " << avgConditionalSize << std::endl
<< "max Conditional Size: " << maxConditionalSize << std::endl
<< "avg Separator Size: " << avgSeparatorSize << std::endl
<< "max Separator Size: " << maxSeparatorSize << std::endl;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesTree<CONDITIONAL,CLIQUE>::CliqueStats
BayesTree<CONDITIONAL,CLIQUE>::CliqueData::getStats() const {
CliqueStats stats;
double sum = 0.0;
size_t max = 0;
BOOST_FOREACH(size_t s, conditionalSizes) {
sum += (double)s;
if(s > max) max = s;
}
stats.avgConditionalSize = sum / (double)conditionalSizes.size();
stats.maxConditionalSize = max;
sum = 0.0;
max = 1;
BOOST_FOREACH(size_t s, separatorSizes) {
sum += (double)s;
if(s > max) max = s;
}
stats.avgSeparatorSize = sum / (double)separatorSizes.size();
stats.maxSeparatorSize = max;
return stats;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::Cliques::print(const std::string& s, const IndexFormatter& indexFormatter) const {
std::cout << s << ":\n";
BOOST_FOREACH(sharedClique clique, *this)
clique->printTree("", indexFormatter);
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
bool BayesTree<CONDITIONAL,CLIQUE>::Cliques::equals(const Cliques& other, double tol) const {
return other == *this;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique
BayesTree<CONDITIONAL,CLIQUE>::addClique(const sharedConditional& conditional, const sharedClique& parent_clique) {
sharedClique new_clique(new Clique(conditional));
addClique(new_clique, parent_clique);
return new_clique;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::addClique(const sharedClique& clique, const sharedClique& parent_clique) {
nodes_.resize(std::max((*clique)->lastFrontalKey()+1, nodes_.size()));
BOOST_FOREACH(Index j, (*clique)->frontals())
nodes_[j] = clique;
if (parent_clique != NULL) {
clique->parent_ = parent_clique;
parent_clique->children_.push_back(clique);
} else {
assert(!root_);
root_ = clique;
}
clique->assertInvariants();
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique BayesTree<CONDITIONAL,CLIQUE>::addClique(
const sharedConditional& conditional, std::list<sharedClique>& child_cliques) {
sharedClique new_clique(new Clique(conditional));
nodes_.resize(std::max(conditional->lastFrontalKey()+1, nodes_.size()));
BOOST_FOREACH(Index j, conditional->frontals())
nodes_[j] = new_clique;
new_clique->children_ = child_cliques;
BOOST_FOREACH(sharedClique& child, child_cliques)
child->parent_ = new_clique;
new_clique->assertInvariants();
return new_clique;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::permuteWithInverse(const Permutation& inversePermutation) {
// recursively permute the cliques and internal conditionals
if (root_)
root_->permuteWithInverse(inversePermutation);
// need to know what the largest key is to get the right number of cliques
Index maxIndex = *std::max_element(inversePermutation.begin(), inversePermutation.end());
// Update the nodes structure
typename BayesTree<CONDITIONAL,CLIQUE>::Nodes newNodes(maxIndex+1);
// inversePermutation.applyToCollection(newNodes, nodes_); // Uses the forward, rather than inverse permutation
for(size_t i = 0; i < nodes_.size(); ++i)
newNodes[inversePermutation[i]] = nodes_[i];
nodes_ = newNodes;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
inline void BayesTree<CONDITIONAL,CLIQUE>::addToCliqueFront(BayesTree<CONDITIONAL,CLIQUE>& bayesTree, const sharedConditional& conditional, const sharedClique& clique) {
static const bool debug = false;
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
// Debug check to make sure the conditional variable is ordered lower than
// its parents and that all of its parents are present either in this
// clique or its separator.
BOOST_FOREACH(Index parent, conditional->parents()) {
assert(parent > conditional->lastFrontalKey());
const Clique& cliquer(*clique);
assert(find(cliquer->begin(), cliquer->end(), parent) != cliquer->end());
}
#endif
if(debug) conditional->print("Adding conditional ");
if(debug) clique->print("To clique ");
Index j = conditional->lastFrontalKey();
bayesTree.nodes_.resize(std::max(j+1, bayesTree.nodes_.size()));
bayesTree.nodes_[j] = clique;
FastVector<Index> newIndices((*clique)->size() + 1);
newIndices[0] = j;
std::copy((*clique)->begin(), (*clique)->end(), newIndices.begin()+1);
clique->conditional_ = CONDITIONAL::FromKeys(newIndices, (*clique)->nrFrontals() + 1);
if(debug) clique->print("Expanded clique is ");
clique->assertInvariants();
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::removeClique(sharedClique clique) {
if (clique->isRoot())
root_.reset();
else { // detach clique from parent
sharedClique parent = clique->parent_.lock();
typename FastList<typename CLIQUE::shared_ptr>::iterator child = std::find(parent->children().begin(), parent->children().end(), clique);
assert(child != parent->children().end());
parent->children().erase(child);
}
// orphan my children
BOOST_FOREACH(sharedClique child, clique->children_)
child->parent_ = typename Clique::weak_ptr();
BOOST_FOREACH(Index j, clique->conditional()->frontals()) {
nodes_[j].reset();
}
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::recursiveTreeBuild(const boost::shared_ptr<BayesTreeClique<IndexConditional> >& symbolic,
const std::vector<boost::shared_ptr<CONDITIONAL> >& conditionals,
const typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique& parent) {
// Helper function to build a non-symbolic tree (e.g. Gaussian) using a
// symbolic tree, used in the BT(BN) constructor.
// Build the current clique
FastList<typename CONDITIONAL::shared_ptr> cliqueConditionals;
BOOST_FOREACH(Index j, symbolic->conditional()->frontals()) {
cliqueConditionals.push_back(conditionals[j]); }
typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique thisClique(new CLIQUE(CONDITIONAL::Combine(cliqueConditionals.begin(), cliqueConditionals.end())));
// Add the new clique with the current parent
this->addClique(thisClique, parent);
// Build the children, whose parent is the new clique
BOOST_FOREACH(const BayesTree<IndexConditional>::sharedClique& child, symbolic->children()) {
this->recursiveTreeBuild(child, conditionals, thisClique); }
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
BayesTree<CONDITIONAL,CLIQUE>::BayesTree(const BayesNet<CONDITIONAL>& bayesNet) {
// First generate symbolic BT to determine clique structure
BayesTree<IndexConditional> sbt(bayesNet);
// Build index of variables to conditionals
std::vector<boost::shared_ptr<CONDITIONAL> > conditionals(sbt.root()->conditional()->frontals().back() + 1);
BOOST_FOREACH(const boost::shared_ptr<CONDITIONAL>& c, bayesNet) {
if(c->nrFrontals() != 1)
throw std::invalid_argument("BayesTree constructor from BayesNet only supports single frontal variable conditionals");
if(c->firstFrontalKey() >= conditionals.size())
throw std::invalid_argument("An inconsistent BayesNet was passed into the BayesTree constructor!");
if(conditionals[c->firstFrontalKey()])
throw std::invalid_argument("An inconsistent BayesNet with duplicate frontal variables was passed into the BayesTree constructor!");
conditionals[c->firstFrontalKey()] = c;
}
// Build the new tree
this->recursiveTreeBuild(sbt.root(), conditionals, sharedClique());
}
/* ************************************************************************* */
template<>
inline BayesTree<IndexConditional>::BayesTree(const BayesNet<IndexConditional>& bayesNet) {
BayesNet<IndexConditional>::const_reverse_iterator rit;
for ( rit=bayesNet.rbegin(); rit != bayesNet.rend(); ++rit )
insert(*this, *rit);
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
BayesTree<CONDITIONAL,CLIQUE>::BayesTree(const BayesNet<CONDITIONAL>& bayesNet, std::list<BayesTree<CONDITIONAL,CLIQUE> > subtrees) {
if (bayesNet.size() == 0)
throw std::invalid_argument("BayesTree::insert: empty bayes net!");
// get the roots of child subtrees and merge their nodes_
std::list<sharedClique> childRoots;
typedef BayesTree<CONDITIONAL,CLIQUE> Tree;
BOOST_FOREACH(const Tree& subtree, subtrees) {
nodes_.assign(subtree.nodes_.begin(), subtree.nodes_.end());
childRoots.push_back(subtree.root());
}
// create a new clique and add all the conditionals to the clique
sharedClique new_clique;
typename BayesNet<CONDITIONAL>::sharedConditional conditional;
BOOST_REVERSE_FOREACH(conditional, bayesNet) {
if (!new_clique.get())
new_clique = addClique(conditional,childRoots);
else
addToCliqueFront(*this, conditional, new_clique);
}
root_ = new_clique;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
BayesTree<CONDITIONAL,CLIQUE>::BayesTree(const This& other) {
*this = other;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
BayesTree<CONDITIONAL,CLIQUE>& BayesTree<CONDITIONAL,CLIQUE>::operator=(const This& other) {
this->clear();
other.cloneTo(*this);
return *this;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::print(const std::string& s, const IndexFormatter& indexFormatter) const {
if (root_.use_count() == 0) {
std::cout << "WARNING: BayesTree.print encountered a forest..." << std::endl;
return;
}
std::cout << s << ": clique size == " << size() << ", node size == " << nodes_.size() << std::endl;
if (nodes_.empty()) return;
root_->printTree("", indexFormatter);
}
/* ************************************************************************* */
// binary predicate to test equality of a pair for use in equals
template<class CONDITIONAL, class CLIQUE>
bool check_sharedCliques(
const typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique& v1,
const typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique& v2
) {
return (!v1 && !v2) || (v1 && v2 && v1->equals(*v2));
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
bool BayesTree<CONDITIONAL,CLIQUE>::equals(const BayesTree<CONDITIONAL,CLIQUE>& other,
double tol) const {
return size()==other.size() &&
std::equal(nodes_.begin(), nodes_.end(), other.nodes_.begin(), &check_sharedCliques<CONDITIONAL,CLIQUE>);
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
template<class CONTAINER>
inline Index BayesTree<CONDITIONAL,CLIQUE>::findParentClique(const CONTAINER& parents) const {
typename CONTAINER::const_iterator lowestOrderedParent = min_element(parents.begin(), parents.end());
assert(lowestOrderedParent != parents.end());
return *lowestOrderedParent;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::insert(BayesTree<CONDITIONAL,CLIQUE>& bayesTree, const sharedConditional& conditional)
{
static const bool debug = false;
// get indices and parents
const typename CONDITIONAL::Parents& parents = conditional->parents();
if(debug) conditional->print("Adding conditional ");
// if no parents, start a new root clique
if (parents.empty()) {
if(debug) std::cout << "No parents so making root" << std::endl;
bayesTree.root_ = bayesTree.addClique(conditional);
return;
}
// otherwise, find the parent clique by using the index data structure
// to find the lowest-ordered parent
Index parentRepresentative = bayesTree.findParentClique(parents);
if(debug) std::cout << "First-eliminated parent is " << parentRepresentative << ", have " << bayesTree.nodes_.size() << " nodes." << std::endl;
sharedClique parent_clique = bayesTree[parentRepresentative];
if(debug) parent_clique->print("Parent clique is ");
// if the parents and parent clique have the same size, add to parent clique
if ((*parent_clique)->size() == size_t(parents.size())) {
if(debug) std::cout << "Adding to parent clique" << std::endl;
addToCliqueFront(bayesTree, conditional, parent_clique);
} else {
if(debug) std::cout << "Starting new clique" << std::endl;
// otherwise, start a new clique and add it to the tree
bayesTree.addClique(conditional,parent_clique);
}
}
/* ************************************************************************* */
//TODO: remove this function after removing TSAM.cpp
template<class CONDITIONAL, class CLIQUE>
typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique BayesTree<CONDITIONAL,CLIQUE>::insert(
const sharedConditional& clique, std::list<sharedClique>& children, bool isRootClique) {
if (clique->nrFrontals() == 0)
throw std::invalid_argument("BayesTree::insert: empty clique!");
// create a new clique and add all the conditionals to the clique
sharedClique new_clique = addClique(clique, children);
if (isRootClique) root_ = new_clique;
return new_clique;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::fillNodesIndex(const sharedClique& subtree) {
// Add each frontal variable of this root node
BOOST_FOREACH(const Index& j, subtree->conditional()->frontals()) { nodes_[j] = subtree; }
// Fill index for each child
typedef typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique sharedClique;
BOOST_FOREACH(const sharedClique& child, subtree->children_) {
fillNodesIndex(child); }
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::insert(const sharedClique& subtree) {
if(subtree) {
// Find the parent clique of the new subtree. By the running intersection
// property, those separator variables in the subtree that are ordered
// lower than the highest frontal variable of the subtree root will all
// appear in the separator of the subtree root.
if(subtree->conditional()->parents().empty()) {
assert(!root_);
root_ = subtree;
} else {
Index parentRepresentative = findParentClique(subtree->conditional()->parents());
sharedClique parent = (*this)[parentRepresentative];
parent->children_ += subtree;
subtree->parent_ = parent; // set new parent!
}
// Now fill in the nodes index
if(nodes_.size() == 0 ||
*std::max_element(subtree->conditional()->beginFrontals(), subtree->conditional()->endFrontals()) > (nodes_.size() - 1)) {
nodes_.resize(subtree->conditional()->lastFrontalKey() + 1);
}
fillNodesIndex(subtree);
}
}
/* ************************************************************************* */
// First finds clique marginal then marginalizes that
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename CONDITIONAL::FactorType::shared_ptr BayesTree<CONDITIONAL,CLIQUE>::marginalFactor(
Index j, Eliminate function) const
{
gttic(BayesTree_marginalFactor);
// get clique containing Index j
sharedClique clique = (*this)[j];
// calculate or retrieve its marginal P(C) = P(F,S)
#ifdef OLD_SHORTCUT_MARGINALS
FactorGraph<FactorType> cliqueMarginal = clique->marginal(root_,function);
#else
FactorGraph<FactorType> cliqueMarginal = clique->marginal2(root_,function);
#endif
// Reduce the variable indices to start at zero
gttic(Reduce);
const Permutation reduction = internal::createReducingPermutation(cliqueMarginal.keys());
internal::Reduction inverseReduction = internal::Reduction::CreateAsInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, cliqueMarginal) {
if(factor) factor->reduceWithInverse(inverseReduction); }
gttoc(Reduce);
// now, marginalize out everything that is not variable j
GenericSequentialSolver<FactorType> solver(cliqueMarginal);
boost::shared_ptr<FactorType> result = solver.marginalFactor(inverseReduction[j], function);
// Undo the reduction
gttic(Undo_Reduce);
result->permuteWithInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, cliqueMarginal) {
if(factor) factor->permuteWithInverse(reduction); }
gttoc(Undo_Reduce);
return result;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesNet<CONDITIONAL>::shared_ptr BayesTree<CONDITIONAL,CLIQUE>::marginalBayesNet(
Index j, Eliminate function) const
{
gttic(BayesTree_marginalBayesNet);
// calculate marginal as a factor graph
FactorGraph<FactorType> fg;
fg.push_back(this->marginalFactor(j,function));
// Reduce the variable indices to start at zero
gttic(Reduce);
const Permutation reduction = internal::createReducingPermutation(fg.keys());
internal::Reduction inverseReduction = internal::Reduction::CreateAsInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, fg) {
if(factor) factor->reduceWithInverse(inverseReduction); }
gttoc(Reduce);
// eliminate factor graph marginal to a Bayes net
boost::shared_ptr<BayesNet<CONDITIONAL> > bn = GenericSequentialSolver<FactorType>(fg).eliminate(function);
// Undo the reduction
gttic(Undo_Reduce);
bn->permuteWithInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, fg) {
if(factor) factor->permuteWithInverse(reduction); }
gttoc(Undo_Reduce);
return bn;
}
/* ************************************************************************* */
// Find two cliques, their joint, then marginalizes
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename FactorGraph<typename CONDITIONAL::FactorType>::shared_ptr
BayesTree<CONDITIONAL,CLIQUE>::joint(Index j1, Index j2, Eliminate function) const {
gttic(BayesTree_joint);
// get clique C1 and C2
sharedClique C1 = (*this)[j1], C2 = (*this)[j2];
gttic(Lowest_common_ancestor);
// Find lowest common ancestor clique
sharedClique B; {
// Build two paths to the root
FastList<sharedClique> path1, path2; {
sharedClique p = C1;
while(p) {
path1.push_front(p);
p = p->parent();
}
} {
sharedClique p = C2;
while(p) {
path2.push_front(p);
p = p->parent();
}
}
// Find the path intersection
B = this->root();
typename FastList<sharedClique>::const_iterator p1 = path1.begin(), p2 = path2.begin();
while(p1 != path1.end() && p2 != path2.end() && *p1 == *p2) {
B = *p1;
++p1;
++p2;
}
}
gttoc(Lowest_common_ancestor);
// Compute marginal on lowest common ancestor clique
gttic(LCA_marginal);
FactorGraph<FactorType> p_B = B->marginal2(this->root(), function);
gttoc(LCA_marginal);
// Compute shortcuts of the requested cliques given the lowest common ancestor
gttic(Clique_shortcuts);
BayesNet<CONDITIONAL> p_C1_Bred = C1->shortcut(B, function);
BayesNet<CONDITIONAL> p_C2_Bred = C2->shortcut(B, function);
gttoc(Clique_shortcuts);
// Factor the shortcuts to be conditioned on the full root
// Get the set of variables to eliminate, which is C1\B.
gttic(Full_root_factoring);
sharedConditional p_C1_B; {
std::vector<Index> C1_minus_B; {
FastSet<Index> C1_minus_B_set(C1->conditional()->beginParents(), C1->conditional()->endParents());
BOOST_FOREACH(const Index j, *B->conditional()) {
C1_minus_B_set.erase(j); }
C1_minus_B.assign(C1_minus_B_set.begin(), C1_minus_B_set.end());
}
// Factor into C1\B | B.
FactorGraph<FactorType> temp_remaining;
boost::tie(p_C1_B, temp_remaining) = FactorGraph<FactorType>(p_C1_Bred).eliminate(C1_minus_B, function);
}
sharedConditional p_C2_B; {
std::vector<Index> C2_minus_B; {
FastSet<Index> C2_minus_B_set(C2->conditional()->beginParents(), C2->conditional()->endParents());
BOOST_FOREACH(const Index j, *B->conditional()) {
C2_minus_B_set.erase(j); }
C2_minus_B.assign(C2_minus_B_set.begin(), C2_minus_B_set.end());
}
// Factor into C2\B | B.
FactorGraph<FactorType> temp_remaining;
boost::tie(p_C2_B, temp_remaining) = FactorGraph<FactorType>(p_C2_Bred).eliminate(C2_minus_B, function);
}
gttoc(Full_root_factoring);
gttic(Variable_joint);
// Build joint on all involved variables
FactorGraph<FactorType> p_BC1C2;
p_BC1C2.push_back(p_B);
p_BC1C2.push_back(p_C1_B->toFactor());
p_BC1C2.push_back(p_C2_B->toFactor());
if(C1 != B)
p_BC1C2.push_back(C1->conditional()->toFactor());
if(C2 != B)
p_BC1C2.push_back(C2->conditional()->toFactor());
// Reduce the variable indices to start at zero
gttic(Reduce);
const Permutation reduction = internal::createReducingPermutation(p_BC1C2.keys());
internal::Reduction inverseReduction = internal::Reduction::CreateAsInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, p_BC1C2) {
if(factor) factor->reduceWithInverse(inverseReduction); }
std::vector<Index> js; js.push_back(inverseReduction[j1]); js.push_back(inverseReduction[j2]);
gttoc(Reduce);
// now, marginalize out everything that is not variable j
GenericSequentialSolver<FactorType> solver(p_BC1C2);
boost::shared_ptr<FactorGraph<FactorType> > result = solver.jointFactorGraph(js, function);
// Undo the reduction
gttic(Undo_Reduce);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, *result) {
if(factor) factor->permuteWithInverse(reduction); }
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, p_BC1C2) {
if(factor) factor->permuteWithInverse(reduction); }
gttoc(Undo_Reduce);
return result;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesNet<CONDITIONAL>::shared_ptr BayesTree<CONDITIONAL,CLIQUE>::jointBayesNet(
Index j1, Index j2, Eliminate function) const {
// eliminate factor graph marginal to a Bayes net
return GenericSequentialSolver<FactorType> (
*this->joint(j1, j2, function)).eliminate(function);
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::clear() {
// Remove all nodes and clear the root pointer
nodes_.clear();
root_.reset();
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::removePath(sharedClique clique,
BayesNet<CONDITIONAL>& bn, typename BayesTree<CONDITIONAL,CLIQUE>::Cliques& orphans) {
// base case is NULL, if so we do nothing and return empties above
if (clique!=NULL) {
// remove the clique from orphans in case it has been added earlier
orphans.remove(clique);
// remove me
this->removeClique(clique);
// remove path above me
this->removePath(typename Clique::shared_ptr(clique->parent_.lock()), bn, orphans);
// add children to list of orphans (splice also removed them from clique->children_)
orphans.splice(orphans.begin(), clique->children_);
bn.push_back(clique->conditional());
}
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
template<class CONTAINER>
void BayesTree<CONDITIONAL,CLIQUE>::removeTop(const CONTAINER& keys,
BayesNet<CONDITIONAL>& bn, typename BayesTree<CONDITIONAL,CLIQUE>::Cliques& orphans) {
// process each key of the new factor
BOOST_FOREACH(const Index& j, keys) {
// get the clique
if(j < nodes_.size()) {
const sharedClique& clique(nodes_[j]);
if(clique) {
// remove path from clique to root
this->removePath(clique, bn, orphans);
}
}
}
// Delete cachedShortcuts for each orphan subtree
//TODO: Consider Improving
BOOST_FOREACH(sharedClique& orphan, orphans)
orphan->deleteCachedShortcuts();
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
typename BayesTree<CONDITIONAL,CLIQUE>::Cliques BayesTree<CONDITIONAL,CLIQUE>::removeSubtree(
const sharedClique& subtree)
{
// Result clique list
Cliques cliques;
cliques.push_back(subtree);
// Remove the first clique from its parents
if(!subtree->isRoot())
subtree->parent()->children().remove(subtree);
else
root_.reset();
// Add all subtree cliques and erase the children and parent of each
for(typename Cliques::iterator clique = cliques.begin(); clique != cliques.end(); ++clique)
{
// Add children
BOOST_FOREACH(const sharedClique& child, (*clique)->children()) {
cliques.push_back(child); }
// Delete cached shortcuts
(*clique)->deleteCachedShortcutsNonRecursive();
// Remove this node from the nodes index
BOOST_FOREACH(Index j, (*clique)->conditional()->frontals()) {
nodes_[j].reset(); }
// Erase the parent and children pointers
(*clique)->parent_.reset();
(*clique)->children_.clear();
}
return cliques;
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::cloneTo(This& newTree) const {
if(root())
cloneTo(newTree, root(), sharedClique());
}
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void BayesTree<CONDITIONAL,CLIQUE>::cloneTo(
This& newTree, const sharedClique& subtree, const sharedClique& parent) const {
sharedClique newClique(subtree->clone());
newTree.addClique(newClique, parent);
BOOST_FOREACH(const sharedClique& childClique, subtree->children()) {
cloneTo(newTree, childClique, newClique);
}
}
/* ************************************************************************* */
} // \namespace gtsam

View File

@ -0,0 +1,509 @@
/* ----------------------------------------------------------------------------
* 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 BayesTree-inl.h
* @brief Bayes Tree is a tree of cliques of a Bayes Chain
* @author Frank Dellaert
* @author Michael Kaess
* @author Viorela Ila
* @author Richard Roberts
*/
#pragma once
#include <gtsam/inference/Ordering.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/base/treeTraversal-inst.h>
#include <gtsam/base/timing.h>
#include <boost/optional.hpp>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <fstream>
using boost::assign::cref_list_of;
namespace gtsam {
/* ************************************************************************* */
template<class CLIQUE>
BayesTreeCliqueData BayesTree<CLIQUE>::getCliqueData() const {
BayesTreeCliqueData data;
BOOST_FOREACH(const sharedClique& root, roots_)
getCliqueData(data, root);
return data;
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::getCliqueData(BayesTreeCliqueData& data, sharedClique clique) const {
data.conditionalSizes.push_back(clique->conditional()->nrFrontals());
data.separatorSizes.push_back(clique->conditional()->nrParents());
BOOST_FOREACH(sharedClique c, clique->children) {
getCliqueData(data, c);
}
}
/* ************************************************************************* */
template<class CLIQUE>
size_t BayesTree<CLIQUE>::numCachedSeparatorMarginals() const {
size_t count = 0;
BOOST_FOREACH(const sharedClique& root, roots_)
count += root->numCachedSeparatorMarginals();
return count;
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::saveGraph(const std::string &s, const KeyFormatter& keyFormatter) const {
if (roots_.empty()) throw std::invalid_argument("the root of Bayes tree has not been initialized!");
std::ofstream of(s.c_str());
of<< "digraph G{\n";
BOOST_FOREACH(const sharedClique& root, roots_)
saveGraph(of, root, keyFormatter);
of<<"}";
of.close();
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::saveGraph(std::ostream &s, sharedClique clique, const KeyFormatter& indexFormatter, int parentnum) const {
static int num = 0;
bool first = true;
std::stringstream out;
out << num;
std::string parent = out.str();
parent += "[label=\"";
BOOST_FOREACH(Key index, clique->conditional_->frontals()) {
if(!first) parent += ","; first = false;
parent += indexFormatter(index);
}
if(clique->parent()){
parent += " : ";
s << parentnum << "->" << num << "\n";
}
first = true;
BOOST_FOREACH(Key sep, clique->conditional_->parents()) {
if(!first) parent += ","; first = false;
parent += indexFormatter(sep);
}
parent += "\"];\n";
s << parent;
parentnum = num;
BOOST_FOREACH(sharedClique c, clique->children) {
num++;
saveGraph(s, c, indexFormatter, parentnum);
}
}
/* ************************************************************************* */
template<class CLIQUE>
size_t BayesTree<CLIQUE>::size() const {
size_t size = 0;
BOOST_FOREACH(const sharedClique& clique, roots_)
size += clique->treeSize();
return size;
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::addClique(const sharedClique& clique, const sharedClique& parent_clique) {
BOOST_FOREACH(Index j, clique->conditional()->frontals())
nodes_[j] = clique;
if (parent_clique != NULL) {
clique->parent_ = parent_clique;
parent_clique->children.push_back(clique);
} else {
roots_.push_back(clique);
}
}
/* ************************************************************************* */
// TODO: Clean up
namespace {
template<class FACTOR, class CLIQUE>
int _pushClique(FactorGraph<FACTOR>& fg, const boost::shared_ptr<CLIQUE>& clique) {
fg.push_back(clique->conditional_);
return 0;
}
template<class FACTOR, class CLIQUE>
struct _pushCliqueFunctor {
_pushCliqueFunctor(FactorGraph<FACTOR>& graph_) : graph(graph_) {}
FactorGraph<FACTOR>& graph;
int operator()(const boost::shared_ptr<CLIQUE>& clique, int dummy) {
graph.push_back(clique->conditional_);
return 0;
}
};
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::addFactorsToGraph(FactorGraph<FactorType>& graph) const
{
// Traverse the BayesTree and add all conditionals to this graph
int data = 0; // Unused
_pushCliqueFunctor<FactorType,CLIQUE> functor(graph);
treeTraversal::DepthFirstForest(*this, data, functor); // FIXME: sort of works?
// treeTraversal::DepthFirstForest(*this, data, boost::bind(&_pushClique<FactorType,CLIQUE>, boost::ref(graph), _1));
}
/* ************************************************************************* */
template<class CLIQUE>
BayesTree<CLIQUE>::BayesTree(const This& other) {
*this = other;
}
/* ************************************************************************* */
namespace {
template<typename NODE>
boost::shared_ptr<NODE>
BayesTreeCloneForestVisitorPre(const boost::shared_ptr<NODE>& node, const boost::shared_ptr<NODE>& parentPointer)
{
// Clone the current node and add it to its cloned parent
boost::shared_ptr<NODE> clone = boost::make_shared<NODE>(*node);
clone->children.clear();
clone->parent_ = parentPointer;
parentPointer->children.push_back(clone);
return clone;
}
}
/* ************************************************************************* */
template<class CLIQUE>
BayesTree<CLIQUE>& BayesTree<CLIQUE>::operator=(const This& other) {
this->clear();
boost::shared_ptr<Clique> rootContainer = boost::make_shared<Clique>();
treeTraversal::DepthFirstForest(other, rootContainer, BayesTreeCloneForestVisitorPre<Clique>);
BOOST_FOREACH(const sharedClique& root, rootContainer->children) {
root->parent_ = typename Clique::weak_ptr(); // Reset the parent since it's set to the dummy clique
insertRoot(root);
}
return *this;
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::print(const std::string& s, const KeyFormatter& keyFormatter) const {
std::cout << s << ": cliques: " << size() << ", variables: " << nodes_.size() << std::endl;
treeTraversal::PrintForest(*this, s, keyFormatter);
}
/* ************************************************************************* */
// binary predicate to test equality of a pair for use in equals
template<class CLIQUE>
bool check_sharedCliques(
const std::pair<Key, typename BayesTree<CLIQUE>::sharedClique>& v1,
const std::pair<Key, typename BayesTree<CLIQUE>::sharedClique>& v2
) {
return v1.first == v2.first &&
((!v1.second && !v2.second) || (v1.second && v2.second && v1.second->equals(*v2.second)));
}
/* ************************************************************************* */
template<class CLIQUE>
bool BayesTree<CLIQUE>::equals(const BayesTree<CLIQUE>& other, double tol) const {
return size()==other.size() &&
std::equal(nodes_.begin(), nodes_.end(), other.nodes_.begin(), &check_sharedCliques<CLIQUE>);
}
/* ************************************************************************* */
template<class CLIQUE>
template<class CONTAINER>
Key BayesTree<CLIQUE>::findParentClique(const CONTAINER& parents) const {
typename CONTAINER::const_iterator lowestOrderedParent = min_element(parents.begin(), parents.end());
assert(lowestOrderedParent != parents.end());
return *lowestOrderedParent;
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::fillNodesIndex(const sharedClique& subtree) {
// Add each frontal variable of this root node
BOOST_FOREACH(const Key& j, subtree->conditional()->frontals()) {
bool inserted = nodes_.insert(std::make_pair(j, subtree)).second;
assert(inserted); (void)inserted;
}
// Fill index for each child
typedef typename BayesTree<CLIQUE>::sharedClique sharedClique;
BOOST_FOREACH(const sharedClique& child, subtree->children) {
fillNodesIndex(child); }
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::insertRoot(const sharedClique& subtree) {
roots_.push_back(subtree); // Add to roots
fillNodesIndex(subtree); // Populate nodes index
}
/* ************************************************************************* */
// First finds clique marginal then marginalizes that
/* ************************************************************************* */
template<class CLIQUE>
typename BayesTree<CLIQUE>::sharedConditional
BayesTree<CLIQUE>::marginalFactor(Key j, const Eliminate& function) const
{
gttic(BayesTree_marginalFactor);
// get clique containing Index j
sharedClique clique = this->clique(j);
// calculate or retrieve its marginal P(C) = P(F,S)
FactorGraphType cliqueMarginal = clique->marginal2(function);
// Now, marginalize out everything that is not variable j
BayesNetType marginalBN = *cliqueMarginal.marginalMultifrontalBayesNet(
Ordering(cref_list_of<1,Key>(j)), boost::none, function);
// The Bayes net should contain only one conditional for variable j, so return it
return marginalBN.front();
}
/* ************************************************************************* */
// Find two cliques, their joint, then marginalizes
/* ************************************************************************* */
template<class CLIQUE>
typename BayesTree<CLIQUE>::sharedFactorGraph
BayesTree<CLIQUE>::joint(Key j1, Key j2, const Eliminate& function) const
{
gttic(BayesTree_joint);
return boost::make_shared<FactorGraphType>(*jointBayesNet(j1, j2, function));
}
/* ************************************************************************* */
template<class CLIQUE>
typename BayesTree<CLIQUE>::sharedBayesNet
BayesTree<CLIQUE>::jointBayesNet(Key j1, Key j2, const Eliminate& function) const
{
gttic(BayesTree_jointBayesNet);
// get clique C1 and C2
sharedClique C1 = (*this)[j1], C2 = (*this)[j2];
gttic(Lowest_common_ancestor);
// Find lowest common ancestor clique
sharedClique B; {
// Build two paths to the root
FastList<sharedClique> path1, path2; {
sharedClique p = C1;
while(p) {
path1.push_front(p);
p = p->parent();
}
} {
sharedClique p = C2;
while(p) {
path2.push_front(p);
p = p->parent();
}
}
// Find the path intersection
typename FastList<sharedClique>::const_iterator p1 = path1.begin(), p2 = path2.begin();
if(*p1 == *p2)
B = *p1;
while(p1 != path1.end() && p2 != path2.end() && *p1 == *p2) {
B = *p1;
++p1;
++p2;
}
}
if(!B)
throw std::invalid_argument("BayesTree::jointBayesNet does not yet work for joints across a forest");
gttoc(Lowest_common_ancestor);
// Compute marginal on lowest common ancestor clique
gttic(LCA_marginal);
FactorGraphType p_B = B->marginal2(function);
gttoc(LCA_marginal);
// Compute shortcuts of the requested cliques given the lowest common ancestor
gttic(Clique_shortcuts);
BayesNetType p_C1_Bred = C1->shortcut(B, function);
BayesNetType p_C2_Bred = C2->shortcut(B, function);
gttoc(Clique_shortcuts);
// Factor the shortcuts to be conditioned on the full root
// Get the set of variables to eliminate, which is C1\B.
gttic(Full_root_factoring);
boost::shared_ptr<typename EliminationTraitsType::BayesTreeType> p_C1_B; {
std::vector<Index> C1_minus_B; {
FastSet<Index> C1_minus_B_set(C1->conditional()->beginParents(), C1->conditional()->endParents());
BOOST_FOREACH(const Index j, *B->conditional()) {
C1_minus_B_set.erase(j); }
C1_minus_B.assign(C1_minus_B_set.begin(), C1_minus_B_set.end());
}
// Factor into C1\B | B.
sharedFactorGraph temp_remaining;
boost::tie(p_C1_B, temp_remaining) =
FactorGraphType(p_C1_Bred).eliminatePartialMultifrontal(Ordering(C1_minus_B), function);
}
boost::shared_ptr<typename EliminationTraitsType::BayesTreeType> p_C2_B; {
std::vector<Index> C2_minus_B; {
FastSet<Index> C2_minus_B_set(C2->conditional()->beginParents(), C2->conditional()->endParents());
BOOST_FOREACH(const Index j, *B->conditional()) {
C2_minus_B_set.erase(j); }
C2_minus_B.assign(C2_minus_B_set.begin(), C2_minus_B_set.end());
}
// Factor into C2\B | B.
sharedFactorGraph temp_remaining;
boost::tie(p_C2_B, temp_remaining) =
FactorGraphType(p_C2_Bred).eliminatePartialMultifrontal(Ordering(C2_minus_B), function);
}
gttoc(Full_root_factoring);
gttic(Variable_joint);
// Build joint on all involved variables
FactorGraphType p_BC1C2;
p_BC1C2 += p_B;
p_BC1C2 += *p_C1_B;
p_BC1C2 += *p_C2_B;
if(C1 != B)
p_BC1C2 += C1->conditional();
if(C2 != B)
p_BC1C2 += C2->conditional();
// now, marginalize out everything that is not variable j1 or j2
return p_BC1C2.marginalMultifrontalBayesNet(Ordering(cref_list_of<2,Key>(j1)(j2)), boost::none, function);
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::clear() {
// Remove all nodes and clear the root pointer
nodes_.clear();
roots_.clear();
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::deleteCachedShortcuts() {
BOOST_FOREACH(const sharedClique& root, roots_) {
root->deleteCachedShortcuts();
}
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::removeClique(sharedClique clique)
{
if (clique->isRoot()) {
typename std::vector<sharedClique>::iterator root = std::find(roots_.begin(), roots_.end(), clique);
if(root != roots_.end())
roots_.erase(root);
} else { // detach clique from parent
sharedClique parent = clique->parent_.lock();
typename std::vector<sharedClique>::iterator child = std::find(parent->children.begin(), parent->children.end(), clique);
assert(child != parent->children.end());
parent->children.erase(child);
}
// orphan my children
BOOST_FOREACH(sharedClique child, clique->children)
child->parent_ = typename Clique::weak_ptr();
BOOST_FOREACH(Key j, clique->conditional()->frontals()) {
nodes_.erase(j);
}
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::removePath(sharedClique clique, BayesNetType& bn, Cliques& orphans)
{
// base case is NULL, if so we do nothing and return empties above
if (clique) {
// remove the clique from orphans in case it has been added earlier
orphans.remove(clique);
// remove me
this->removeClique(clique);
// remove path above me
this->removePath(typename Clique::shared_ptr(clique->parent_.lock()), bn, orphans);
// add children to list of orphans (splice also removed them from clique->children_)
orphans.insert(orphans.begin(), clique->children.begin(), clique->children.end());
clique->children.clear();
bn.push_back(clique->conditional_);
}
}
/* ************************************************************************* */
template<class CLIQUE>
void BayesTree<CLIQUE>::removeTop(const std::vector<Key>& keys, BayesNetType& bn, Cliques& orphans)
{
// process each key of the new factor
BOOST_FOREACH(const Key& j, keys)
{
// get the clique
// TODO: Nodes will be searched again in removeClique
typename Nodes::const_iterator node = nodes_.find(j);
if(node != nodes_.end()) {
// remove path from clique to root
this->removePath(node->second, bn, orphans);
}
}
// Delete cachedShortcuts for each orphan subtree
//TODO: Consider Improving
BOOST_FOREACH(sharedClique& orphan, orphans)
orphan->deleteCachedShortcuts();
}
/* ************************************************************************* */
template<class CLIQUE>
typename BayesTree<CLIQUE>::Cliques BayesTree<CLIQUE>::removeSubtree(
const sharedClique& subtree)
{
// Result clique list
Cliques cliques;
cliques.push_back(subtree);
// Remove the first clique from its parents
if(!subtree->isRoot())
subtree->parent()->children.erase(std::find(
subtree->parent()->children.begin(), subtree->parent()->children.end(), subtree));
else
roots_.erase(std::find(roots_.begin(), roots_.end(), subtree));
// Add all subtree cliques and erase the children and parent of each
for(typename Cliques::iterator clique = cliques.begin(); clique != cliques.end(); ++clique)
{
// Add children
BOOST_FOREACH(const sharedClique& child, (*clique)->children) {
cliques.push_back(child); }
// Delete cached shortcuts
(*clique)->deleteCachedShortcutsNonRecursive();
// Remove this node from the nodes index
BOOST_FOREACH(Key j, (*clique)->conditional()->frontals()) {
nodes_.erase(j); }
// Erase the parent and children pointers
(*clique)->parent_.reset();
(*clique)->children.clear();
}
return cliques;
}
}
/// namespace gtsam

View File

@ -0,0 +1,62 @@
/* ----------------------------------------------------------------------------
* 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 BayesTree.cpp
* @brief Bayes Tree is a tree of cliques of a Bayes Chain
* @author Frank Dellaert
* @author Michael Kaess
* @author Viorela Ila
* @author Richard Roberts
*/
#include <gtsam/inference/BayesTree.h>
#include <boost/foreach.hpp>
namespace gtsam {
/* ************************************************************************* */
void BayesTreeCliqueStats::print(const std::string& s) const {
std::cout << s
<< "avg Conditional Size: " << avgConditionalSize << std::endl
<< "max Conditional Size: " << maxConditionalSize << std::endl
<< "avg Separator Size: " << avgSeparatorSize << std::endl
<< "max Separator Size: " << maxSeparatorSize << std::endl;
}
/* ************************************************************************* */
BayesTreeCliqueStats BayesTreeCliqueData::getStats() const
{
BayesTreeCliqueStats stats;
double sum = 0.0;
size_t max = 0;
BOOST_FOREACH(size_t s, conditionalSizes) {
sum += (double)s;
if(s > max) max = s;
}
stats.avgConditionalSize = sum / (double)conditionalSizes.size();
stats.maxConditionalSize = max;
sum = 0.0;
max = 1;
BOOST_FOREACH(size_t s, separatorSizes) {
sum += (double)s;
if(s > max) max = s;
}
stats.avgSeparatorSize = sum / (double)separatorSizes.size();
stats.maxSeparatorSize = max;
return stats;
}
}

View File

@ -1,418 +1,281 @@
/* ----------------------------------------------------------------------------
* 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 BayesTree.h
* @brief Bayes Tree is a tree of cliques of a Bayes Chain
* @author Frank Dellaert
*/
// \callgraph
#pragma once
#include <vector>
#include <stdexcept>
#include <deque>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/lexical_cast.hpp>
#include <gtsam/global_includes.h>
#include <gtsam/base/FastList.h>
#include <gtsam/inference/FactorGraph.h>
#include <gtsam/inference/BayesNet.h>
#include <gtsam/inference/BayesTreeCliqueBase.h>
#include <gtsam/inference/IndexConditional.h>
#include <gtsam/linear/VectorValues.h>
namespace gtsam {
// Forward declaration of BayesTreeClique which is defined below BayesTree in this file
template<class CONDITIONAL> struct BayesTreeClique;
/**
* Bayes tree
* @tparam CONDITIONAL The type of the conditional densities, i.e. the type of node in the underlying Bayes chain,
* which could be a ConditionalProbabilityTable, a GaussianConditional, or a SymbolicConditional.
* @tparam CLIQUE The type of the clique data structure, defaults to BayesTreeClique, normally do not change this
* as it is only used when developing special versions of BayesTree, e.g. for ISAM2.
*
* \addtogroup Multifrontal
* \nosubgrouping
*/
template<class CONDITIONAL, class CLIQUE=BayesTreeClique<CONDITIONAL> >
class BayesTree {
public:
typedef BayesTree<CONDITIONAL, CLIQUE> This;
typedef boost::shared_ptr<BayesTree<CONDITIONAL, CLIQUE> > shared_ptr;
typedef boost::shared_ptr<CONDITIONAL> sharedConditional;
typedef boost::shared_ptr<BayesNet<CONDITIONAL> > sharedBayesNet;
typedef CONDITIONAL ConditionalType;
typedef typename CONDITIONAL::FactorType FactorType;
typedef typename FactorGraph<FactorType>::Eliminate Eliminate;
typedef CLIQUE Clique; ///< The clique type, normally BayesTreeClique
// typedef for shared pointers to cliques
typedef boost::shared_ptr<Clique> sharedClique;
// A convenience class for a list of shared cliques
struct Cliques : public FastList<sharedClique> {
void print(const std::string& s = "Cliques",
const IndexFormatter& indexFormatter = DefaultIndexFormatter) const;
bool equals(const Cliques& other, double tol = 1e-9) const;
};
/** clique statistics */
struct CliqueStats {
double avgConditionalSize;
std::size_t maxConditionalSize;
double avgSeparatorSize;
std::size_t maxSeparatorSize;
void print(const std::string& s = "") const ;
};
/** store all the sizes */
struct CliqueData {
std::vector<std::size_t> conditionalSizes;
std::vector<std::size_t> separatorSizes;
CliqueStats getStats() const;
};
/** Map from indices to Clique */
typedef std::vector<sharedClique> Nodes;
protected:
/** Map from indices to Clique */
Nodes nodes_;
/** Root clique */
sharedClique root_;
public:
/// @name Standard Constructors
/// @{
/** Create an empty Bayes Tree */
BayesTree() {}
/** Create a Bayes Tree from a Bayes Net (requires CONDITIONAL is IndexConditional *or* CONDITIONAL::Combine) */
explicit BayesTree(const BayesNet<CONDITIONAL>& bayesNet);
/** Copy constructor */
BayesTree(const This& other);
/** Assignment operator */
This& operator=(const This& other);
/// @}
/// @name Advanced Constructors
/// @{
/**
* Create a Bayes Tree from a Bayes Net and some subtrees. The Bayes net corresponds to the
* new root clique and the subtrees are connected to the root clique.
*/
BayesTree(const BayesNet<CONDITIONAL>& bayesNet, std::list<BayesTree<CONDITIONAL,CLIQUE> > subtrees);
/** Destructor */
virtual ~BayesTree() {}
/// @}
/// @name Testable
/// @{
/** check equality */
bool equals(const BayesTree<CONDITIONAL,CLIQUE>& other, double tol = 1e-9) const;
/** print */
void print(const std::string& s = "",
const IndexFormatter& indexFormatter = DefaultIndexFormatter ) const;
/// @}
/// @name Standard Interface
/// @{
/**
* Find parent clique of a conditional. It will look at all parents and
* return the one with the lowest index in the ordering.
*/
template<class CONTAINER>
Index findParentClique(const CONTAINER& parents) const;
/** number of cliques */
inline size_t size() const {
if(root_)
return root_->treeSize();
else
return 0;
}
/** number of nodes */
inline size_t nrNodes() const { return nodes_.size(); }
/** Check if there are any cliques in the tree */
inline bool empty() const {
return nodes_.empty();
}
/** return nodes */
const Nodes& nodes() const { return nodes_; }
/** return root clique */
const sharedClique& root() const { return root_; }
/** find the clique that contains the variable with Index j */
inline sharedClique operator[](Index j) const {
return nodes_.at(j);
}
/** alternate syntax for matlab: find the clique that contains the variable with Index j */
inline sharedClique clique(Index j) const {
return nodes_.at(j);
}
/** Gather data on all cliques */
CliqueData getCliqueData() const;
/** Collect number of cliques with cached separator marginals */
size_t numCachedSeparatorMarginals() const;
/** return marginal on any variable */
typename FactorType::shared_ptr marginalFactor(Index j, Eliminate function) const;
/**
* return marginal on any variable, as a Bayes Net
* NOTE: this function calls marginal, and then eliminates it into a Bayes Net
* This is more expensive than the above function
*/
typename BayesNet<CONDITIONAL>::shared_ptr marginalBayesNet(Index j, Eliminate function) const;
/**
* return joint on two variables
* Limitation: can only calculate joint if cliques are disjoint or one of them is root
*/
typename FactorGraph<FactorType>::shared_ptr joint(Index j1, Index j2, Eliminate function) const;
/**
* return joint on two variables as a BayesNet
* Limitation: can only calculate joint if cliques are disjoint or one of them is root
*/
typename BayesNet<CONDITIONAL>::shared_ptr jointBayesNet(Index j1, Index j2, Eliminate function) const;
/**
* Read only with side effects
*/
/** saves the Tree to a text file in GraphViz format */
void saveGraph(const std::string& s, const IndexFormatter& indexFormatter = DefaultIndexFormatter ) const;
/// @}
/// @name Advanced Interface
/// @{
/** Access the root clique (non-const version) */
sharedClique& root() { return root_; }
/** Access the nodes (non-cost version) */
Nodes& nodes() { return nodes_; }
/** Remove all nodes */
void clear();
/** Clear all shortcut caches - use before timing on marginal calculation to avoid residual cache data */
void deleteCachedShortcuts() {
root_->deleteCachedShortcuts();
}
/** Apply a permutation to the full tree - also updates the nodes structure */
void permuteWithInverse(const Permutation& inversePermutation);
/**
* Remove path from clique to root and return that path as factors
* plus a list of orphaned subtree roots. Used in removeTop below.
*/
void removePath(sharedClique clique, BayesNet<CONDITIONAL>& bn, Cliques& orphans);
/**
* Given a list of indices, turn "contaminated" part of the tree back into a factor graph.
* Factors and orphans are added to the in/out arguments.
*/
template<class CONTAINER>
void removeTop(const CONTAINER& indices, BayesNet<CONDITIONAL>& bn, Cliques& orphans);
/**
* Remove the requested subtree. */
Cliques removeSubtree(const sharedClique& subtree);
/**
* Hang a new subtree off of the existing tree. This finds the appropriate
* parent clique for the subtree (which may be the root), and updates the
* nodes index with the new cliques in the subtree. None of the frontal
* variables in the subtree may appear in the separators of the existing
* BayesTree.
*/
void insert(const sharedClique& subtree);
/** Insert a new conditional
* This function only applies for Symbolic case with IndexCondtional,
* We make it static so that it won't be compiled in GaussianConditional case.
* */
static void insert(BayesTree<CONDITIONAL,CLIQUE>& bayesTree, const sharedConditional& conditional);
/**
* Insert a new clique corresponding to the given Bayes net.
* It is the caller's responsibility to decide whether the given Bayes net is a valid clique,
* i.e. all the variables (frontal and separator) are connected
*/
sharedClique insert(const sharedConditional& clique,
std::list<sharedClique>& children, bool isRootClique = false);
/**
* Create a clone of this object as a shared pointer
* Necessary for inheritance in matlab interface
*/
virtual shared_ptr clone() const {
return shared_ptr(new This(*this));
}
protected:
/** private helper method for saving the Tree to a text file in GraphViz format */
void saveGraph(std::ostream &s, sharedClique clique, const IndexFormatter& indexFormatter,
int parentnum = 0) const;
/** Gather data on a single clique */
void getCliqueData(CliqueData& stats, sharedClique clique) const;
/** remove a clique: warning, can result in a forest */
void removeClique(sharedClique clique);
/** add a clique (top down) */
sharedClique addClique(const sharedConditional& conditional, const sharedClique& parent_clique = sharedClique());
/** add a clique (top down) */
void addClique(const sharedClique& clique, const sharedClique& parent_clique = sharedClique());
/** add a clique (bottom up) */
sharedClique addClique(const sharedConditional& conditional, std::list<sharedClique>& child_cliques);
/**
* Add a conditional to the front of a clique, i.e. a conditional whose
* parents are already in the clique or its separators. This function does
* not check for this condition, it just updates the data structures.
*/
static void addToCliqueFront(BayesTree<CONDITIONAL,CLIQUE>& bayesTree,
const sharedConditional& conditional, const sharedClique& clique);
/** Fill the nodes index for a subtree */
void fillNodesIndex(const sharedClique& subtree);
/** Helper function to build a non-symbolic tree (e.g. Gaussian) using a
* symbolic tree, used in the BT(BN) constructor.
*/
void recursiveTreeBuild(const boost::shared_ptr<BayesTreeClique<IndexConditional> >& symbolic,
const std::vector<boost::shared_ptr<CONDITIONAL> >& conditionals,
const typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique& parent);
private:
/** deep copy to another tree */
void cloneTo(This& newTree) const;
/** deep copy to another tree */
void cloneTo(This& newTree, const sharedClique& subtree, const sharedClique& parent) const;
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(nodes_);
ar & BOOST_SERIALIZATION_NVP(root_);
}
/// @}
}; // BayesTree
/* ************************************************************************* */
template<class CONDITIONAL, class CLIQUE>
void _BayesTree_dim_adder(
std::vector<size_t>& dims,
const typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique& clique) {
if(clique) {
// Add dims from this clique
for(typename CONDITIONAL::const_iterator it = (*clique)->beginFrontals(); it != (*clique)->endFrontals(); ++it)
dims[*it] = (*clique)->dim(it);
// Traverse children
typedef typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique sharedClique;
BOOST_FOREACH(const sharedClique& child, clique->children()) {
_BayesTree_dim_adder<CONDITIONAL,CLIQUE>(dims, child);
}
}
}
/* ************************************************************************* */
template<class CONDITIONAL,class CLIQUE>
boost::shared_ptr<VectorValues> allocateVectorValues(const BayesTree<CONDITIONAL,CLIQUE>& bt) {
std::vector<size_t> dimensions(bt.nodes().size(), 0);
_BayesTree_dim_adder<CONDITIONAL,CLIQUE>(dimensions, bt.root());
return boost::shared_ptr<VectorValues>(new VectorValues(dimensions));
}
/* ************************************************************************* */
/**
* A Clique in the tree is an incomplete Bayes net: the variables
* in the Bayes net are the frontal nodes, and the variables conditioned
* on are the separator. We also have pointers up and down the tree.
*
* Since our Conditional class already handles multiple frontal variables,
* this Clique contains exactly 1 conditional.
*
* This is the default clique type in a BayesTree, but some algorithms, like
* iSAM2 (see ISAM2Clique), use a different clique type in order to store
* extra data along with the clique.
*/
template<class CONDITIONAL>
struct BayesTreeClique : public BayesTreeCliqueBase<BayesTreeClique<CONDITIONAL>, CONDITIONAL> {
public:
typedef CONDITIONAL ConditionalType;
typedef BayesTreeClique<CONDITIONAL> This;
typedef BayesTreeCliqueBase<This, CONDITIONAL> Base;
typedef boost::shared_ptr<This> shared_ptr;
typedef boost::weak_ptr<This> weak_ptr;
BayesTreeClique() {}
BayesTreeClique(const typename ConditionalType::shared_ptr& conditional) : Base(conditional) {}
BayesTreeClique(const std::pair<typename ConditionalType::shared_ptr, typename ConditionalType::FactorType::shared_ptr>& result) : Base(result) {}
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
}
};
} /// namespace gtsam
#include <gtsam/inference/BayesTree-inl.h>
#include <gtsam/inference/BayesTreeCliqueBase-inl.h>
/* ----------------------------------------------------------------------------
* 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 BayesTree.h
* @brief Bayes Tree is a tree of cliques of a Bayes Chain
* @author Frank Dellaert
*/
// \callgraph
#pragma once
#include <vector>
#include <string>
#include <gtsam/base/types.h>
#include <gtsam/base/FastList.h>
#include <gtsam/base/FastMap.h>
namespace gtsam {
// Forward declarations
template<class FACTOR> class FactorGraph;
template<class BAYESTREE, class GRAPH> class JunctionTree;
/* ************************************************************************* */
/** clique statistics */
struct GTSAM_EXPORT BayesTreeCliqueStats {
double avgConditionalSize;
std::size_t maxConditionalSize;
double avgSeparatorSize;
std::size_t maxSeparatorSize;
void print(const std::string& s = "") const ;
};
/** store all the sizes */
struct GTSAM_EXPORT BayesTreeCliqueData {
std::vector<std::size_t> conditionalSizes;
std::vector<std::size_t> separatorSizes;
BayesTreeCliqueStats getStats() const;
};
/* ************************************************************************* */
/**
* Bayes tree
* @tparam CONDITIONAL The type of the conditional densities, i.e. the type of node in the underlying Bayes chain,
* which could be a ConditionalProbabilityTable, a GaussianConditional, or a SymbolicConditional.
* @tparam CLIQUE The type of the clique data structure, defaults to BayesTreeClique, normally do not change this
* as it is only used when developing special versions of BayesTree, e.g. for ISAM2.
*
* \addtogroup Multifrontal
* \nosubgrouping
*/
template<class CLIQUE>
class BayesTree
{
protected:
typedef BayesTree<CLIQUE> This;
typedef boost::shared_ptr<This> shared_ptr;
public:
typedef CLIQUE Clique; ///< The clique type, normally BayesTreeClique
typedef boost::shared_ptr<Clique> sharedClique; ///< Shared pointer to a clique
typedef Clique Node; ///< Synonym for Clique (TODO: remove)
typedef sharedClique sharedNode; ///< Synonym for sharedClique (TODO: remove)
typedef typename CLIQUE::ConditionalType ConditionalType;
typedef boost::shared_ptr<ConditionalType> sharedConditional;
typedef typename CLIQUE::BayesNetType BayesNetType;
typedef boost::shared_ptr<BayesNetType> sharedBayesNet;
typedef typename CLIQUE::FactorType FactorType;
typedef boost::shared_ptr<FactorType> sharedFactor;
typedef typename CLIQUE::FactorGraphType FactorGraphType;
typedef boost::shared_ptr<FactorGraphType> sharedFactorGraph;
typedef typename FactorGraphType::Eliminate Eliminate;
typedef typename CLIQUE::EliminationTraitsType EliminationTraitsType;
/** A convenience class for a list of shared cliques */
typedef FastList<sharedClique> Cliques;
/** Map from keys to Clique */
typedef FastMap<Key, sharedClique> Nodes;
protected:
/** Map from indices to Clique */
Nodes nodes_;
/** Root cliques */
std::vector<sharedClique> roots_;
/// @name Standard Constructors
/// @{
/** Create an empty Bayes Tree */
BayesTree() {}
/** Copy constructor */
BayesTree(const This& other);
/// @}
/** Assignment operator */
This& operator=(const This& other);
/// @name Testable
/// @{
/** check equality */
bool equals(const This& other, double tol = 1e-9) const;
public:
/** print */
void print(const std::string& s = "",
const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
/// @}
/// @name Standard Interface
/// @{
/** number of cliques */
size_t size() const;
/** Check if there are any cliques in the tree */
inline bool empty() const {
return nodes_.empty();
}
/** return nodes */
const Nodes& nodes() const { return nodes_; }
/** Access node by variable */
const sharedNode operator[](Key j) const { return nodes_.at(j); }
/** return root cliques */
const std::vector<sharedClique>& roots() const { return roots_; }
/** alternate syntax for matlab: find the clique that contains the variable with Index j */
const sharedClique& clique(Key j) const {
typename Nodes::const_iterator c = nodes_.find(j);
if(c == nodes_.end())
throw std::out_of_range("Requested the BayesTree clique for a key that is not in the BayesTree");
else
return c->second;
}
/** Gather data on all cliques */
BayesTreeCliqueData getCliqueData() const;
/** Collect number of cliques with cached separator marginals */
size_t numCachedSeparatorMarginals() const;
/** Return marginal on any variable. Note that this actually returns a conditional, for which a
* solution may be directly obtained by calling .solve() on the returned object.
* Alternatively, it may be directly used as its factor base class. For example, for Gaussian
* systems, this returns a GaussianConditional, which inherits from JacobianFactor and
* GaussianFactor. */
sharedConditional marginalFactor(Key j, const Eliminate& function = EliminationTraitsType::DefaultEliminate) const;
/**
* return joint on two variables
* Limitation: can only calculate joint if cliques are disjoint or one of them is root
*/
sharedFactorGraph joint(Index j1, Index j2, const Eliminate& function = EliminationTraitsType::DefaultEliminate) const;
/**
* return joint on two variables as a BayesNet
* Limitation: can only calculate joint if cliques are disjoint or one of them is root
*/
sharedBayesNet jointBayesNet(Index j1, Index j2, const Eliminate& function = EliminationTraitsType::DefaultEliminate) const;
/**
* Read only with side effects
*/
/** saves the Tree to a text file in GraphViz format */
void saveGraph(const std::string& s, const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
/// @}
/// @name Advanced Interface
/// @{
/**
* Find parent clique of a conditional. It will look at all parents and
* return the one with the lowest index in the ordering.
*/
template<class CONTAINER>
Index findParentClique(const CONTAINER& parents) const;
/** Remove all nodes */
void clear();
/** Clear all shortcut caches - use before timing on marginal calculation to avoid residual cache data */
void deleteCachedShortcuts();
/**
* Remove path from clique to root and return that path as factors
* plus a list of orphaned subtree roots. Used in removeTop below.
*/
void removePath(sharedClique clique, BayesNetType& bn, Cliques& orphans);
/**
* Given a list of indices, turn "contaminated" part of the tree back into a factor graph.
* Factors and orphans are added to the in/out arguments.
*/
void removeTop(const std::vector<Key>& keys, BayesNetType& bn, Cliques& orphans);
/**
* Remove the requested subtree. */
Cliques removeSubtree(const sharedClique& subtree);
/** Insert a new subtree with known parent clique. This function does not check that the
* specified parent is the correct parent. This function updates all of the internal data
* structures associated with adding a subtree, such as populating the nodes index. */
void insertRoot(const sharedClique& subtree);
/** add a clique (top down) */
void addClique(const sharedClique& clique, const sharedClique& parent_clique = sharedClique());
/** Add all cliques in this BayesTree to the specified factor graph */
void addFactorsToGraph(FactorGraph<FactorType>& graph) const;
protected:
/** private helper method for saving the Tree to a text file in GraphViz format */
void saveGraph(std::ostream &s, sharedClique clique, const KeyFormatter& keyFormatter,
int parentnum = 0) const;
/** Gather data on a single clique */
void getCliqueData(BayesTreeCliqueData& stats, sharedClique clique) const;
/** remove a clique: warning, can result in a forest */
void removeClique(sharedClique clique);
/** Fill the nodes index for a subtree */
void fillNodesIndex(const sharedClique& subtree);
// Friend JunctionTree because it directly fills roots and nodes index.
template<class BAYESRTEE, class GRAPH> friend class JunctionTree;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(nodes_);
ar & BOOST_SERIALIZATION_NVP(roots_);
}
/// @}
}; // BayesTree
/* ************************************************************************* */
template<class CLIQUE>
class BayesTreeOrphanWrapper : public CLIQUE::ConditionalType
{
public:
typedef CLIQUE CliqueType;
typedef typename CLIQUE::ConditionalType Base;
boost::shared_ptr<CliqueType> clique;
BayesTreeOrphanWrapper(const boost::shared_ptr<CliqueType>& clique) :
clique(clique)
{
// Store parent keys in our base type factor so that eliminating those parent keys will pull
// this subtree into the elimination.
this->keys_.assign(clique->conditional()->beginParents(), clique->conditional()->endParents());
}
};
} /// namespace gtsam

View File

@ -1,301 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 BayesTreeCliqueBase-inl.h
* @brief Base class for cliques of a BayesTree
* @author Richard Roberts and Frank Dellaert
*/
#pragma once
#include <gtsam/inference/GenericSequentialSolver.h>
#include <boost/foreach.hpp>
namespace gtsam {
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
void BayesTreeCliqueBase<DERIVED, CONDITIONAL>::assertInvariants() const {
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
std::vector<Index> BayesTreeCliqueBase<DERIVED, CONDITIONAL>::separator_setminus_B(
derived_ptr B) const {
sharedConditional p_F_S = this->conditional();
std::vector<Index> &indicesB = B->conditional()->keys();
std::vector<Index> S_setminus_B;
std::set_difference(p_F_S->beginParents(), p_F_S->endParents(), //
indicesB.begin(), indicesB.end(), back_inserter(S_setminus_B));
return S_setminus_B;
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
std::vector<Index> BayesTreeCliqueBase<DERIVED, CONDITIONAL>::shortcut_indices(
derived_ptr B, const FactorGraph<FactorType>& p_Cp_B) const
{
gttic(shortcut_indices);
std::set<Index> allKeys = p_Cp_B.keys();
std::vector<Index> &indicesB = B->conditional()->keys();
std::vector<Index> S_setminus_B = separator_setminus_B(B); // TODO, get as argument?
std::vector<Index> keep;
// keep = S\B intersect allKeys
std::set_intersection(S_setminus_B.begin(), S_setminus_B.end(), //
allKeys.begin(), allKeys.end(), back_inserter(keep));
// keep += B intersect allKeys
std::set_intersection(indicesB.begin(), indicesB.end(), //
allKeys.begin(), allKeys.end(), back_inserter(keep));
// BOOST_FOREACH(Index j, keep) std::cout << j << " "; std::cout << std::endl;
return keep;
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
BayesTreeCliqueBase<DERIVED, CONDITIONAL>::BayesTreeCliqueBase(
const sharedConditional& conditional) :
conditional_(conditional) {
assertInvariants();
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
BayesTreeCliqueBase<DERIVED, CONDITIONAL>::BayesTreeCliqueBase(
const std::pair<sharedConditional,
boost::shared_ptr<typename ConditionalType::FactorType> >& result) :
conditional_(result.first) {
assertInvariants();
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
void BayesTreeCliqueBase<DERIVED, CONDITIONAL>::print(const std::string& s,
const IndexFormatter& indexFormatter) const {
conditional_->print(s, indexFormatter);
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
size_t BayesTreeCliqueBase<DERIVED, CONDITIONAL>::treeSize() const {
size_t size = 1;
BOOST_FOREACH(const derived_ptr& child, children_)
size += child->treeSize();
return size;
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
size_t BayesTreeCliqueBase<DERIVED, CONDITIONAL>::numCachedSeparatorMarginals() const {
if (!cachedSeparatorMarginal_)
return 0;
size_t subtree_count = 1;
BOOST_FOREACH(const derived_ptr& child, children_)
subtree_count += child->numCachedSeparatorMarginals();
return subtree_count;
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
void BayesTreeCliqueBase<DERIVED, CONDITIONAL>::printTree(
const std::string& indent, const IndexFormatter& indexFormatter) const {
asDerived(this)->print(indent, indexFormatter);
BOOST_FOREACH(const derived_ptr& child, children_)
child->printTree(indent + " ", indexFormatter);
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
void BayesTreeCliqueBase<DERIVED, CONDITIONAL>::permuteWithInverse(
const Permutation& inversePermutation) {
conditional_->permuteWithInverse(inversePermutation);
BOOST_FOREACH(const derived_ptr& child, children_) {
child->permuteWithInverse(inversePermutation);
}
assertInvariants();
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
bool BayesTreeCliqueBase<DERIVED, CONDITIONAL>::reduceSeparatorWithInverse(
const internal::Reduction& inverseReduction)
{
bool changed = conditional_->reduceSeparatorWithInverse(inverseReduction);
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
if(!changed) {
BOOST_FOREACH(const derived_ptr& child, children_) {
assert(child->reduceSeparatorWithInverse(inverseReduction) == false); }
}
#endif
if(changed) {
BOOST_FOREACH(const derived_ptr& child, children_) {
(void) child->reduceSeparatorWithInverse(inverseReduction); }
}
assertInvariants();
return changed;
}
/* ************************************************************************* */
// The shortcut density is a conditional P(S|R) of the separator of this
// clique on the root. We can compute it recursively from the parent shortcut
// P(Sp|R) as \int P(Fp|Sp) P(Sp|R), where Fp are the frontal nodes in p
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
BayesNet<CONDITIONAL> BayesTreeCliqueBase<DERIVED, CONDITIONAL>::shortcut(
derived_ptr B, Eliminate function) const
{
gttic(BayesTreeCliqueBase_shortcut);
// We only calculate the shortcut when this clique is not B
// and when the S\B is not empty
std::vector<Index> S_setminus_B = separator_setminus_B(B);
if (B.get() != this && !S_setminus_B.empty()) {
// Obtain P(Cp||B) = P(Fp|Sp) * P(Sp||B) as a factor graph
derived_ptr parent(parent_.lock());
gttoc(BayesTreeCliqueBase_shortcut);
FactorGraph<FactorType> p_Cp_B(parent->shortcut(B, function)); // P(Sp||B)
gttic(BayesTreeCliqueBase_shortcut);
p_Cp_B.push_back(parent->conditional()->toFactor()); // P(Fp|Sp)
// Determine the variables we want to keepSet, S union B
std::vector<Index> keep = shortcut_indices(B, p_Cp_B);
// Reduce the variable indices to start at zero
gttic(Reduce);
const Permutation reduction = internal::createReducingPermutation(p_Cp_B.keys());
internal::Reduction inverseReduction = internal::Reduction::CreateAsInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, p_Cp_B) {
if(factor) factor->reduceWithInverse(inverseReduction); }
inverseReduction.applyInverse(keep);
gttoc(Reduce);
// Create solver that will marginalize for us
GenericSequentialSolver<FactorType> solver(p_Cp_B);
// Finally, we only want to have S\B variables in the Bayes net, so
size_t nrFrontals = S_setminus_B.size();
BayesNet<CONDITIONAL> result = *solver.conditionalBayesNet(keep, nrFrontals, function);
// Undo the reduction
gttic(Undo_Reduce);
BOOST_FOREACH(const typename boost::shared_ptr<FactorType>& factor, p_Cp_B) {
if (factor) factor->permuteWithInverse(reduction); }
result.permuteWithInverse(reduction);
gttoc(Undo_Reduce);
assertInvariants();
return result;
} else {
return BayesNet<CONDITIONAL>();
}
}
/* ************************************************************************* */
// separator marginal, uses separator marginal of parent recursively
// P(C) = P(F|S) P(S)
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
FactorGraph<typename BayesTreeCliqueBase<DERIVED, CONDITIONAL>::FactorType> BayesTreeCliqueBase<
DERIVED, CONDITIONAL>::separatorMarginal(derived_ptr R, Eliminate function) const
{
gttic(BayesTreeCliqueBase_separatorMarginal);
// Check if the Separator marginal was already calculated
if (!cachedSeparatorMarginal_) {
gttic(BayesTreeCliqueBase_separatorMarginal_cachemiss);
// If this is the root, there is no separator
if (R.get() == this) {
// we are root, return empty
FactorGraph<FactorType> empty;
cachedSeparatorMarginal_ = empty;
} else {
// Obtain P(S) = \int P(Cp) = \int P(Fp|Sp) P(Sp)
// initialize P(Cp) with the parent separator marginal
derived_ptr parent(parent_.lock());
gttoc(BayesTreeCliqueBase_separatorMarginal_cachemiss); // Flatten recursion in timing outline
gttoc(BayesTreeCliqueBase_separatorMarginal);
FactorGraph<FactorType> p_Cp(parent->separatorMarginal(R, function)); // P(Sp)
gttic(BayesTreeCliqueBase_separatorMarginal);
gttic(BayesTreeCliqueBase_separatorMarginal_cachemiss);
// now add the parent conditional
p_Cp.push_back(parent->conditional()->toFactor()); // P(Fp|Sp)
// Reduce the variable indices to start at zero
gttic(Reduce);
const Permutation reduction = internal::createReducingPermutation(p_Cp.keys());
internal::Reduction inverseReduction = internal::Reduction::CreateAsInverse(reduction);
BOOST_FOREACH(const boost::shared_ptr<FactorType>& factor, p_Cp) {
if(factor) factor->reduceWithInverse(inverseReduction); }
// The variables we want to keepSet are exactly the ones in S
sharedConditional p_F_S = this->conditional();
std::vector<Index> indicesS(p_F_S->beginParents(), p_F_S->endParents());
inverseReduction.applyInverse(indicesS);
gttoc(Reduce);
// Create solver that will marginalize for us
GenericSequentialSolver<FactorType> solver(p_Cp);
cachedSeparatorMarginal_ = *(solver.jointBayesNet(indicesS, function));
// Undo the reduction
gttic(Undo_Reduce);
BOOST_FOREACH(const typename boost::shared_ptr<FactorType>& factor, p_Cp) {
if (factor) factor->permuteWithInverse(reduction); }
BOOST_FOREACH(const typename boost::shared_ptr<FactorType>& factor, *cachedSeparatorMarginal_) {
if (factor) factor->permuteWithInverse(reduction); }
gttoc(Undo_Reduce);
}
} else {
gttic(BayesTreeCliqueBase_separatorMarginal_cachehit);
}
// return the shortcut P(S||B)
return *cachedSeparatorMarginal_; // return the cached version
}
/* ************************************************************************* */
// marginal2, uses separator marginal of parent recursively
// P(C) = P(F|S) P(S)
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
FactorGraph<typename BayesTreeCliqueBase<DERIVED, CONDITIONAL>::FactorType> BayesTreeCliqueBase<
DERIVED, CONDITIONAL>::marginal2(derived_ptr R, Eliminate function) const
{
gttic(BayesTreeCliqueBase_marginal2);
// initialize with separator marginal P(S)
FactorGraph<FactorType> p_C(this->separatorMarginal(R, function));
// add the conditional P(F|S)
p_C.push_back(this->conditional()->toFactor());
return p_C;
}
/* ************************************************************************* */
template<class DERIVED, class CONDITIONAL>
void BayesTreeCliqueBase<DERIVED, CONDITIONAL>::deleteCachedShortcuts() {
// When a shortcut is requested, all of the shortcuts between it and the
// root are also generated. So, if this clique's cached shortcut is set,
// recursively call over all child cliques. Otherwise, it is unnecessary.
if (cachedSeparatorMarginal_) {
BOOST_FOREACH(derived_ptr& child, children_) {
child->deleteCachedShortcuts();
}
//Delete CachedShortcut for this clique
cachedSeparatorMarginal_ = boost::none;
}
}
}

View File

@ -0,0 +1,217 @@
/* ----------------------------------------------------------------------------
* 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 BayesTreeCliqueBase-inst.h
* @brief Base class for cliques of a BayesTree
* @author Richard Roberts and Frank Dellaert
*/
#pragma once
#include <gtsam/inference/BayesTreeCliqueBase.h>
#include <gtsam/base/timing.h>
#include <boost/foreach.hpp>
namespace gtsam {
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
void BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::setEliminationResult(
const typename FactorGraphType::EliminationResult& eliminationResult)
{
conditional_ = eliminationResult.first;
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
bool BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::equals(
const DERIVED& other, double tol) const
{
return (!conditional_ && !other.conditional())
|| conditional_->equals(*other.conditional(), tol);
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
std::vector<Key>
BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::separator_setminus_B(const derived_ptr& B) const
{
FastSet<Key> p_F_S_parents(this->conditional()->beginParents(), this->conditional()->endParents());
FastSet<Key> indicesB(B->conditional()->begin(), B->conditional()->end());
std::vector<Key> S_setminus_B;
std::set_difference(p_F_S_parents.begin(), p_F_S_parents.end(),
indicesB.begin(), indicesB.end(), back_inserter(S_setminus_B));
return S_setminus_B;
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
std::vector<Key> BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::shortcut_indices(
const derived_ptr& B, const FactorGraphType& p_Cp_B) const
{
gttic(shortcut_indices);
FastSet<Key> allKeys = p_Cp_B.keys();
FastSet<Key> indicesB(B->conditional()->begin(), B->conditional()->end());
std::vector<Key> S_setminus_B = separator_setminus_B(B);
std::vector<Key> keep;
// keep = S\B intersect allKeys (S_setminus_B is already sorted)
std::set_intersection(S_setminus_B.begin(), S_setminus_B.end(), //
allKeys.begin(), allKeys.end(), back_inserter(keep));
// keep += B intersect allKeys
std::set_intersection(indicesB.begin(), indicesB.end(), //
allKeys.begin(), allKeys.end(), back_inserter(keep));
return keep;
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
void BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::print(
const std::string& s, const KeyFormatter& keyFormatter) const
{
conditional_->print(s, keyFormatter);
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
size_t BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::treeSize() const {
size_t size = 1;
BOOST_FOREACH(const derived_ptr& child, children)
size += child->treeSize();
return size;
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
size_t BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::numCachedSeparatorMarginals() const
{
if (!cachedSeparatorMarginal_)
return 0;
size_t subtree_count = 1;
BOOST_FOREACH(const derived_ptr& child, children)
subtree_count += child->numCachedSeparatorMarginals();
return subtree_count;
}
/* ************************************************************************* */
// The shortcut density is a conditional P(S|R) of the separator of this
// clique on the root. We can compute it recursively from the parent shortcut
// P(Sp|R) as \int P(Fp|Sp) P(Sp|R), where Fp are the frontal nodes in p
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
typename BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::BayesNetType
BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::shortcut(const derived_ptr& B, Eliminate function) const
{
gttic(BayesTreeCliqueBase_shortcut);
// We only calculate the shortcut when this clique is not B
// and when the S\B is not empty
std::vector<Key> S_setminus_B = separator_setminus_B(B);
if (!parent_.expired() /*(if we're not the root)*/ && !S_setminus_B.empty())
{
// Obtain P(Cp||B) = P(Fp|Sp) * P(Sp||B) as a factor graph
derived_ptr parent(parent_.lock());
gttoc(BayesTreeCliqueBase_shortcut);
FactorGraphType p_Cp_B(parent->shortcut(B, function)); // P(Sp||B)
gttic(BayesTreeCliqueBase_shortcut);
p_Cp_B += parent->conditional_; // P(Fp|Sp)
// Determine the variables we want to keepSet, S union B
std::vector<Key> keep = shortcut_indices(B, p_Cp_B);
// Marginalize out everything except S union B
boost::shared_ptr<FactorGraphType> p_S_B = p_Cp_B.marginal(keep, function);
return *p_S_B->eliminatePartialSequential(S_setminus_B, function).first;
}
else
{
return BayesNetType();
}
}
/* ************************************************************************* */
// separator marginal, uses separator marginal of parent recursively
// P(C) = P(F|S) P(S)
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
typename BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::FactorGraphType
BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::separatorMarginal(Eliminate function) const
{
gttic(BayesTreeCliqueBase_separatorMarginal);
// Check if the Separator marginal was already calculated
if (!cachedSeparatorMarginal_)
{
gttic(BayesTreeCliqueBase_separatorMarginal_cachemiss);
// If this is the root, there is no separator
if (parent_.expired() /*(if we're the root)*/)
{
// we are root, return empty
FactorGraphType empty;
cachedSeparatorMarginal_ = empty;
}
else
{
// Obtain P(S) = \int P(Cp) = \int P(Fp|Sp) P(Sp)
// initialize P(Cp) with the parent separator marginal
derived_ptr parent(parent_.lock());
gttoc(BayesTreeCliqueBase_separatorMarginal_cachemiss); // Flatten recursion in timing outline
gttoc(BayesTreeCliqueBase_separatorMarginal);
FactorGraphType p_Cp(parent->separatorMarginal(function)); // P(Sp)
gttic(BayesTreeCliqueBase_separatorMarginal);
gttic(BayesTreeCliqueBase_separatorMarginal_cachemiss);
// now add the parent conditional
p_Cp += parent->conditional_; // P(Fp|Sp)
// The variables we want to keepSet are exactly the ones in S
std::vector<Key> indicesS(this->conditional()->beginParents(), this->conditional()->endParents());
cachedSeparatorMarginal_ = *p_Cp.marginalMultifrontalBayesNet(Ordering(indicesS), boost::none, function);
}
}
// return the shortcut P(S||B)
return *cachedSeparatorMarginal_; // return the cached version
}
/* ************************************************************************* */
// marginal2, uses separator marginal of parent recursively
// P(C) = P(F|S) P(S)
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
typename BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::FactorGraphType
BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::marginal2(Eliminate function) const
{
gttic(BayesTreeCliqueBase_marginal2);
// initialize with separator marginal P(S)
FactorGraphType p_C = this->separatorMarginal(function);
// add the conditional P(F|S)
p_C += boost::shared_ptr<FactorType>(this->conditional_);
return p_C;
}
/* ************************************************************************* */
template<class DERIVED, class FACTORGRAPH>
void BayesTreeCliqueBase<DERIVED, FACTORGRAPH>::deleteCachedShortcuts() {
// When a shortcut is requested, all of the shortcuts between it and the
// root are also generated. So, if this clique's cached shortcut is set,
// recursively call over all child cliques. Otherwise, it is unnecessary.
if (cachedSeparatorMarginal_) {
BOOST_FOREACH(derived_ptr& child, children) {
child->deleteCachedShortcuts();
}
//Delete CachedShortcut for this clique
cachedSeparatorMarginal_ = boost::none;
}
}
}

View File

@ -1,273 +1,168 @@
/* ----------------------------------------------------------------------------
* 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 BayesTreeCliqueBase.h
* @brief Base class for cliques of a BayesTree
* @author Richard Roberts and Frank Dellaert
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/weak_ptr.hpp>
#include <gtsam/global_includes.h>
#include <gtsam/inference/FactorGraph.h>
#include <gtsam/inference/BayesNet.h>
namespace gtsam {
template<class CONDITIONAL, class CLIQUE> class BayesTree;
}
namespace gtsam {
/**
* This is the base class for BayesTree cliques. The default and standard
* derived type is BayesTreeClique, but some algorithms, like iSAM2, use a
* different clique type in order to store extra data along with the clique.
*
* This class is templated on the derived class (i.e. the curiously recursive
* template pattern). The advantage of this over using virtual classes is
* that it avoids the need for casting to get the derived type. This is
* possible because all cliques in a BayesTree are the same type - if they
* were not then we'd need a virtual class.
*
* @tparam DERIVED The derived clique type.
* @tparam CONDITIONAL The conditional type.
* \nosubgrouping
*/
template<class DERIVED, class CONDITIONAL>
struct BayesTreeCliqueBase {
public:
typedef BayesTreeCliqueBase<DERIVED, CONDITIONAL> This;
typedef DERIVED DerivedType;
typedef CONDITIONAL ConditionalType;
typedef boost::shared_ptr<ConditionalType> sharedConditional;
typedef boost::shared_ptr<This> shared_ptr;
typedef boost::weak_ptr<This> weak_ptr;
typedef boost::shared_ptr<DerivedType> derived_ptr;
typedef boost::weak_ptr<DerivedType> derived_weak_ptr;
typedef typename ConditionalType::FactorType FactorType;
typedef typename FactorGraph<FactorType>::Eliminate Eliminate;
protected:
/// @name Standard Constructors
/// @{
/** Default constructor */
BayesTreeCliqueBase() {}
/** Construct from a conditional, leaving parent and child pointers uninitialized */
BayesTreeCliqueBase(const sharedConditional& conditional);
/** Construct from an elimination result, which is a pair<CONDITIONAL,FACTOR> */
BayesTreeCliqueBase(
const std::pair<sharedConditional,
boost::shared_ptr<typename ConditionalType::FactorType> >& result);
/// @}
/// This stores the Cached separator margnal P(S)
mutable boost::optional<FactorGraph<FactorType> > cachedSeparatorMarginal_;
public:
sharedConditional conditional_;
derived_weak_ptr parent_;
FastList<derived_ptr> children_;
/// @name Testable
/// @{
/** check equality */
bool equals(const This& other, double tol = 1e-9) const {
return (!conditional_ && !other.conditional())
|| conditional_->equals(*other.conditional(), tol);
}
/** print this node */
void print(const std::string& s = "", const IndexFormatter& indexFormatter =
DefaultIndexFormatter) const;
/** print this node and entire subtree below it */
void printTree(const std::string& indent = "",
const IndexFormatter& indexFormatter = DefaultIndexFormatter) const;
/// @}
/// @name Standard Interface
/// @{
/** Access the conditional */
const sharedConditional& conditional() const {
return conditional_;
}
/** is this the root of a Bayes tree ? */
inline bool isRoot() const {
return parent_.expired();
}
/** The size of subtree rooted at this clique, i.e., nr of Cliques */
size_t treeSize() const;
/** Collect number of cliques with cached separator marginals */
size_t numCachedSeparatorMarginals() const;
/** The arrow operator accesses the conditional */
const ConditionalType* operator->() const {
return conditional_.get();
}
/** return the const reference of children */
const std::list<derived_ptr>& children() const {
return children_;
}
/** return a shared_ptr to the parent clique */
derived_ptr parent() const {
return parent_.lock();
}
/// @}
/// @name Advanced Interface
/// @{
/** The arrow operator accesses the conditional */
ConditionalType* operator->() {
return conditional_.get();
}
/** return the reference of children non-const version*/
FastList<derived_ptr>& children() {
return children_;
}
/** Construct shared_ptr from a conditional, leaving parent and child pointers uninitialized */
static derived_ptr Create(const sharedConditional& conditional) {
return boost::make_shared<DerivedType>(conditional);
}
/** Construct shared_ptr from a FactorGraph<FACTOR>::EliminationResult. In this class
* the conditional part is kept and the factor part is ignored, but in derived clique
* types, such as ISAM2Clique, the factor part is kept as a cached factor.
* @param result An elimination result, which is a pair<CONDITIONAL,FACTOR>
*/
static derived_ptr Create(const std::pair<sharedConditional,
boost::shared_ptr<typename ConditionalType::FactorType> >& result) {
return boost::make_shared<DerivedType>(result);
}
/** Returns a new clique containing a copy of the conditional but without
* the parent and child clique pointers.
*/
derived_ptr clone() const {
return Create(sharedConditional(new ConditionalType(*conditional_)));
}
/** Permute the variables in the whole subtree rooted at this clique */
void permuteWithInverse(const Permutation& inversePermutation);
/** Permute variables when they only appear in the separators. In this
* case the running intersection property will be used to prevent always
* traversing the whole tree. Returns whether any separator variables in
* this subtree were reordered.
*/
bool reduceSeparatorWithInverse(const internal::Reduction& inverseReduction);
/** return the conditional P(S|Root) on the separator given the root */
BayesNet<ConditionalType> shortcut(derived_ptr root, Eliminate function) const;
/** return the marginal P(S) on the separator */
FactorGraph<FactorType> separatorMarginal(derived_ptr root, Eliminate function) const;
/** return the marginal P(C) of the clique, using marginal caching */
FactorGraph<FactorType> marginal2(derived_ptr root, Eliminate function) const;
/**
* This deletes the cached shortcuts of all cliques (subtree) below this clique.
* This is performed when the bayes tree is modified.
*/
void deleteCachedShortcuts();
const boost::optional<FactorGraph<FactorType> >& cachedSeparatorMarginal() const {
return cachedSeparatorMarginal_; }
friend class BayesTree<ConditionalType, DerivedType> ;
protected:
/// assert invariants that have to hold in a clique
void assertInvariants() const;
/// Calculate set \f$ S \setminus B \f$ for shortcut calculations
std::vector<Index> separator_setminus_B(derived_ptr B) const;
/// Calculate set \f$ S_p \cap B \f$ for shortcut calculations
std::vector<Index> parent_separator_intersection_B(derived_ptr B) const;
/**
* Determine variable indices to keep in recursive separator shortcut calculation
* The factor graph p_Cp_B has keys from the parent clique Cp and from B.
* But we only keep the variables not in S union B.
*/
std::vector<Index> shortcut_indices(derived_ptr B,
const FactorGraph<FactorType>& p_Cp_B) const;
/** Non-recursive delete cached shortcuts and marginals - internal only. */
void deleteCachedShortcutsNonRecursive() { cachedSeparatorMarginal_ = boost::none; }
private:
/**
* Cliques cannot be copied except by the clone() method, which does not
* copy the parent and child pointers.
*/
BayesTreeCliqueBase(const This& other) {
assert(false);
}
/** Cliques cannot be copied except by the clone() method, which does not
* copy the parent and child pointers.
*/
This& operator=(const This& other) {
assert(false);
return *this;
}
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(conditional_);
ar & BOOST_SERIALIZATION_NVP(parent_);
ar & BOOST_SERIALIZATION_NVP(children_);
}
/// @}
};
// \struct Clique
template<class DERIVED, class CONDITIONAL>
const DERIVED* asDerived(
const BayesTreeCliqueBase<DERIVED, CONDITIONAL>* base) {
return static_cast<const DERIVED*>(base);
}
template<class DERIVED, class CONDITIONAL>
DERIVED* asDerived(BayesTreeCliqueBase<DERIVED, CONDITIONAL>* base) {
return static_cast<DERIVED*>(base);
}
}
/* ----------------------------------------------------------------------------
* 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 BayesTreeCliqueBase.h
* @brief Base class for cliques of a BayesTree
* @author Richard Roberts and Frank Dellaert
*/
#pragma once
#include <boost/optional.hpp>
#include <gtsam/base/types.h>
namespace gtsam {
// Forward declarations
template<class CLIQUE> class BayesTree;
template<class GRAPH> struct EliminationTraits;
/**
* This is the base class for BayesTree cliques. The default and standard derived type is
* BayesTreeClique, but some algorithms, like iSAM2, use a different clique type in order to store
* extra data along with the clique.
*
* This class is templated on the derived class (i.e. the curiously recursive template pattern).
* The advantage of this over using virtual classes is that it avoids the need for casting to get
* the derived type. This is possible because all cliques in a BayesTree are the same type - if
* they were not then we'd need a virtual class.
*
* @tparam DERIVED The derived clique type.
* @tparam CONDITIONAL The conditional type.
* \nosubgrouping */
template<class DERIVED, class FACTORGRAPH>
class BayesTreeCliqueBase
{
private:
typedef BayesTreeCliqueBase<DERIVED, FACTORGRAPH> This;
typedef DERIVED DerivedType;
typedef EliminationTraits<FACTORGRAPH> EliminationTraitsType;
typedef boost::shared_ptr<This> shared_ptr;
typedef boost::weak_ptr<This> weak_ptr;
typedef boost::shared_ptr<DerivedType> derived_ptr;
typedef boost::weak_ptr<DerivedType> derived_weak_ptr;
public:
typedef FACTORGRAPH FactorGraphType;
typedef typename EliminationTraitsType::BayesNetType BayesNetType;
typedef typename BayesNetType::ConditionalType ConditionalType;
typedef boost::shared_ptr<ConditionalType> sharedConditional;
typedef typename FactorGraphType::FactorType FactorType;
typedef typename FactorGraphType::Eliminate Eliminate;
protected:
/// @name Standard Constructors
/// @{
/** Default constructor */
BayesTreeCliqueBase() {}
/** Construct from a conditional, leaving parent and child pointers uninitialized */
BayesTreeCliqueBase(const sharedConditional& conditional) : conditional_(conditional) {}
/// @}
/// This stores the Cached separator margnal P(S)
mutable boost::optional<FactorGraphType> cachedSeparatorMarginal_;
public:
sharedConditional conditional_;
derived_weak_ptr parent_;
std::vector<derived_ptr> children;
/// Fill the elimination result produced during elimination. Here this just stores the
/// conditional and ignores the remaining factor, but this is overridden in ISAM2Clique
/// to also cache the remaining factor.
void setEliminationResult(const typename FactorGraphType::EliminationResult& eliminationResult);
/// @name Testable
/// @{
/** check equality */
bool equals(const DERIVED& other, double tol = 1e-9) const;
/** print this node */
void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
/// @}
/// @name Standard Interface
/// @{
/** Access the conditional */
const sharedConditional& conditional() const { return conditional_; }
/** is this the root of a Bayes tree ? */
inline bool isRoot() const { return parent_.expired(); }
/** The size of subtree rooted at this clique, i.e., nr of Cliques */
size_t treeSize() const;
/** Collect number of cliques with cached separator marginals */
size_t numCachedSeparatorMarginals() const;
/** return a shared_ptr to the parent clique */
derived_ptr parent() const { return parent_.lock(); }
/// @}
/// @name Advanced Interface
/// @{
/** return the conditional P(S|Root) on the separator given the root */
BayesNetType shortcut(const derived_ptr& root, Eliminate function = EliminationTraitsType::DefaultEliminate) const;
/** return the marginal P(S) on the separator */
FactorGraphType separatorMarginal(Eliminate function = EliminationTraitsType::DefaultEliminate) const;
/** return the marginal P(C) of the clique, using marginal caching */
FactorGraphType marginal2(Eliminate function = EliminationTraitsType::DefaultEliminate) const;
/**
* This deletes the cached shortcuts of all cliques (subtree) below this clique.
* This is performed when the bayes tree is modified.
*/
void deleteCachedShortcuts();
const boost::optional<FactorGraphType>& cachedSeparatorMarginal() const {
return cachedSeparatorMarginal_; }
friend class BayesTree<DerivedType>;
protected:
/// Calculate set \f$ S \setminus B \f$ for shortcut calculations
std::vector<Key> separator_setminus_B(const derived_ptr& B) const;
/** Determine variable indices to keep in recursive separator shortcut calculation The factor
* graph p_Cp_B has keys from the parent clique Cp and from B. But we only keep the variables
* not in S union B. */
std::vector<Key> shortcut_indices(const derived_ptr& B, const FactorGraphType& p_Cp_B) const;
/** Non-recursive delete cached shortcuts and marginals - internal only. */
void deleteCachedShortcutsNonRecursive() { cachedSeparatorMarginal_ = boost::none; }
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(conditional_);
ar & BOOST_SERIALIZATION_NVP(parent_);
ar & BOOST_SERIALIZATION_NVP(children);
}
/// @}
};
}

View File

@ -1,112 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 ClusterTree-inl.h
* @date July 13, 2010
* @author Kai Ni
* @author Frank Dellaert
* @brief: Collects factorgraph fragments defined on variable clusters, arranged in a tree
*/
#pragma once
#include <iostream>
#include <boost/foreach.hpp>
#include <gtsam/inference/ClusterTree.h>
namespace gtsam {
/* ************************************************************************* *
* Cluster
* ************************************************************************* */
template<class FG>
template<class Iterator>
ClusterTree<FG>::Cluster::Cluster(const FG& fg, Index key, Iterator firstSeparator, Iterator lastSeparator) :
FG(fg), frontal(1, key), separator(firstSeparator, lastSeparator) {}
/* ************************************************************************* */
template<class FG>
template<typename FRONTALIT, typename SEPARATORIT>
ClusterTree<FG>::Cluster::Cluster(
const FG& fg, FRONTALIT firstFrontal, FRONTALIT lastFrontal, SEPARATORIT firstSeparator, SEPARATORIT lastSeparator) :
FG(fg), frontal(firstFrontal, lastFrontal), separator(firstSeparator, lastSeparator) {}
/* ************************************************************************* */
template<class FG>
template<typename FRONTALIT, typename SEPARATORIT>
ClusterTree<FG>::Cluster::Cluster(
FRONTALIT firstFrontal, FRONTALIT lastFrontal, SEPARATORIT firstSeparator, SEPARATORIT lastSeparator) :
frontal(firstFrontal, lastFrontal), separator(firstSeparator, lastSeparator) {}
/* ************************************************************************* */
template<class FG>
void ClusterTree<FG>::Cluster::addChild(typename ClusterTree<FG>::Cluster::shared_ptr child) {
children_.push_back(child);
}
/* ************************************************************************* */
template<class FG>
bool ClusterTree<FG>::Cluster::equals(const Cluster& other) const {
if (frontal != other.frontal) return false;
if (separator != other.separator) return false;
if (children_.size() != other.children_.size()) return false;
typename std::list<shared_ptr>::const_iterator it1 = children_.begin();
typename std::list<shared_ptr>::const_iterator it2 = other.children_.begin();
for (; it1 != children_.end(); it1++, it2++)
if (!(*it1)->equals(**it2)) return false;
return true;
}
/* ************************************************************************* */
template<class FG>
void ClusterTree<FG>::Cluster::print(const std::string& indent,
const IndexFormatter& formatter) const {
std::cout << indent;
BOOST_FOREACH(const Index key, frontal)
std::cout << formatter(key) << " ";
std::cout << ": ";
BOOST_FOREACH(const Index key, separator)
std::cout << key << " ";
std::cout << std::endl;
}
/* ************************************************************************* */
template<class FG>
void ClusterTree<FG>::Cluster::printTree(const std::string& indent,
const IndexFormatter& formatter) const {
print(indent, formatter);
BOOST_FOREACH(const shared_ptr& child, children_)
child->printTree(indent + " ", formatter);
}
/* ************************************************************************* *
* ClusterTree
* ************************************************************************* */
template<class FG>
void ClusterTree<FG>::print(const std::string& str,
const IndexFormatter& formatter) const {
std::cout << str << std::endl;
if (root_) root_->printTree("", formatter);
}
/* ************************************************************************* */
template<class FG>
bool ClusterTree<FG>::equals(const ClusterTree<FG>& other, double tol) const {
if (!root_ && !other.root_) return true;
if (!root_ || !other.root_) return false;
return root_->equals(*other.root_);
}
} //namespace gtsam

View File

@ -1,141 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 ClusterTree.h
* @date July 13, 2010
* @author Kai Ni
* @author Frank Dellaert
* @brief: Collects factorgraph fragments defined on variable clusters, arranged in a tree
*/
#pragma once
#include <list>
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <gtsam/global_includes.h>
namespace gtsam {
/**
* A cluster-tree is associated with a factor graph and is defined as in Koller-Friedman:
* each node k represents a subset \f$ C_k \sub X \f$, and the tree is family preserving, in that
* each factor \f$ f_i \f$ is associated with a single cluster and \f$ scope(f_i) \sub C_k \f$.
* \nosubgrouping
*/
template <class FG>
class ClusterTree {
public:
// Access to factor types
typedef typename FG::KeyType KeyType;
protected:
// the class for subgraphs that also include the pointers to the parents and two children
class Cluster : public FG {
public:
typedef typename boost::shared_ptr<Cluster> shared_ptr;
typedef typename boost::weak_ptr<Cluster> weak_ptr;
const std::vector<Index> frontal; // the frontal variables
const std::vector<Index> separator; // the separator variables
protected:
weak_ptr parent_; // the parent cluster
std::list<shared_ptr> children_; // the child clusters
const typename FG::sharedFactor eliminated_; // the eliminated factor to pass on to the parent
public:
/// Construct empty clique
Cluster() {}
/** Create a node with a single frontal variable */
template<typename Iterator>
Cluster(const FG& fg, Index key, Iterator firstSeparator, Iterator lastSeparator);
/** Create a node with several frontal variables */
template<typename FRONTALIT, typename SEPARATORIT>
Cluster(const FG& fg, FRONTALIT firstFrontal, FRONTALIT lastFrontal, SEPARATORIT firstSeparator, SEPARATORIT lastSeparator);
/** Create a node with several frontal variables */
template<typename FRONTALIT, typename SEPARATORIT>
Cluster(FRONTALIT firstFrontal, FRONTALIT lastFrontal, SEPARATORIT firstSeparator, SEPARATORIT lastSeparator);
/// print
void print(const std::string& indent, const IndexFormatter& formatter = DefaultIndexFormatter) const;
/// print the enire tree
void printTree(const std::string& indent, const IndexFormatter& formatter = DefaultIndexFormatter) const;
/// check equality
bool equals(const Cluster& other) const;
/// get a reference to the children
const std::list<shared_ptr>& children() const { return children_; }
/// add a child
void addChild(shared_ptr child);
/// get or set the parent
weak_ptr& parent() { return parent_; }
};
/// @name Advanced Interface
/// @{
/// typedef for shared pointers to clusters
typedef typename Cluster::shared_ptr sharedCluster;
/// Root cluster
sharedCluster root_;
public:
/// @}
/// @name Standard Constructors
/// @{
/// constructor of empty tree
ClusterTree() {}
/// @}
/// @name Standard Interface
/// @{
/// return the root cluster
sharedCluster root() const { return root_; }
/// @}
/// @name Testable
/// @{
/// print the object
void print(const std::string& str="", const IndexFormatter& formatter = DefaultIndexFormatter) const;
/** check equality */
bool equals(const ClusterTree<FG>& other, double tol = 1e-9) const;
/// @}
}; // ClusterTree
} // namespace gtsam
#include <gtsam/inference/ClusterTree-inl.h>

View File

@ -0,0 +1,48 @@
/* ----------------------------------------------------------------------------
* 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 Conditional.h
* @brief Base class for conditional densities
* @author Frank Dellaert
*/
// \callgraph
#pragma once
#include <boost/foreach.hpp>
#include <iostream>
#include <gtsam/inference/Conditional.h>
namespace gtsam {
/* ************************************************************************* */
template<class FACTOR, class DERIVEDFACTOR>
void Conditional<FACTOR,DERIVEDFACTOR>::print(const std::string& s, const KeyFormatter& formatter) const {
std::cout << s << " P(";
BOOST_FOREACH(Key key, frontals())
std::cout << " " << formatter(key);
if (nrParents() > 0)
std::cout << " |";
BOOST_FOREACH(Key parent, parents())
std::cout << " " << formatter(parent);
std::cout << ")" << std::endl;
}
/* ************************************************************************* */
template<class FACTOR, class DERIVEDFACTOR>
bool Conditional<FACTOR,DERIVEDFACTOR>::equals(const This& c, double tol) const
{
return nrFrontals_ == c.nrFrontals_;
}
}

View File

@ -1,266 +1,147 @@
/* ----------------------------------------------------------------------------
* 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 Conditional.h
* @brief Base class for conditional densities
* @author Frank Dellaert
*/
// \callgraph
#pragma once
#include <gtsam/inference/Factor.h>
#include <boost/range.hpp>
#include <iostream>
#include <list>
namespace gtsam {
/**
* Base class for conditional densities, templated on KEY type. This class
* provides storage for the keys involved in a conditional, and iterators and
* access to the frontal and separator keys.
*
* Derived classes *must* redefine the Factor and shared_ptr typedefs to refer
* to the associated factor type and shared_ptr type of the derived class. See
* IndexConditional and GaussianConditional for examples.
* \nosubgrouping
*/
template<typename KEY>
class Conditional: public gtsam::Factor<KEY> {
private:
/** Create keys by adding key in front */
template<typename ITERATOR>
static std::vector<KEY> MakeKeys(KEY key, ITERATOR firstParent,
ITERATOR lastParent) {
std::vector<KeyType> keys((lastParent - firstParent) + 1);
std::copy(firstParent, lastParent, keys.begin() + 1);
keys[0] = key;
return keys;
}
protected:
/** The first nFrontal variables are frontal and the rest are parents. */
size_t nrFrontals_;
// Calls the base class assertInvariants, which checks for unique keys
void assertInvariants() const {
Factor<KEY>::assertInvariants();
}
public:
typedef KEY KeyType;
typedef Conditional<KeyType> This;
typedef Factor<KeyType> Base;
/**
* Typedef to the factor type that produces this conditional and that this
* conditional can be converted to using a factor constructor. Derived
* classes must redefine this.
*/
typedef gtsam::Factor<KeyType> FactorType;
/** A shared_ptr to this class. Derived classes must redefine this. */
typedef boost::shared_ptr<This> shared_ptr;
/** Iterator over keys */
typedef typename FactorType::iterator iterator;
/** Const iterator over keys */
typedef typename FactorType::const_iterator const_iterator;
/** View of the frontal keys (call frontals()) */
typedef boost::iterator_range<const_iterator> Frontals;
/** View of the separator keys (call parents()) */
typedef boost::iterator_range<const_iterator> Parents;
/// @name Standard Constructors
/// @{
/** Empty Constructor to make serialization possible */
Conditional() :
nrFrontals_(0) {
assertInvariants();
}
/** No parents */
Conditional(KeyType key) :
FactorType(key), nrFrontals_(1) {
assertInvariants();
}
/** Single parent */
Conditional(KeyType key, KeyType parent) :
FactorType(key, parent), nrFrontals_(1) {
assertInvariants();
}
/** Two parents */
Conditional(KeyType key, KeyType parent1, KeyType parent2) :
FactorType(key, parent1, parent2), nrFrontals_(1) {
assertInvariants();
}
/** Three parents */
Conditional(KeyType key, KeyType parent1, KeyType parent2, KeyType parent3) :
FactorType(key, parent1, parent2, parent3), nrFrontals_(1) {
assertInvariants();
}
/// @}
/// @name Advanced Constructors
/// @{
/** Constructor from a frontal variable and a vector of parents */
Conditional(KeyType key, const std::vector<KeyType>& parents) :
FactorType(MakeKeys(key, parents.begin(), parents.end())), nrFrontals_(
1) {
assertInvariants();
}
/** Constructor from keys and nr of frontal variables */
Conditional(const std::vector<Index>& keys, size_t nrFrontals) :
FactorType(keys), nrFrontals_(nrFrontals) {
assertInvariants();
}
/** Constructor from keys and nr of frontal variables */
Conditional(const std::list<Index>& keys, size_t nrFrontals) :
FactorType(keys.begin(),keys.end()), nrFrontals_(nrFrontals) {
assertInvariants();
}
/// @}
/// @name Testable
/// @{
/** print with optional formatter */
void print(const std::string& s = "Conditional",
const IndexFormatter& formatter = DefaultIndexFormatter) const;
/** check equality */
template<class DERIVED>
bool equals(const DERIVED& c, double tol = 1e-9) const {
return nrFrontals_ == c.nrFrontals_ && FactorType::equals(c, tol);
}
/// @}
/// @name Standard Interface
/// @{
/** return the number of frontals */
size_t nrFrontals() const {
return nrFrontals_;
}
/** return the number of parents */
size_t nrParents() const {
return FactorType::size() - nrFrontals_;
}
/** Special accessor when there is only one frontal variable. */
KeyType firstFrontalKey() const {
assert(nrFrontals_>0);
return FactorType::front();
}
KeyType lastFrontalKey() const {
assert(nrFrontals_>0);
return *(endFrontals() - 1);
}
/** 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());
}
/** Iterators over frontal and parent variables. */
const_iterator beginFrontals() const {
return FactorType::begin();
} ///<TODO: comment
const_iterator endFrontals() const {
return FactorType::begin() + nrFrontals_;
} ///<TODO: comment
const_iterator beginParents() const {
return FactorType::begin() + nrFrontals_;
} ///<TODO: comment
const_iterator endParents() const {
return FactorType::end();
} ///<TODO: comment
/// @}
/// @name Advanced Interface
/// @{
/** Mutable iterators and accessors */
iterator beginFrontals() {
return FactorType::begin();
} ///<TODO: comment
iterator endFrontals() {
return FactorType::begin() + nrFrontals_;
} ///<TODO: comment
iterator beginParents() {
return FactorType::begin() + nrFrontals_;
} ///<TODO: comment
iterator endParents() {
return FactorType::end();
} ///<TODO: comment
///TODO: comment
boost::iterator_range<iterator> frontals() {
return boost::make_iterator_range(beginFrontals(), endFrontals());
}
///TODO: comment
boost::iterator_range<iterator> parents() {
return boost::make_iterator_range(beginParents(), endParents());
}
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
ar & BOOST_SERIALIZATION_NVP(nrFrontals_);
}
/// @}
};
/* ************************************************************************* */
template<typename KEY>
void Conditional<KEY>::print(const std::string& s,
const IndexFormatter& formatter) const {
std::cout << s << " P(";
BOOST_FOREACH(KeyType key, frontals())
std::cout << " " << formatter(key);
if (nrParents() > 0)
std::cout << " |";
BOOST_FOREACH(KeyType parent, parents())
std::cout << " " << formatter(parent);
std::cout << ")" << std::endl;
}
} // gtsam
/* ----------------------------------------------------------------------------
* 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 Conditional.h
* @brief Base class for conditional densities
* @author Frank Dellaert
*/
// \callgraph
#pragma once
#include <boost/range.hpp>
#include <gtsam/inference/Key.h>
namespace gtsam {
/**
* Base class for conditional densities, templated on KEY type. This class
* provides storage for the keys involved in a conditional, and iterators and
* access to the frontal and separator keys.
*
* Derived classes *must* redefine the Factor and shared_ptr typedefs to refer
* to the associated factor type and shared_ptr type of the derived class. See
* IndexConditional and GaussianConditional for examples.
* \nosubgrouping
*/
template<class FACTOR, class DERIVEDCONDITIONAL>
class Conditional
{
protected:
/** The first nrFrontal variables are frontal and the rest are parents. */
size_t nrFrontals_;
private:
/// Typedef to this class
typedef Conditional<FACTOR,DERIVEDCONDITIONAL> This;
public:
/** View of the frontal keys (call frontals()) */
typedef boost::iterator_range<typename FACTOR::const_iterator> Frontals;
/** View of the separator keys (call parents()) */
typedef boost::iterator_range<typename FACTOR::const_iterator> Parents;
protected:
/// @name Standard Constructors
/// @{
/** Empty Constructor to make serialization possible */
Conditional() : nrFrontals_(0) {}
/** Constructor */
Conditional(size_t nrFrontals) : nrFrontals_(nrFrontals) {}
/// @}
/// @name Testable
/// @{
/** print with optional formatter */
void print(const std::string& s = "Conditional", const KeyFormatter& formatter = DefaultKeyFormatter) const;
/** check equality */
bool equals(const This& c, double tol = 1e-9) const;
/// @}
public:
/// @name Standard Interface
/// @{
/** return the number of frontals */
size_t nrFrontals() const { return nrFrontals_; }
/** return the number of parents */
size_t nrParents() const { return asFactor().size() - nrFrontals_; }
/** Convenience function to get the first frontal key */
Key firstFrontalKey() const {
if(nrFrontals_ > 0)
return asFactor().front();
else
throw std::invalid_argument("Requested Conditional::firstFrontalKey from a conditional with zero frontal keys");
}
/** 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()); }
/** Iterator pointing to first frontal key. */
typename FACTOR::const_iterator beginFrontals() const { return asFactor().begin(); }
/** Iterator pointing past the last frontal key. */
typename FACTOR::const_iterator endFrontals() const { return asFactor().begin() + nrFrontals_; }
/** Iterator pointing to the first parent key. */
typename FACTOR::const_iterator beginParents() const { return endFrontals(); }
/** Iterator pointing past the last parent key. */
typename FACTOR::const_iterator endParents() const { return asFactor().end(); }
/// @}
/// @name Advanced Interface
/// @{
/** Mutable iterator pointing to first frontal key. */
typename FACTOR::iterator beginFrontals() { return asFactor().begin(); }
/** Mutable iterator pointing past the last frontal key. */
typename FACTOR::iterator endFrontals() { return asFactor().begin() + nrFrontals_; }
/** Mutable iterator pointing to the first parent key. */
typename FACTOR::iterator beginParents() { return asFactor().begin() + nrFrontals_; }
/** Mutable iterator pointing past the last parent key. */
typename FACTOR::iterator endParents() { return asFactor().end(); }
private:
// Cast to factor type (non-const) (casts down to derived conditional type, then up to factor type)
FACTOR& asFactor() { return static_cast<FACTOR&>(static_cast<DERIVEDCONDITIONAL&>(*this)); }
// Cast to derived type (const) (casts down to derived conditional type, then up to factor type)
const FACTOR& asFactor() const { return static_cast<const FACTOR&>(static_cast<const DERIVEDCONDITIONAL&>(*this)); }
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(nrFrontals_);
}
/// @}
};
} // gtsam

View File

@ -0,0 +1,298 @@
/* ----------------------------------------------------------------------------
* 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 EliminateableFactorGraph.h
* @brief Variable elimination algorithms for factor graphs
* @author Richard Roberts
* @date Apr 21, 2013
*/
#pragma once
#include <gtsam/inference/EliminateableFactorGraph.h>
#include <gtsam/inference/inferenceExceptions.h>
namespace gtsam {
/* ************************************************************************* */
template<class FACTORGRAPH>
boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesNetType>
EliminateableFactorGraph<FACTORGRAPH>::eliminateSequential(
OptionalOrdering ordering, const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(ordering && variableIndex) {
gttic(eliminateSequential);
// Do elimination
std::pair<boost::shared_ptr<BayesNetType>, boost::shared_ptr<FactorGraphType> > result
= EliminationTreeType(asDerived(), *variableIndex, *ordering).eliminate(function);
// If any factors are remaining, the ordering was incomplete
if(!result.second->empty())
throw InconsistentEliminationRequested();
// Return the Bayes net
return result.first;
}
else if(!variableIndex) {
// If no VariableIndex provided, compute one and call this function again IMPORTANT: we check
// for no variable index first so that it's always computed if we need to call COLAMD because
// no Ordering is provided.
return eliminateSequential(ordering, function, VariableIndex(asDerived()));
}
else /*if(!ordering)*/ {
// If no Ordering provided, compute one and call this function again. We are guaranteed to
// have a VariableIndex already here because we computed one if needed in the previous 'else'
// block.
return eliminateSequential(Ordering::COLAMD(*variableIndex), function);
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesTreeType>
EliminateableFactorGraph<FACTORGRAPH>::eliminateMultifrontal(
OptionalOrdering ordering, const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(ordering && variableIndex) {
gttic(eliminateMultifrontal);
// Do elimination with given ordering
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> > result
= JunctionTreeType(EliminationTreeType(asDerived(), *variableIndex, *ordering)).eliminate(function);
// If any factors are remaining, the ordering was incomplete
if(!result.second->empty())
throw InconsistentEliminationRequested();
// Return the Bayes tree
return result.first;
}
else if(!variableIndex) {
// If no VariableIndex provided, compute one and call this function again IMPORTANT: we check
// for no variable index first so that it's always computed if we need to call COLAMD because
// no Ordering is provided.
return eliminateMultifrontal(ordering, function, VariableIndex(asDerived()));
}
else /*if(!ordering)*/ {
// If no Ordering provided, compute one and call this function again. We are guaranteed to
// have a VariableIndex already here because we computed one if needed in the previous 'else'
// block.
return eliminateMultifrontal(Ordering::COLAMD(*variableIndex), function);
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
std::pair<boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesNetType>, boost::shared_ptr<FACTORGRAPH> >
EliminateableFactorGraph<FACTORGRAPH>::eliminatePartialSequential(
const Ordering& ordering, const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex) {
gttic(eliminatePartialSequential);
// Do elimination
return EliminationTreeType(asDerived(), *variableIndex, ordering).eliminate(function);
} else {
// If no variable index is provided, compute one and call this function again
return eliminatePartialSequential(ordering, function, VariableIndex(asDerived()));
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
std::pair<boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesNetType>, boost::shared_ptr<FACTORGRAPH> >
EliminateableFactorGraph<FACTORGRAPH>::eliminatePartialSequential(
const std::vector<Key>& variables, const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex) {
gttic(eliminatePartialSequential);
// Compute full ordering
Ordering fullOrdering = Ordering::COLAMDConstrainedFirst(*variableIndex, variables);
// Split off the part of the ordering for the variables being eliminated
Ordering ordering(fullOrdering.begin(), fullOrdering.begin() + variables.size());
return eliminatePartialSequential(ordering, function, variableIndex);
} else {
// If no variable index is provided, compute one and call this function again
return eliminatePartialSequential(variables, function, VariableIndex(asDerived()));
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
std::pair<boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesTreeType>, boost::shared_ptr<FACTORGRAPH> >
EliminateableFactorGraph<FACTORGRAPH>::eliminatePartialMultifrontal(
const Ordering& ordering, const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex) {
gttic(eliminatePartialMultifrontal);
// Do elimination
return JunctionTreeType(EliminationTreeType(asDerived(), *variableIndex, ordering)).eliminate(function);
} else {
// If no variable index is provided, compute one and call this function again
return eliminatePartialMultifrontal(ordering, function, VariableIndex(asDerived()));
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
std::pair<boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesTreeType>, boost::shared_ptr<FACTORGRAPH> >
EliminateableFactorGraph<FACTORGRAPH>::eliminatePartialMultifrontal(
const std::vector<Key>& variables, const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex) {
gttic(eliminatePartialMultifrontal);
// Compute full ordering
Ordering fullOrdering = Ordering::COLAMDConstrainedFirst(*variableIndex, variables);
// Split off the part of the ordering for the variables being eliminated
Ordering ordering(fullOrdering.begin(), fullOrdering.begin() + variables.size());
return eliminatePartialMultifrontal(ordering, function, variableIndex);
} else {
// If no variable index is provided, compute one and call this function again
return eliminatePartialMultifrontal(variables, function, VariableIndex(asDerived()));
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesNetType>
EliminateableFactorGraph<FACTORGRAPH>::marginalMultifrontalBayesNet(
boost::variant<const Ordering&, const std::vector<Key>&> variables,
OptionalOrdering marginalizedVariableOrdering,
const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex)
{
if(marginalizedVariableOrdering)
{
gttic(marginalMultifrontalBayesNet);
// An ordering was provided for the marginalized variables, so we can first eliminate them
// in the order requested.
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> > eliminated =
eliminatePartialMultifrontal(*marginalizedVariableOrdering, function, *variableIndex);
if(const Ordering* varsAsOrdering = boost::get<const Ordering&>(&variables))
{
// An ordering was also provided for the unmarginalized variables, so we can also
// eliminate them in the order requested.
return eliminated.second->eliminateSequential(*varsAsOrdering, function);
}
else
{
// No ordering was provided for the unmarginalized variables, so order them with COLAMD.
return eliminated.second->eliminateSequential(boost::none, function);
}
}
else
{
// No ordering was provided for the marginalized variables, so order them using constrained
// COLAMD.
bool unmarginalizedAreOrdered = (boost::get<const Ordering&>(&variables) != 0);
const std::vector<Key>* variablesOrOrdering =
unmarginalizedAreOrdered ?
boost::get<const Ordering&>(&variables) : boost::get<const std::vector<Key>&>(&variables);
Ordering totalOrdering =
Ordering::COLAMDConstrainedLast(*variableIndex, *variablesOrOrdering, unmarginalizedAreOrdered);
// Split up ordering
const size_t nVars = variablesOrOrdering->size();
Ordering marginalizationOrdering(totalOrdering.begin(), totalOrdering.end() - nVars);
Ordering marginalVarsOrdering(totalOrdering.end() - nVars, totalOrdering.end());
// Call this function again with the computed orderings
return marginalMultifrontalBayesNet(marginalVarsOrdering, marginalizationOrdering, function, *variableIndex);
}
} else {
// If no variable index is provided, compute one and call this function again
return marginalMultifrontalBayesNet(variables, marginalizedVariableOrdering, function, VariableIndex(asDerived()));
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
boost::shared_ptr<typename EliminateableFactorGraph<FACTORGRAPH>::BayesTreeType>
EliminateableFactorGraph<FACTORGRAPH>::marginalMultifrontalBayesTree(
boost::variant<const Ordering&, const std::vector<Key>&> variables,
OptionalOrdering marginalizedVariableOrdering,
const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex)
{
if(marginalizedVariableOrdering)
{
gttic(marginalMultifrontalBayesTree);
// An ordering was provided for the marginalized variables, so we can first eliminate them
// in the order requested.
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> > eliminated =
eliminatePartialMultifrontal(*marginalizedVariableOrdering, function, *variableIndex);
if(const Ordering* varsAsOrdering = boost::get<const Ordering&>(&variables))
{
// An ordering was also provided for the unmarginalized variables, so we can also
// eliminate them in the order requested.
return eliminated.second->eliminateMultifrontal(*varsAsOrdering, function);
}
else
{
// No ordering was provided for the unmarginalized variables, so order them with COLAMD.
return eliminated.second->eliminateMultifrontal(boost::none, function);
}
}
else
{
// No ordering was provided for the marginalized variables, so order them using constrained
// COLAMD.
bool unmarginalizedAreOrdered = (boost::get<const Ordering&>(&variables) != 0);
const std::vector<Key>* variablesOrOrdering =
unmarginalizedAreOrdered ?
boost::get<const Ordering&>(&variables) : boost::get<const std::vector<Key>&>(&variables);
Ordering totalOrdering =
Ordering::COLAMDConstrainedLast(*variableIndex, *variablesOrOrdering, unmarginalizedAreOrdered);
// Split up ordering
const size_t nVars = variablesOrOrdering->size();
Ordering marginalizationOrdering(totalOrdering.begin(), totalOrdering.end() - nVars);
Ordering marginalVarsOrdering(totalOrdering.end() - nVars, totalOrdering.end());
// Call this function again with the computed orderings
return marginalMultifrontalBayesTree(marginalVarsOrdering, marginalizationOrdering, function, *variableIndex);
}
} else {
// If no variable index is provided, compute one and call this function again
return marginalMultifrontalBayesTree(variables, marginalizedVariableOrdering, function, VariableIndex(asDerived()));
}
}
/* ************************************************************************* */
template<class FACTORGRAPH>
boost::shared_ptr<FACTORGRAPH>
EliminateableFactorGraph<FACTORGRAPH>::marginal(
const std::vector<Key>& variables,
const Eliminate& function, OptionalVariableIndex variableIndex) const
{
if(variableIndex)
{
// Compute a total ordering for all variables
Ordering totalOrdering = Ordering::COLAMDConstrainedLast(*variableIndex, variables);
// Split out the part for the marginalized variables
Ordering marginalizationOrdering(totalOrdering.begin(), totalOrdering.end() - variables.size());
// Eliminate and return the remaining factor graph
return eliminatePartialMultifrontal(marginalizationOrdering, function, *variableIndex).second;
}
else
{
// If no variable index is provided, compute one and call this function again
return marginal(variables, function, VariableIndex(asDerived()));
}
}
}

View File

@ -0,0 +1,236 @@
/* ----------------------------------------------------------------------------
* 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 EliminateableFactorGraph.h
* @brief Variable elimination algorithms for factor graphs
* @author Richard Roberts
* @date Apr 21, 2013
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/variant.hpp>
#include <boost/optional.hpp>
#include <gtsam/inference/Ordering.h>
#include <gtsam/inference/VariableIndex.h>
namespace gtsam {
/// Traits class for eliminateable factor graphs, specifies the types that result from
/// elimination, etc. This must be defined for each factor graph that inherits from
/// EliminateableFactorGraph.
template<class GRAPH>
struct EliminationTraits
{
// Template for deriving:
// typedef MyFactor FactorType; ///< Type of factors in factor graph (e.g. GaussianFactor)
// typedef MyFactorGraphType FactorGraphType; ///< Type of the factor graph (e.g. GaussianFactorGraph)
// typedef MyConditional ConditionalType; ///< Type of conditionals from elimination (e.g. GaussianConditional)
// typedef MyBayesNet BayesNetType; ///< Type of Bayes net from sequential elimination (e.g. GaussianBayesNet)
// typedef MyEliminationTree EliminationTreeType; ///< Type of elimination tree (e.g. GaussianEliminationTree)
// typedef MyBayesTree BayesTreeType; ///< Type of Bayes tree (e.g. GaussianBayesTree)
// typedef MyJunctionTree JunctionTreeType; ///< Type of Junction tree (e.g. GaussianJunctionTree)
// static pair<shared_ptr<ConditionalType>, shared_ptr<FactorType>
// DefaultEliminate(
// const MyFactorGraph& factors, const Ordering& keys); ///< The default dense elimination function
};
/** EliminateableFactorGraph is a base class for factor graphs that contains elimination
* algorithms. Any factor graph holding eliminateable factors can derive from this class to
* expose functions for computing marginals, conditional marginals, doing multifrontal and
* sequential elimination, etc. */
template<class FACTORGRAPH>
class EliminateableFactorGraph
{
private:
typedef EliminateableFactorGraph<FACTORGRAPH> This; ///< Typedef to this class.
typedef FACTORGRAPH FactorGraphType; ///< Typedef to factor graph type
// Base factor type stored in this graph (private because derived classes will get this from
// their FactorGraph base class)
typedef typename EliminationTraits<FactorGraphType>::FactorType _FactorType;
public:
/// Typedef to the specific EliminationTraits for this graph
typedef EliminationTraits<FactorGraphType> EliminationTraitsType;
/// Conditional type stored in the Bayes net produced by elimination
typedef typename EliminationTraitsType::ConditionalType ConditionalType;
/// Bayes net type produced by sequential elimination
typedef typename EliminationTraitsType::BayesNetType BayesNetType;
/// Elimination tree type that can do sequential elimination of this graph
typedef typename EliminationTraitsType::EliminationTreeType EliminationTreeType;
/// Bayes tree type produced by multifrontal elimination
typedef typename EliminationTraitsType::BayesTreeType BayesTreeType;
/// Junction tree type that can do multifrontal elimination of this graph
typedef typename EliminationTraitsType::JunctionTreeType JunctionTreeType;
/// The pair of conditional and remaining factor produced by a single dense elimination step on
/// a subgraph.
typedef std::pair<boost::shared_ptr<ConditionalType>, boost::shared_ptr<_FactorType> > EliminationResult;
/// The function type that does a single dense elimination step on a subgraph.
typedef boost::function<EliminationResult(const FactorGraphType&, const Ordering&)> Eliminate;
/// Typedef for an optional ordering as an argument to elimination functions
typedef boost::optional<const Ordering&> OptionalOrdering;
/// Typedef for an optional variable index as an argument to elimination functions
typedef boost::optional<const VariableIndex&> OptionalVariableIndex;
/** Do sequential elimination of all variables to produce a Bayes net. If an ordering is not
* provided, the ordering provided by COLAMD will be used.
*
* <b> Example - Full Cholesky elimination in COLAMD order: </b>
* \code
* boost::shared_ptr<GaussianBayesNet> result = graph.eliminateSequential(EliminateCholesky);
* \endcode
*
* <b> Example - Full QR elimination in specified order:
* \code
* boost::shared_ptr<GaussianBayesNet> result = graph.eliminateSequential(EliminateQR, myOrdering);
* \endcode
*
* <b> Example - Reusing an existing VariableIndex to improve performance, and using COLAMD ordering: </b>
* \code
* VariableIndex varIndex(graph); // Build variable index
* Data data = otherFunctionUsingVariableIndex(graph, varIndex); // Other code that uses variable index
* boost::shared_ptr<GaussianBayesNet> result = graph.eliminateSequential(EliminateQR, boost::none, varIndex);
* \endcode
* */
boost::shared_ptr<BayesNetType> eliminateSequential(
OptionalOrdering ordering = boost::none,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Do multifrontal elimination of all variables to produce a Bayes tree. If an ordering is not
* provided, the ordering provided by COLAMD will be used.
*
* <b> Example - Full Cholesky elimination in COLAMD order: </b>
* \code
* boost::shared_ptr<GaussianBayesTree> result = graph.eliminateMultifrontal(EliminateCholesky);
* \endcode
*
* <b> Example - Full QR elimination in specified order:
* \code
* boost::shared_ptr<GaussianBayesTree> result = graph.eliminateMultifrontal(EliminateQR, myOrdering);
* \endcode
*
* <b> Example - Reusing an existing VariableIndex to improve performance, and using COLAMD ordering: </b>
* \code
* VariableIndex varIndex(graph); // Build variable index
* Data data = otherFunctionUsingVariableIndex(graph, varIndex); // Other code that uses variable index
* boost::shared_ptr<GaussianBayesTree> result = graph.eliminateMultifrontal(EliminateQR, boost::none, varIndex);
* \endcode
* */
boost::shared_ptr<BayesTreeType> eliminateMultifrontal(
OptionalOrdering ordering = boost::none,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Do sequential elimination of some variables, in \c ordering provided, to produce a Bayes net
* and a remaining factor graph. This computes the factorization \f$ p(X) = p(A|B) p(B) \f$,
* where \f$ A = \f$ \c variables, \f$ X \f$ is all the variables in the factor graph, and \f$
* B = X\backslash A \f$. */
std::pair<boost::shared_ptr<BayesNetType>, boost::shared_ptr<FactorGraphType> >
eliminatePartialSequential(
const Ordering& ordering,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Do sequential elimination of the given \c variables in an ordering computed by COLAMD to
* produce a Bayes net and a remaining factor graph. This computes the factorization \f$ p(X)
* = p(A|B) p(B) \f$, where \f$ A = \f$ \c variables, \f$ X \f$ is all the variables in the
* factor graph, and \f$ B = X\backslash A \f$. */
std::pair<boost::shared_ptr<BayesNetType>, boost::shared_ptr<FactorGraphType> >
eliminatePartialSequential(
const std::vector<Key>& variables,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Do multifrontal elimination of some variables, in \c ordering provided, to produce a Bayes
* tree and a remaining factor graph. This computes the factorization \f$ p(X) = p(A|B) p(B)
* \f$, where \f$ A = \f$ \c variables, \f$ X \f$ is all the variables in the factor graph, and
* \f$ B = X\backslash A \f$. */
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> >
eliminatePartialMultifrontal(
const Ordering& ordering,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Do multifrontal elimination of the given \c variables in an ordering computed by COLAMD to
* produce a Bayes net and a remaining factor graph. This computes the factorization \f$ p(X)
* = p(A|B) p(B) \f$, where \f$ A = \f$ \c variables, \f$ X \f$ is all the variables in the
* factor graph, and \f$ B = X\backslash A \f$. */
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> >
eliminatePartialMultifrontal(
const std::vector<Key>& variables,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Compute the marginal of the requested variables and return the result as a Bayes net.
* @param variables Determines the variables whose marginal to compute, if provided as an
* Ordering they will be ordered in the returned BayesNet as specified, and if provided
* as a vector<Key> they will be ordered using constrained COLAMD.
* @param marginalizedVariableOrdering Optional ordering for the variables being marginalized
* out, i.e. all variables not in \c variables. If this is boost::none, the ordering
* will be computed with COLAMD.
* @param function Optional dense elimination function, if not provided the default will be
* used.
* @param variableIndex Optional pre-computed VariableIndex for the factor graph, if not
* provided one will be computed. */
boost::shared_ptr<BayesNetType> marginalMultifrontalBayesNet(
boost::variant<const Ordering&, const std::vector<Key>&> variables,
OptionalOrdering marginalizedVariableOrdering = boost::none,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Compute the marginal of the requested variables and return the result as a Bayes tree.
* @param variables Determines the variables whose marginal to compute, if provided as an
* Ordering they will be ordered in the returned BayesNet as specified, and if provided
* as a vector<Key> they will be ordered using constrained COLAMD.
* @param marginalizedVariableOrdering Optional ordering for the variables being marginalized
* out, i.e. all variables not in \c variables. If this is boost::none, the ordering
* will be computed with COLAMD.
* @param function Optional dense elimination function, if not provided the default will be
* used.
* @param variableIndex Optional pre-computed VariableIndex for the factor graph, if not
* provided one will be computed. */
boost::shared_ptr<BayesTreeType> marginalMultifrontalBayesTree(
boost::variant<const Ordering&, const std::vector<Key>&> variables,
OptionalOrdering marginalizedVariableOrdering = boost::none,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
/** Compute the marginal factor graph of the requested variables. */
boost::shared_ptr<FactorGraphType> marginal(
const std::vector<Key>& variables,
const Eliminate& function = EliminationTraitsType::DefaultEliminate,
OptionalVariableIndex variableIndex = boost::none) const;
private:
// Access the derived factor graph class
const FactorGraphType& asDerived() const { return static_cast<const FactorGraphType&>(*this); }
// Access the derived factor graph class
FactorGraphType& asDerived() { return static_cast<FactorGraphType&>(*this); }
};
}

View File

@ -1,234 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 EliminationTree-inl.h
* @author Frank Dellaert
* @date Oct 13, 2010
*/
#pragma once
#include <gtsam/base/timing.h>
#include <gtsam/base/FastSet.h>
#include <gtsam/inference/EliminationTree.h>
#include <gtsam/inference/VariableSlots.h>
#include <gtsam/inference/IndexFactor.h>
#include <gtsam/inference/IndexConditional.h>
#include <boost/foreach.hpp>
#include <boost/static_assert.hpp>
#include <iostream>
#include <set>
#include <vector>
namespace gtsam {
/* ************************************************************************* */
template<class FACTOR>
typename EliminationTree<FACTOR>::sharedFactor EliminationTree<FACTOR>::eliminate_(
Eliminate function, Conditionals& conditionals) const {
static const bool debug = false;
if(debug) std::cout << "ETree: eliminating " << this->key_ << std::endl;
if(this->key_ < conditionals.size()) { // If it is requested to eliminate the current variable
// Create the list of factors to be eliminated, initially empty, and reserve space
FactorGraph<FACTOR> factors;
factors.reserve(this->factors_.size() + this->subTrees_.size());
// Add all factors associated with the current node
factors.push_back(this->factors_.begin(), this->factors_.end());
// for all subtrees, eliminate into Bayes net and a separator factor, added to [factors]
BOOST_FOREACH(const shared_ptr& child, subTrees_)
factors.push_back(child->eliminate_(function, conditionals)); // TODO: spawn thread
// TODO: wait for completion of all threads
// Combine all factors (from this node and from subtrees) into a joint factor
typename FactorGraph<FACTOR>::EliminationResult
eliminated(function(factors, 1));
conditionals[this->key_] = eliminated.first;
if(debug) std::cout << "Eliminated " << this->key_ << " to get:\n";
if(debug) eliminated.first->print("Conditional: ");
if(debug) eliminated.second->print("Factor: ");
return eliminated.second;
} else {
// Eliminate each child but discard the result.
BOOST_FOREACH(const shared_ptr& child, subTrees_) {
(void)child->eliminate_(function, conditionals);
}
return sharedFactor(); // Return a NULL factor
}
}
/* ************************************************************************* */
template<class FACTOR>
std::vector<Index> EliminationTree<FACTOR>::ComputeParents(const VariableIndex& structure) {
// Number of factors and variables
const size_t m = structure.nFactors();
const size_t n = structure.size();
static const Index none = std::numeric_limits<Index>::max();
// Allocate result parent vector and vector of last factor columns
std::vector<Index> parents(n, none);
std::vector<Index> prevCol(m, none);
// for column j \in 1 to n do
for (Index j = 0; j < n; j++) {
// for row i \in Struct[A*j] do
BOOST_FOREACH(const size_t i, structure[j]) {
if (prevCol[i] != none) {
Index k = prevCol[i];
// find root r of the current tree that contains k
Index r = k;
while (parents[r] != none)
r = parents[r];
if (r != j) parents[r] = j;
}
prevCol[i] = j;
}
}
return parents;
}
/* ************************************************************************* */
template<class FACTOR>
template<class DERIVEDFACTOR>
typename EliminationTree<FACTOR>::shared_ptr EliminationTree<FACTOR>::Create(
const FactorGraph<DERIVEDFACTOR>& factorGraph,
const VariableIndex& structure) {
static const bool debug = false;
gttic(ET_Create1);
gttic(ET_ComputeParents);
// Compute the tree structure
std::vector<Index> parents(ComputeParents(structure));
gttoc(ET_ComputeParents);
// Number of variables
const size_t n = structure.size();
static const Index none = std::numeric_limits<Index>::max();
// Create tree structure
gttic(assemble_tree);
std::vector<shared_ptr> trees(n);
for (Index k = 1; k <= n; k++) {
Index j = n - k; // Start at the last variable and loop down to 0
trees[j].reset(new EliminationTree(j)); // Create a new node on this variable
if (parents[j] != none) // If this node has a parent, add it to the parent's children
trees[parents[j]]->add(trees[j]);
else if(!structure[j].empty() && j != n - 1) // If a node other than the last has no parents, this is a forest
throw DisconnectedGraphException();
}
gttoc(assemble_tree);
// Hang factors in right places
gttic(hang_factors);
BOOST_FOREACH(const typename boost::shared_ptr<DERIVEDFACTOR>& factor, factorGraph) {
if(factor && factor->size() > 0) {
Index j = *std::min_element(factor->begin(), factor->end());
if(j < structure.size())
trees[j]->add(factor);
}
}
gttoc(hang_factors);
if(debug)
trees.back()->print("ETree: ");
// Check that this is not null
assert(trees.back().get());
return trees.back();
}
/* ************************************************************************* */
template<class FACTOR>
template<class DERIVEDFACTOR>
typename EliminationTree<FACTOR>::shared_ptr
EliminationTree<FACTOR>::Create(const FactorGraph<DERIVEDFACTOR>& factorGraph) {
gttic(ET_Create2);
// Build variable index
const VariableIndex variableIndex(factorGraph);
// Build elimination tree
return Create(factorGraph, variableIndex);
}
/* ************************************************************************* */
template<class FACTORGRAPH>
void EliminationTree<FACTORGRAPH>::print(const std::string& name,
const IndexFormatter& formatter) const {
std::cout << name << " (" << formatter(key_) << ")" << std::endl;
BOOST_FOREACH(const sharedFactor& factor, factors_) {
factor->print(name + " ", formatter); }
BOOST_FOREACH(const shared_ptr& child, subTrees_) {
child->print(name + " ", formatter); }
}
/* ************************************************************************* */
template<class FACTORGRAPH>
bool EliminationTree<FACTORGRAPH>::equals(const EliminationTree<FACTORGRAPH>& expected, double tol) const {
if(this->key_ == expected.key_ && this->factors_ == expected.factors_
&& this->subTrees_.size() == expected.subTrees_.size()) {
typename SubTrees::const_iterator this_subtree = this->subTrees_.begin();
typename SubTrees::const_iterator expected_subtree = expected.subTrees_.begin();
while(this_subtree != this->subTrees_.end())
if( ! (*(this_subtree++))->equals(**(expected_subtree++), tol))
return false;
return true;
} else
return false;
}
/* ************************************************************************* */
template<class FACTOR>
typename EliminationTree<FACTOR>::BayesNet::shared_ptr
EliminationTree<FACTOR>::eliminatePartial(typename EliminationTree<FACTOR>::Eliminate function, size_t nrToEliminate) const {
// call recursive routine
gttic(ET_recursive_eliminate);
if(nrToEliminate > this->key_ + 1)
throw std::invalid_argument("Requested that EliminationTree::eliminatePartial eliminate more variables than exist");
Conditionals conditionals(nrToEliminate); // reserve a vector of conditional shared pointers
(void)eliminate_(function, conditionals); // modify in place
gttoc(ET_recursive_eliminate);
// Add conditionals to BayesNet
gttic(assemble_BayesNet);
typename BayesNet::shared_ptr bayesNet(new BayesNet);
BOOST_FOREACH(const typename BayesNet::sharedConditional& conditional, conditionals) {
if(conditional)
bayesNet->push_back(conditional);
}
gttoc(assemble_BayesNet);
return bayesNet;
}
/* ************************************************************************* */
template<class FACTOR>
typename EliminationTree<FACTOR>::BayesNet::shared_ptr
EliminationTree<FACTOR>::eliminate(Eliminate function) const {
size_t nrConditionals = this->key_ + 1; // root key has highest index
return eliminatePartial(function, nrConditionals);
}
}

View File

@ -0,0 +1,290 @@
/* ----------------------------------------------------------------------------
* 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 EliminationTree-inl.h
* @author Frank Dellaert
* @author Richard Roberts
* @date Oct 13, 2010
*/
#pragma once
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>
#include <stack>
#include <gtsam/base/timing.h>
#include <gtsam/base/treeTraversal-inst.h>
#include <gtsam/inference/EliminationTree.h>
#include <gtsam/inference/VariableIndex.h>
#include <gtsam/inference/Ordering.h>
#include <gtsam/inference/inference-inst.h>
namespace gtsam {
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
typename EliminationTree<BAYESNET,GRAPH>::sharedFactor
EliminationTree<BAYESNET,GRAPH>::Node::eliminate(
const boost::shared_ptr<BayesNetType>& output,
const Eliminate& function, const std::vector<sharedFactor>& childrenResults) const
{
// This function eliminates one node (Node::eliminate) - see below eliminate for the whole tree.
assert(childrenResults.size() == children.size());
// Gather factors
FactorGraphType gatheredFactors;
gatheredFactors.reserve(factors.size() + children.size());
gatheredFactors.push_back(factors.begin(), factors.end());
gatheredFactors.push_back(childrenResults.begin(), childrenResults.end());
// Do dense elimination step
std::vector<Key> keyAsVector(1); keyAsVector[0] = key;
std::pair<boost::shared_ptr<ConditionalType>, boost::shared_ptr<FactorType> > eliminationResult =
function(gatheredFactors, Ordering(keyAsVector));
// Add conditional to BayesNet
output->push_back(eliminationResult.first);
// Return result
return eliminationResult.second;
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
void EliminationTree<BAYESNET,GRAPH>::Node::print(
const std::string& str, const KeyFormatter& keyFormatter) const
{
std::cout << str << "(" << keyFormatter(key) << ")\n";
BOOST_FOREACH(const sharedFactor& factor, factors) {
if(factor)
factor->print(str);
else
std::cout << str << "null factor\n";
}
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
EliminationTree<BAYESNET,GRAPH>::EliminationTree(const FactorGraphType& graph,
const VariableIndex& structure, const Ordering& order)
{
gttic(EliminationTree_Contructor);
// Number of factors and variables - NOTE in the case of partial elimination, n here may
// be fewer variables than are actually present in the graph.
const size_t m = graph.size();
const size_t n = order.size();
static const size_t none = std::numeric_limits<size_t>::max();
// Allocate result parent vector and vector of last factor columns
std::vector<sharedNode> nodes(n);
std::vector<size_t> parents(n, none);
std::vector<size_t> prevCol(m, none);
std::vector<bool> factorUsed(m, false);
try {
// for column j \in 1 to n do
for (size_t j = 0; j < n; j++)
{
// Retrieve the factors involving this variable and create the current node
const VariableIndex::Factors& factors = structure[order[j]];
nodes[j] = boost::make_shared<Node>();
nodes[j]->key = order[j];
// for row i \in Struct[A*j] do
BOOST_FOREACH(const size_t i, factors) {
// If we already hit a variable in this factor, make the subtree containing the previous
// variable in this factor a child of the current node. This means that the variables
// eliminated earlier in the factor depend on the later variables in the factor. If we
// haven't yet hit a variable in this factor, we add the factor to the current node.
// TODO: Store root shortcuts instead of parents.
if (prevCol[i] != none) {
size_t k = prevCol[i];
// Find root r of the current tree that contains k. Use raw pointers in computing the
// parents to avoid changing the reference counts while traversing up the tree.
size_t r = k;
while (parents[r] != none)
r = parents[r];
// If the root of the subtree involving this node is actually the current node,
// TODO: what does this mean? forest?
if (r != j) {
// Now that we found the root, hook up parent and child pointers in the nodes.
parents[r] = j;
nodes[j]->children.push_back(nodes[r]);
}
} else {
// Add the current factor to the current node since we are at the first variable in this
// factor.
nodes[j]->factors.push_back(graph[i]);
factorUsed[i] = true;
}
prevCol[i] = j;
}
}
} catch(std::invalid_argument& e) {
// If this is thrown from structure[order[j]] above, it means that it was requested to
// eliminate a variable not present in the graph, so throw a more informative error message.
(void)e; // Prevent unused variable warning
throw std::invalid_argument("EliminationTree: given ordering contains variables that are not involved in the factor graph");
} catch(...) {
throw;
}
// Find roots
assert(parents.empty() || parents.back() == none); // We expect the last-eliminated node to be a root no matter what
for(size_t j = 0; j < n; ++j)
if(parents[j] == none)
roots_.push_back(nodes[j]);
// Gather remaining factors (exclude null factors)
for(size_t i = 0; i < m; ++i)
if(!factorUsed[i] && graph[i])
remainingFactors_.push_back(graph[i]);
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
EliminationTree<BAYESNET,GRAPH>::EliminationTree(
const FactorGraphType& factorGraph, const Ordering& order)
{
gttic(ET_Create2);
// Build variable index first
const VariableIndex variableIndex(factorGraph);
This temp(factorGraph, variableIndex, order);
this->swap(temp); // Swap in the tree, and temp will be deleted
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
EliminationTree<BAYESNET,GRAPH>&
EliminationTree<BAYESNET,GRAPH>::operator=(const EliminationTree<BAYESNET,GRAPH>& other)
{
// Start by duplicating the tree.
roots_ = treeTraversal::CloneForest(other);
// Assign the remaining factors - these are pointers to factors in the original factor graph and
// we do not clone them.
remainingFactors_ = other.remainingFactors_;
return *this;
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
std::pair<boost::shared_ptr<BAYESNET>, boost::shared_ptr<GRAPH> >
EliminationTree<BAYESNET,GRAPH>::eliminate(Eliminate function) const
{
gttic(EliminationTree_eliminate);
// Allocate result
boost::shared_ptr<BayesNetType> result = boost::make_shared<BayesNetType>();
// Run tree elimination algorithm
std::vector<sharedFactor> remainingFactors = inference::EliminateTree(result, *this, function);
// Add remaining factors that were not involved with eliminated variables
boost::shared_ptr<FactorGraphType> allRemainingFactors = boost::make_shared<FactorGraphType>();
allRemainingFactors->push_back(remainingFactors_.begin(), remainingFactors_.end());
allRemainingFactors->push_back(remainingFactors.begin(), remainingFactors.end());
// Return result
return std::make_pair(result, allRemainingFactors);
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
void EliminationTree<BAYESNET,GRAPH>::print(const std::string& name, const KeyFormatter& formatter) const
{
treeTraversal::PrintForest(*this, name, formatter);
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
bool EliminationTree<BAYESNET,GRAPH>::equals(const This& expected, double tol) const
{
// Depth-first-traversal stacks
std::stack<sharedNode, std::vector<sharedNode> > stack1, stack2;
// Add roots in sorted order
{
FastMap<Key,sharedNode> keys;
BOOST_FOREACH(const sharedNode& root, this->roots_) { keys.insert(std::make_pair(root->key, root)); }
typedef typename FastMap<Key,sharedNode>::value_type Key_Node;
BOOST_FOREACH(const Key_Node& key_node, keys) { stack1.push(key_node.second); }
}
{
FastMap<Key,sharedNode> keys;
BOOST_FOREACH(const sharedNode& root, expected.roots_) { keys.insert(std::make_pair(root->key, root)); }
typedef typename FastMap<Key,sharedNode>::value_type Key_Node;
BOOST_FOREACH(const Key_Node& key_node, keys) { stack2.push(key_node.second); }
}
// Traverse, adding children in sorted order
while(!stack1.empty() && !stack2.empty()) {
// Pop nodes
sharedNode node1 = stack1.top();
stack1.pop();
sharedNode node2 = stack2.top();
stack2.pop();
// Compare nodes
if(node1->key != node2->key)
return false;
if(node1->factors.size() != node2->factors.size()) {
return false;
} else {
for(typename Node::Factors::const_iterator it1 = node1->factors.begin(), it2 = node2->factors.begin();
it1 != node1->factors.end(); ++it1, ++it2) // Only check it1 == end because we already returned false for different counts
{
if(*it1 && *it2) {
if(!(*it1)->equals(**it2, tol))
return false;
} else if((*it1 && !*it2) || (*it2 && !*it1)) {
return false;
}
}
}
// Add children in sorted order
{
FastMap<Key,sharedNode> keys;
BOOST_FOREACH(const sharedNode& node, node1->children) { keys.insert(std::make_pair(node->key, node)); }
typedef typename FastMap<Key,sharedNode>::value_type Key_Node;
BOOST_FOREACH(const Key_Node& key_node, keys) { stack1.push(key_node.second); }
}
{
FastMap<Key,sharedNode> keys;
BOOST_FOREACH(const sharedNode& node, node2->children) { keys.insert(std::make_pair(node->key, node)); }
typedef typename FastMap<Key,sharedNode>::value_type Key_Node;
BOOST_FOREACH(const Key_Node& key_node, keys) { stack2.push(key_node.second); }
}
}
// If either stack is not empty, the number of nodes differed
if(!stack1.empty() || !stack2.empty())
return false;
return true;
}
/* ************************************************************************* */
template<class BAYESNET, class GRAPH>
void EliminationTree<BAYESNET,GRAPH>::swap(This& other) {
roots_.swap(other.roots_);
remainingFactors_.swap(other.remainingFactors_);
}
}

View File

@ -1,187 +1,167 @@
/* ----------------------------------------------------------------------------
* 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 EliminationTree.h
* @author Frank Dellaert
* @date Oct 13, 2010
*/
#pragma once
#include <utility>
#include <gtsam/base/FastSet.h>
#include <gtsam/base/Testable.h>
#include <gtsam/inference/VariableIndex.h>
#include <gtsam/inference/FactorGraph.h>
class EliminationTreeTester; // for unit tests, see testEliminationTree
namespace gtsam { template<class CONDITIONAL> class BayesNet; }
namespace gtsam {
/**
* An elimination tree is a data structure used intermediately during
* elimination. In future versions it will be used to save work between
* multiple eliminations.
*
* When a variable is eliminated, a new factor is created by combining that
* variable's neighboring factors. The new combined factor involves the combined
* factors' involved variables. When the lowest-ordered one of those variables
* is eliminated, it consumes that combined factor. In the elimination tree,
* that lowest-ordered variable is the parent of the variable that was eliminated to
* produce the combined factor. This yields a tree in general, and not a chain
* because of the implicit sparse structure of the resulting Bayes net.
*
* This structure is examined even more closely in a JunctionTree, which
* additionally identifies cliques in the chordal Bayes net.
* \nosubgrouping
*/
template<class FACTOR>
class EliminationTree {
public:
typedef EliminationTree<FACTOR> This; ///< This class
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer to this class
typedef typename boost::shared_ptr<FACTOR> sharedFactor; ///< Shared pointer to a factor
typedef gtsam::BayesNet<typename FACTOR::ConditionalType> BayesNet; ///< The BayesNet corresponding to FACTOR
typedef FACTOR Factor;
typedef typename FACTOR::KeyType KeyType;
/** Typedef for an eliminate subroutine */
typedef typename FactorGraph<FACTOR>::Eliminate Eliminate;
private:
/** concept check */
GTSAM_CONCEPT_TESTABLE_TYPE(FACTOR)
typedef FastList<sharedFactor> Factors;
typedef FastList<shared_ptr> SubTrees;
typedef std::vector<typename FACTOR::ConditionalType::shared_ptr> Conditionals;
Index key_; ///< index associated with root // FIXME: doesn't this require that "Index" is the type of keys in the generic factor?
Factors factors_; ///< factors associated with root
SubTrees subTrees_; ///< sub-trees
public:
/// @name Standard Constructors
/// @{
/**
* Named constructor to build the elimination tree of a factor graph using
* pre-computed column structure.
* @param factorGraph The factor graph for which to build the elimination tree
* @param structure The set of factors involving each variable. If this is not
* precomputed, you can call the Create(const FactorGraph<DERIVEDFACTOR>&)
* named constructor instead.
* @return The elimination tree
*/
template<class DERIVEDFACTOR>
static shared_ptr Create(const FactorGraph<DERIVEDFACTOR>& factorGraph, const VariableIndex& structure);
/** Named constructor to build the elimination tree of a factor graph. Note
* that this has to compute the column structure as a VariableIndex, so if you
* already have this precomputed, use the Create(const FactorGraph<DERIVEDFACTOR>&, const VariableIndex&)
* named constructor instead.
* @param factorGraph The factor graph for which to build the elimination tree
*/
template<class DERIVEDFACTOR>
static shared_ptr Create(const FactorGraph<DERIVEDFACTOR>& factorGraph);
/// @}
/// @name Standard Interface
/// @{
/** Eliminate the factors to a Bayes Net
* @param function The function to use to eliminate, see the namespace functions
* in GaussianFactorGraph.h
* @return The BayesNet resulting from elimination
*/
typename BayesNet::shared_ptr eliminate(Eliminate function) const;
/** Eliminate the factors to a Bayes Net and return the remaining factor
* @param function The function to use to eliminate, see the namespace functions
* in GaussianFactorGraph.h
* @return The BayesNet resulting from elimination and the remaining factor
*/
typename BayesNet::shared_ptr eliminatePartial(Eliminate function, size_t nrToEliminate) const;
/// @}
/// @name Testable
/// @{
/** Print the tree to cout */
void print(const std::string& name = "EliminationTree: ",
const IndexFormatter& formatter = DefaultIndexFormatter) const;
/** Test whether the tree is equal to another */
bool equals(const EliminationTree& other, double tol = 1e-9) const;
/// @}
private:
/** default constructor, private, as you should use Create below */
EliminationTree(Index key = 0) : key_(key) {}
/**
* Static internal function to build a vector of parent pointers using the
* algorithm of Gilbert et al., 2001, BIT.
*/
static std::vector<Index> ComputeParents(const VariableIndex& structure);
/** add a factor, for Create use only */
void add(const sharedFactor& factor) { factors_.push_back(factor); }
/** add a subtree, for Create use only */
void add(const shared_ptr& child) { subTrees_.push_back(child); }
/**
* Recursive routine that eliminates the factors arranged in an elimination tree
* @param Conditionals is a vector of shared pointers that will be modified in place
*/
sharedFactor eliminate_(Eliminate function, Conditionals& conditionals) const;
/// Allow access to constructor and add methods for testing purposes
friend class ::EliminationTreeTester;
};
/**
* An exception thrown when attempting to eliminate a disconnected factor
* graph, which is not currently possible in gtsam. If you need to work with
* disconnected graphs, a workaround is to create zero-information factors to
* bridge the disconnects. To do this, create any factor type (e.g.
* BetweenFactor or RangeFactor) with the noise model
* <tt>sharedPrecision(dim, 0.0)</tt>, where \c dim is the appropriate
* dimensionality for that factor.
*/
struct DisconnectedGraphException : public std::exception {
DisconnectedGraphException() {}
virtual ~DisconnectedGraphException() throw() {}
/// Returns the string "Attempting to eliminate a disconnected graph - this is not currently possible in gtsam."
virtual const char* what() const throw() {
return
"Attempting to eliminate a disconnected graph - this is not currently possible in\n"
"GTSAM. You may add \"empty\" BetweenFactor's to join disconnected graphs, these\n"
"will affect the symbolic structure and solving complexity of the graph but not\n"
"the solution. To do this, create BetweenFactor's with zero-precision noise\n"
"models, i.e. noiseModel::Isotropic::Precision(n, 0.0);\n"; }
};
}
#include <gtsam/inference/EliminationTree-inl.h>
/* ----------------------------------------------------------------------------
* 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 EliminationTree.h
* @author Frank Dellaert
* @author Richard Roberts
* @date Oct 13, 2010
*/
#pragma once
#include <utility>
#include <boost/shared_ptr.hpp>
#include <gtsam/base/Testable.h>
class EliminationTreeTester; // for unit tests, see testEliminationTree
namespace gtsam {
class VariableIndex;
class Ordering;
/**
* An elimination tree is a data structure used intermediately during
* elimination. In future versions it will be used to save work between
* multiple eliminations.
*
* When a variable is eliminated, a new factor is created by combining that
* variable's neighboring factors. The new combined factor involves the combined
* factors' involved variables. When the lowest-ordered one of those variables
* is eliminated, it consumes that combined factor. In the elimination tree,
* that lowest-ordered variable is the parent of the variable that was eliminated to
* produce the combined factor. This yields a tree in general, and not a chain
* because of the implicit sparse structure of the resulting Bayes net.
*
* This structure is examined even more closely in a JunctionTree, which
* additionally identifies cliques in the chordal Bayes net.
* \nosubgrouping
*/
template<class BAYESNET, class GRAPH>
class EliminationTree
{
protected:
typedef EliminationTree<BAYESNET, GRAPH> This; ///< This class
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer to this class
public:
typedef GRAPH FactorGraphType; ///< The factor graph type
typedef typename GRAPH::FactorType FactorType; ///< The type of factors
typedef typename boost::shared_ptr<FactorType> sharedFactor; ///< Shared pointer to a factor
typedef BAYESNET BayesNetType; ///< The BayesNet corresponding to FACTOR
typedef typename BayesNetType::ConditionalType ConditionalType; ///< The type of conditionals
typedef typename boost::shared_ptr<ConditionalType> sharedConditional; ///< Shared pointer to a conditional
typedef typename GRAPH::Eliminate Eliminate;
struct Node {
typedef std::vector<sharedFactor> Factors;
typedef std::vector<boost::shared_ptr<Node> > Children;
Key key; ///< key associated with root
Factors factors; ///< factors associated with root
Children children; ///< sub-trees
sharedFactor eliminate(const boost::shared_ptr<BayesNetType>& output,
const Eliminate& function, const std::vector<sharedFactor>& childrenFactors) const;
void print(const std::string& str, const KeyFormatter& keyFormatter) const;
};
typedef boost::shared_ptr<Node> sharedNode; ///< Shared pointer to Node
protected:
/** concept check */
GTSAM_CONCEPT_TESTABLE_TYPE(FactorType);
std::vector<sharedNode> roots_;
std::vector<sharedFactor> remainingFactors_;
/// @name Standard Constructors
/// @{
/**
* Build the elimination tree of a factor graph using pre-computed column structure.
* @param factorGraph The factor graph for which to build the elimination tree
* @param structure The set of factors involving each variable. If this is not
* precomputed, you can call the Create(const FactorGraph<DERIVEDFACTOR>&)
* named constructor instead.
* @return The elimination tree
*/
EliminationTree(const FactorGraphType& factorGraph,
const VariableIndex& structure, const Ordering& order);
/** Build the elimination tree of a factor graph. Note that this has to compute the column
* structure as a VariableIndex, so if you already have this precomputed, use the other
* constructor instead.
* @param factorGraph The factor graph for which to build the elimination tree
*/
EliminationTree(const FactorGraphType& factorGraph, const Ordering& order);
/** Copy constructor - makes a deep copy of the tree structure, but only pointers to factors are
* copied, factors are not cloned. */
EliminationTree(const This& other) { *this = other; }
/** Assignment operator - makes a deep copy of the tree structure, but only pointers to factors
* are copied, factors are not cloned. */
This& operator=(const This& other);
/// @}
public:
/// @name Standard Interface
/// @{
/** Eliminate the factors to a Bayes net and remaining factor graph
* @param function The function to use to eliminate, see the namespace functions
* in GaussianFactorGraph.h
* @return The Bayes net and factor graph resulting from elimination
*/
std::pair<boost::shared_ptr<BayesNetType>, boost::shared_ptr<FactorGraphType> >
eliminate(Eliminate function) const;
/// @}
/// @name Testable
/// @{
/** Print the tree to cout */
void print(const std::string& name = "EliminationTree: ",
const KeyFormatter& formatter = DefaultKeyFormatter) const;
protected:
/** Test whether the tree is equal to another */
bool equals(const This& other, double tol = 1e-9) const;
/// @}
public:
/// @name Advanced Interface
/// @{
/** Return the set of roots (one for a tree, multiple for a forest) */
const std::vector<sharedNode>& roots() const { return roots_; }
/** Return the remaining factors that are not pulled into elimination */
const std::vector<sharedFactor>& remainingFactors() const { return remainingFactors_; }
/** Swap the data of this tree with another one, this operation is very fast. */
void swap(This& other);
protected:
/// Protected default constructor
EliminationTree() {}
private:
/// Allow access to constructor and add methods for testing purposes
friend class ::EliminationTreeTester;
};
}

View File

@ -1,108 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 Factor-inl.h
* @author Richard Roberts
* @date Sep 1, 2010
*/
#pragma once
#include <gtsam/inference/Factor.h>
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
#include <stdexcept>
namespace gtsam {
/* ************************************************************************* */
template<typename KEY>
Factor<KEY>::Factor(const Factor<KEY>& f) :
keys_(f.keys_) {
assertInvariants();
}
/* ************************************************************************* */
template<typename KEY>
Factor<KEY>::Factor(const ConditionalType& c) :
keys_(c.keys()) {
// assert(c.nrFrontals() == 1);
assertInvariants();
}
/* ************************************************************************* */
template<typename KEY>
void Factor<KEY>::assertInvariants() const {
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
// Check that keys are all unique
std::multiset<KeyType> nonunique(keys_.begin(), keys_.end());
std::set<KeyType> unique(keys_.begin(), keys_.end());
bool correct = (nonunique.size() == unique.size())
&& std::equal(nonunique.begin(), nonunique.end(), unique.begin());
if (!correct) throw std::logic_error(
"Factor::assertInvariants: Factor contains duplicate keys");
#endif
}
/* ************************************************************************* */
template<typename KEY>
void Factor<KEY>::print(const std::string& s,
const IndexFormatter& formatter) const {
printKeys(s,formatter);
}
/* ************************************************************************* */
template<typename KEY>
void Factor<KEY>::printKeys(const std::string& s, const IndexFormatter& formatter) const {
std::cout << s << " ";
BOOST_FOREACH(KEY key, keys_) std::cout << " " << formatter(key);
std::cout << std::endl;
}
/* ************************************************************************* */
template<typename KEY>
bool Factor<KEY>::equals(const This& other, double tol) const {
return keys_ == other.keys_;
}
/* ************************************************************************* */
#ifdef TRACK_ELIMINATE
template<typename KEY>
template<class COND>
typename COND::shared_ptr Factor<KEY>::eliminateFirst() {
assert(!keys_.empty());
assertInvariants();
KEY eliminated = keys_.front();
keys_.erase(keys_.begin());
assertInvariants();
return typename COND::shared_ptr(new COND(eliminated, keys_));
}
/* ************************************************************************* */
template<typename KEY>
template<class COND>
typename BayesNet<COND>::shared_ptr Factor<KEY>::eliminate(size_t nrFrontals) {
assert(keys_.size() >= nrFrontals);
assertInvariants();
typename BayesNet<COND>::shared_ptr fragment(new BayesNet<COND> ());
const_iterator it = this->begin();
for (KEY n = 0; n < nrFrontals; ++n, ++it)
fragment->push_back(COND::FromRange(it, const_iterator(this->end()), 1));
if (nrFrontals > 0) keys_.assign(fragment->back()->beginParents(),
fragment->back()->endParents());
assertInvariants();
return fragment;
}
#endif
}

View File

@ -0,0 +1,47 @@
/* ----------------------------------------------------------------------------
* 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 Factor.cpp
* @brief The base class for all factors
* @author Kai Ni
* @author Frank Dellaert
* @author Richard Roberts
*/
// \callgraph
#include <boost/foreach.hpp>
#include <iostream>
#include <gtsam/inference/Factor.h>
namespace gtsam {
/* ************************************************************************* */
void Factor::print(const std::string& s, const KeyFormatter& formatter) const
{
return this->printKeys(s, formatter);
}
/* ************************************************************************* */
void Factor::printKeys(const std::string& s, const KeyFormatter& formatter) const {
std::cout << s << " ";
BOOST_FOREACH(Key key, keys_) std::cout << " " << formatter(key);
std::cout << std::endl;
}
/* ************************************************************************* */
bool Factor::equals(const This& other, double tol) const {
return keys_ == other.keys_;
}
}

View File

@ -21,212 +21,151 @@
#pragma once
#include <set>
#include <vector>
#include <boost/serialization/nvp.hpp>
#include <boost/foreach.hpp>
#include <boost/function/function1.hpp>
#include <boost/lexical_cast.hpp>
#include <gtsam/global_includes.h>
#include <gtsam/base/FastMap.h>
#include <gtsam/base/types.h>
#include <gtsam/inference/Key.h>
namespace gtsam {
template<class KEY> class Conditional;
/**
* This is the base class for all factor types. It is templated on a KEY type,
* which will be the type used to label variables. Key types currently in use
* in gtsam are Index with symbolic (IndexFactor, SymbolicFactorGraph) and
* Gaussian factors (GaussianFactor, JacobianFactor, HessianFactor, GaussianFactorGraph),
* and Key with nonlinear factors (NonlinearFactor, NonlinearFactorGraph).
* though currently only IndexFactor and IndexConditional derive from this
* class, using Index keys. This class does not store any data other than its
* keys. Derived classes store data such as matrices and probability tables.
*
* Note that derived classes *must* redefine the ConditionalType and shared_ptr
* typedefs to refer to the associated conditional and shared_ptr types of the
* derived class. See IndexFactor, JacobianFactor, etc. for examples.
*
* This class is \b not virtual for performance reasons - derived symbolic classes,
* IndexFactor and IndexConditional, need to be created and destroyed quickly
* during symbolic elimination. GaussianFactor and NonlinearFactor are virtual.
* \nosubgrouping
*/
template<typename KEY>
class Factor {
public:
typedef KEY KeyType; ///< The KEY template parameter
typedef Factor<KeyType> This; ///< This class
/**
* Typedef to the conditional type obtained by eliminating this factor,
* derived classes must redefine this.
* This is the base class for all factor types. It is templated on a KEY type,
* which will be the type used to label variables. Key types currently in use
* in gtsam are Index with symbolic (IndexFactor, SymbolicFactorGraph) and
* Gaussian factors (GaussianFactor, JacobianFactor, HessianFactor, GaussianFactorGraph),
* and Key with nonlinear factors (NonlinearFactor, NonlinearFactorGraph).
* though currently only IndexFactor and IndexConditional derive from this
* class, using Index keys. This class does not store any data other than its
* keys. Derived classes store data such as matrices and probability tables.
*
* Note that derived classes *must* redefine the ConditionalType and shared_ptr
* typedefs to refer to the associated conditional and shared_ptr types of the
* derived class. See IndexFactor, JacobianFactor, etc. for examples.
*
* This class is \b not virtual for performance reasons - derived symbolic classes,
* IndexFactor and IndexConditional, need to be created and destroyed quickly
* during symbolic elimination. GaussianFactor and NonlinearFactor are virtual.
* \nosubgrouping
*/
typedef Conditional<KeyType> ConditionalType;
class GTSAM_EXPORT Factor
{
/// A shared_ptr to this class, derived classes must redefine this.
typedef boost::shared_ptr<Factor> shared_ptr;
private:
// These typedefs are private because they must be overridden in derived classes.
typedef Factor This; ///< This class
typedef boost::shared_ptr<Factor> shared_ptr; ///< A shared_ptr to this class.
/// Iterator over keys
typedef typename std::vector<KeyType>::iterator iterator;
public:
/// Iterator over keys
typedef std::vector<Key>::iterator iterator;
/// Const iterator over keys
typedef typename std::vector<KeyType>::const_iterator const_iterator;
/// Const iterator over keys
typedef std::vector<Key>::const_iterator const_iterator;
protected:
protected:
/// The keys involved in this factor
std::vector<KeyType> keys_;
/// The keys involved in this factor
std::vector<Key> keys_;
public:
/// @name Standard Constructors
/// @{
/// @name Standard Constructors
/// @{
/** Default constructor for I/O */
Factor() {}
/** Copy constructor */
Factor(const This& f);
/** Construct factor from container of keys. This constructor is used internally from derived factor
* constructors, either from a container of keys or from a boost::assign::list_of. */
template<typename CONTAINER>
Factor(const CONTAINER& keys) : keys_(keys.begin(), keys.end()) {}
/** Construct from conditional, calls ConditionalType::toFactor() */
Factor(const ConditionalType& c);
/** Construct factor from iterator keys. This constructor may be used internally from derived
* factor constructors, although our code currently does not use this. */
template<typename ITERATOR>
Factor(ITERATOR first, ITERATOR last) : keys_(first, last) {}
/** Default constructor for I/O */
Factor() {}
/** Construct factor from container of keys. This is called internally from derived factor static
* factor methods, as a workaround for not being able to call the protected constructors above. */
template<typename CONTAINER>
static Factor FromKeys(const CONTAINER& keys) {
return Factor(keys.begin(), keys.end()); }
/** Construct unary factor */
Factor(KeyType key) : keys_(1) {
keys_[0] = key; assertInvariants(); }
/** Construct factor from iterator keys. This is called internally from derived factor static
* factor methods, as a workaround for not being able to call the protected constructors above. */
template<typename ITERATOR>
static Factor FromIterators(ITERATOR first, ITERATOR last) {
return Factor(first, last); }
/** Construct binary factor */
Factor(KeyType key1, KeyType key2) : keys_(2) {
keys_[0] = key1; keys_[1] = key2; assertInvariants(); }
/// @}
/** Construct ternary factor */
Factor(KeyType key1, KeyType key2, KeyType key3) : keys_(3) {
keys_[0] = key1; keys_[1] = key2; keys_[2] = key3; assertInvariants(); }
public:
/// @name Standard Interface
/// @{
/** Construct 4-way factor */
Factor(KeyType key1, KeyType key2, KeyType key3, KeyType key4) : keys_(4) {
keys_[0] = key1; keys_[1] = key2; keys_[2] = key3; keys_[3] = key4; assertInvariants(); }
/// First key
Key front() const { return keys_.front(); }
/** Construct 5-way factor */
Factor(KeyType key1, KeyType key2, KeyType key3, KeyType key4, KeyType key5) : keys_(5) {
keys_[0] = key1; keys_[1] = key2; keys_[2] = key3; keys_[3] = key4; keys_[4] = key5; assertInvariants(); }
/// Last key
Key back() const { return keys_.back(); }
/** Construct 6-way factor */
Factor(KeyType key1, KeyType key2, KeyType key3, KeyType key4, KeyType key5, KeyType key6) : keys_(6) {
keys_[0] = key1; keys_[1] = key2; keys_[2] = key3; keys_[3] = key4; keys_[4] = key5; keys_[5] = key6; assertInvariants(); }
/// find
const_iterator find(Key key) const { return std::find(begin(), end(), key); }
/// @}
/// @name Advanced Constructors
/// @{
/// Access the factor's involved variable keys
const std::vector<Key>& keys() const { return keys_; }
/** Construct n-way factor */
Factor(const std::set<KeyType>& keys) {
BOOST_FOREACH(const KeyType& key, keys) keys_.push_back(key);
assertInvariants();
}
/** Iterator at beginning of involved variable keys */
const_iterator begin() const { return keys_.begin(); }
/** Construct n-way factor */
Factor(const std::vector<KeyType>& keys) : keys_(keys) {
assertInvariants();
}
/** Iterator at end of involved variable keys */
const_iterator end() const { return keys_.end(); }
/** Constructor from a collection of keys */
template<class KEYITERATOR> Factor(KEYITERATOR beginKey, KEYITERATOR endKey) :
keys_(beginKey, endKey) { assertInvariants(); }
/**
* @return the number of variables involved in this factor
*/
size_t size() const { return keys_.size(); }
/// @}
/// @}
#ifdef TRACK_ELIMINATE
/**
* eliminate the first variable involved in this factor
* @return a conditional on the eliminated variable
*/
template<class CONDITIONAL>
typename CONDITIONAL::shared_ptr eliminateFirst();
/**
* eliminate the first nrFrontals frontal variables.
*/
template<class CONDITIONAL>
typename BayesNet<CONDITIONAL>::shared_ptr eliminate(size_t nrFrontals = 1);
#endif
/// @name Testable
/// @{
/// @name Standard Interface
/// @{
/// print
void print(const std::string& s = "Factor", const KeyFormatter& formatter = DefaultKeyFormatter) const;
/// First key
KeyType front() const { return keys_.front(); }
/// print only keys
void printKeys(const std::string& s = "Factor", const KeyFormatter& formatter = DefaultKeyFormatter) const;
/// Last key
KeyType back() const { return keys_.back(); }
protected:
/// check equality
bool equals(const This& other, double tol = 1e-9) const;
/// find
const_iterator find(KeyType key) const { return std::find(begin(), end(), key); }
/// @}
/// Access the factor's involved variable keys
const std::vector<KeyType>& keys() const { return keys_; }
public:
/// @name Advanced Interface
/// @{
/** iterators */
const_iterator begin() const { return keys_.begin(); } ///TODO: comment
const_iterator end() const { return keys_.end(); } ///TODO: comment
/** @return keys involved in this factor */
std::vector<Key>& keys() { return keys_; }
/**
* @return the number of variables involved in this factor
*/
size_t size() const { return keys_.size(); }
/** Iterator at beginning of involved variable keys */
iterator begin() { return keys_.begin(); }
/// @}
/// @name Testable
/// @{
/** Iterator at end of involved variable keys */
iterator end() { return keys_.end(); }
/// print
void print(const std::string& s = "Factor",
const IndexFormatter& formatter = DefaultIndexFormatter) const;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(keys_);
}
/// print only keys
void printKeys(const std::string& s = "Factor",
const IndexFormatter& formatter = DefaultIndexFormatter) const;
/// @}
/// check equality
bool equals(const This& other, double tol = 1e-9) const;
/// @}
/// @name Advanced Interface
/// @{
/**
* @return keys involved in this factor
*/
std::vector<KeyType>& keys() { return keys_; }
/** mutable iterators */
iterator begin() { return keys_.begin(); } ///TODO: comment
iterator end() { return keys_.end(); } ///TODO: comment
protected:
friend class JacobianFactor;
friend class HessianFactor;
/// Internal consistency check that is run frequently when in debug mode.
/// If NDEBUG is defined, this is empty and optimized out.
void assertInvariants() const;
private:
/** Serialization function */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(keys_);
}
/// @}
};
};
}
#include <gtsam/inference/Factor-inl.h>

View File

@ -1,288 +0,0 @@
/* ----------------------------------------------------------------------------
* 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-inl.h
* This is a template definition file, include it where needed (only!)
* so that the appropriate code is generated and link errors avoided.
* @brief Factor Graph Base Class
* @author Carlos Nieto
* @author Frank Dellaert
* @author Alireza Fathi
* @author Michael Kaess
*/
#pragma once
#include <gtsam/base/FastSet.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/inference/VariableIndex.h>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/format.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <stdio.h>
#include <list>
#include <sstream>
#include <stdexcept>
namespace gtsam {
/* ************************************************************************* */
template<class FACTOR>
template<class CONDITIONAL>
FactorGraph<FACTOR>::FactorGraph(const BayesNet<CONDITIONAL>& bayesNet) {
factors_.reserve(bayesNet.size());
BOOST_FOREACH(const typename CONDITIONAL::shared_ptr& cond, bayesNet) {
this->push_back(cond->toFactor());
}
}
/* ************************************************************************* */
template<class FACTOR>
void FactorGraph<FACTOR>::print(const std::string& s,
const IndexFormatter& formatter) const {
std::cout << s << std::endl;
std::cout << "size: " << size() << std::endl;
for (size_t i = 0; i < factors_.size(); i++) {
std::stringstream ss;
ss << "factor " << i << ": ";
if (factors_[i] != NULL) factors_[i]->print(ss.str(), formatter);
}
}
/* ************************************************************************* */
template<class FACTOR>
bool FactorGraph<FACTOR>::equals(const This& fg, double tol) const {
/** check whether the two factor graphs have the same number of factors_ */
if (factors_.size() != fg.size()) return false;
/** check whether the factors_ are the same */
for (size_t i = 0; i < factors_.size(); i++) {
// TODO: Doesn't this force order of factor insertion?
sharedFactor f1 = factors_[i], f2 = fg.factors_[i];
if (f1 == NULL && f2 == NULL) continue;
if (f1 == NULL || f2 == NULL) return false;
if (!f1->equals(*f2, tol)) return false;
}
return true;
}
/* ************************************************************************* */
template<class FACTOR>
size_t FactorGraph<FACTOR>::nrFactors() const {
size_t size_ = 0;
BOOST_FOREACH(const sharedFactor& factor, factors_)
if (factor) size_++;
return size_;
}
/* ************************************************************************* */
template<class FACTOR>
std::set<typename FACTOR::KeyType> FactorGraph<FACTOR>::keys() const {
std::set<KeyType> allKeys;
BOOST_FOREACH(const sharedFactor& factor, factors_)
if (factor) {
BOOST_FOREACH(Index j, factor->keys())
allKeys.insert(j);
}
return allKeys;
}
/* ************************************************************************* */
template<class FACTOR>
std::pair<typename FactorGraph<FACTOR>::sharedConditional, FactorGraph<FACTOR> >
FactorGraph<FACTOR>::eliminateFrontals(size_t nFrontals, const Eliminate& eliminate) const
{
// Build variable index
VariableIndex variableIndex(*this);
// Find first variable
Index firstIndex = 0;
while(firstIndex < variableIndex.size() && variableIndex[firstIndex].empty())
++ firstIndex;
// Check that number of variables is in bounds
if(firstIndex + nFrontals > variableIndex.size())
throw std::invalid_argument("Requested to eliminate more frontal variables than exist in the factor graph.");
// Get set of involved factors
FastSet<size_t> involvedFactorIs;
for(Index j = firstIndex; j < firstIndex + nFrontals; ++j) {
BOOST_FOREACH(size_t i, variableIndex[j]) {
involvedFactorIs.insert(i);
}
}
// Separate factors into involved and remaining
FactorGraph<FactorType> involvedFactors;
FactorGraph<FactorType> remainingFactors;
FastSet<size_t>::const_iterator involvedFactorIsIt = involvedFactorIs.begin();
for(size_t i = 0; i < this->size(); ++i) {
if(involvedFactorIsIt != involvedFactorIs.end() && *involvedFactorIsIt == i) {
// If the current factor is involved, add it to involved and increment involved iterator
involvedFactors.push_back((*this)[i]);
++ involvedFactorIsIt;
} else {
// If not involved, add to remaining
remainingFactors.push_back((*this)[i]);
}
}
// Do dense elimination on the involved factors
typename FactorGraph<FactorType>::EliminationResult eliminationResult =
eliminate(involvedFactors, nFrontals);
// Add the remaining factor back into the factor graph
remainingFactors.push_back(eliminationResult.second);
// Return the eliminated factor and remaining factor graph
return std::make_pair(eliminationResult.first, remainingFactors);
}
/* ************************************************************************* */
template<class FACTOR>
std::pair<typename FactorGraph<FACTOR>::sharedConditional, FactorGraph<FACTOR> >
FactorGraph<FACTOR>::eliminate(const std::vector<KeyType>& variables, const Eliminate& eliminateFcn,
boost::optional<const VariableIndex&> variableIndex_) const
{
const VariableIndex& variableIndex =
variableIndex_ ? *variableIndex_ : VariableIndex(*this);
// First find the involved factors
FactorGraph<FACTOR> involvedFactors;
Index highestInvolvedVariable = 0; // Largest index of the variables in the involved factors
// First get the indices of the involved factors, but uniquely in a set
FastSet<size_t> involvedFactorIndices;
BOOST_FOREACH(Index variable, variables) {
involvedFactorIndices.insert(variableIndex[variable].begin(), variableIndex[variable].end()); }
// Add the factors themselves to involvedFactors and update largest index
involvedFactors.reserve(involvedFactorIndices.size());
BOOST_FOREACH(size_t factorIndex, involvedFactorIndices) {
const sharedFactor factor = this->at(factorIndex);
involvedFactors.push_back(factor); // Add involved factor
highestInvolvedVariable = std::max( // Updated largest index
highestInvolvedVariable,
*std::max_element(factor->begin(), factor->end()));
}
sharedConditional conditional;
sharedFactor remainingFactor;
if(involvedFactors.size() > 0) {
// Now permute the variables to be eliminated to the front of the ordering
Permutation toFront = Permutation::PullToFront(variables, highestInvolvedVariable+1);
Permutation toFrontInverse = *toFront.inverse();
BOOST_FOREACH(const sharedFactor& factor, involvedFactors) {
factor->permuteWithInverse(toFrontInverse); }
// Eliminate into conditional and remaining factor
EliminationResult eliminated = eliminateFcn(involvedFactors, variables.size());
conditional = eliminated.first;
remainingFactor = eliminated.second;
// Undo the permutation
BOOST_FOREACH(const sharedFactor& factor, involvedFactors) {
factor->permuteWithInverse(toFront); }
conditional->permuteWithInverse(toFront);
remainingFactor->permuteWithInverse(toFront);
} else {
// Eliminate 0 variables
EliminationResult eliminated = eliminateFcn(involvedFactors, 0);
conditional = eliminated.first;
remainingFactor = eliminated.second;
}
// Build the remaining graph, without the removed factors
FactorGraph<FACTOR> remainingGraph;
remainingGraph.reserve(this->size() - involvedFactors.size() + 1);
FastSet<size_t>::const_iterator involvedFactorIndexIt = involvedFactorIndices.begin();
for(size_t i = 0; i < this->size(); ++i) {
if(involvedFactorIndexIt != involvedFactorIndices.end() && *involvedFactorIndexIt == i)
++ involvedFactorIndexIt;
else
remainingGraph.push_back(this->at(i));
}
// Add the remaining factor if it is not empty.
if(remainingFactor->size() != 0)
remainingGraph.push_back(remainingFactor);
return std::make_pair(conditional, remainingGraph);
}
/* ************************************************************************* */
template<class FACTOR>
void FactorGraph<FACTOR>::replace(size_t index, sharedFactor factor) {
if (index >= factors_.size()) throw std::invalid_argument(boost::str(
boost::format("Factor graph does not contain a factor with index %d.")
% index));
// Replace the factor
factors_[index] = factor;
}
/* ************************************************************************* */
template<class FACTORGRAPH>
FACTORGRAPH combine(const FACTORGRAPH& fg1, const FACTORGRAPH& fg2) {
// create new linear factor graph equal to the first one
FACTORGRAPH fg = fg1;
// add the second factors_ in the graph
fg.push_back(fg2);
return fg;
}
/* ************************************************************************* */
template<class DERIVEDFACTOR, class KEY>
typename DERIVEDFACTOR::shared_ptr Combine(const FactorGraph<DERIVEDFACTOR>& factors,
const FastMap<KEY, std::vector<KEY> >& variableSlots) {
typedef const std::pair<const KEY, std::vector<KEY> > KeySlotPair;
// Local functional for getting keys out of key-value pairs
struct Local { static KEY FirstOf(const KeySlotPair& pr) { return pr.first; } };
return typename DERIVEDFACTOR::shared_ptr(new DERIVEDFACTOR(
boost::make_transform_iterator(variableSlots.begin(), &Local::FirstOf),
boost::make_transform_iterator(variableSlots.end(), &Local::FirstOf)));
}
/* ************************************************************************* */
// Recursive function to add factors in cliques to vector of factors_io
template<class FACTOR, class CONDITIONAL, class CLIQUE>
void _FactorGraph_BayesTree_adder(
std::vector<typename boost::shared_ptr<FACTOR> >& factors_io,
const typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique& clique) {
if(clique) {
// Add factor from this clique
factors_io.push_back((*clique)->toFactor());
// Traverse children
typedef typename BayesTree<CONDITIONAL,CLIQUE>::sharedClique sharedClique;
BOOST_FOREACH(const sharedClique& child, clique->children())
_FactorGraph_BayesTree_adder<FACTOR,CONDITIONAL,CLIQUE>(factors_io, child);
}
}
/* ************************************************************************* */
template<class FACTOR>
template<class CONDITIONAL, class CLIQUE>
FactorGraph<FACTOR>::FactorGraph(const BayesTree<CONDITIONAL,CLIQUE>& bayesTree) {
factors_.reserve(bayesTree.size());
_FactorGraph_BayesTree_adder<FACTOR,CONDITIONAL,CLIQUE>(factors_, bayesTree.root());
}
/* ************************************************************************* */
} // namespace gtsam

View File

@ -0,0 +1,84 @@
/* ----------------------------------------------------------------------------
* 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-inl.h
* @brief Factor Graph Base Class
* @author Carlos Nieto
* @author Frank Dellaert
* @author Alireza Fathi
* @author Michael Kaess
* @author Richard Roberts
*/
#pragma once
#include <gtsam/inference/FactorGraph.h>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <stdio.h>
#include <sstream>
namespace gtsam {
/* ************************************************************************* */
template<class FACTOR>
void FactorGraph<FACTOR>::print(const std::string& s, const KeyFormatter& formatter) const {
std::cout << s << std::endl;
std::cout << "size: " << size() << std::endl;
for (size_t i = 0; i < factors_.size(); i++) {
std::stringstream ss;
ss << "factor " << i << ": ";
if (factors_[i])
factors_[i]->print(ss.str(), formatter);
}
}
/* ************************************************************************* */
template<class FACTOR>
bool FactorGraph<FACTOR>::equals(const This& fg, double tol) const {
/** check whether the two factor graphs have the same number of factors_ */
if (factors_.size() != fg.size()) return false;
/** check whether the factors_ are the same */
for (size_t i = 0; i < factors_.size(); i++) {
// TODO: Doesn't this force order of factor insertion?
sharedFactor f1 = factors_[i], f2 = fg.factors_[i];
if (f1 == NULL && f2 == NULL) continue;
if (f1 == NULL || f2 == NULL) return false;
if (!f1->equals(*f2, tol)) return false;
}
return true;
}
/* ************************************************************************* */
template<class FACTOR>
size_t FactorGraph<FACTOR>::nrFactors() const {
size_t size_ = 0;
BOOST_FOREACH(const sharedFactor& factor, factors_)
if (factor) size_++;
return size_;
}
/* ************************************************************************* */
template<class FACTOR>
FastSet<Key> FactorGraph<FACTOR>::keys() const {
FastSet<Key> allKeys;
BOOST_FOREACH(const sharedFactor& factor, factors_)
if (factor)
allKeys.insert(factor->begin(), factor->end());
return allKeys;
}
/* ************************************************************************* */
} // namespace gtsam

View File

@ -15,25 +15,59 @@
* @author Carlos Nieto
* @author Christian Potthast
* @author Michael Kaess
* @author Richard Roberts
*/
// \callgraph
#pragma once
#include <gtsam/base/Testable.h>
#include <gtsam/base/FastMap.h>
#include <gtsam/inference/BayesNet.h>
#include <boost/serialization/nvp.hpp>
#include <boost/function.hpp>
#include <set>
#include <boost/assign/list_inserter.hpp>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <type_traits>
#include <gtsam/base/Testable.h>
#include <gtsam/inference/Key.h>
namespace gtsam {
// Forward declarations
template<class CONDITIONAL, class CLIQUE> class BayesTree;
class VariableIndex;
// Forward declarations
template<class CLIQUE> class BayesTree;
/** Helper */
template<class C>
class CRefCallPushBack
{
C& obj;
public:
CRefCallPushBack(C& obj) : obj(obj) {}
template<typename A>
void operator()(const A& a) { obj.push_back(a); }
};
/** Helper */
template<class C>
class RefCallPushBack
{
C& obj;
public:
RefCallPushBack(C& obj) : obj(obj) {}
template<typename A>
void operator()(A& a) { obj.push_back(a); }
};
/** Helper */
template<class C>
class CRefCallAddCopy
{
C& obj;
public:
CRefCallAddCopy(C& obj) : obj(obj) {}
template<typename A>
void operator()(const A& a) { obj.addCopy(a); }
};
/**
* A factor graph is a bipartite graph with factor nodes connected to variable nodes.
@ -44,62 +78,64 @@ class VariableIndex;
class FactorGraph {
public:
typedef FACTOR FactorType; ///< factor type
typedef typename FACTOR::KeyType KeyType; ///< type of Keys we use to index variables with
typedef boost::shared_ptr<FACTOR> sharedFactor; ///< Shared pointer to a factor
typedef boost::shared_ptr<typename FACTOR::ConditionalType> sharedConditional; ///< Shared pointer to a conditional
typedef FactorGraph<FACTOR> This; ///< Typedef for this class
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer for this class
typedef sharedFactor value_type;
typedef typename std::vector<sharedFactor>::iterator iterator;
typedef typename std::vector<sharedFactor>::const_iterator const_iterator;
/** typedef for elimination result */
typedef std::pair<sharedConditional, sharedFactor> EliminationResult;
/** typedef for an eliminate subroutine */
typedef boost::function<EliminationResult(const This&, size_t)> Eliminate;
private:
typedef FactorGraph<FACTOR> This; ///< Typedef for this class
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer for this class
protected:
/** concept check, makes sure FACTOR defines print and equals */
GTSAM_CONCEPT_TESTABLE_TYPE(FACTOR)
/** Collection of factors */
std::vector<sharedFactor> factors_;
public:
/// @name Standard Constructor
/// @name Standard Constructors
/// @{
/** Default constructor */
FactorGraph() {}
/** Constructor from iterator over factors (shared_ptr or plain objects) */
template<typename ITERATOR>
FactorGraph(ITERATOR firstFactor, ITERATOR lastFactor) { push_back(firstFactor, lastFactor); }
/** Construct from container of factors (shared_ptr or plain objects) */
template<class CONTAINER>
explicit FactorGraph(const CONTAINER& factors) { push_back(factors); }
/// @}
/// @name Advanced Constructors
/// @{
// TODO: are these needed?
/**
* @brief Constructor from a Bayes net
* @param bayesNet the Bayes net to convert, type CONDITIONAL must yield compatible factor
* @return a factor graph with all the conditionals, as factors
*/
template<class CONDITIONAL>
FactorGraph(const BayesNet<CONDITIONAL>& bayesNet);
///**
// * @brief Constructor from a Bayes net
// * @param bayesNet the Bayes net to convert, type CONDITIONAL must yield compatible factor
// * @return a factor graph with all the conditionals, as factors
// */
//template<class CONDITIONAL>
//FactorGraph(const BayesNet<CONDITIONAL>& bayesNet);
/** convert from Bayes tree */
template<class CONDITIONAL, class CLIQUE>
FactorGraph(const BayesTree<CONDITIONAL, CLIQUE>& bayesTree);
///** convert from Bayes tree */
//template<class CONDITIONAL, class CLIQUE>
//FactorGraph(const BayesTree<CONDITIONAL, CLIQUE>& bayesTree);
/** convert from a derived type */
template<class DERIVEDFACTOR>
FactorGraph(const FactorGraph<DERIVEDFACTOR>& factors) {
factors_.assign(factors.begin(), factors.end());
}
///** convert from a derived type */
//template<class DERIVEDFACTOR>
//FactorGraph(const FactorGraph<DERIVEDFACTOR>& factors) {
// factors_.assign(factors.begin(), factors.end());
//}
/// @}
public:
/// @name Adding Factors
/// @{
@ -109,69 +145,165 @@ class VariableIndex;
*/
void reserve(size_t size) { factors_.reserve(size); }
/** Add a factor */
// TODO: are these needed?
/** Add a factor directly using a shared_ptr */
template<class DERIVEDFACTOR>
void push_back(const boost::shared_ptr<DERIVEDFACTOR>& factor) {
factors_.push_back(boost::shared_ptr<FACTOR>(factor));
}
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type
push_back(boost::shared_ptr<DERIVEDFACTOR>& factor) {
factors_.push_back(boost::shared_ptr<FACTOR>(factor)); }
/** push back many factors */
void push_back(const This& factors) {
factors_.insert(end(), factors.begin(), factors.end());
}
/** Add a factor directly using a shared_ptr */
void push_back(const sharedFactor& factor) {
factors_.push_back(factor); }
/** push back many factors with an iterator */
/** push back many factors with an iterator over shared_ptr (factors are not copied) */
template<typename ITERATOR>
void push_back(ITERATOR firstFactor, ITERATOR lastFactor) {
factors_.insert(end(), firstFactor, lastFactor);
typename std::enable_if<std::is_base_of<FactorType, typename ITERATOR::value_type::element_type>::value>::type
push_back(ITERATOR firstFactor, ITERATOR lastFactor) {
factors_.insert(end(), firstFactor, lastFactor); }
/** push back many factors as shared_ptr's in a container (factors are not copied) */
template<typename CONTAINER>
typename std::enable_if<std::is_base_of<FactorType, typename CONTAINER::value_type::element_type>::value>::type
push_back(const CONTAINER& container) {
push_back(container.begin(), container.end());
}
/**
* @brief Add a vector of derived factors
* @param factors to add
*/
template<typename DERIVEDFACTOR>
void push_back(const std::vector<typename boost::shared_ptr<DERIVEDFACTOR> >& factors) {
factors_.insert(end(), factors.begin(), factors.end());
/** 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<class CLIQUE>
typename std::enable_if<std::is_base_of<This, typename CLIQUE::FactorGraphType>::value>::type
push_back(const BayesTree<CLIQUE>& bayesTree) {
bayesTree.addFactorsToGraph(*this);
}
/** += syntax for push_back, e.g. graph += f1, f2, f3 */
//template<class T>
//boost::assign::list_inserter<boost::function<void(const T&)> >
// operator+=(const T& factorOrContainer)
//{
// return boost::assign::make_list_inserter(
// boost::bind(&This::push_back<T>, this, _1));
//}
/** Add a factor directly using a shared_ptr */
template<class DERIVEDFACTOR>
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value,
boost::assign::list_inserter<RefCallPushBack<This> > >::type
operator+=(boost::shared_ptr<DERIVEDFACTOR>& factor) {
return boost::assign::make_list_inserter(RefCallPushBack<This>(*this))(factor);
}
/** Add a factor directly using a shared_ptr */
boost::assign::list_inserter<CRefCallPushBack<This> >
operator+=(const sharedFactor& factor) {
return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this))(factor);
}
template<class FACTOR_OR_CONTAINER>
boost::assign::list_inserter<CRefCallPushBack<This> >
operator+=(const FACTOR_OR_CONTAINER& factorOrContainer) {
return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this))(factorOrContainer);
}
///** Add a factor directly using a shared_ptr */
//boost::assign::list_inserter<CRefCallPushBack<This> >
// operator+=(const sharedFactor& factor) {
// return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this));
//}
///** push back many factors as shared_ptr's in a container (factors are not copied) */
//template<typename CONTAINER>
//typename std::enable_if<std::is_base_of<FactorType, typename CONTAINER::value_type::element_type>::value,
// boost::assign::list_inserter<CRefCallPushBack<This> > >::type
// operator+=(const CONTAINER& container) {
// return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this));
//}
///** 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<class CLIQUE>
//boost::assign::list_inserter<CRefCallPushBack<This> >
// operator+=(const BayesTree<CLIQUE>& bayesTree) {
// return boost::assign::make_list_inserter(CRefCallPushBack<This>(*this));
//}
/** Add a factor by value, will be copy-constructed (use push_back with a shared_ptr to avoid
* the copy). */
template<class DERIVEDFACTOR>
typename std::enable_if<std::is_base_of<FactorType, DERIVEDFACTOR>::value>::type
push_back(const DERIVEDFACTOR& factor) {
factors_.push_back(boost::make_shared<DERIVEDFACTOR>(factor)); }
/** push back many factors with an iterator over plain factors (factors are copied) */
template<typename ITERATOR>
typename std::enable_if<std::is_base_of<FactorType, typename ITERATOR::value_type>::value>::type
push_back(ITERATOR firstFactor, ITERATOR lastFactor) {
for(ITERATOR f = firstFactor; f != lastFactor; ++f)
push_back(*f);
}
/** push back many factors as non-pointer objects in a container (factors are copied) */
template<typename CONTAINER>
typename std::enable_if<std::is_base_of<FactorType, typename CONTAINER::value_type>::value>::type
push_back(const CONTAINER& container) {
push_back(container.begin(), container.end());
}
//template<class FACTOR_OR_CONTAINER>
//boost::assign::list_inserter<CRefCallPushBack<This> >
// operator*=(const FACTOR_OR_CONTAINER& factorOrContainer) {
// return boost::assign::make_list_inserter(CRefCallAddCopy<This>(*this));
//}
/// @}
/// @name Testable
/// @{
/** print out graph */
void print(const std::string& s = "FactorGraph",
const IndexFormatter& formatter = DefaultIndexFormatter) const;
const KeyFormatter& formatter = DefaultKeyFormatter) const;
protected:
/** Check equality */
bool equals(const This& fg, double tol = 1e-9) const;
/// @}
public:
/// @name Standard Interface
/// @{
/** return the number of factors and NULLS */
size_t size() const { return factors_.size();}
/** return the number of factors (including any null factors set by remove() ). */
size_t size() const { return factors_.size(); }
/** Simple check for an empty graph - faster than comparing size() to zero */
/** Check if the graph is empty (null factors set by remove() will cause this to return false). */
bool empty() const { return factors_.empty(); }
/** const cast to the underlying vector of factors */
operator const std::vector<sharedFactor>&() const { return factors_; }
/** 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 */
const sharedFactor at(size_t i) const { assert(i<factors_.size()); return factors_[i]; }
sharedFactor& at(size_t i) { assert(i<factors_.size()); return factors_[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); }
/** Checks whether a valid factor exists at the given index */
inline bool exists(size_t i) const { return i<factors_.size() && factors_[i]; }
/** STL begin, so we can use BOOST_FOREACH */
/** Iterator to beginning of factors. */
const_iterator begin() const { return factors_.begin();}
/** STL end, so we can use BOOST_FOREACH */
/** Iterator to end of factors. */
const_iterator end() const { return factors_.end(); }
/** Get the first factor */
@ -180,38 +312,6 @@ class VariableIndex;
/** Get the last factor */
sharedFactor back() const { return factors_.back(); }
/** Eliminate the first \c n frontal variables, returning the resulting
* conditional and remaining factor graph - this is very inefficient for
* eliminating all variables, to do that use EliminationTree or
* JunctionTree.
*/
std::pair<sharedConditional, FactorGraph<FactorType> > eliminateFrontals(size_t nFrontals, const Eliminate& eliminate) const;
/** Factor the factor graph into a conditional and a remaining factor graph.
* Given the factor graph \f$ f(X) \f$, and \c variables to factorize out
* \f$ V \f$, this function factorizes into \f$ f(X) = f(V;Y)f(Y) \f$, where
* \f$ Y := X\V \f$ are the remaining variables. If \f$ f(X) = p(X) \f$ is
* a probability density or likelihood, the factorization produces a
* conditional probability density and a marginal \f$ p(X) = p(V|Y)p(Y) \f$.
*
* For efficiency, this function treats the variables to eliminate
* \c variables as fully-connected, so produces a dense (fully-connected)
* conditional on all of the variables in \c variables, instead of a sparse
* BayesNet. If the variables are not fully-connected, it is more efficient
* to sequentially factorize multiple times.
*/
std::pair<sharedConditional, FactorGraph<FactorType> > eliminate(
const std::vector<KeyType>& variables, const Eliminate& eliminateFcn,
boost::optional<const VariableIndex&> variableIndex = boost::none) const;
/** Eliminate a single variable, by calling FactorGraph::eliminate. */
std::pair<sharedConditional, FactorGraph<FactorType> > eliminateOne(
KeyType variable, const Eliminate& eliminateFcn,
boost::optional<const VariableIndex&> variableIndex = boost::none) const {
std::vector<size_t> variables(1, variable);
return eliminate(variables, eliminateFcn, variableIndex);
}
/// @}
/// @name Modifying Factor Graphs (imperative, discouraged)
/// @{
@ -222,24 +322,33 @@ class VariableIndex;
/** non-const STL-style end() */
iterator end() { return factors_.end(); }
/** resize the factor graph. TODO: effects? */
/** 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.
*/
void resize(size_t size) { factors_.resize(size); }
/** delete factor without re-arranging indexes by inserting a NULL pointer */
inline void remove(size_t i) { factors_[i].reset();}
void remove(size_t i) { factors_[i].reset();}
/** replace a factor by index */
void replace(size_t index, sharedFactor factor);
void replace(size_t index, sharedFactor factor) { at(index) = factor; }
/** Erase factor and rearrange other factors to take up the empty space */
void erase(iterator item) { factors_.erase(item); }
/** Erase factors and rearrange other factors to take up the empty space */
void erase(iterator first, iterator last) { factors_.erase(first, last); }
/// @}
/// @name Advanced Interface
/// @{
/** return the number valid factors */
/** return the number of non-null factors */
size_t nrFactors() const;
/** Potentially very slow function to return all keys involved */
std::set<KeyType> keys() const;
FastSet<Key> keys() const;
private:
@ -254,20 +363,4 @@ class VariableIndex;
}; // FactorGraph
/** Create a combined joint factor (new style for EliminationTree). */
template<class DERIVEDFACTOR, class KEY>
typename DERIVEDFACTOR::shared_ptr Combine(const FactorGraph<DERIVEDFACTOR>& factors,
const FastMap<KEY, std::vector<KEY> >& variableSlots);
/**
* static function that combines two factor graphs
* @param fg1 Linear factor graph
* @param fg2 Linear factor graph
* @return a new combined factor graph
*/
template<class FACTORGRAPH>
FACTORGRAPH combine(const FACTORGRAPH& fg1, const FACTORGRAPH& fg2);
} // namespace gtsam
#include <gtsam/inference/FactorGraph-inl.h>

View File

@ -1,107 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 GenericMultifrontalSolver-inl.h
* @author Richard Roberts
* @date Oct 21, 2010
*/
#pragma once
#include <gtsam/inference/Factor-inl.h>
#include <gtsam/inference/JunctionTree.h>
#include <gtsam/inference/BayesNet-inl.h>
namespace gtsam {
/* ************************************************************************* */
template<class F, class JT>
GenericMultifrontalSolver<F, JT>::GenericMultifrontalSolver(
const FactorGraph<F>& graph) :
structure_(new VariableIndex(graph)), junctionTree_(
new JT(graph, *structure_)) {
}
/* ************************************************************************* */
template<class F, class JT>
GenericMultifrontalSolver<F, JT>::GenericMultifrontalSolver(
const sharedGraph& graph,
const VariableIndex::shared_ptr& variableIndex) :
structure_(variableIndex), junctionTree_(new JT(*graph, *structure_)) {
}
/* ************************************************************************* */
template<class F, class JT>
void GenericMultifrontalSolver<F, JT>::print(const std::string& s) const {
this->structure_->print(s + " structure:\n");
this->junctionTree_->print(s + " jtree:");
}
/* ************************************************************************* */
template<class F, class JT>
bool GenericMultifrontalSolver<F, JT>::equals(
const GenericMultifrontalSolver& expected, double tol) const {
if (!this->structure_->equals(*expected.structure_, tol)) return false;
if (!this->junctionTree_->equals(*expected.junctionTree_, tol)) return false;
return true;
}
/* ************************************************************************* */
template<class F, class JT>
void GenericMultifrontalSolver<F, JT>::replaceFactors(const sharedGraph& graph) {
junctionTree_.reset(new JT(*graph, *structure_));
}
/* ************************************************************************* */
template<class FACTOR, class JUNCTIONTREE>
typename BayesTree<typename FACTOR::ConditionalType>::shared_ptr
GenericMultifrontalSolver<FACTOR, JUNCTIONTREE>::eliminate(Eliminate function) const {
// eliminate junction tree, returns pointer to root
typename BayesTree<typename FACTOR::ConditionalType>::sharedClique
root = junctionTree_->eliminate(function);
// create an empty Bayes tree and insert root clique
typename BayesTree<typename FACTOR::ConditionalType>::shared_ptr
bayesTree(new BayesTree<typename FACTOR::ConditionalType>);
bayesTree->insert(root);
// return the Bayes tree
return bayesTree;
}
/* ************************************************************************* */
template<class F, class JT>
typename FactorGraph<F>::shared_ptr GenericMultifrontalSolver<F, JT>::jointFactorGraph(
const std::vector<Index>& js, Eliminate function) const {
// FIXME: joint for arbitrary sets of variables not present
// TODO: develop and implement theory for shortcuts of more than two variables
if (js.size() != 2) throw std::domain_error(
"*MultifrontalSolver::joint(js) currently can only compute joint marginals\n"
"for exactly two variables. You can call marginal to compute the\n"
"marginal for one variable. *SequentialSolver::joint(js) can compute the\n"
"joint marginal over any number of variables, so use that if necessary.\n");
return eliminate(function)->joint(js[0], js[1], function);
}
/* ************************************************************************* */
template<class F, class JT>
typename boost::shared_ptr<F> GenericMultifrontalSolver<F, JT>::marginalFactor(
Index j, Eliminate function) const {
return eliminate(function)->marginalFactor(j, function);
}
}

View File

@ -1,124 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 GenericMultifrontalSolver.h
* @author Richard Roberts
* @date Oct 21, 2010
*/
#pragma once
#include <utility>
#include <gtsam/inference/JunctionTree.h>
#include <gtsam/inference/FactorGraph.h>
namespace gtsam {
/**
* A Generic Multifrontal Solver class
*
* A solver is given a factor graph at construction, and implements
* a strategy to solve it (in this case, eliminate into a Bayes tree).
* This generic one will create a Bayes tree when eliminate() is called.
*
* Takes two template arguments:
* FACTOR the factor type, e.g., GaussianFactor, DiscreteFactor
* JUNCTIONTREE annoyingly, you also have to supply a compatible JT type
* i.e., one templated on a factor graph with the same factors
* TODO: figure why this is so and possibly fix it
* \nosubgrouping
*/
template<class FACTOR, class JUNCTIONTREE>
class GenericMultifrontalSolver {
protected:
/// Column structure of the factor graph
VariableIndex::shared_ptr structure_;
/// Junction tree that performs elimination.
typename JUNCTIONTREE::shared_ptr junctionTree_;
public:
typedef typename FactorGraph<FACTOR>::shared_ptr sharedGraph;
typedef typename FactorGraph<FACTOR>::Eliminate Eliminate;
/// @name Standard Constructors
/// @{
/**
* Construct the solver for a factor graph. This builds the junction
* tree, which does the symbolic elimination, identifies the cliques,
* and distributes all the factors to the right cliques.
*/
GenericMultifrontalSolver(const FactorGraph<FACTOR>& factorGraph);
/**
* Construct the solver with a shared pointer to a factor graph and to a
* VariableIndex. The solver will store these pointers, so this constructor
* is the fastest.
*/
GenericMultifrontalSolver(const sharedGraph& factorGraph,
const VariableIndex::shared_ptr& variableIndex);
/// @}
/// @name Testable
/// @{
/** Print to cout */
void print(const std::string& name = "GenericMultifrontalSolver: ") const;
/** Test whether is equal to another */
bool equals(const GenericMultifrontalSolver& other, double tol = 1e-9) const;
/// @}
/// @name Standard Interface
/// @{
/**
* Replace the factor graph with a new one having the same structure. The
* This function can be used if the numerical part of the factors changes,
* such as during relinearization or adjusting of noise models.
*/
void replaceFactors(const sharedGraph& factorGraph);
/**
* Eliminate the factor graph sequentially. Uses a column elimination tree
* to recursively eliminate.
*/
typename BayesTree<typename FACTOR::ConditionalType>::shared_ptr
eliminate(Eliminate function) const;
/**
* Compute the marginal joint over a set of variables, by integrating out
* all of the other variables. This function returns the result as a factor
* graph.
*/
typename FactorGraph<FACTOR>::shared_ptr jointFactorGraph(
const std::vector<Index>& js, Eliminate function) const;
/**
* Compute the marginal density over a variable, by integrating out
* all of the other variables. This function returns the result as a factor.
*/
typename boost::shared_ptr<FACTOR> marginalFactor(Index j,
Eliminate function) const;
/// @}
};
} // gtsam
#include <gtsam/inference/GenericMultifrontalSolver-inl.h>

View File

@ -1,203 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 GenericSequentialSolver-inl.h
* @brief Implementation for generic sequential solver
* @author Richard Roberts
* @date Oct 21, 2010
*/
#pragma once
#include <gtsam/inference/Factor.h>
#include <gtsam/inference/FactorGraph.h>
#include <gtsam/inference/EliminationTree.h>
#include <gtsam/inference/BayesNet.h>
#include <gtsam/inference/inference.h>
#include <boost/foreach.hpp>
namespace gtsam {
/* ************************************************************************* */
template<class FACTOR>
GenericSequentialSolver<FACTOR>::GenericSequentialSolver(const FactorGraph<FACTOR>& factorGraph) {
gttic(GenericSequentialSolver_constructor1);
assert(factorGraph.size());
factors_.reset(new FactorGraph<FACTOR>(factorGraph));
structure_.reset(new VariableIndex(factorGraph));
eliminationTree_ = EliminationTree<FACTOR>::Create(*factors_, *structure_);
}
/* ************************************************************************* */
template<class FACTOR>
GenericSequentialSolver<FACTOR>::GenericSequentialSolver(
const sharedFactorGraph& factorGraph,
const boost::shared_ptr<VariableIndex>& variableIndex)
{
gttic(GenericSequentialSolver_constructor2);
factors_ = factorGraph;
structure_ = variableIndex;
eliminationTree_ = EliminationTree<FACTOR>::Create(*factors_, *structure_);
}
/* ************************************************************************* */
template<class FACTOR>
void GenericSequentialSolver<FACTOR>::print(const std::string& s) const {
this->factors_->print(s + " factors:");
this->structure_->print(s + " structure:\n");
this->eliminationTree_->print(s + " etree:");
}
/* ************************************************************************* */
template<class FACTOR>
bool GenericSequentialSolver<FACTOR>::equals(
const GenericSequentialSolver& expected, double tol) const {
if (!this->factors_->equals(*expected.factors_, tol))
return false;
if (!this->structure_->equals(*expected.structure_, tol))
return false;
if (!this->eliminationTree_->equals(*expected.eliminationTree_, tol))
return false;
return true;
}
/* ************************************************************************* */
template<class FACTOR>
void GenericSequentialSolver<FACTOR>::replaceFactors(
const sharedFactorGraph& factorGraph) {
// Reset this shared pointer first to deallocate if possible - for big
// problems there may not be enough memory to store two copies.
eliminationTree_.reset();
factors_ = factorGraph;
eliminationTree_ = EliminationTree<FACTOR>::Create(*factors_, *structure_);
}
/* ************************************************************************* */
template<class FACTOR>
typename GenericSequentialSolver<FACTOR>::sharedBayesNet //
GenericSequentialSolver<FACTOR>::eliminate(Eliminate function) const {
return eliminationTree_->eliminate(function);
}
/* ************************************************************************* */
template<class FACTOR>
typename GenericSequentialSolver<FACTOR>::sharedBayesNet //
GenericSequentialSolver<FACTOR>::eliminate(const Permutation& permutation,
Eliminate function, boost::optional<size_t> nrToEliminate) const
{
gttic(GenericSequentialSolver_eliminate);
// Create inverse permutation
Permutation::shared_ptr permutationInverse(permutation.inverse());
// Permute the factors - NOTE that this permutes the original factors, not
// copies. Other parts of the code may hold shared_ptr's to these factors so
// we must undo the permutation before returning.
BOOST_FOREACH(const typename boost::shared_ptr<FACTOR>& factor, *factors_)
if (factor)
factor->permuteWithInverse(*permutationInverse);
// Eliminate using elimination tree provided
typename EliminationTree<FACTOR>::shared_ptr etree = EliminationTree<FACTOR>::Create(*factors_);
sharedBayesNet bayesNet;
if(nrToEliminate)
bayesNet = etree->eliminatePartial(function, *nrToEliminate);
else
bayesNet = etree->eliminate(function);
// Undo the permutation on the original factors and on the structure.
BOOST_FOREACH(const typename boost::shared_ptr<FACTOR>& factor, *factors_)
if (factor)
factor->permuteWithInverse(permutation);
// Undo the permutation on the conditionals
BOOST_FOREACH(const boost::shared_ptr<Conditional>& c, *bayesNet)
c->permuteWithInverse(permutation);
return bayesNet;
}
/* ************************************************************************* */
template<class FACTOR>
typename GenericSequentialSolver<FACTOR>::sharedBayesNet //
GenericSequentialSolver<FACTOR>::conditionalBayesNet(
const std::vector<Index>& js, size_t nrFrontals,
Eliminate function) const
{
gttic(GenericSequentialSolver_conditionalBayesNet);
// Compute a COLAMD permutation with the marginal variables constrained to the end.
// TODO in case of nrFrontals, the order of js has to be respected here !
Permutation::shared_ptr permutation(
inference::PermutationCOLAMD(*structure_, js, true));
// Eliminate only variables J \cup F from P(J,F,S) to get P(F|S)
size_t nrVariables = structure_->size();
size_t nrMarginalized = nrVariables - js.size();
size_t nrToEliminate = nrMarginalized + nrFrontals;
sharedBayesNet bayesNet = eliminate(*permutation, function, nrToEliminate);
// Get rid of conditionals on variables that we want to marginalize out
for (size_t i = 0; i < nrMarginalized; i++)
bayesNet->pop_front();
return bayesNet;
}
/* ************************************************************************* */
template<class FACTOR>
typename GenericSequentialSolver<FACTOR>::sharedBayesNet //
GenericSequentialSolver<FACTOR>::jointBayesNet(const std::vector<Index>& js,
Eliminate function) const
{
gttic(GenericSequentialSolver_jointBayesNet);
// Compute a COLAMD permutation with the marginal variables constrained to the end.
Permutation::shared_ptr permutation(
inference::PermutationCOLAMD(*structure_, js));
// Eliminate all variables
sharedBayesNet bayesNet = eliminate(*permutation, function);
// Get rid of conditionals on variables that we want to marginalize out
size_t nrMarginalized = bayesNet->size() - js.size();
for (size_t i = 0; i < nrMarginalized; i++)
bayesNet->pop_front();
return bayesNet;
}
/* ************************************************************************* */
template<class FACTOR>
typename FactorGraph<FACTOR>::shared_ptr //
GenericSequentialSolver<FACTOR>::jointFactorGraph(
const std::vector<Index>& js, Eliminate function) const
{
gttic(GenericSequentialSolver_jointFactorGraph);
// Eliminate all variables
typename BayesNet<Conditional>::shared_ptr bayesNet = jointBayesNet(js, function);
return boost::make_shared<FactorGraph<FACTOR> >(*bayesNet);
}
/* ************************************************************************* */
template<class FACTOR>
typename boost::shared_ptr<FACTOR> //
GenericSequentialSolver<FACTOR>::marginalFactor(Index j, Eliminate function) const {
gttic(GenericSequentialSolver_marginalFactor);
// Create a container for the one variable index
std::vector<Index> js(1);
js[0] = j;
// Call joint and return the only factor in the factor graph it returns
// TODO: just call jointBayesNet and grab last conditional, then toFactor....
return (*this->jointFactorGraph(js, function))[0];
}
} // namespace gtsam

View File

@ -1,179 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 GenericSequentialSolver.h
* @brief generic sequential elimination
* @author Richard Roberts
* @date Oct 21, 2010
*/
#pragma once
#include <gtsam/global_includes.h>
#include <gtsam/base/Testable.h>
#include <boost/function.hpp>
#include <boost/optional.hpp>
#include <utility>
#include <vector>
namespace gtsam {
class VariableIndex;
class Permutation;
}
namespace gtsam {
template<class FACTOR> class EliminationTree;
}
namespace gtsam {
template<class FACTOR> class FactorGraph;
}
namespace gtsam {
template<class CONDITIONAL> class BayesNet;
}
namespace gtsam {
/**
* This solver implements sequential variable elimination for factor graphs.
* Underlying this is a column elimination tree, see Gilbert 2001 BIT.
*
* The elimination ordering is "baked in" to the variable indices at this
* stage, i.e. elimination proceeds in order from '0'.
*
* This is not the most efficient algorithm we provide, most efficient is the
* MultifrontalSolver, which examines and uses the clique structure.
* However, sequential variable elimination is easier to understand so this is a good
* starting point to learn about these algorithms and our implementation.
* Additionally, the first step of MFQR is symbolic sequential elimination.
* \nosubgrouping
*/
template<class FACTOR>
class GenericSequentialSolver {
protected:
typedef boost::shared_ptr<FactorGraph<FACTOR> > sharedFactorGraph;
typedef typename FACTOR::ConditionalType Conditional;
typedef typename boost::shared_ptr<Conditional> sharedConditional;
typedef typename boost::shared_ptr<BayesNet<Conditional> > sharedBayesNet;
typedef std::pair<boost::shared_ptr<Conditional>, boost::shared_ptr<FACTOR> > EliminationResult;
typedef boost::function<
EliminationResult(const FactorGraph<FACTOR>&, size_t)> Eliminate;
/** Store the original factors for computing marginals
* TODO Frank says: really? Marginals should be computed from result.
*/
sharedFactorGraph factors_;
/** Store column structure of the factor graph. Why? */
boost::shared_ptr<VariableIndex> structure_;
/** Elimination tree that performs elimination */
boost::shared_ptr<EliminationTree<FACTOR> > eliminationTree_;
/** concept checks */
GTSAM_CONCEPT_TESTABLE_TYPE(FACTOR)
// GTSAM_CONCEPT_TESTABLE_TYPE(EliminationTree)
/**
* Eliminate in a different order, given a permutation
*/
sharedBayesNet eliminate(const Permutation& permutation, Eliminate function,
boost::optional<size_t> nrToEliminate = boost::none ///< If given a number of variables to eliminate, will only eliminate that many
) const;
public:
/// @name Standard Constructors
/// @{
/**
* Construct the solver for a factor graph. This builds the elimination
* tree, which already does some of the work of elimination.
*/
GenericSequentialSolver(const FactorGraph<FACTOR>& factorGraph);
/**
* Construct the solver with a shared pointer to a factor graph and to a
* VariableIndex. The solver will store these pointers, so this constructor
* is the fastest.
*/
GenericSequentialSolver(const sharedFactorGraph& factorGraph,
const boost::shared_ptr<VariableIndex>& variableIndex);
/// @}
/// @name Testable
/// @{
/** Print to cout */
void print(const std::string& name = "GenericSequentialSolver: ") const;
/** Test whether is equal to another */
bool equals(const GenericSequentialSolver& other, double tol = 1e-9) const;
/// @}
/// @name Standard Interface
/// @{
/**
* Replace the factor graph with a new one having the same structure. The
* This function can be used if the numerical part of the factors changes,
* such as during relinearization or adjusting of noise models.
*/
void replaceFactors(const sharedFactorGraph& factorGraph);
/**
* Eliminate the factor graph sequentially. Uses a column elimination tree
* to recursively eliminate.
*/
sharedBayesNet eliminate(Eliminate function) const;
/**
* Compute a conditional density P(F|S) while marginalizing out variables J
* P(F|S) is obtained by P(J,F,S)=P(J|F,S)P(F|S)P(S) and dropping P(S)
* Returns the result as a Bayes net.
*/
sharedBayesNet
conditionalBayesNet(const std::vector<Index>& js, size_t nrFrontals,
Eliminate function) const;
/**
* Compute the marginal joint over a set of variables, by integrating out
* all of the other variables. Returns the result as a Bayes net
*/
sharedBayesNet
jointBayesNet(const std::vector<Index>& js, Eliminate function) const;
/**
* Compute the marginal joint over a set of variables, by integrating out
* all of the other variables. Returns the result as a factor graph.
*/
typename FactorGraph<FACTOR>::shared_ptr
jointFactorGraph(const std::vector<Index>& js, Eliminate function) const;
/**
* Compute the marginal Gaussian density over a variable, by integrating out
* all of the other variables. This function returns the result as a factor.
*/
typename boost::shared_ptr<FACTOR>
marginalFactor(Index j, Eliminate function) const;
/// @}
}
;
// GenericSequentialSolver
}// namespace gtsam
#include <gtsam/inference/GenericSequentialSolver-inl.h>

View File

@ -1,74 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 ISAM-inl.h
* @brief Incremental update functionality (iSAM) for BayesTree.
* @author Michael Kaess
*/
#pragma once
#include <boost/foreach.hpp>
#include <boost/assign/std/list.hpp> // for operator +=
#include <gtsam/inference/Conditional.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/inference/GenericMultifrontalSolver.h>
namespace gtsam {
/* ************************************************************************* */
template<class CONDITIONAL>
ISAM<CONDITIONAL>::ISAM() : BayesTree<CONDITIONAL>() {}
/* ************************************************************************* */
template<class CONDITIONAL>
template<class FG> void ISAM<CONDITIONAL>::update_internal(
const FG& newFactors, Cliques& orphans, typename FG::Eliminate function) {
// Remove the contaminated part of the Bayes tree
// Throw exception if disconnected
BayesNet<CONDITIONAL> bn;
if (!this->empty()) {
this->removeTop(newFactors.keys(), bn, orphans);
if (bn.empty())
throw std::runtime_error(
"ISAM::update_internal(): no variables in common between existing Bayes tree and incoming factors!");
}
FG factors(bn);
// add the factors themselves
factors.push_back(newFactors);
// eliminate into a Bayes net
GenericMultifrontalSolver<typename CONDITIONAL::FactorType, JunctionTree<FG> > solver(factors);
boost::shared_ptr<BayesTree<CONDITIONAL> > bayesTree;
bayesTree = solver.eliminate(function);
this->root_ = bayesTree->root();
this->nodes_ = bayesTree->nodes();
// add orphans to the bottom of the new tree
BOOST_FOREACH(sharedClique orphan, orphans)
this->insert(orphan);
}
/* ************************************************************************* */
template<class CONDITIONAL>
template<class FG>
void ISAM<CONDITIONAL>::update(const FG& newFactors,
typename FG::Eliminate function) {
Cliques orphans;
this->update_internal(newFactors, orphans, function);
}
}
/// namespace gtsam

View File

@ -0,0 +1,65 @@
/* ----------------------------------------------------------------------------
* 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 ISAM-inl.h
* @brief Incremental update functionality (iSAM) for BayesTree.
* @author Michael Kaess
*/
#pragma once
#include <gtsam/inference/ISAM.h>
#include <gtsam/inference/VariableIndex.h>
namespace gtsam {
/* ************************************************************************* */
template<class BAYESTREE>
void ISAM<BAYESTREE>::update_internal(const FactorGraphType& newFactors, Cliques& orphans, const Eliminate& function)
{
// Remove the contaminated part of the Bayes tree
// Throw exception if disconnected
BayesNetType bn;
if (!this->empty()) {
const FastSet<Key> newFactorKeys = newFactors.keys();
this->removeTop(std::vector<Key>(newFactorKeys.begin(), newFactorKeys.end()), bn, orphans);
}
// Add the removed top and the new factors
FactorGraphType factors;
factors += bn;
factors += newFactors;
// Add the orphaned subtrees
BOOST_FOREACH(const sharedClique& orphan, orphans)
factors += boost::make_shared<BayesTreeOrphanWrapper<Clique> >(orphan);
// eliminate into a Bayes net
const VariableIndex varIndex(factors);
const FastSet<Key> newFactorKeys = newFactors.keys();
const Ordering constrainedOrdering =
Ordering::COLAMDConstrainedLast(varIndex, std::vector<Key>(newFactorKeys.begin(), newFactorKeys.end()));
Base bayesTree = *factors.eliminateMultifrontal(constrainedOrdering, function, varIndex);
this->roots_.insert(this->roots_.end(), bayesTree.roots().begin(), bayesTree.roots().end());
this->nodes_.insert(bayesTree.nodes().begin(), bayesTree.nodes().end());
}
/* ************************************************************************* */
template<class BAYESTREE>
void ISAM<BAYESTREE>::update(const FactorGraphType& newFactors, const Eliminate& function)
{
Cliques orphans;
this->update_internal(newFactors, orphans, function);
}
}
/// namespace gtsam

View File

@ -18,8 +18,6 @@
// \callgraph
#pragma once
#include <gtsam/inference/BayesTree.h>
namespace gtsam {
/**
@ -27,12 +25,19 @@ namespace gtsam {
* Given a set of new factors, it re-eliminates the invalidated part of the tree.
* \nosubgrouping
*/
template<class CONDITIONAL>
class ISAM: public BayesTree<CONDITIONAL> {
template<class BAYESTREE>
class ISAM: public BAYESTREE
{
private:
typedef BayesTree<CONDITIONAL> Base;
typedef BAYESTREE Base;
typedef typename Base::BayesNetType BayesNetType;
typedef typename Base::FactorGraphType FactorGraphType;
typedef typename Base::Clique Clique;
typedef typename Base::sharedClique sharedClique;
typedef typename Base::Cliques Cliques;
typedef typename Base::Eliminate Eliminate;
typedef typename Base::EliminationTraitsType EliminationTraitsType;
public:
@ -40,12 +45,10 @@ namespace gtsam {
/// @{
/** Create an empty Bayes Tree */
ISAM();
ISAM() {}
/** Copy constructor */
ISAM(const Base& bayesTree) :
Base(bayesTree) {
}
ISAM(const Base& bayesTree) : Base(bayesTree) {}
/// @}
/// @name Advanced Interface Interface
@ -56,21 +59,14 @@ namespace gtsam {
* @param newFactors is a factor graph that contains the new factors
* @param function an elimination routine
*/
template<class FG>
void update(const FG& newFactors, typename FG::Eliminate function);
typedef typename BayesTree<CONDITIONAL>::sharedClique sharedClique; ///<TODO: comment
typedef typename BayesTree<CONDITIONAL>::Cliques Cliques; ///<TODO: comment
void update(const FactorGraphType& newFactors, const Eliminate& function = EliminationTraitsType::DefaultEliminate);
/** update_internal provides access to list of orphans for drawing purposes */
template<class FG>
void update_internal(const FG& newFactors, Cliques& orphans,
typename FG::Eliminate function);
void update_internal(const FactorGraphType& newFactors, Cliques& orphans,
const Eliminate& function = EliminationTraitsType::DefaultEliminate);
/// @}
};
}/// namespace gtsam
#include <gtsam/inference/ISAM-inl.h>

View File

@ -1,65 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 IndexConditional.cpp
* @author Richard Roberts
* @date Oct 17, 2010
*/
#include <gtsam/base/FastSet.h>
#include <gtsam/inference/IndexConditional.h>
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
#include <boost/lambda/lambda.hpp>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
namespace gtsam {
using namespace std;
using namespace boost::lambda;
/* ************************************************************************* */
void IndexConditional::assertInvariants() const {
// Checks for uniqueness of keys
Base::assertInvariants();
}
/* ************************************************************************* */
bool IndexConditional::reduceSeparatorWithInverse(const internal::Reduction& inverseReduction) {
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
BOOST_FOREACH(KeyType key, frontals()) { assert(inverseReduction.find(key) == inverseReduction.end()); }
#endif
bool parentChanged = false;
BOOST_FOREACH(KeyType& parent, parents()) {
internal::Reduction::const_iterator it = inverseReduction.find(parent);
if(it != inverseReduction.end()) {
parentChanged = true;
parent = it->second;
}
}
assertInvariants();
return parentChanged;
}
/* ************************************************************************* */
void IndexConditional::permuteWithInverse(const Permutation& inversePermutation) {
BOOST_FOREACH(Index& key, keys())
key = inversePermutation[key];
assertInvariants();
}
/* ************************************************************************* */
}

View File

@ -1,135 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 IndexConditional.h
* @author Richard Roberts
* @date Oct 17, 2010
*/
#pragma once
#include <gtsam/global_includes.h>
#include <gtsam/inference/Conditional.h>
#include <gtsam/inference/IndexFactor.h>
#include <gtsam/inference/Permutation.h>
namespace gtsam {
/**
* IndexConditional serves two purposes. It is the base class for
* GaussianConditional, and also functions as a symbolic conditional with
* Index keys, produced by symbolic elimination of IndexFactor.
*
* It derives from Conditional with a key type of Index, which is an
* unsigned integer.
* \nosubgrouping
*/
class IndexConditional : public Conditional<Index> {
protected:
// Checks that frontal indices are sorted and lower than parent indices
GTSAM_EXPORT void assertInvariants() const;
public:
typedef IndexConditional This;
typedef Conditional<Index> Base;
typedef IndexFactor FactorType;
typedef boost::shared_ptr<IndexConditional> shared_ptr;
/// @name Standard Constructors
/// @{
/** Empty Constructor to make serialization possible */
IndexConditional() { assertInvariants(); }
/** No parents */
IndexConditional(Index j) : Base(j) { assertInvariants(); }
/** Single parent */
IndexConditional(Index j, Index parent) : Base(j, parent) { assertInvariants(); }
/** Two parents */
IndexConditional(Index j, Index parent1, Index parent2) : Base(j, parent1, parent2) { assertInvariants(); }
/** Three parents */
IndexConditional(Index j, Index parent1, Index parent2, Index parent3) : Base(j, parent1, parent2, parent3) { assertInvariants(); }
/// @}
/// @name Advanced Constructors
/// @{
/** Constructor from a frontal variable and a vector of parents */
IndexConditional(Index j, const std::vector<Index>& parents) : Base(j, parents) {
assertInvariants();
}
/** Constructor from keys and nr of frontal variables */
IndexConditional(const std::vector<Index>& keys, size_t nrFrontals) :
Base(keys, nrFrontals) {
assertInvariants();
}
/** Constructor from keys and nr of frontal variables */
IndexConditional(const std::list<Index>& keys, size_t nrFrontals) :
Base(keys, nrFrontals) {
assertInvariants();
}
/// @}
/// @name Standard Interface
/// @{
/** Named constructor directly returning a shared pointer */
template<class KEYS>
static shared_ptr FromKeys(const KEYS& keys, size_t nrFrontals) {
shared_ptr conditional(new IndexConditional());
conditional->keys_.assign(keys.begin(), keys.end());
conditional->nrFrontals_ = nrFrontals;
return conditional;
}
/** Convert to a factor */
IndexFactor::shared_ptr toFactor() const {
return IndexFactor::shared_ptr(new IndexFactor(*this));
}
/// @}
/// @name Advanced Interface
/// @{
/** 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.
*/
GTSAM_EXPORT bool reduceSeparatorWithInverse(const internal::Reduction& inverseReduction);
/**
* Permutes the Conditional, but for efficiency requires the permutation
* to already be inverted.
*/
GTSAM_EXPORT void permuteWithInverse(const Permutation& inversePermutation);
private:
/** Serialization function */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
}
/// @}
};
}

View File

@ -1,71 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 IndexFactor.cpp
* @author Richard Roberts
* @date Oct 17, 2010
*/
#include <gtsam/inference/IndexFactor.h>
#include <gtsam/inference/Factor.h>
#include <gtsam/inference/IndexConditional.h>
using namespace std;
namespace gtsam {
/* ************************************************************************* */
void IndexFactor::assertInvariants() const {
Base::assertInvariants();
}
/* ************************************************************************* */
IndexFactor::IndexFactor(const IndexConditional& c) :
Base(c) {
assertInvariants();
}
/* ************************************************************************* */
#ifdef TRACK_ELIMINATE
boost::shared_ptr<IndexConditional> IndexFactor::eliminateFirst() {
boost::shared_ptr<IndexConditional> result(Base::eliminateFirst<
IndexConditional>());
assertInvariants();
return result;
}
/* ************************************************************************* */
boost::shared_ptr<BayesNet<IndexConditional> > IndexFactor::eliminate(
size_t nrFrontals) {
boost::shared_ptr<BayesNet<IndexConditional> > result(Base::eliminate<
IndexConditional>(nrFrontals));
assertInvariants();
return result;
}
#endif
/* ************************************************************************* */
void IndexFactor::permuteWithInverse(const Permutation& inversePermutation) {
BOOST_FOREACH(Index& key, keys())
key = inversePermutation[key];
assertInvariants();
}
/* ************************************************************************* */
void IndexFactor::reduceWithInverse(const internal::Reduction& inverseReduction) {
BOOST_FOREACH(Index& key, keys())
key = inverseReduction[key];
assertInvariants();
}
/* ************************************************************************* */
} // gtsam

View File

@ -1,179 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 IndexFactor.h
* @author Richard Roberts
* @date Oct 17, 2010
*/
#pragma once
#include <gtsam/inference/Factor.h>
#include <gtsam/inference/Permutation.h>
namespace gtsam {
// Forward declaration of IndexConditional
class IndexConditional;
/**
* IndexFactor serves two purposes. It is the base class for all linear
* factors (GaussianFactor, JacobianFactor, HessianFactor), and also
* functions as a symbolic factor with Index keys, used to do symbolic
* elimination by JunctionTree.
*
* It derives from Factor with a key type of Index, an unsigned integer.
* \nosubgrouping
*/
class IndexFactor: public Factor<Index> {
protected:
/// @name Advanced Interface
/// @{
/// Internal function for checking class invariants (unique keys for this factor)
GTSAM_EXPORT void assertInvariants() const;
/// @}
public:
typedef IndexFactor This;
typedef Factor<Index> Base;
/** Elimination produces an IndexConditional */
typedef IndexConditional ConditionalType;
/** Overriding the shared_ptr typedef */
typedef boost::shared_ptr<IndexFactor> shared_ptr;
/// @name Standard Interface
/// @{
/** Copy constructor */
IndexFactor(const This& f) :
Base(f) {
assertInvariants();
}
/** Construct from derived type */
GTSAM_EXPORT IndexFactor(const IndexConditional& c);
/** Default constructor for I/O */
IndexFactor() {
assertInvariants();
}
/** Construct unary factor */
IndexFactor(Index j) :
Base(j) {
assertInvariants();
}
/** Construct binary factor */
IndexFactor(Index j1, Index j2) :
Base(j1, j2) {
assertInvariants();
}
/** Construct ternary factor */
IndexFactor(Index j1, Index j2, Index j3) :
Base(j1, j2, j3) {
assertInvariants();
}
/** Construct 4-way factor */
IndexFactor(Index j1, Index j2, Index j3, Index j4) :
Base(j1, j2, j3, j4) {
assertInvariants();
}
/** Construct 5-way factor */
IndexFactor(Index j1, Index j2, Index j3, Index j4, Index j5) :
Base(j1, j2, j3, j4, j5) {
assertInvariants();
}
/** Construct 6-way factor */
IndexFactor(Index j1, Index j2, Index j3, Index j4, Index j5, Index j6) :
Base(j1, j2, j3, j4, j5, j6) {
assertInvariants();
}
/// @}
/// @name Advanced Constructors
/// @{
/** Construct n-way factor */
IndexFactor(const std::set<Index>& js) :
Base(js) {
assertInvariants();
}
/** Construct n-way factor */
IndexFactor(const std::vector<Index>& js) :
Base(js) {
assertInvariants();
}
/** Constructor from a collection of keys */
template<class KeyIterator> IndexFactor(KeyIterator beginKey,
KeyIterator endKey) :
Base(beginKey, endKey) {
assertInvariants();
}
/// @}
#ifdef TRACK_ELIMINATE
/**
* eliminate the first variable involved in this factor
* @return a conditional on the eliminated variable
*/
GTSAM_EXPORT boost::shared_ptr<ConditionalType> eliminateFirst();
/** eliminate the first nrFrontals frontal variables.*/
GTSAM_EXPORT boost::shared_ptr<BayesNet<ConditionalType> > eliminate(size_t nrFrontals =
1);
#endif
/// @name Advanced Interface
/// @{
/**
* Permutes the factor, but for efficiency requires the permutation
* to already be inverted.
*/
GTSAM_EXPORT void permuteWithInverse(const Permutation& inversePermutation);
/**
* Apply a reduction, which is a remapping of variable indices.
*/
GTSAM_EXPORT void reduceWithInverse(const internal::Reduction& inverseReduction);
virtual ~IndexFactor() {
}
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
}
/// @}
}; // IndexFactor
}

View File

@ -1,205 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 JunctionTree-inl.h
* @date Feb 4, 2010
* @author Kai Ni
* @author Frank Dellaert
* @brief The junction tree, template bodies
*/
#pragma once
#include <gtsam/inference/SymbolicFactorGraph.h>
#include <gtsam/inference/VariableSlots.h>
#include <gtsam/inference/EliminationTree.h>
#include <gtsam/base/timing.h>
#include <boost/foreach.hpp>
namespace gtsam {
/* ************************************************************************* */
template <class FG, class BTCLIQUE>
void JunctionTree<FG,BTCLIQUE>::construct(const FG& fg, const VariableIndex& variableIndex) {
gttic(JT_symbolic_ET);
const typename EliminationTree<IndexFactor>::shared_ptr symETree =
EliminationTree<IndexFactor>::Create(fg, variableIndex);
assert(symETree.get());
gttoc(JT_symbolic_ET);
gttic(JT_symbolic_eliminate);
SymbolicBayesNet::shared_ptr sbn = symETree->eliminate(&EliminateSymbolic);
assert(sbn.get());
gttoc(JT_symbolic_eliminate);
gttic(symbolic_BayesTree);
SymbolicBayesTree sbt(*sbn);
gttoc(symbolic_BayesTree);
// distribute factors
gttic(distributeFactors);
this->root_ = distributeFactors(fg, sbt.root());
gttoc(distributeFactors);
}
/* ************************************************************************* */
template <class FG, class BTCLIQUE>
JunctionTree<FG,BTCLIQUE>::JunctionTree(const FG& fg) {
gttic(VariableIndex);
VariableIndex varIndex(fg);
gttoc(VariableIndex);
construct(fg, varIndex);
}
/* ************************************************************************* */
template <class FG, class BTCLIQUE>
JunctionTree<FG,BTCLIQUE>::JunctionTree(const FG& fg, const VariableIndex& variableIndex) {
construct(fg, variableIndex);
}
/* ************************************************************************* */
template<class FG, class BTCLIQUE>
typename JunctionTree<FG,BTCLIQUE>::sharedClique JunctionTree<FG,BTCLIQUE>::distributeFactors(
const FG& fg, const SymbolicBayesTree::sharedClique& bayesClique) {
// Build "target" index. This is an index for each variable of the factors
// that involve this variable as their *lowest-ordered* variable. For each
// factor, it is the lowest-ordered variable of that factor that pulls the
// factor into elimination, after which all of the information in the
// factor is contained in the eliminated factors that are passed up the
// tree as elimination continues.
// Two stages - first build an array of the lowest-ordered variable in each
// factor and find the last variable to be eliminated.
std::vector<Index> lowestOrdered(fg.size(), std::numeric_limits<Index>::max());
Index maxVar = 0;
for(size_t i=0; i<fg.size(); ++i)
if(fg[i]) {
typename FG::FactorType::const_iterator min = std::min_element(fg[i]->begin(), fg[i]->end());
if(min != fg[i]->end()) {
lowestOrdered[i] = *min;
maxVar = std::max(maxVar, *min);
}
}
// Now add each factor to the list corresponding to its lowest-ordered
// variable.
std::vector<FastList<size_t> > targets(maxVar+1);
for(size_t i=0; i<lowestOrdered.size(); ++i)
if(lowestOrdered[i] != std::numeric_limits<Index>::max())
targets[lowestOrdered[i]].push_back(i);
// Now call the recursive distributeFactors
return distributeFactors(fg, targets, bayesClique);
}
/* ************************************************************************* */
template<class FG, class BTCLIQUE>
typename JunctionTree<FG,BTCLIQUE>::sharedClique JunctionTree<FG,BTCLIQUE>::distributeFactors(const FG& fg,
const std::vector<FastList<size_t> >& targets,
const SymbolicBayesTree::sharedClique& bayesClique) {
if(bayesClique) {
// create a new clique in the junction tree
sharedClique clique(new Clique((*bayesClique)->beginFrontals(), (*bayesClique)->endFrontals(),
(*bayesClique)->beginParents(), (*bayesClique)->endParents()));
// count the factors for this cluster to pre-allocate space
{
size_t nFactors = 0;
BOOST_FOREACH(const Index frontal, clique->frontal) {
// There may be less variables in "targets" than there really are if
// some of the highest-numbered variables do not pull in any factors.
if(frontal < targets.size())
nFactors += targets[frontal].size(); }
clique->reserve(nFactors);
}
// add the factors to this cluster
BOOST_FOREACH(const Index frontal, clique->frontal) {
if(frontal < targets.size()) {
BOOST_FOREACH(const size_t factorI, targets[frontal]) {
clique->push_back(fg[factorI]); } } }
// recursively call the children
BOOST_FOREACH(const typename SymbolicBayesTree::sharedClique bayesChild, bayesClique->children()) {
sharedClique child = distributeFactors(fg, targets, bayesChild);
clique->addChild(child);
child->parent() = clique;
}
return clique;
} else
return sharedClique();
}
/* ************************************************************************* */
template<class FG, class BTCLIQUE>
std::pair<typename JunctionTree<FG,BTCLIQUE>::BTClique::shared_ptr,
typename FG::sharedFactor> JunctionTree<FG,BTCLIQUE>::eliminateOneClique(
typename FG::Eliminate function,
const boost::shared_ptr<const Clique>& current) const {
FG fg; // factor graph will be assembled from local factors and marginalized children
fg.reserve(current->size() + current->children().size());
fg.push_back(*current); // add the local factors
// receive the factors from the child and its clique point
std::list<typename BTClique::shared_ptr> children;
BOOST_FOREACH(const boost::shared_ptr<const Clique>& child, current->children()) {
std::pair<typename BTClique::shared_ptr, typename FG::sharedFactor> tree_factor(
eliminateOneClique(function, child));
children.push_back(tree_factor.first);
fg.push_back(tree_factor.second);
}
// eliminate the combined factors
// warning: fg is being eliminated in-place and will contain marginal afterwards
// Now that we know which factors and variables, and where variables
// come from and go to, create and eliminate the new joint factor.
gttic(CombineAndEliminate);
typename FG::EliminationResult eliminated(function(fg,
current->frontal.size()));
gttoc(CombineAndEliminate);
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
assert(std::equal(eliminated.second->begin(), eliminated.second->end(), current->separator.begin()));
#endif
gttic(Update_tree);
// create a new clique corresponding the combined factors
typename BTClique::shared_ptr new_clique(BTClique::Create(eliminated));
new_clique->children_ = children;
BOOST_FOREACH(typename BTClique::shared_ptr& childRoot, children) {
childRoot->parent_ = new_clique;
}
gttoc(Update_tree);
return std::make_pair(new_clique, eliminated.second);
}
/* ************************************************************************* */
template<class FG, class BTCLIQUE>
typename BTCLIQUE::shared_ptr JunctionTree<FG,BTCLIQUE>::eliminate(
typename FG::Eliminate function) const {
if (this->root()) {
gttic(JT_eliminate);
std::pair<typename BTClique::shared_ptr, typename FG::sharedFactor> ret =
this->eliminateOneClique(function, this->root());
if (ret.second->size() != 0) throw std::runtime_error(
"JuntionTree::eliminate: elimination failed because of factors left over!");
gttoc(JT_eliminate);
return ret.first;
} else
return typename BTClique::shared_ptr();
}
} //namespace gtsam

View File

@ -0,0 +1,313 @@
/* ----------------------------------------------------------------------------
* 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 JunctionTree-inl.h
* @date Feb 4, 2010
* @author Kai Ni
* @author Frank Dellaert
* @author Richard Roberts
* @brief The junction tree, template bodies
*/
#pragma once
#include <gtsam/base/timing.h>
#include <gtsam/base/treeTraversal-inst.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/inference/JunctionTree.h>
#include <gtsam/symbolic/SymbolicConditional.h>
#include <gtsam/symbolic/SymbolicFactorGraph.h>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
namespace gtsam {
namespace {
/* ************************************************************************* */
template<class BAYESTREE, class GRAPH>
struct ConstructorTraversalData {
ConstructorTraversalData* const parentData;
typename JunctionTree<BAYESTREE,GRAPH>::sharedNode myJTNode;
std::vector<SymbolicConditional::shared_ptr> childSymbolicConditionals;
std::vector<SymbolicFactor::shared_ptr> childSymbolicFactors;
ConstructorTraversalData(ConstructorTraversalData* _parentData) : parentData(_parentData) {}
};
/* ************************************************************************* */
// Pre-order visitor function
template<class BAYESTREE, class GRAPH, class ETREE_NODE>
ConstructorTraversalData<BAYESTREE,GRAPH> ConstructorTraversalVisitorPre(
const boost::shared_ptr<ETREE_NODE>& node,
ConstructorTraversalData<BAYESTREE,GRAPH>& parentData)
{
// On the pre-order pass, before children have been visited, we just set up a traversal data
// structure with its own JT node, and create a child pointer in its parent.
ConstructorTraversalData<BAYESTREE,GRAPH> myData = ConstructorTraversalData<BAYESTREE,GRAPH>(&parentData);
myData.myJTNode = boost::make_shared<typename JunctionTree<BAYESTREE,GRAPH>::Node>();
myData.myJTNode->keys.push_back(node->key);
myData.myJTNode->factors.insert(myData.myJTNode->factors.begin(), node->factors.begin(), node->factors.end());
parentData.myJTNode->children.push_back(myData.myJTNode);
return myData;
}
/* ************************************************************************* */
// Post-order visitor function
template<class BAYESTREE, class GRAPH, class ETREE_NODE>
void ConstructorTraversalVisitorPost(
const boost::shared_ptr<ETREE_NODE>& ETreeNode,
const ConstructorTraversalData<BAYESTREE,GRAPH>& myData)
{
// In this post-order visitor, we combine the symbolic elimination results from the
// elimination tree children and symbolically eliminate the current elimination tree node. We
// then check whether each of our elimination tree child nodes should be merged with us. The
// check for this is that our number of symbolic elimination parents is exactly 1 less than
// our child's symbolic elimination parents - this condition indicates that eliminating the
// current node did not introduce any parents beyond those already in the child.
// Do symbolic elimination for this node
SymbolicFactorGraph symbolicFactors;
symbolicFactors.reserve(ETreeNode->factors.size() + myData.childSymbolicFactors.size());
// Add symbolic versions of the ETree node factors
BOOST_FOREACH(const typename GRAPH::sharedFactor& factor, ETreeNode->factors) {
symbolicFactors.push_back(boost::make_shared<SymbolicFactor>(
SymbolicFactor::FromKeys(*factor))); }
// Add symbolic factors passed up from children
symbolicFactors.push_back(myData.childSymbolicFactors.begin(), myData.childSymbolicFactors.end());
Ordering keyAsOrdering; keyAsOrdering.push_back(ETreeNode->key);
std::pair<SymbolicConditional::shared_ptr, SymbolicFactor::shared_ptr> symbolicElimResult =
EliminateSymbolic(symbolicFactors, keyAsOrdering);
// Store symbolic elimination results in the parent
myData.parentData->childSymbolicConditionals.push_back(symbolicElimResult.first);
myData.parentData->childSymbolicFactors.push_back(symbolicElimResult.second);
// Merge our children if they are in our clique - if our conditional has exactly one fewer
// parent than our child's conditional.
const size_t myNrParents = symbolicElimResult.first->nrParents();
size_t nrMergedChildren = 0;
assert(myData.myJTNode->children.size() == myData.childSymbolicConditionals.size());
// Loop over children
int combinedProblemSize = (int)symbolicElimResult.first->size();
for(size_t child = 0; child < myData.childSymbolicConditionals.size(); ++child) {
// Check if we should merge the child
if(myNrParents + 1 == myData.childSymbolicConditionals[child]->nrParents()) {
// Get a reference to the child, adjusting the index to account for children previously
// merged and removed from the child list.
const typename JunctionTree<BAYESTREE,GRAPH>::Node& childToMerge =
*myData.myJTNode->children[child - nrMergedChildren];
// Merge keys, factors, and children.
myData.myJTNode->keys.insert(myData.myJTNode->keys.begin(), childToMerge.keys.begin(), childToMerge.keys.end());
myData.myJTNode->factors.insert(myData.myJTNode->factors.end(), childToMerge.factors.begin(), childToMerge.factors.end());
myData.myJTNode->children.insert(myData.myJTNode->children.end(), childToMerge.children.begin(), childToMerge.children.end());
// Remove child from list.
myData.myJTNode->children.erase(myData.myJTNode->children.begin() + (child - nrMergedChildren));
// Increment number of merged children
++ nrMergedChildren;
// Increment problem size
combinedProblemSize = std::max(combinedProblemSize, childToMerge.problemSize_);
}
}
myData.myJTNode->problemSize_ = combinedProblemSize;
}
/* ************************************************************************* */
// Elimination traversal data - stores a pointer to the parent data and collects the factors
// resulting from elimination of the children. Also sets up BayesTree cliques with parent and
// child pointers.
template<class JUNCTIONTREE>
struct EliminationData {
EliminationData* const parentData;
size_t myIndexInParent;
std::vector<typename JUNCTIONTREE::sharedFactor> childFactors;
boost::shared_ptr<typename JUNCTIONTREE::BayesTreeType::Node> bayesTreeNode;
EliminationData(EliminationData* _parentData, size_t nChildren) :
parentData(_parentData),
bayesTreeNode(boost::make_shared<typename JUNCTIONTREE::BayesTreeType::Node>())
{
if(parentData) {
myIndexInParent = parentData->childFactors.size();
parentData->childFactors.push_back(typename JUNCTIONTREE::sharedFactor());
} else {
myIndexInParent = 0;
}
// Set up BayesTree parent and child pointers
if(parentData) {
if(parentData->parentData) // If our parent is not the dummy node
bayesTreeNode->parent_ = parentData->bayesTreeNode;
parentData->bayesTreeNode->children.push_back(bayesTreeNode);
}
}
};
/* ************************************************************************* */
// Elimination pre-order visitor - just creates the EliminationData structure for the visited
// node.
template<class JUNCTIONTREE>
EliminationData<JUNCTIONTREE> eliminationPreOrderVisitor(
const typename JUNCTIONTREE::sharedNode& node, EliminationData<JUNCTIONTREE>& parentData)
{
return EliminationData<JUNCTIONTREE>(&parentData, node->children.size());
}
/* ************************************************************************* */
// Elimination post-order visitor - combine the child factors with our own factors, add the
// resulting conditional to the BayesTree, and add the remaining factor to the parent.
template<class JUNCTIONTREE>
struct EliminationPostOrderVisitor
{
const typename JUNCTIONTREE::Eliminate& eliminationFunction;
typename JUNCTIONTREE::BayesTreeType::Nodes& nodesIndex;
EliminationPostOrderVisitor(const typename JUNCTIONTREE::Eliminate& eliminationFunction,
typename JUNCTIONTREE::BayesTreeType::Nodes& nodesIndex) :
eliminationFunction(eliminationFunction), nodesIndex(nodesIndex) {}
void operator()(const typename JUNCTIONTREE::sharedNode& node, EliminationData<JUNCTIONTREE>& myData)
{
// Typedefs
typedef typename JUNCTIONTREE::sharedFactor sharedFactor;
typedef typename JUNCTIONTREE::FactorType FactorType;
typedef typename JUNCTIONTREE::FactorGraphType FactorGraphType;
typedef typename JUNCTIONTREE::ConditionalType ConditionalType;
typedef typename JUNCTIONTREE::BayesTreeType::Node BTNode;
// Gather factors
FactorGraphType gatheredFactors;
gatheredFactors.reserve(node->factors.size() + node->children.size());
gatheredFactors += node->factors;
gatheredFactors += myData.childFactors;
// Check for Bayes tree orphan subtrees, and add them to our children
BOOST_FOREACH(const sharedFactor& f, node->factors)
{
if(const BayesTreeOrphanWrapper<BTNode>* asSubtree = dynamic_cast<const BayesTreeOrphanWrapper<BTNode>*>(f.get()))
{
myData.bayesTreeNode->children.push_back(asSubtree->clique);
asSubtree->clique->parent_ = myData.bayesTreeNode;
}
}
// Do dense elimination step
std::pair<boost::shared_ptr<ConditionalType>, boost::shared_ptr<FactorType> > eliminationResult =
eliminationFunction(gatheredFactors, Ordering(node->keys));
// Store conditional in BayesTree clique, and in the case of ISAM2Clique also store the remaining factor
myData.bayesTreeNode->setEliminationResult(eliminationResult);
// Fill nodes index - we do this here instead of calling insertRoot at the end to avoid
// putting orphan subtrees in the index - they'll already be in the index of the ISAM2
// object they're added to.
BOOST_FOREACH(const Key& j, myData.bayesTreeNode->conditional()->frontals())
nodesIndex.insert(std::make_pair(j, myData.bayesTreeNode)).second;
// Store remaining factor in parent's gathered factors
if(!eliminationResult.second->empty())
myData.parentData->childFactors[myData.myIndexInParent] = eliminationResult.second;
}
};
}
/* ************************************************************************* */
template<class BAYESTREE, class GRAPH>
void JunctionTree<BAYESTREE,GRAPH>::Node::print(
const std::string& s, const KeyFormatter& keyFormatter) const
{
std::cout << s;
BOOST_FOREACH(Key j, keys)
std::cout << j << " ";
std::cout << "problemSize = " << problemSize_ << std::endl;
}
/* ************************************************************************* */
template<class BAYESTREE, class GRAPH>
void JunctionTree<BAYESTREE,GRAPH>::print(
const std::string& s, const KeyFormatter& keyFormatter) const
{
treeTraversal::PrintForest(*this, s, keyFormatter);
}
/* ************************************************************************* */
template<class BAYESTREE, class GRAPH>
template<class ETREE>
JunctionTree<BAYESTREE,GRAPH>
JunctionTree<BAYESTREE,GRAPH>::FromEliminationTree(const ETREE& eliminationTree)
{
gttic(JunctionTree_FromEliminationTree);
// Here we rely on the BayesNet having been produced by this elimination tree, such that the
// conditionals are arranged in DFS post-order. We traverse the elimination tree, and inspect
// the symbolic conditional corresponding to each node. The elimination tree node is added to
// the same clique with its parent if it has exactly one more Bayes net conditional parent than
// does its elimination tree parent.
// Traverse the elimination tree, doing symbolic elimination and merging nodes as we go. Gather
// the created junction tree roots in a dummy Node.
typedef typename ETREE::Node ETreeNode;
ConstructorTraversalData<BAYESTREE, GRAPH> rootData(0);
rootData.myJTNode = boost::make_shared<Node>(); // Make a dummy node to gather the junction tree roots
treeTraversal::DepthFirstForest(eliminationTree, rootData,
ConstructorTraversalVisitorPre<BAYESTREE,GRAPH,ETreeNode>, ConstructorTraversalVisitorPost<BAYESTREE,GRAPH,ETreeNode>);
// Assign roots from the dummy node
This result;
result.roots_ = rootData.myJTNode->children;
// Transfer remaining factors from elimination tree
result.remainingFactors_ = eliminationTree.remainingFactors();
return result;
}
/* ************************************************************************* */
template<class BAYESTREE, class GRAPH>
JunctionTree<BAYESTREE,GRAPH>& JunctionTree<BAYESTREE,GRAPH>::operator=(const This& other)
{
// Start by duplicating the tree.
roots_ = treeTraversal::CloneForest(other);
// Assign the remaining factors - these are pointers to factors in the original factor graph and
// we do not clone them.
remainingFactors_ = other.remainingFactors_;
return *this;
}
/* ************************************************************************* */
template<class BAYESTREE, class GRAPH>
std::pair<boost::shared_ptr<BAYESTREE>, boost::shared_ptr<GRAPH> >
JunctionTree<BAYESTREE,GRAPH>::eliminate(const Eliminate& function) const
{
gttic(JunctionTree_eliminate);
// Do elimination (depth-first traversal). The rootsContainer stores a 'dummy' BayesTree node
// that contains all of the roots as its children. rootsContainer also stores the remaining
// uneliminated factors passed up from the roots.
boost::shared_ptr<BayesTreeType> result = boost::make_shared<BayesTreeType>();
EliminationData<This> rootsContainer(0, roots_.size());
EliminationPostOrderVisitor<This> visitorPost(function, result->nodes_);
//tbb::task_scheduler_init init(1);
treeTraversal::DepthFirstForest/*Parallel*/(*this, rootsContainer,
eliminationPreOrderVisitor<This>, visitorPost/*, 10*/);
// Create BayesTree from roots stored in the dummy BayesTree node.
result->roots_.insert(result->roots_.end(), rootsContainer.bayesTreeNode->children.begin(), rootsContainer.bayesTreeNode->children.end());
// Add remaining factors that were not involved with eliminated variables
boost::shared_ptr<FactorGraphType> allRemainingFactors = boost::make_shared<FactorGraphType>();
allRemainingFactors->reserve(remainingFactors_.size() + rootsContainer.childFactors.size());
allRemainingFactors->push_back(remainingFactors_.begin(), remainingFactors_.end());
BOOST_FOREACH(const sharedFactor& factor, rootsContainer.childFactors)
if(factor)
allRemainingFactors->push_back(factor);
// Return result
return std::make_pair(result, allRemainingFactors);
}
} //namespace gtsam

View File

@ -14,18 +14,14 @@
* @date Feb 4, 2010
* @author Kai Ni
* @author Frank Dellaert
* @brief: The junction tree
* @author Richard Roberts
* @brief The junction tree
*/
#pragma once
#include <set>
#include <vector>
#include <gtsam/base/FastList.h>
#include <gtsam/inference/BayesTree.h>
#include <gtsam/inference/ClusterTree.h>
#include <gtsam/inference/IndexConditional.h>
#include <gtsam/inference/VariableIndex.h>
#include <gtsam/base/Testable.h>
#include <gtsam/inference/Key.h>
namespace gtsam {
@ -46,90 +42,103 @@ namespace gtsam {
* The tree structure and elimination method are exactly analagous to the EliminationTree,
* except that in the JunctionTree, at each node multiple variables are eliminated at a time.
*
*
* \addtogroup Multifrontal
* \nosubgrouping
*/
template<class FG, class BTCLIQUE=typename BayesTree<typename FG::FactorType::ConditionalType>::Clique>
class JunctionTree: public ClusterTree<FG> {
template<class BAYESTREE, class GRAPH>
class JunctionTree {
public:
/// In a junction tree each cluster is associated with a clique
typedef typename ClusterTree<FG>::Cluster Clique;
typedef typename Clique::shared_ptr sharedClique; ///< Shared pointer to a clique
typedef GRAPH FactorGraphType; ///< The factor graph type
typedef typename GRAPH::FactorType FactorType; ///< The type of factors
typedef JunctionTree<BAYESTREE, GRAPH> This; ///< This class
typedef boost::shared_ptr<This> shared_ptr; ///< Shared pointer to this class
typedef boost::shared_ptr<FactorType> sharedFactor; ///< Shared pointer to a factor
typedef BAYESTREE BayesTreeType; ///< The BayesTree type produced by elimination
typedef typename BayesTreeType::ConditionalType ConditionalType; ///< The type of conditionals
typedef boost::shared_ptr<ConditionalType> sharedConditional; ///< Shared pointer to a conditional
typedef typename FactorGraphType::Eliminate Eliminate; ///< Typedef for an eliminate subroutine
/// The BayesTree type produced by elimination
typedef BTCLIQUE BTClique;
struct Node {
typedef std::vector<Key> Keys;
typedef std::vector<sharedFactor> Factors;
typedef std::vector<boost::shared_ptr<Node> > Children;
/// Shared pointer to this class
typedef boost::shared_ptr<JunctionTree<FG> > shared_ptr;
Keys keys; ///< Frontal keys of this node
Factors factors; ///< Factors associated with this node
Children children; ///< sub-trees
int problemSize_;
/// We will frequently refer to a symbolic Bayes tree, used to find the clique structure
typedef gtsam::BayesTree<IndexConditional> SymbolicBayesTree;
int problemSize() const { return problemSize_; }
/** print this node */
void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
};
typedef boost::shared_ptr<Node> sharedNode; ///< Shared pointer to Node
private:
/// @name Advanced Interface
/// @{
/** concept check */
GTSAM_CONCEPT_TESTABLE_TYPE(FactorType);
/// distribute the factors along the cluster tree
sharedClique distributeFactors(const FG& fg,
const SymbolicBayesTree::sharedClique& clique);
std::vector<sharedNode> roots_;
std::vector<sharedFactor> remainingFactors_;
/// distribute the factors along the cluster tree
sharedClique distributeFactors(const FG& fg, const std::vector<FastList<size_t> >& targets,
const SymbolicBayesTree::sharedClique& clique);
/// recursive elimination function
std::pair<typename BTClique::shared_ptr, typename FG::sharedFactor>
eliminateOneClique(typename FG::Eliminate function,
const boost::shared_ptr<const Clique>& clique) const;
/// internal constructor
void construct(const FG& fg, const VariableIndex& variableIndex);
/// @}
public:
protected:
/// @name Standard Constructors
/// @{
/** Default constructor */
JunctionTree() {}
/** Build the junction tree from an elimination tree. */
template<class ETREE>
static This FromEliminationTree(const ETREE& eliminationTree);
/** Copy constructor - makes a deep copy of the tree structure, but only pointers to factors are
* copied, factors are not cloned. */
JunctionTree(const This& other) { *this = other; }
/** Named constructor to build the junction tree of a factor graph. Note
* that this has to compute the column structure as a VariableIndex, so if you
* already have this precomputed, use the JunctionTree(const FG&, const VariableIndex&)
* constructor instead.
* @param factorGraph The factor graph for which to build the elimination tree
*/
JunctionTree(const FG& factorGraph);
/** Construct from a factor graph and pre-computed variable index.
* @param fg The factor graph for which to build the junction tree
* @param structure The set of factors involving each variable. If this is not
* precomputed, you can call the JunctionTree(const FG&)
* constructor instead.
*/
JunctionTree(const FG& fg, const VariableIndex& variableIndex);
/** Assignment operator - makes a deep copy of the tree structure, but only pointers to factors
* are copied, factors are not cloned. */
This& operator=(const This& other);
/// @}
public:
/// @name Standard Interface
/// @{
/** Eliminate the factors in the subgraphs to produce a BayesTree.
* @param function The function used to eliminate, see the namespace functions
* in GaussianFactorGraph.h
* @return The BayesTree resulting from elimination
*/
typename BTClique::shared_ptr eliminate(typename FG::Eliminate function) const;
/** Eliminate the factors to a Bayes net and remaining factor graph
* @param function The function to use to eliminate, see the namespace functions
* in GaussianFactorGraph.h
* @return The Bayes net and factor graph resulting from elimination
*/
std::pair<boost::shared_ptr<BayesTreeType>, boost::shared_ptr<FactorGraphType> >
eliminate(const Eliminate& function) const;
/** Print the junction tree */
void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
/// @}
}; // JunctionTree
/// @name Advanced Interface
/// @{
/** Return the set of roots (one for a tree, multiple for a forest) */
const std::vector<sharedNode>& roots() const { return roots_; }
} // namespace gtsam
/** Return the remaining factors that are not pulled into elimination */
const std::vector<sharedFactor>& remainingFactors() const { return remainingFactors_; }
#include <gtsam/inference/JunctionTree-inl.h>
/// @}
private:
// Private default constructor (used in static construction methods)
JunctionTree() {}
};
}

View File

@ -22,19 +22,11 @@
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include <gtsam/nonlinear/Key.h>
#include <gtsam/inference/Key.h>
#include <gtsam/nonlinear/LabeledSymbol.h>
namespace gtsam {
/* ************************************************************************* */
std::string _defaultKeyFormatter(Key key) {
const Symbol asSymbol(key);
if(asSymbol.chr() > 0)
return (std::string)asSymbol;
else
return boost::lexical_cast<std::string>(key);
}
/* ************************************************************************* */
std::string _multirobotKeyFormatter(gtsam::Key key) {

View File

@ -27,19 +27,6 @@
namespace gtsam {
/// Integer nonlinear key type
typedef size_t Key;
/// Typedef for a function to format a key, i.e. to convert it to a string
typedef boost::function<std::string(Key)> KeyFormatter;
// Helper function for DefaultKeyFormatter
GTSAM_EXPORT std::string _defaultKeyFormatter(Key key);
/// The default KeyFormatter, which is used if no KeyFormatter is passed to
/// a nonlinear 'print' function. Automatically detects plain integer keys
/// and Symbol keys.
static const KeyFormatter DefaultKeyFormatter = &_defaultKeyFormatter;
// Helper function for Multi-robot Key Formatter
GTSAM_EXPORT std::string _multirobotKeyFormatter(gtsam::Key key);

View File

@ -0,0 +1,232 @@
/* ----------------------------------------------------------------------------
* 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 Ordering.cpp
* @author Richard Roberts
* @date Sep 2, 2010
*/
#include <vector>
#include <limits>
#include <boost/format.hpp>
#include <gtsam/inference/Ordering.h>
#include <gtsam/3rdparty/CCOLAMD/Include/ccolamd.h>
using namespace std;
namespace gtsam {
/* ************************************************************************* */
FastMap<Key, size_t> Ordering::invert() const
{
FastMap<Key, size_t> inverted;
for(size_t pos = 0; pos < this->size(); ++pos)
inverted.insert(make_pair((*this)[pos], pos));
return inverted;
}
/* ************************************************************************* */
Ordering Ordering::COLAMD(const VariableIndex& variableIndex)
{
// Call constrained version with all groups set to zero
vector<int> dummy_groups(variableIndex.size(), 0);
return Ordering::COLAMDConstrained(variableIndex, dummy_groups);
}
/* ************************************************************************* */
Ordering Ordering::COLAMDConstrained(
const VariableIndex& variableIndex, std::vector<int>& cmember)
{
gttic(Ordering_COLAMDConstrained);
gttic(Prepare);
size_t nEntries = variableIndex.nEntries(), nFactors = variableIndex.nFactors(), nVars = variableIndex.size();
// Convert to compressed column major format colamd wants it in (== MATLAB format!)
size_t Alen = ccolamd_recommended((int)nEntries, (int)nFactors, (int)nVars); /* colamd arg 3: size of the array A */
vector<int> A = vector<int>(Alen); /* colamd arg 4: row indices of A, of size Alen */
vector<int> p = vector<int>(nVars + 1); /* colamd arg 5: column pointers of A, of size n_col+1 */
// Fill in input data for COLAMD
p[0] = 0;
int count = 0;
vector<Key> keys(nVars); // Array to store the keys in the order we add them so we can retrieve them in permuted order
size_t index = 0;
BOOST_FOREACH(const VariableIndex::value_type key_factors, variableIndex) {
// Arrange factor indices into COLAMD format
const VariableIndex::Factors& column = key_factors.second;
size_t lastFactorId = numeric_limits<size_t>::max();
BOOST_FOREACH(size_t factorIndex, column) {
if(lastFactorId != numeric_limits<size_t>::max())
assert(factorIndex > lastFactorId);
A[count++] = (int)factorIndex; // copy sparse column
}
p[index+1] = count; // column j (base 1) goes from A[j-1] to A[j]-1
// Store key in array and increment index
keys[index] = key_factors.first;
++ index;
}
assert((size_t)count == variableIndex.nEntries());
//double* knobs = NULL; /* colamd arg 6: parameters (uses defaults if NULL) */
double knobs[CCOLAMD_KNOBS];
ccolamd_set_defaults(knobs);
knobs[CCOLAMD_DENSE_ROW]=-1;
knobs[CCOLAMD_DENSE_COL]=-1;
int stats[CCOLAMD_STATS]; /* colamd arg 7: colamd output statistics and error codes */
gttoc(Prepare);
// call colamd, result will be in p
/* returns (1) if successful, (0) otherwise*/
if(nVars > 0) {
gttic(ccolamd);
int rv = ccolamd((int)nFactors, (int)nVars, (int)Alen, &A[0], &p[0], knobs, stats, &cmember[0]);
if(rv != 1)
throw runtime_error((boost::format("ccolamd failed with return value %1%")%rv).str());
}
// ccolamd_report(stats);
gttic(Fill_Ordering);
// Convert elimination ordering in p to an ordering
Ordering result;
result.resize(nVars);
for(size_t j = 0; j < nVars; ++j)
result[j] = keys[p[j]];
gttoc(Fill_Ordering);
return result;
}
/* ************************************************************************* */
Ordering Ordering::COLAMDConstrainedLast(
const VariableIndex& variableIndex, const std::vector<Key>& constrainLast, bool forceOrder)
{
gttic(Ordering_COLAMDConstrainedLast);
size_t n = variableIndex.size();
std::vector<int> cmember(n, 0);
// Build a mapping to look up sorted Key indices by Key
FastMap<Key, size_t> keyIndices;
size_t j = 0;
BOOST_FOREACH(const VariableIndex::value_type key_factors, variableIndex)
keyIndices.insert(keyIndices.end(), make_pair(key_factors.first, j++));
// If at least some variables are not constrained to be last, constrain the
// ones that should be constrained.
int group = (constrainLast.size() != n ? 1 : 0);
BOOST_FOREACH(Key key, constrainLast) {
cmember[keyIndices.at(key)] = group;
if(forceOrder)
++ group;
}
return Ordering::COLAMDConstrained(variableIndex, cmember);
}
/* ************************************************************************* */
Ordering Ordering::COLAMDConstrainedFirst(
const VariableIndex& variableIndex, const std::vector<Key>& constrainFirst, bool forceOrder)
{
gttic(Ordering_COLAMDConstrainedFirst);
const int none = -1;
size_t n = variableIndex.size();
std::vector<int> cmember(n, none);
// Build a mapping to look up sorted Key indices by Key
FastMap<Key, size_t> keyIndices;
size_t j = 0;
BOOST_FOREACH(const VariableIndex::value_type key_factors, variableIndex)
keyIndices.insert(keyIndices.end(), make_pair(key_factors.first, j++));
// If at least some variables are not constrained to be last, constrain the
// ones that should be constrained.
int group = 0;
BOOST_FOREACH(Key key, constrainFirst) {
cmember[keyIndices.at(key)] = group;
if(forceOrder)
++ group;
}
if(!forceOrder)
++ group;
BOOST_FOREACH(int& c, cmember)
if(c == none)
c = group;
return Ordering::COLAMDConstrained(variableIndex, cmember);
}
/* ************************************************************************* */
Ordering Ordering::COLAMDConstrained(const VariableIndex& variableIndex,
const FastMap<Key, int>& groups)
{
gttic(Ordering_COLAMDConstrained);
size_t n = variableIndex.size();
std::vector<int> cmember(n, 0);
// Build a mapping to look up sorted Key indices by Key
FastMap<Key, size_t> keyIndices;
size_t j = 0;
BOOST_FOREACH(const VariableIndex::value_type key_factors, variableIndex)
keyIndices.insert(keyIndices.end(), make_pair(key_factors.first, j++));
// Assign groups
typedef FastMap<Key, int>::value_type key_group;
BOOST_FOREACH(const key_group& p, groups) {
// FIXME: check that no groups are skipped
cmember[keyIndices.at(p.first)] = p.second;
}
return Ordering::COLAMDConstrained(variableIndex, cmember);
}
/* ************************************************************************* */
void Ordering::print(const std::string& str, const KeyFormatter& keyFormatter) const
{
cout << str;
// Print ordering in index order
// Print the ordering with varsPerLine ordering entries printed on each line,
// for compactness.
static const size_t varsPerLine = 10;
bool endedOnNewline = false;
for(size_t i = 0; i < size(); ++i) {
if(i % varsPerLine == 0)
cout << "Position " << i << ": ";
if(i % varsPerLine != 0)
cout << ", ";
cout << keyFormatter(at(i));
if(i % varsPerLine == varsPerLine - 1) {
cout << "\n";
endedOnNewline = true;
} else {
endedOnNewline = false;
}
}
if(!endedOnNewline)
cout << "\n";
cout.flush();
}
/* ************************************************************************* */
bool Ordering::equals(const Ordering& other, double tol) const
{
return (*this) == other;
}
}

161
gtsam/inference/Ordering.h Normal file
View File

@ -0,0 +1,161 @@
/* ----------------------------------------------------------------------------
* 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 Ordering.h
* @author Richard Roberts
* @date Sep 2, 2010
*/
#pragma once
#include <vector>
#include <boost/assign/list_inserter.hpp>
#include <gtsam/inference/Key.h>
#include <gtsam/inference/VariableIndex.h>
#include <gtsam/inference/FactorGraph.h>
namespace gtsam {
class Ordering : public std::vector<Key> {
protected:
typedef std::vector<Key> Base;
public:
typedef Ordering This; ///< Typedef to this class
typedef boost::shared_ptr<This> shared_ptr; ///< shared_ptr to this class
/// Create an empty ordering
GTSAM_EXPORT Ordering() {}
/// Create from a container
template<typename KEYS>
explicit Ordering(const KEYS& keys) : Base(keys.begin(), keys.end()) {}
/// Create an ordering using iterators over keys
template<typename ITERATOR>
Ordering(ITERATOR firstKey, ITERATOR lastKey) : Base(firstKey, lastKey) {}
/// Add new variables to the ordering as ordering += key1, key2, ... Equivalent to calling
/// push_back.
boost::assign::list_inserter<boost::assign_detail::call_push_back<This> >
operator+=(Key key) {
return boost::assign::make_list_inserter(boost::assign_detail::call_push_back<This>(*this))(key);
}
/// Invert (not reverse) the ordering - returns a map from key to order position
FastMap<Key, size_t> invert() const;
/// @name Fill-reducing Orderings @{
/// Compute a fill-reducing ordering using COLAMD from a factor graph (see details for note on
/// performance). This internally builds a VariableIndex so if you already have a VariableIndex,
/// it is faster to use COLAMD(const VariableIndex&)
template<class FACTOR>
static Ordering COLAMD(const FactorGraph<FACTOR>& graph) {
return COLAMD(VariableIndex(graph)); }
/// Compute a fill-reducing ordering using COLAMD from a VariableIndex.
static GTSAM_EXPORT Ordering COLAMD(const VariableIndex& variableIndex);
/// Compute a fill-reducing ordering using constrained COLAMD from a factor graph (see details
/// for note on performance). This internally builds a VariableIndex so if you already have a
/// VariableIndex, it is faster to use COLAMD(const VariableIndex&). This function constrains
/// the variables in \c constrainLast to the end of the ordering, and orders all other variables
/// before in a fill-reducing ordering. If \c forceOrder is true, the variables in \c
/// constrainLast will be ordered in the same order specified in the vector<Key> \c
/// constrainLast. If \c forceOrder is false, the variables in \c constrainLast will be
/// ordered after all the others, but will be rearranged by CCOLAMD to reduce fill-in as well.
template<class FACTOR>
static Ordering COLAMDConstrainedLast(const FactorGraph<FACTOR>& graph,
const std::vector<Key>& constrainLast, bool forceOrder = false) {
return COLAMDConstrainedLast(VariableIndex(graph), constrainLast, forceOrder); }
/// Compute a fill-reducing ordering using constrained COLAMD from a VariableIndex. This
/// function constrains the variables in \c constrainLast to the end of the ordering, and orders
/// all other variables before in a fill-reducing ordering. If \c forceOrder is true, the
/// variables in \c constrainLast will be ordered in the same order specified in the vector<Key>
/// \c constrainLast. If \c forceOrder is false, the variables in \c constrainLast will be
/// ordered after all the others, but will be rearranged by CCOLAMD to reduce fill-in as well.
static GTSAM_EXPORT Ordering COLAMDConstrainedLast(const VariableIndex& variableIndex,
const std::vector<Key>& constrainLast, bool forceOrder = false);
/// Compute a fill-reducing ordering using constrained COLAMD from a factor graph (see details
/// for note on performance). This internally builds a VariableIndex so if you already have a
/// VariableIndex, it is faster to use COLAMD(const VariableIndex&). This function constrains
/// the variables in \c constrainLast to the end of the ordering, and orders all other variables
/// before in a fill-reducing ordering. If \c forceOrder is true, the variables in \c
/// constrainLast will be ordered in the same order specified in the vector<Key> \c
/// constrainLast. If \c forceOrder is false, the variables in \c constrainFirst will be
/// ordered after all the others, but will be rearranged by CCOLAMD to reduce fill-in as well.
template<class FACTOR>
static Ordering COLAMDConstrainedFirst(const FactorGraph<FACTOR>& graph,
const std::vector<Key>& constrainFirst, bool forceOrder = false) {
return COLAMDConstrainedFirst(VariableIndex(graph), constrainFirst, forceOrder); }
/// Compute a fill-reducing ordering using constrained COLAMD from a VariableIndex. This
/// function constrains the variables in \c constrainFirst to the front of the ordering, and
/// orders all other variables after in a fill-reducing ordering. If \c forceOrder is true, the
/// variables in \c constrainFirst will be ordered in the same order specified in the
/// vector<Key> \c constrainFirst. If \c forceOrder is false, the variables in \c
/// constrainFirst will be ordered after all the others, but will be rearranged by CCOLAMD to
/// reduce fill-in as well.
static GTSAM_EXPORT Ordering COLAMDConstrainedFirst(const VariableIndex& variableIndex,
const std::vector<Key>& constrainFirst, bool forceOrder = false);
/// Compute a fill-reducing ordering using constrained COLAMD from a factor graph (see details
/// for note on performance). This internally builds a VariableIndex so if you already have a
/// VariableIndex, it is faster to use COLAMD(const VariableIndex&). In this function, a group
/// for each variable should be specified in \c groups, and each group of variables will appear
/// in the ordering in group index order. \c groups should be a map from Key to group index.
/// The group indices used should be consecutive starting at 0, but may appear in \c groups in
/// arbitrary order. Any variables not present in \c groups will be assigned to group 0. This
/// function simply fills the \c cmember argument to CCOLAMD with the supplied indices, see the
/// CCOLAMD documentation for more information.
template<class FACTOR>
static Ordering COLAMDConstrained(const FactorGraph<FACTOR>& graph,
const FastMap<Key, int>& groups) {
return COLAMDConstrained(VariableIndex(graph), groups); }
/// Compute a fill-reducing ordering using constrained COLAMD from a VariableIndex. In this
/// function, a group for each variable should be specified in \c groups, and each group of
/// variables will appear in the ordering in group index order. \c groups should be a map from
/// Key to group index. The group indices used should be consecutive starting at 0, but may
/// appear in \c groups in arbitrary order. Any variables not present in \c groups will be
/// assigned to group 0. This function simply fills the \c cmember argument to CCOLAMD with the
/// supplied indices, see the CCOLAMD documentation for more information.
static GTSAM_EXPORT Ordering COLAMDConstrained(const VariableIndex& variableIndex,
const FastMap<Key, int>& groups);
/// @}
/// @name Testable @{
GTSAM_EXPORT void print(const std::string& str = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
GTSAM_EXPORT bool equals(const Ordering& other, double tol = 1e-9) const;
/// @}
private:
/// Internal COLAMD function
static GTSAM_EXPORT Ordering COLAMDConstrained(
const VariableIndex& variableIndex, std::vector<int>& cmember);
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
}
};
}

View File

@ -1,226 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 Permutation.cpp
* @author Richard Roberts
* @date Oct 9, 2010
*/
#include <gtsam/inference/Permutation.h>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <boost/foreach.hpp>
using namespace std;
namespace gtsam {
/* ************************************************************************* */
Permutation Permutation::Identity(Index nVars) {
Permutation ret(nVars);
for(Index i=0; i<nVars; ++i)
ret[i] = i;
return ret;
}
/* ************************************************************************* */
Permutation Permutation::PullToFront(const vector<Index>& toFront, size_t size, bool filterDuplicates) {
Permutation ret(size);
// Mask of which variables have been pulled, used to reorder
vector<bool> pulled(size, false);
// Put the pulled variables at the front of the permutation and set up the
// pulled flags.
size_t toFrontUniqueSize = 0;
for(Index j=0; j<toFront.size(); ++j) {
if(!pulled[toFront[j]]) {
ret[j] = toFront[j];
pulled[toFront[j]] = true;
++ toFrontUniqueSize;
} else if(!filterDuplicates) {
stringstream ss;
ss << "Duplicate variable given as input to Permutation::PullToFront:\n";
ss << " toFront:";
BOOST_FOREACH(Index i, toFront) { ss << " " << i; }
ss << ", size = " << size << endl;
throw invalid_argument(ss.str());
}
}
// Fill in the rest of the variables
Index nextVar = toFrontUniqueSize;
for(Index j=0; j<size; ++j)
if(!pulled[j])
ret[nextVar++] = j;
assert(nextVar == size);
return ret;
}
/* ************************************************************************* */
Permutation Permutation::PushToBack(const std::vector<Index>& toBack, size_t size, bool filterDuplicates) {
Permutation ret(size);
// Mask of which variables have been pushed, used to reorder
vector<bool> pushed(size, false);
// Put the pushed variables at the back of the permutation and set up the
// pushed flags;
Index nextVar = size;
size_t toBackUniqueSize = 0;
if(toBack.size() > 0) {
Index j = toBack.size();
do {
-- j;
if(!pushed[toBack[j]]) {
ret[--nextVar] = toBack[j];
pushed[toBack[j]] = true;
++ toBackUniqueSize;
} else if(!filterDuplicates) {
stringstream ss;
ss << "Duplicate variable given as input to Permutation::PushToBack:\n";
ss << " toBack:";
BOOST_FOREACH(Index i, toBack) { ss << " " << i; }
ss << ", size = " << size << endl;
throw invalid_argument(ss.str());
}
} while(j > 0);
}
assert(nextVar == size - toBackUniqueSize);
// Fill in the rest of the variables
nextVar = 0;
for(Index j=0; j<size; ++j)
if(!pushed[j])
ret[nextVar++] = j;
assert(nextVar == size - toBackUniqueSize);
return ret;
}
/* ************************************************************************* */
Permutation::shared_ptr Permutation::permute(const Permutation& permutation) const {
const size_t nVars = permutation.size();
Permutation::shared_ptr result(new Permutation(nVars));
for(size_t j=0; j<nVars; ++j) {
assert(permutation[j] < rangeIndices_.size());
(*result)[j] = operator[](permutation[j]);
}
return result;
}
/* ************************************************************************* */
Permutation::shared_ptr Permutation::inverse() const {
Permutation::shared_ptr result(new Permutation(this->size()));
for(Index i=0; i<this->size(); ++i) {
assert((*this)[i] < this->size());
(*result)[(*this)[i]] = i;
}
return result;
}
/* ************************************************************************* */
void Permutation::print(const std::string& str) const {
std::cout << str << " ";
BOOST_FOREACH(Index s, rangeIndices_) { std::cout << s << " "; }
std::cout << std::endl;
}
namespace internal {
/* ************************************************************************* */
Reduction Reduction::CreateAsInverse(const Permutation& p) {
Reduction result;
for(Index j = 0; j < p.size(); ++j)
result.insert(std::make_pair(p[j], j));
return result;
}
/* ************************************************************************* */
Reduction Reduction::CreateFromPartialPermutation(const Permutation& selector, const Permutation& p) {
if(selector.size() != p.size())
throw invalid_argument("internal::Reduction::CreateFromPartialPermutation called with selector and permutation of different sizes");
Reduction result;
for(size_t dstSlot = 0; dstSlot < p.size(); ++dstSlot)
result.insert(make_pair(selector[dstSlot], selector[p[dstSlot]]));
return result;
}
/* ************************************************************************* */
void Reduction::applyInverse(std::vector<Index>& js) const {
BOOST_FOREACH(Index& j, js) {
j = this->find(j)->second;
}
}
/* ************************************************************************* */
Permutation Reduction::inverse() const {
Index maxIndex = 0;
BOOST_FOREACH(const value_type& target_source, *this) {
if(target_source.second > maxIndex)
maxIndex = target_source.second;
}
Permutation result(maxIndex + 1);
BOOST_FOREACH(const value_type& target_source, *this) {
result[target_source.second] = target_source.first;
}
return result;
}
/* ************************************************************************* */
const Index& Reduction::operator[](const Index& j) {
iterator it = this->find(j);
if(it == this->end())
return j;
else
return it->second;
}
/* ************************************************************************* */
const Index& Reduction::operator[](const Index& j) const {
const_iterator it = this->find(j);
if(it == this->end())
return j;
else
return it->second;
}
/* ************************************************************************* */
void Reduction::print(const std::string& s) const {
cout << s << " reduction:" << endl;
BOOST_FOREACH(const value_type& p, *this)
cout << " " << p.first << " : " << p.second << endl;
}
/* ************************************************************************* */
bool Reduction::equals(const Reduction& other, double tol) const {
return (const Base&)(*this) == (const Base&)other;
}
/* ************************************************************************* */
Permutation createReducingPermutation(const std::set<Index>& indices) {
Permutation p(indices.size());
Index newJ = 0;
BOOST_FOREACH(Index j, indices) {
p[newJ] = j;
++ newJ;
}
return p;
}
} // \namespace internal
/* ************************************************************************* */
} // \namespace gtsam

View File

@ -1,215 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 Permutation.h
* @author Richard Roberts
* @date Sep 12, 2010
*/
#pragma once
#include <vector>
#include <string>
#include <set>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <gtsam/global_includes.h>
#include <gtsam/base/FastMap.h>
namespace gtsam {
/**
* A permutation reorders variables, for example to reduce fill-in during
* elimination. To save computation, the permutation can be applied to
* the necessary data structures only once, then multiple computations
* performed on the permuted problem. For example, in an iterative
* non-linear setting, a permutation can be created from the symbolic graph
* structure and applied to the ordering of nonlinear variables once, so
* that linearized factor graphs are already correctly ordered and need
* not be permuted.
*
* Consistent with their mathematical definition, permutations are functions
* that accept the destination index and return the source index. For example,
* to permute data structure A into a new data structure B using permutation P,
* the mapping is B[i] = A[P[i]]. This is the behavior implemented by
* B = A.permute(P).
*
* For some data structures in GTSAM, for efficiency it is necessary to have
* the inverse of the desired permutation. In this case, the data structure
* will implement permuteWithInverse(P) instead of permute(P). You may already
* have the inverse permutation by construction, or may instead compute it with
* Pinv = P.inverse().
*
* Permutations can be composed and inverted
* in order to create new indexing for a structure.
* \nosubgrouping
*/
class GTSAM_EXPORT Permutation {
protected:
std::vector<Index> rangeIndices_;
public:
typedef boost::shared_ptr<Permutation> shared_ptr;
typedef std::vector<Index>::const_iterator const_iterator;
typedef std::vector<Index>::iterator iterator;
/// @name Standard Constructors
/// @{
/**
* Create an empty permutation. This cannot do anything, but you can later
* assign to it.
*/
Permutation() {}
/**
* Create an uninitialized permutation. You must assign all values using the
* square bracket [] operator or they will be undefined!
*/
Permutation(Index nVars) : rangeIndices_(nVars) {}
/// @}
/// @name Testable
/// @{
/** Print */
void print(const std::string& str = "Permutation: ") const;
/** Check equality */
bool equals(const Permutation& rhs, double tol=0.0) const { return rangeIndices_ == rhs.rangeIndices_; }
/// @}
/// @name Standard Interface
/// @{
/**
* Return the source index of the supplied destination index.
*/
Index operator[](Index variable) const { check(variable); return rangeIndices_[variable]; }
/**
* Return the source index of the supplied destination index. This version allows modification.
*/
Index& operator[](Index variable) { check(variable); return rangeIndices_[variable]; }
/**
* Return the source index of the supplied destination index. Synonym for operator[](Index).
*/
Index at(Index variable) const { return operator[](variable); }
/**
* Return the source index of the supplied destination index. This version allows modification. Synonym for operator[](Index).
*/
Index& at(Index variable) { return operator[](variable); }
/**
* The number of variables in the range of this permutation, i.e. the
* destination space.
*/
Index size() const { return rangeIndices_.size(); }
/** Whether the permutation contains any entries */
bool empty() const { return rangeIndices_.empty(); }
/**
* Resize the permutation. You must fill in the new entries if new new size
* is larger than the old one. If the new size is smaller, entries from the
* end are discarded.
*/
void resize(size_t newSize) { rangeIndices_.resize(newSize); }
/**
* Return an identity permutation.
*/
static Permutation Identity(Index nVars);
/**
* Create a permutation that pulls the given variables to the front while
* pushing the rest to the back.
*/
static Permutation PullToFront(const std::vector<Index>& toFront, size_t size, bool filterDuplicates = false);
/**
* Create a permutation that pushes the given variables to the back while
* pulling the rest to the front.
*/
static Permutation PushToBack(const std::vector<Index>& toBack, size_t size, bool filterDuplicates = false);
/**
* Permute the permutation, p1.permute(p2)[i] is equivalent to p1[p2[i]].
*/
Permutation::shared_ptr permute(const Permutation& permutation) const;
/**
* Return the inverse permutation. This is only possible if this is a non-
* reducing permutation, that is, (*this)[i] < this->size() for all
* i < size(). If NDEBUG is not defined, this conditional will be checked.
*/
Permutation::shared_ptr inverse() const;
const_iterator begin() const { return rangeIndices_.begin(); } ///< Iterate through the indices
const_iterator end() const { return rangeIndices_.end(); } ///< Iterate through the indices
/** Apply the permutation to a collection, which must have operator[] defined.
* Note that permutable gtsam data structures typically have their own
* permute function to apply a permutation. Permutation::applyToCollection is
* a generic function, e.g. for STL classes.
* @param input The input collection.
* @param output The preallocated output collection, which is assigned output[i] = input[permutation[i]]
*/
template<typename INPUT_COLLECTION, typename OUTPUT_COLLECTION>
void applyToCollection(OUTPUT_COLLECTION& output, const INPUT_COLLECTION& input) const {
for(size_t i = 0; i < output.size(); ++i) output[i] = input[(*this)[i]]; }
/// @}
/// @name Advanced Interface
/// @{
iterator begin() { return rangeIndices_.begin(); } ///< Iterate through the indices
iterator end() { return rangeIndices_.end(); } ///< Iterate through the indices
protected:
void check(Index variable) const { assert(variable < rangeIndices_.size()); }
/// @}
};
namespace internal {
/**
* An internal class used for storing and applying a permutation from a map
*/
class Reduction : public gtsam::FastMap<Index,Index> {
public:
typedef gtsam::FastMap<Index,Index> Base;
GTSAM_EXPORT static Reduction CreateAsInverse(const Permutation& p);
GTSAM_EXPORT static Reduction CreateFromPartialPermutation(const Permutation& selector, const Permutation& p);
GTSAM_EXPORT void applyInverse(std::vector<Index>& js) const;
GTSAM_EXPORT Permutation inverse() const;
GTSAM_EXPORT const Index& operator[](const Index& j);
GTSAM_EXPORT const Index& operator[](const Index& j) const;
GTSAM_EXPORT void print(const std::string& s="") const;
GTSAM_EXPORT bool equals(const Reduction& other, double tol = 1e-9) const;
};
/**
* Reduce the variable indices so that those in the set are mapped to start at zero
*/
GTSAM_EXPORT Permutation createReducingPermutation(const std::set<Index>& indices);
} // \namespace internal
} // \namespace gtsam

View File

@ -1,132 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 SymbolicFactorGraph.cpp
* @date Oct 29, 2009
* @author Frank Dellaert
*/
#include <gtsam/inference/SymbolicFactorGraph.h>
#include <gtsam/inference/BayesNet.h>
#include <gtsam/inference/EliminationTree.h>
#include <gtsam/inference/IndexConditional.h>
#include <boost/make_shared.hpp>
namespace gtsam {
using namespace std;
/* ************************************************************************* */
SymbolicFactorGraph::SymbolicFactorGraph(const SymbolicBayesNet& bayesNet) :
FactorGraph<IndexFactor>(bayesNet) {}
/* ************************************************************************* */
SymbolicFactorGraph::SymbolicFactorGraph(const SymbolicBayesTree& bayesTree) :
FactorGraph<IndexFactor>(bayesTree) {}
/* ************************************************************************* */
void SymbolicFactorGraph::push_factor(Index key) {
push_back(boost::make_shared<IndexFactor>(key));
}
/** Push back binary factor */
void SymbolicFactorGraph::push_factor(Index key1, Index key2) {
push_back(boost::make_shared<IndexFactor>(key1,key2));
}
/** Push back ternary factor */
void SymbolicFactorGraph::push_factor(Index key1, Index key2, Index key3) {
push_back(boost::make_shared<IndexFactor>(key1,key2,key3));
}
/** Push back 4-way factor */
void SymbolicFactorGraph::push_factor(Index key1, Index key2, Index key3, Index key4) {
push_back(boost::make_shared<IndexFactor>(key1,key2,key3,key4));
}
/* ************************************************************************* */
FastSet<Index>
SymbolicFactorGraph::keys() const {
FastSet<Index> keys;
BOOST_FOREACH(const sharedFactor& factor, *this) {
if(factor) keys.insert(factor->begin(), factor->end()); }
return keys;
}
/* ************************************************************************* */
std::pair<SymbolicFactorGraph::sharedConditional, SymbolicFactorGraph>
SymbolicFactorGraph::eliminateFrontals(size_t nFrontals) const
{
return FactorGraph<IndexFactor>::eliminateFrontals(nFrontals, EliminateSymbolic);
}
/* ************************************************************************* */
std::pair<SymbolicFactorGraph::sharedConditional, SymbolicFactorGraph>
SymbolicFactorGraph::eliminate(const std::vector<Index>& variables) const
{
return FactorGraph<IndexFactor>::eliminate(variables, EliminateSymbolic);
}
/* ************************************************************************* */
std::pair<SymbolicFactorGraph::sharedConditional, SymbolicFactorGraph>
SymbolicFactorGraph::eliminateOne(Index variable) const
{
return FactorGraph<IndexFactor>::eliminateOne(variable, EliminateSymbolic);
}
/* ************************************************************************* */
void SymbolicFactorGraph::permuteWithInverse(
const Permutation& inversePermutation) {
BOOST_FOREACH(const sharedFactor& factor, factors_) {
if(factor)
factor->permuteWithInverse(inversePermutation);
}
}
/* ************************************************************************* */
void SymbolicFactorGraph::reduceWithInverse(
const internal::Reduction& inverseReduction) {
BOOST_FOREACH(const sharedFactor& factor, factors_) {
if(factor)
factor->reduceWithInverse(inverseReduction);
}
}
/* ************************************************************************* */
IndexFactor::shared_ptr CombineSymbolic(
const FactorGraph<IndexFactor>& factors, const FastMap<Index,
vector<Index> >& variableSlots) {
IndexFactor::shared_ptr combined(Combine<IndexFactor, Index> (factors, variableSlots));
// combined->assertInvariants();
return combined;
}
/* ************************************************************************* */
pair<IndexConditional::shared_ptr, IndexFactor::shared_ptr> //
EliminateSymbolic(const FactorGraph<IndexFactor>& factors, size_t nrFrontals) {
FastSet<Index> keys;
BOOST_FOREACH(const IndexFactor::shared_ptr& factor, factors)
BOOST_FOREACH(Index var, *factor)
keys.insert(var);
if (keys.size() < nrFrontals) throw invalid_argument(
"EliminateSymbolic requested to eliminate more variables than exist in graph.");
vector<Index> newKeys(keys.begin(), keys.end());
return make_pair(boost::make_shared<IndexConditional>(newKeys, nrFrontals),
boost::make_shared<IndexFactor>(newKeys.begin() + nrFrontals, newKeys.end()));
}
/* ************************************************************************* */
}

View File

@ -1,151 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 SymbolicFactorGraph.h
* @date Oct 29, 2009
* @author Frank Dellaert
*/
#pragma once
#include <gtsam/global_includes.h>
#include <gtsam/inference/FactorGraph.h>
#include <gtsam/inference/IndexFactor.h>
namespace gtsam { template<class FACTOR> class EliminationTree; }
namespace gtsam { template<class CONDITIONAL> class BayesNet; }
namespace gtsam { template<class CONDITIONAL, class CLIQUE> class BayesTree; }
namespace gtsam { class IndexConditional; }
namespace gtsam {
typedef EliminationTree<IndexFactor> SymbolicEliminationTree;
typedef BayesNet<IndexConditional> SymbolicBayesNet;
typedef BayesTree<IndexConditional> SymbolicBayesTree;
/** Symbolic IndexFactor Graph
* \nosubgrouping
*/
class SymbolicFactorGraph: public FactorGraph<IndexFactor> {
public:
/// @name Standard Constructors
/// @{
/** Construct empty factor graph */
SymbolicFactorGraph() {
}
/** Construct from a BayesNet */
GTSAM_EXPORT SymbolicFactorGraph(const SymbolicBayesNet& bayesNet);
/** Construct from a BayesTree */
GTSAM_EXPORT SymbolicFactorGraph(const SymbolicBayesTree& bayesTree);
/**
* Construct from a factor graph of any type
*/
template<class FACTOR>
SymbolicFactorGraph(const FactorGraph<FACTOR>& fg);
/** Eliminate the first \c n frontal variables, returning the resulting
* conditional and remaining factor graph - this is very inefficient for
* eliminating all variables, to do that use EliminationTree or
* JunctionTree. Note that this version simply calls
* FactorGraph<IndexFactor>::eliminateFrontals with EliminateSymbolic
* as the eliminate function argument.
*/
GTSAM_EXPORT std::pair<sharedConditional, SymbolicFactorGraph> eliminateFrontals(size_t nFrontals) const;
/** Factor the factor graph into a conditional and a remaining factor graph.
* Given the factor graph \f$ f(X) \f$, and \c variables to factorize out
* \f$ V \f$, this function factorizes into \f$ f(X) = f(V;Y)f(Y) \f$, where
* \f$ Y := X\V \f$ are the remaining variables. If \f$ f(X) = p(X) \f$ is
* a probability density or likelihood, the factorization produces a
* conditional probability density and a marginal \f$ p(X) = p(V|Y)p(Y) \f$.
*
* For efficiency, this function treats the variables to eliminate
* \c variables as fully-connected, so produces a dense (fully-connected)
* conditional on all of the variables in \c variables, instead of a sparse
* BayesNet. If the variables are not fully-connected, it is more efficient
* to sequentially factorize multiple times.
* Note that this version simply calls
* FactorGraph<GaussianFactor>::eliminate with EliminateSymbolic as the eliminate
* function argument.
*/
GTSAM_EXPORT std::pair<sharedConditional, SymbolicFactorGraph> eliminate(const std::vector<Index>& variables) const;
/** Eliminate a single variable, by calling SymbolicFactorGraph::eliminate. */
GTSAM_EXPORT std::pair<sharedConditional, SymbolicFactorGraph> eliminateOne(Index variable) const;
/// @}
/// @name Standard Interface
/// @{
/**
* Return the set of variables involved in the factors (computes a set
* union).
*/
GTSAM_EXPORT FastSet<Index> keys() const;
/// @}
/// @name Advanced Interface
/// @{
/** Push back unary factor */
GTSAM_EXPORT void push_factor(Index key);
/** Push back binary factor */
GTSAM_EXPORT void push_factor(Index key1, Index key2);
/** Push back ternary factor */
GTSAM_EXPORT void push_factor(Index key1, Index key2, Index key3);
/** Push back 4-way factor */
GTSAM_EXPORT void push_factor(Index key1, Index key2, Index key3, Index key4);
/** Permute the variables in the factors */
GTSAM_EXPORT void permuteWithInverse(const Permutation& inversePermutation);
/** Apply a reduction, which is a remapping of variable indices. */
GTSAM_EXPORT void reduceWithInverse(const internal::Reduction& inverseReduction);
};
/** Create a combined joint factor (new style for EliminationTree). */
GTSAM_EXPORT IndexFactor::shared_ptr CombineSymbolic(const FactorGraph<IndexFactor>& factors,
const FastMap<Index, std::vector<Index> >& variableSlots);
/**
* CombineAndEliminate provides symbolic elimination.
* Combine and eliminate can also be called separately, but for this and
* derived classes calling them separately generally does extra work.
*/
GTSAM_EXPORT std::pair<boost::shared_ptr<IndexConditional>, boost::shared_ptr<IndexFactor> >
EliminateSymbolic(const FactorGraph<IndexFactor>&, size_t nrFrontals = 1);
/// @}
/** Template function implementation */
template<class FACTOR>
SymbolicFactorGraph::SymbolicFactorGraph(const FactorGraph<FACTOR>& fg) {
for (size_t i = 0; i < fg.size(); i++) {
if (fg[i]) {
IndexFactor::shared_ptr factor(new IndexFactor(*fg[i]));
push_back(factor);
} else
push_back(IndexFactor::shared_ptr());
}
}
} // namespace gtsam

View File

@ -1,25 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 SymbolicMultifrontalSolver.cpp
* @author Richard Roberts
* @date Oct 22, 2010
*/
#include <gtsam/inference/SymbolicMultifrontalSolver.h>
#include <gtsam/inference/JunctionTree.h>
namespace gtsam {
//template class GenericMultifrontalSolver<IndexFactor, JunctionTree<FactorGraph<IndexFactor> > >;
}

View File

@ -1,71 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 SymbolicMultifrontalSolver.h
* @author Richard Roberts
* @date Oct 22, 2010
*/
#pragma once
#include <gtsam/inference/GenericMultifrontalSolver.h>
#include <gtsam/inference/SymbolicFactorGraph.h>
namespace gtsam {
class SymbolicMultifrontalSolver : GenericMultifrontalSolver<IndexFactor, JunctionTree<FactorGraph<IndexFactor> > > {
protected:
typedef GenericMultifrontalSolver<IndexFactor, JunctionTree<FactorGraph<IndexFactor> > > Base;
public:
/**
* Construct the solver for a factor graph. This builds the junction
* tree, which does the symbolic elimination, identifies the cliques,
* and distributes all the factors to the right cliques.
*/
SymbolicMultifrontalSolver(const SymbolicFactorGraph& factorGraph) : Base(factorGraph) {};
/**
* Construct the solver with a shared pointer to a factor graph and to a
* VariableIndex. The solver will store these pointers, so this constructor
* is the fastest.
*/
SymbolicMultifrontalSolver(const SymbolicFactorGraph::shared_ptr& factorGraph,
const VariableIndex::shared_ptr& variableIndex) : Base(factorGraph, variableIndex) {};
/** Print to cout */
void print(const std::string& name = "SymbolicMultifrontalSolver: ") const { Base::print(name); };
/** Test whether is equal to another */
bool equals(const SymbolicMultifrontalSolver& other, double tol = 1e-9) const { return Base::equals(other, tol); };
/**
* Eliminate the factor graph sequentially. Uses a column elimination tree
* to recursively eliminate.
*/
SymbolicBayesTree::shared_ptr eliminate() const { return Base::eliminate(&EliminateSymbolic); };
/**
* Compute the marginal joint over a set of variables, by integrating out
* all of the other variables. Returns the result as a factor graph.
*/
SymbolicFactorGraph::shared_ptr jointFactorGraph(const std::vector<Index>& js) const { return Base::jointFactorGraph(js, &EliminateSymbolic); };
/**
* Compute the marginal Gaussian density over a variable, by integrating out
* all of the other variables. This function returns the result as a factor.
*/
IndexFactor::shared_ptr marginalFactor(Index j) const { return Base::marginalFactor(j, &EliminateSymbolic); };
};
}

View File

@ -1,26 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 SymbolicSequentialSolver.cpp
* @author Richard Roberts
* @date Oct 21, 2010
*/
#include <gtsam/inference/SymbolicSequentialSolver.h>
#include <gtsam/inference/GenericSequentialSolver-inl.h>
namespace gtsam {
// An explicit instantiation to be compiled into the library
//template class GenericSequentialSolver<IndexFactor>;
}

View File

@ -1,88 +0,0 @@
/* ----------------------------------------------------------------------------
* 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 SymbolicSequentialSolver.h
* @author Richard Roberts
* @date Oct 21, 2010
*/
#pragma once
#include <gtsam/inference/GenericSequentialSolver.h>
#include <gtsam/inference/SymbolicFactorGraph.h>
namespace gtsam {
class SymbolicSequentialSolver : GenericSequentialSolver<IndexFactor> {
protected:
typedef GenericSequentialSolver<IndexFactor> Base;
public:
/**
* Construct the solver for a factor graph. This builds the junction
* tree, which does the symbolic elimination, identifies the cliques,
* and distributes all the factors to the right cliques.
*/
SymbolicSequentialSolver(const SymbolicFactorGraph& factorGraph) : Base(factorGraph) {};
/**
* Construct the solver with a shared pointer to a factor graph and to a
* VariableIndex. The solver will store these pointers, so this constructor
* is the fastest.
*/
SymbolicSequentialSolver(const SymbolicFactorGraph::shared_ptr& factorGraph,
const VariableIndex::shared_ptr& variableIndex) : Base(factorGraph, variableIndex) {};
/** Print to cout */
void print(const std::string& name = "SymbolicSequentialSolver: ") const { Base::print(name); };
/** Test whether is equal to another */
bool equals(const SymbolicSequentialSolver& other, double tol = 1e-9) const { return Base::equals(other, tol); };
/**
* Eliminate the factor graph sequentially. Uses a column elimination tree
* to recursively eliminate.
*/
SymbolicBayesNet::shared_ptr eliminate() const
{ return Base::eliminate(&EliminateSymbolic); };
/**
* Compute a conditional density P(F|S) while marginalizing out variables J
* P(F|S) is obtained by P(J,F,S)=P(J|F,S)P(F|S)P(S) and dropping P(S)
* Returns the result as a Bayes net.
*/
SymbolicBayesNet::shared_ptr conditionalBayesNet(const std::vector<Index>& js, size_t nrFrontals) const
{ return Base::conditionalBayesNet(js, nrFrontals, &EliminateSymbolic); };
/**
* Compute the marginal joint over a set of variables, by integrating out
* all of the other variables. Returns the result as a Bayes net.
*/
SymbolicBayesNet::shared_ptr jointBayesNet(const std::vector<Index>& js) const
{ return Base::jointBayesNet(js, &EliminateSymbolic); };
/**
* Compute the marginal joint over a set of variables, by integrating out
* all of the other variables. Returns the result as a factor graph.
*/
SymbolicFactorGraph::shared_ptr jointFactorGraph(const std::vector<Index>& js) const
{ return Base::jointFactorGraph(js, &EliminateSymbolic); };
/**
* Compute the marginal Gaussian density over a variable, by integrating out
* all of the other variables. This function returns the result as a factor.
*/
IndexFactor::shared_ptr marginalFactor(Index j) const { return Base::marginalFactor(j, &EliminateSymbolic); };
};
}

View File

@ -0,0 +1,83 @@
/* ----------------------------------------------------------------------------
* 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 VariableIndex-inl.h
* @author Richard Roberts
* @date March 26, 2013
*/
#pragma once
#include <gtsam/inference/VariableIndex.h>
namespace gtsam {
/* ************************************************************************* */
template<class FG>
void VariableIndex::augment(const FG& factors)
{
gttic(VariableIndex_augment);
// Save original number of factors for keeping track of indices
const size_t originalNFactors = nFactors_;
// Augment index for each factor
for(size_t i = 0; i < factors.size(); ++i) {
if(factors[i]) {
const size_t globalI = originalNFactors + i;
BOOST_FOREACH(const Key key, *factors[i]) {
index_[key].push_back(globalI);
++ nEntries_;
}
}
++ nFactors_; // Increment factor count even if factors are null, to keep indices consistent
}
}
/* ************************************************************************* */
template<typename ITERATOR, class FG>
void VariableIndex::remove(ITERATOR firstFactor, ITERATOR lastFactor, const FG& factors)
{
gttic(VariableIndex_remove);
// NOTE: We intentionally do not decrement nFactors_ because the factor
// indices need to remain consistent. Removing factors from a factor graph
// does not shift the indices of other factors. Also, we keep nFactors_
// one greater than the highest-numbered factor referenced in a VariableIndex.
ITERATOR factorIndex = firstFactor;
size_t i = 0;
for( ; factorIndex != lastFactor; ++factorIndex, ++i) {
if(i >= factors.size())
throw std::invalid_argument("Internal error, requested inconsistent number of factor indices and factors in VariableIndex::remove");
if(factors[i]) {
BOOST_FOREACH(Key j, *factors[i]) {
Factors& factorEntries = internalAt(j);
Factors::iterator entry = std::find(factorEntries.begin(), factorEntries.end(), *factorIndex);
if(entry == factorEntries.end())
throw std::invalid_argument("Internal error, indices and factors passed into VariableIndex::remove are not consistent with the existing variable index");
factorEntries.erase(entry);
-- nEntries_;
}
}
}
}
/* ************************************************************************* */
template<typename ITERATOR>
void VariableIndex::removeUnusedVariables(ITERATOR firstKey, ITERATOR lastKey) {
for(ITERATOR key = firstKey; key != lastKey; ++key) {
assert(internalAt(*key).empty());
index_.erase(*key);
}
}
}

View File

@ -1,104 +1,58 @@
/* ----------------------------------------------------------------------------
* 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 VariableIndex.cpp
* @author Richard Roberts
* @date Oct 22, 2010
*/
#include <iostream>
#include <gtsam/inference/VariableIndex.h>
#include <gtsam/inference/Permutation.h>
namespace gtsam {
using namespace std;
/* ************************************************************************* */
bool VariableIndex::equals(const VariableIndex& other, double tol) const {
if(this->nEntries_ == other.nEntries_ && this->nFactors_ == other.nFactors_) {
for(size_t var=0; var < max(this->index_.size(), other.index_.size()); ++var)
if(var >= this->index_.size() || var >= other.index_.size()) {
if(!((var >= this->index_.size() && other.index_[var].empty()) ||
(var >= other.index_.size() && this->index_[var].empty())))
return false;
} else if(this->index_[var] != other.index_[var])
return false;
} else
return false;
return true;
}
/* ************************************************************************* */
void VariableIndex::print(const string& str) const {
cout << str;
cout << "nEntries = " << nEntries() << ", nFactors = " << nFactors() << "\n";
for(Index var = 0; var < size(); ++var) {
cout << "var " << var << ":";
BOOST_FOREACH(const size_t factor, index_[var])
cout << " " << factor;
cout << "\n";
}
cout << flush;
}
/* ************************************************************************* */
void VariableIndex::outputMetisFormat(ostream& os) const {
os << size() << " " << nFactors() << "\n";
// run over variables, which will be hyper-edges.
BOOST_FOREACH(const Factors& variable, index_) {
// every variable is a hyper-edge covering its factors
BOOST_FOREACH(const size_t factor, variable)
os << (factor+1) << " "; // base 1
os << "\n";
}
os << flush;
}
/* ************************************************************************* */
void VariableIndex::permuteInPlace(const Permutation& permutation) {
// Create new index and move references to data into it in permuted order
vector<VariableIndex::Factors> newIndex(this->size());
for(Index i = 0; i < newIndex.size(); ++i)
newIndex[i].swap(this->index_[permutation[i]]);
// Move reference to entire index into the VariableIndex
index_.swap(newIndex);
}
/* ************************************************************************* */
void VariableIndex::permuteInPlace(const Permutation& selector, const Permutation& permutation) {
if(selector.size() != permutation.size())
throw invalid_argument("VariableIndex::permuteInPlace (partial permutation version) called with selector and permutation of different sizes.");
// Create new index the size of the permuted entries
vector<VariableIndex::Factors> newIndex(selector.size());
// Permute the affected entries into the new index
for(size_t dstSlot = 0; dstSlot < selector.size(); ++dstSlot)
newIndex[dstSlot].swap(this->index_[selector[permutation[dstSlot]]]);
// Put the affected entries back in the new order
for(size_t slot = 0; slot < selector.size(); ++slot)
this->index_[selector[slot]].swap(newIndex[slot]);
}
/* ************************************************************************* */
void VariableIndex::removeUnusedAtEnd(size_t nToRemove) {
#ifdef GTSAM_EXTRA_CONSISTENCY_CHECKS
for(size_t i = this->size() - nToRemove; i < this->size(); ++i)
if(!(*this)[i].empty())
throw std::invalid_argument("Attempting to remove non-empty variables with VariableIndex::removeUnusedAtEnd()");
#endif
index_.resize(this->size() - nToRemove);
}
}
/* ----------------------------------------------------------------------------
* 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 VariableIndex.cpp
* @author Richard Roberts
* @date March 26, 2013
*/
#include <iostream>
#include <gtsam/inference/VariableIndex.h>
namespace gtsam {
using namespace std;
/* ************************************************************************* */
bool VariableIndex::equals(const VariableIndex& other, double tol) const {
return this->nEntries_ == other.nEntries_ && this->nFactors_ == other.nFactors_
&& this->index_ == other.index_;
}
/* ************************************************************************* */
void VariableIndex::print(const string& str) const {
cout << str;
cout << "nEntries = " << nEntries() << ", nFactors = " << nFactors() << "\n";
BOOST_FOREACH(KeyMap::value_type key_factors, index_) {
cout << "var " << key_factors.first << ":";
BOOST_FOREACH(const size_t factor, key_factors.second)
cout << " " << factor;
cout << "\n";
}
cout.flush();
}
/* ************************************************************************* */
void VariableIndex::outputMetisFormat(ostream& os) const {
os << size() << " " << nFactors() << "\n";
// run over variables, which will be hyper-edges.
BOOST_FOREACH(KeyMap::value_type key_factors, index_) {
// every variable is a hyper-edge covering its factors
BOOST_FOREACH(const size_t factor, key_factors.second)
os << (factor+1) << " "; // base 1
os << "\n";
}
os << flush;
}
}

View File

@ -1,292 +1,179 @@
/* ----------------------------------------------------------------------------
* 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 VariableIndex.h
* @author Richard Roberts
* @date Sep 12, 2010
*/
#pragma once
#include <vector>
#include <deque>
#include <stdexcept>
#include <boost/foreach.hpp>
#include <gtsam/base/FastList.h>
#include <gtsam/global_includes.h>
#include <gtsam/base/timing.h>
namespace gtsam {
class Permutation;
/**
* The VariableIndex class computes and stores the block column structure of a
* factor graph. The factor graph stores a collection of factors, each of
* which involves a set of variables. In contrast, the VariableIndex is built
* from a factor graph prior to elimination, and stores the list of factors
* that involve each variable. This information is stored as a deque of
* lists of factor indices.
* \nosubgrouping
*/
class GTSAM_EXPORT VariableIndex {
public:
typedef boost::shared_ptr<VariableIndex> shared_ptr;
typedef FastList<size_t> Factors;
typedef Factors::iterator Factor_iterator;
typedef Factors::const_iterator Factor_const_iterator;
protected:
std::vector<Factors> index_;
size_t nFactors_; // Number of factors in the original factor graph.
size_t nEntries_; // Sum of involved variable counts of each factor.
public:
/// @name Standard Constructors
/// @{
/** Default constructor, creates an empty VariableIndex */
VariableIndex() : nFactors_(0), nEntries_(0) {}
/**
* Create a VariableIndex that computes and stores the block column structure
* of a factor graph. This constructor is used when the number of variables
* is known beforehand.
*/
template<class FG> VariableIndex(const FG& factorGraph, Index nVariables);
/**
* Create a VariableIndex that computes and stores the block column structure
* of a factor graph.
*/
template<class FG> VariableIndex(const FG& factorGraph);
/// @}
/// @name Standard Interface
/// @{
/**
* The number of variable entries. This is one greater than the variable
* with the highest index.
*/
Index size() const { return index_.size(); }
/** The number of factors in the original factor graph */
size_t nFactors() const { return nFactors_; }
/** The number of nonzero blocks, i.e. the number of variable-factor entries */
size_t nEntries() const { return nEntries_; }
/** Access a list of factors by variable */
const Factors& operator[](Index variable) const { checkVar(variable); return index_[variable]; }
/// @}
/// @name Testable
/// @{
/** Test for equality (for unit tests and debug assertions). */
bool equals(const VariableIndex& other, double tol=0.0) const;
/** Print the variable index (for unit tests and debugging). */
void print(const std::string& str = "VariableIndex: ") const;
/**
* Output dual hypergraph to Metis file format for use with hmetis
* In the dual graph, variables are hyperedges, factors are nodes.
*/
void outputMetisFormat(std::ostream& os) const;
/// @}
/// @name Advanced Interface
/// @{
/**
* Augment the variable index with new factors. This can be used when
* solving problems incrementally.
*/
template<class FG> void augment(const FG& factors);
/**
* 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 graph
* does not shift the indices of other factors. Also, we keep nFactors_
* one greater than the highest-numbered factor referenced in a VariableIndex.
*
* @param indices The indices of the factors to remove, which must match \c factors
* @param factors The factors being removed, which must symbolically correspond
* exactly to the factors with the specified \c indices that were added.
*/
template<typename CONTAINER, class FG> void remove(const CONTAINER& indices, const FG& factors);
/// Permute the variables in the VariableIndex according to the given permutation
void permuteInPlace(const Permutation& permutation);
/// Permute the variables in the VariableIndex according to the given partial permutation
void permuteInPlace(const Permutation& selector, const Permutation& permutation);
/** Remove unused empty variables at the end of the ordering (in debug mode
* verifies they are empty).
* @param nToRemove The number of unused variables at the end to remove
*/
void removeUnusedAtEnd(size_t nToRemove);
protected:
Factor_iterator factorsBegin(Index variable) { checkVar(variable); return index_[variable].begin(); }
Factor_iterator factorsEnd(Index variable) { checkVar(variable); return index_[variable].end(); }
Factor_const_iterator factorsBegin(Index variable) const { checkVar(variable); return index_[variable].begin(); }
Factor_const_iterator factorsEnd(Index variable) const { checkVar(variable); return index_[variable].end(); }
/// Internal constructor to allocate a VariableIndex of the requested size
VariableIndex(size_t nVars) : index_(nVars), nFactors_(0), nEntries_(0) {}
/// Internal check of the validity of a variable
void checkVar(Index variable) const { assert(variable < index_.size()); }
/// Internal function to populate the variable index from a factor graph
template<class FG> void fill(const FG& factorGraph);
/// @}
private:
/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(index_);
ar & BOOST_SERIALIZATION_NVP(nFactors_);
ar & BOOST_SERIALIZATION_NVP(nEntries_);
}
};
/* ************************************************************************* */
template<class FG>
void VariableIndex::fill(const FG& factorGraph) {
gttic(VariableIndex_fill);
// Build index mapping from variable id to factor index
for(size_t fi=0; fi<factorGraph.size(); ++fi) {
if(factorGraph[fi]) {
BOOST_FOREACH(const Index key, factorGraph[fi]->keys()) {
if(key < index_.size()) {
index_[key].push_back(fi);
++ nEntries_;
}
}
}
++ nFactors_; // Increment factor count even if factors are null, to keep indices consistent
}
}
/* ************************************************************************* */
template<class FG>
VariableIndex::VariableIndex(const FG& factorGraph) :
nFactors_(0), nEntries_(0)
{
gttic(VariableIndex_constructor);
// If the factor graph is empty, return an empty index because inside this
// if block we assume at least one factor.
if(factorGraph.size() > 0) {
gttic(VariableIndex_constructor_find_highest);
// Find highest-numbered variable
Index maxVar = 0;
BOOST_FOREACH(const typename FG::sharedFactor& factor, factorGraph) {
if(factor) {
BOOST_FOREACH(const Index key, factor->keys()) {
if(key > maxVar)
maxVar = key;
}
}
}
gttoc(VariableIndex_constructor_find_highest);
// Allocate array
gttic(VariableIndex_constructor_allocate);
index_.resize(maxVar+1);
gttoc(VariableIndex_constructor_allocate);
fill(factorGraph);
}
}
/* ************************************************************************* */
template<class FG>
VariableIndex::VariableIndex(const FG& factorGraph, Index nVariables) :
nFactors_(0), nEntries_(0)
{
gttic(VariableIndex_constructor_allocate);
index_.resize(nVariables);
gttoc(VariableIndex_constructor_allocate);
fill(factorGraph);
}
/* ************************************************************************* */
template<class FG>
void VariableIndex::augment(const FG& factors) {
gttic(VariableIndex_augment);
// If the factor graph is empty, return an empty index because inside this
// if block we assume at least one factor.
if(factors.size() > 0) {
// Find highest-numbered variable
Index maxVar = 0;
BOOST_FOREACH(const typename FG::sharedFactor& factor, factors) {
if(factor) {
BOOST_FOREACH(const Index key, factor->keys()) {
if(key > maxVar)
maxVar = key;
}
}
}
// Allocate index
index_.resize(std::max(index_.size(), maxVar+1));
// Augment index mapping from variable id to factor index
size_t orignFactors = nFactors_;
for(size_t fi=0; fi<factors.size(); ++fi) {
if(factors[fi]) {
BOOST_FOREACH(const Index key, factors[fi]->keys()) {
index_[key].push_back(orignFactors + fi);
++ nEntries_;
}
}
++ nFactors_; // Increment factor count even if factors are null, to keep indices consistent
}
}
}
/* ************************************************************************* */
template<typename CONTAINER, class FG>
void VariableIndex::remove(const CONTAINER& indices, const FG& factors) {
gttic(VariableIndex_remove);
// NOTE: We intentionally do not decrement nFactors_ because the factor
// indices need to remain consistent. Removing factors from a factor graph
// does not shift the indices of other factors. Also, we keep nFactors_
// one greater than the highest-numbered factor referenced in a VariableIndex.
for(size_t fi=0; fi<factors.size(); ++fi)
if(factors[fi]) {
for(size_t ji = 0; ji < factors[fi]->keys().size(); ++ji) {
Factors& factorEntries = index_[factors[fi]->keys()[ji]];
Factors::iterator entry = std::find(factorEntries.begin(), factorEntries.end(), indices[fi]);
if(entry == factorEntries.end())
throw std::invalid_argument("Internal error, indices and factors passed into VariableIndex::remove are not consistent with the existing variable index");
factorEntries.erase(entry);
-- nEntries_;
}
}
}
}
/* ----------------------------------------------------------------------------
* 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 VariableIndex.h
* @author Richard Roberts
* @date March 26, 2013
*/
#pragma once
#include <vector>
#include <deque>
#include <stdexcept>
#include <boost/foreach.hpp>
#include <gtsam/base/FastList.h>
#include <gtsam/base/FastMap.h>
#include <gtsam/base/types.h>
#include <gtsam/base/timing.h>
#include <gtsam/inference/Key.h>
namespace gtsam {
/**
* The VariableIndex class computes and stores the block column structure of a
* factor graph. The factor graph stores a collection of factors, each of
* which involves a set of variables. In contrast, the VariableIndex is built
* from a factor graph prior to elimination, and stores the list of factors
* that involve each variable. This information is stored as a deque of
* lists of factor indices.
* \nosubgrouping
*/
class GTSAM_EXPORT VariableIndex {
public:
typedef boost::shared_ptr<VariableIndex> shared_ptr;
typedef FastList<size_t> Factors;
typedef Factors::iterator Factor_iterator;
typedef Factors::const_iterator Factor_const_iterator;
protected:
typedef FastMap<Key,Factors> KeyMap;
KeyMap index_;
size_t nFactors_; // Number of factors in the original factor graph.
size_t nEntries_; // Sum of involved variable counts of each factor.
public:
typedef KeyMap::const_iterator const_iterator;
typedef KeyMap::const_iterator iterator;
typedef KeyMap::value_type value_type;
public:
/// @name Standard Constructors
/// @{
/** Default constructor, creates an empty VariableIndex */
VariableIndex() : nFactors_(0), nEntries_(0) {}
/**
* Create a VariableIndex that computes and stores the block column structure
* of a factor graph.
*/
template<class FG>
VariableIndex(const FG& factorGraph) : nFactors_(0), nEntries_(0) { augment(factorGraph); }
/// @}
/// @name Standard Interface
/// @{
/**
* The number of variable entries. This is one greater than the variable
* with the highest index.
*/
Index size() const { return index_.size(); }
/** The number of factors in the original factor graph */
size_t nFactors() const { return nFactors_; }
/** The number of nonzero blocks, i.e. the number of variable-factor entries */
size_t nEntries() const { return nEntries_; }
/** Access a list of factors by variable */
const Factors& operator[](Key variable) const {
KeyMap::const_iterator item = index_.find(variable);
if(item == index_.end())
throw std::invalid_argument("Requested non-existent variable from VariableIndex");
else
return item->second;
}
/// @}
/// @name Testable
/// @{
/** Test for equality (for unit tests and debug assertions). */
bool equals(const VariableIndex& other, double tol=0.0) const;
/** Print the variable index (for unit tests and debugging). */
void print(const std::string& str = "VariableIndex: ") const;
/**
* Output dual hypergraph to Metis file format for use with hmetis
* In the dual graph, variables are hyperedges, factors are nodes.
*/
void outputMetisFormat(std::ostream& os) const;
/// @}
/// @name Advanced Interface
/// @{
/**
* Augment the variable index with new factors. This can be used when
* solving problems incrementally.
*/
template<class FG>
void augment(const FG& factors);
/**
* 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
* graph does not shift the indices of other factors. Also, we keep nFactors_ one greater than
* the highest-numbered factor referenced in a VariableIndex.
*
* @param indices The indices of the factors to remove, which must match \c factors
* @param factors The factors being removed, which must symbolically correspond exactly to the
* factors with the specified \c indices that were added.
*/
template<typename ITERATOR, class FG>
void remove(ITERATOR firstFactor, ITERATOR lastFactor, const FG& factors);
/** Remove unused empty variables (in debug mode verifies they are empty). */
template<typename ITERATOR>
void removeUnusedVariables(ITERATOR firstKey, ITERATOR lastKey);
/** Iterator to the first variable entry */
const_iterator begin() const { return index_.begin(); }
/** Iterator to the first variable entry */
const_iterator end() const { return index_.end(); }
/** Find the iterator for the requested variable entry */
const_iterator find(Key key) const { return index_.find(key); }
protected:
Factor_iterator factorsBegin(Index variable) { return internalAt(variable).begin(); }
Factor_iterator factorsEnd(Index variable) { return internalAt(variable).end(); }
Factor_const_iterator factorsBegin(Index variable) const { return internalAt(variable).begin(); }
Factor_const_iterator factorsEnd(Index variable) const { return internalAt(variable).end(); }
/// Internal version of 'at' that asserts existence
const Factors& internalAt(Key variable) const {
const KeyMap::const_iterator item = index_.find(variable);
assert(item != index_.end());
return item->second; }
/// Internal version of 'at' that asserts existence
Factors& internalAt(Key variable) {
const KeyMap::iterator item = index_.find(variable);
assert(item != index_.end());
return item->second; }
/// @}
};
}
#include <gtsam/inference/VariableIndex-inl.h>

View File

@ -37,7 +37,7 @@ void VariableSlots::print(const std::string& str) const {
for(size_t i=0; i<this->begin()->second.size(); ++i) {
cout << " \t";
BOOST_FOREACH(const value_type& slot, *this) {
if(slot.second[i] == numeric_limits<Index>::max())
if(slot.second[i] == numeric_limits<size_t>::max())
cout << "x" << "\t";
else
cout << slot.second[i] << "\t";

View File

@ -11,15 +11,16 @@
/**
* @file VariableSlots.h
* @brief VariableSlots describes the structure of a combined factor in terms of where each block comes from in the source factors.
* @brief VariableSlots describes the structure of a combined factor in terms of where each block
* comes from in the source factors.
* @author Richard Roberts
* @date Oct 4, 2010
*/
* @date Oct 4, 2010 */
#pragma once
#include <gtsam/global_includes.h>
#include <gtsam/base/FastMap.h>
#include <gtsam/base/timing.h>
#include <iostream>
@ -31,31 +32,29 @@
namespace gtsam {
/**
* A combined factor is assembled as one block of rows for each component
* factor. In each row-block (factor), some of the column-blocks (variables)
* may be empty since factors involving different sets of variables are
* interleaved.
*
* VariableSlots describes the 2D block structure of the combined factor. It
* is essentially a map<Index, vector<size_t> >. The Index is the real
* variable index of the combined factor slot. The vector<size_t> tells, for
* each row-block (factor), which column-block (variable slot) from the
* component factor appears in this block of the combined factor.
*
* As an example, if the combined factor contains variables 1, 3, and 5, then
* "variableSlots[3][2] == 0" indicates that column-block 1 (corresponding to
* variable index 3), row-block 2 (also meaning component factor 2), comes from
* column-block 0 of component factor 2.
*
* \nosubgrouping
*/
/** A combined factor is assembled as one block of rows for each component
* factor. In each row-block (factor), some of the column-blocks (variables)
* may be empty since factors involving different sets of variables are
* interleaved.
*
* VariableSlots describes the 2D block structure of the combined factor. It
* is a map<Index, vector<size_t> >. The Index is the real
* variable index of the combined factor slot. The vector<size_t> tells, for
* each row-block (factor), which column-block (variable slot) from the
* component factor appears in this block of the combined factor.
*
* As an example, if the combined factor contains variables 1, 3, and 5, then
* "variableSlots[3][2] == 0" indicates that column-block 1 (corresponding to
* variable index 3), row-block 2 (also meaning component factor 2), comes from
* column-block 0 of component factor 2.
*
* \nosubgrouping */
class VariableSlots : public FastMap<Index, std::vector<Index> > {
class VariableSlots : public FastMap<Index, std::vector<size_t> > {
public:
typedef FastMap<Index, std::vector<Index> > Base;
typedef FastMap<Index, std::vector<size_t> > Base;
/// @name Standard Constructors
/// @{
@ -69,6 +68,7 @@ public:
VariableSlots(const FG& factorGraph);
/// @}
/// @name Testable
/// @{
@ -79,12 +79,13 @@ public:
GTSAM_EXPORT bool equals(const VariableSlots& rhs, double tol = 0.0) const;
/// @}
};
/* ************************************************************************* */
template<class FG>
VariableSlots::VariableSlots(const FG& factorGraph) {
VariableSlots::VariableSlots(const FG& factorGraph)
{
gttic(VariableSlots_constructor);
static const bool debug = false;
// Compute a mapping (called variableSlots) *from* each involved
@ -96,7 +97,7 @@ VariableSlots::VariableSlots(const FG& factorGraph) {
size_t jointFactorPos = 0;
BOOST_FOREACH(const typename FG::sharedFactor& factor, factorGraph) {
assert(factor);
Index factorVarSlot = 0;
size_t factorVarSlot = 0;
BOOST_FOREACH(const Index involvedVariable, *factor) {
// Set the slot in this factor for this variable. If the
// variable was not already discovered, create an array for it
@ -105,9 +106,9 @@ VariableSlots::VariableSlots(const FG& factorGraph) {
// the array entry for each factor that will indicate the factor
// does not involve the variable.
iterator thisVarSlots; bool inserted;
boost::tie(thisVarSlots, inserted) = this->insert(make_pair(involvedVariable, std::vector<Index>()));
boost::tie(thisVarSlots, inserted) = this->insert(make_pair(involvedVariable, std::vector<size_t>()));
if(inserted)
thisVarSlots->second.resize(factorGraph.size(), std::numeric_limits<Index>::max());
thisVarSlots->second.resize(factorGraph.size(), std::numeric_limits<size_t>::max());
thisVarSlots->second[jointFactorPos] = factorVarSlot;
if(debug) std::cout << " var " << involvedVariable << " rowblock " << jointFactorPos << " comes from factor's slot " << factorVarSlot << std::endl;
++ factorVarSlot;

Some files were not shown because too many files have changed in this diff Show More