diff --git a/cpp/Makefile.am b/cpp/Makefile.am index 2ea2b9d37..06371c0cb 100644 --- a/cpp/Makefile.am +++ b/cpp/Makefile.am @@ -100,9 +100,9 @@ testBinaryBayesNet_SOURCES = testBinaryBayesNet.cpp testBinaryBayesNet_LDADD = libgtsam.la # Gaussian inference -headers += GaussianFactorSet.h -sources += Errors.cpp VectorConfig.cpp GaussianFactor.cpp GaussianFactorGraph.cpp GaussianConditional.cpp GaussianBayesNet.cpp -check_PROGRAMS += testVectorConfig testGaussianFactor testGaussianFactorGraph testGaussianConditional testGaussianBayesNet +headers += GaussianFactorSet.h NoiseModel.h +sources += Errors.cpp VectorConfig.cpp GaussianFactor.cpp GaussianFactorGraph.cpp GaussianConditional.cpp GaussianBayesNet.cpp NoiseModel.cpp +check_PROGRAMS += testVectorConfig testGaussianFactor testGaussianFactorGraph testGaussianConditional testGaussianBayesNet testNoiseModel testVectorConfig_SOURCES = testVectorConfig.cpp testVectorConfig_LDADD = libgtsam.la testGaussianFactor_SOURCES = $(example) testGaussianFactor.cpp @@ -113,6 +113,8 @@ testGaussianConditional_SOURCES = $(example) testGaussianConditional.cpp testGaussianConditional_LDADD = libgtsam.la testGaussianBayesNet_SOURCES = $(example) testGaussianBayesNet.cpp testGaussianBayesNet_LDADD = libgtsam.la +testNoiseModel_SOURCES = testNoiseModel.cpp +testNoiseModel_LDADD = libgtsam.la # Iterative Methods headers += iterative-inl.h @@ -278,11 +280,3 @@ CXXLINK = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ # rule to run an executable %.run: % libgtsam.la ./$^ - -# to compile colamd and cppunitlite first when make all and make install. -# The part after external_libs is copied from automatically generated Makefile when without the following line -libgtsam.la: external_libs $(libgtsam_la_OBJECTS) $(libgtsam_la_DEPENDENCIES) - $(libgtsam_la_LINK) -rpath $(libdir) $(libgtsam_la_OBJECTS) $(libgtsam_la_LIBADD) $(LIBS) - - - diff --git a/cpp/Matrix.cpp b/cpp/Matrix.cpp index ca72f4c42..759abb943 100644 --- a/cpp/Matrix.cpp +++ b/cpp/Matrix.cpp @@ -624,6 +624,23 @@ Matrix inverse_square_root(const Matrix& A) { return vector_scale(S, V); // V*S; } +/* ************************************************************************* */ +Matrix square_root_positive(const Matrix& A) { + size_t m = A.size2(), n = A.size1(); + if (m!=n) + throw invalid_argument("inverse_square_root: A must be square"); + + // Perform SVD, TODO: symmetric SVD? + Matrix U,V; + Vector S; + svd(A,U,S,V); + + // invert and sqrt diagonal of S + // We also arbitrarily choose sign to make result have positive signs + for(size_t i = 0; i +#include +//#include +//using namespace std; + +#include "Vector.h" +#include "Matrix.h" + +namespace gtsam { + + // Forward declaration + class NoiseModel; + + /***************************************************************************** + * NoiseModelBase is the abstract base class for all noise models. NoiseModels + * must implement a 'whiten' function to normalize an error vector, and an + * 'unwhiten' function to unnormalize an error vector. + */ + class NoiseModelBase { + public: + /** + * Whiten an error vector. + */ + virtual Vector whiten(const Vector& v) const = 0; + + /** + * Unwhiten an error vector. + */ + virtual Vector unwhiten(const Vector& v) const = 0; + + friend class NoiseModel; + + private: + /** + * Used internally to duplicate the object while retaining the type. + */ + virtual boost::shared_ptr clone() const = 0; + }; + + + + /***************************************************************************** + * NoiseModel is a container for NoiseModelBase, which internally stores + * a shared_ptr to a NoiseModelBase as to support fast and compact storage and + * copies. Copying this class simply assigns the internal shared_ptr. + */ + class NoiseModel { + private: + const boost::shared_ptr base_; + + /** + * Fast constructor, simply assigns shared_ptr. + */ + NoiseModel(boost::shared_ptr noiseModel): base_(noiseModel) { + /*std::cout << "Assigning pointer" << std::endl;*/ + } + + public: + /** + * Fast copy constructor, simply assigns shared_ptr. + */ + NoiseModel(const NoiseModel& noiseModel): base_(noiseModel.base_) { /*std::cout << "Assigning pointer" << std::endl;*/ } + + /** + * Constructor that creates a fast-copyable NoiseModel class by cloning + * a non-pointer NoiseModelBase. The type is retained and can be retrieved + * using a dynamic_cast. + */ + template + NoiseModel(const T& noiseModel): base_(noiseModel.clone()) {} + + /** + * Cast to boost::shared_ptr to retrieve a pointer to the + * NoiseModelBase type. Can be used with dynamic_pointer_cast to retrieve + * the type at runtime. + * E.g.: dynamic_pointer_cast(noiseModel). + */ + operator const boost::shared_ptr () const { + return base_; } + + /** + * Call the NoiseModelBase virtual whiten function + */ + Vector whiten(const Vector& v) const { return base_->whiten(v); } + + /** + * Call the NoiseModelBase virtual unwhiten function + */ + Vector unwhiten(const Vector& v) const { return base_->unwhiten(v); } + + template friend boost::shared_ptr dynamic_pointer_cast(const NoiseModel& p); + }; + + template + boost::shared_ptr dynamic_pointer_cast(const NoiseModel& p) { + return boost::dynamic_pointer_cast(p.base_); } + + + + /***************************************************************************** + * An isotropic noise model assigns the same sigma to each vector element. + * This class has no public constructors. Instead, use either either the + * Sigma or Variance class. + */ + class Isotropic : public NoiseModelBase { + protected: + double sigma_; + double invsigma_; + + Isotropic(double sigma): sigma_(sigma), invsigma_(1.0/sigma) {} + Isotropic(const Isotropic& isotropic): + sigma_(isotropic.sigma_), invsigma_(isotropic.invsigma_) {} + + public: + /** + * Whiten error vector by dividing by sigma + */ + virtual Vector whiten(const Vector& v) const { return v * invsigma_; } + + /** + * Unwhiten error vector by multiplying by sigma + */ + virtual Vector unwhiten(const Vector& v) const { return v * sigma_; } + + /** + * Clone is used to duplicate object while retaining type + */ + boost::shared_ptr clone() const { + /*cout << "Cloning Isotropic" << endl;*/ + return boost::shared_ptr(new Isotropic(*this)); } + }; + + + /***************************************************************************** + * A diagonal noise model implements a diagonal covariance matrix, with the + * elements of the diagonal specified in a Vector. This class has no public + * constructors, instead, use either the Sigmas or Variances class. + */ + class Diagonal : public NoiseModelBase { + protected: + Vector sigmas_; + Vector invsigmas_; + + Diagonal() {} + Diagonal(const Vector& sigmas): sigmas_(sigmas), invsigmas_(1.0 / sigmas) {} + Diagonal(const Diagonal& d): sigmas_(d.sigmas_), invsigmas_(d.invsigmas_) {} + + public: + /** + * Whiten error vector by dividing by sigmas + */ + virtual Vector whiten(const Vector& v) const { return emul(v, invsigmas_); } + + /** + * Unwhiten error vector by multiplying by sigmas + */ + virtual Vector unwhiten(const Vector& v) const { return emul(v, sigmas_); } + + /** + * Clone is used to duplicate object while retaining type + */ + boost::shared_ptr clone() const { + /*cout << "Cloning Isotropic" << endl;*/ + return boost::shared_ptr(new Diagonal(*this)); } + }; + + + /***************************************************************************** + * A full covariance noise model. + */ + class FullCovariance : public NoiseModelBase { + protected: + Matrix sqrt_covariance_; + Matrix sqrt_inv_covariance_; + + public: + FullCovariance(const Matrix& covariance): + sqrt_covariance_(square_root_positive(covariance)), + sqrt_inv_covariance_(inverse_square_root(covariance)) {} + + FullCovariance(const FullCovariance& c): + sqrt_covariance_(c.sqrt_covariance_), sqrt_inv_covariance_(c.sqrt_inv_covariance_) {} + + /** + * Whiten error vector by dividing by sigmas + */ + virtual Vector whiten(const Vector& v) const { return sqrt_inv_covariance_ * v; } + + /** + * Unwhiten error vector by multiplying by sigmas + */ + virtual Vector unwhiten(const Vector& v) const { return sqrt_covariance_ * v; } + + /** + * Clone is used to duplicate object while retaining type + */ + boost::shared_ptr clone() const { + /*cout << "Cloning Isotropic" << endl;*/ + return boost::shared_ptr(new FullCovariance(*this)); } + }; + + + + /***************************************************************************** + * An isotropic noise model using sigma, the noise standard + * deviation. + */ + class Sigma : public Isotropic { + public: + Sigma(const Sigma& isotropic): Isotropic(isotropic) { /*cout << "Constructing Sigma from Sigma" << endl;*/ } + + Sigma(double sigma): Isotropic(sigma) { /*cout << "Constructing Sigma from double" << endl;*/ } + + boost::shared_ptr clone() const { + return boost::shared_ptr(new Sigma(*this)); } + }; + + + /***************************************************************************** + * An isotropic noise model using the noise variance = sigma^2. + */ + class Variance : public Isotropic { + public: + Variance(const Variance& v): Isotropic(v) {} + + Variance(double variance): Isotropic(sqrt(variance)) {} + + boost::shared_ptr clone() const { + return boost::shared_ptr(new Variance(*this)); } + }; + + + /***************************************************************************** + * A diagonal noise model created by specifying a Vector of sigmas, i.e. + * standard devations, i.e. the diagonal of the square root covariance + * matrix. + */ + class Sigmas : public Diagonal { + public: + Sigmas(const Sigmas& s): Diagonal(s) {} + + Sigmas(const Vector& sigmas): Diagonal(sigmas) {} + + boost::shared_ptr clone() const { + return boost::shared_ptr(new Sigmas(*this)); } + }; + + + /***************************************************************************** + * A diagonal noise model created by specifying a Vector of variances, i.e. + * i.e. the diagonal of the covariance matrix. + */ + class Variances : public Diagonal { + public: + Variances(const Variances& s): Diagonal(s) {} + + Variances(const Vector& variances) { + std::transform(variances.begin(), variances.end(), sigmas_.begin(), sqrt); + invsigmas_ = 1.0 / sigmas_; + } + + boost::shared_ptr clone() const { + return boost::shared_ptr(new Variances(*this)); } + }; + +} diff --git a/cpp/Vector.cpp b/cpp/Vector.cpp index 843d00d45..475537878 100644 --- a/cpp/Vector.cpp +++ b/cpp/Vector.cpp @@ -209,6 +209,14 @@ namespace gtsam { return *(std::max_element(a.begin(), a.end())); } + /* ************************************************************************* */ + Vector operator/(double s, const Vector& v) { + Vector result(v.size()); + for(size_t i = 0; i < v.size(); i++) + result[i] = s / v[i]; + return result; + } + /* ************************************************************************* */ pair house(Vector &x) { diff --git a/cpp/Vector.h b/cpp/Vector.h index 01a0cb353..2a028ce15 100644 --- a/cpp/Vector.h +++ b/cpp/Vector.h @@ -187,6 +187,11 @@ double max(const Vector &a); /** Dot product */ inline double dot(const Vector &a, const Vector& b) { return sum(emul(a,b)); } +/** + * Divide every element of a Vector into a scalar + */ +Vector operator/(double s, const Vector& v); + /** * house(x,j) computes HouseHolder vector v and scaling factor beta * from x, such that the corresponding Householder reflection zeroes out diff --git a/cpp/testMatrix.cpp b/cpp/testMatrix.cpp index 29a308ab2..23815bfdf 100644 --- a/cpp/testMatrix.cpp +++ b/cpp/testMatrix.cpp @@ -603,6 +603,26 @@ TEST( matrix, inverse_square_root ) EQUALITY(measurement_covariance,inverse(actual*actual)); } +/* ************************************************************************* */ +TEST( matrix, square_root_positive ) +{ + Matrix cov = Matrix_(3,3, + 4.25, 1.5, 0.0, + 1.5, 2.0, 0.0, + 0.0, 0.0, 1.0); + + Matrix expected = Matrix_(3,3, + 2.01246117974981, 0.447213595499958, 0.0, + 0.447213595499958, 1.34164078649987, 0.0, + 0.0, 0.0, 1.0); + + Matrix actual = square_root_positive(cov); + + CHECK(assert_equal(expected, actual)); + CHECK(assert_equal(cov, trans(actual)*actual)); +} + + /* ************************************************************************* */ int main() { TestResult tr; return TestRegistry::runAllTests(tr); } /* ************************************************************************* */ diff --git a/cpp/testNoiseModel.cpp b/cpp/testNoiseModel.cpp new file mode 100644 index 000000000..a4a5da3ad --- /dev/null +++ b/cpp/testNoiseModel.cpp @@ -0,0 +1,57 @@ +/* + * testNoiseModel.cpp + * + * Created on: Jan 13, 2010 + * Author: richard + */ + +#include + +#include +#include +#include "NoiseModel.h" + +using namespace std; +using namespace boost; +using namespace gtsam; + +class TakesNoiseModel { +public: + NoiseModel noiseModel_; +public: + //template + TakesNoiseModel(const NoiseModel& noiseModel): noiseModel_(noiseModel) {} +}; + + +TEST(NoiseModel, sharedptr) +{ + TakesNoiseModel tnm1(Sigma(1.0)); + cout << endl; + TakesNoiseModel tnm2(tnm1.noiseModel_); + + if(dynamic_pointer_cast(tnm1.noiseModel_)) + cout << "tnm1 has a Sigma!" << endl; + if(dynamic_pointer_cast(tnm1.noiseModel_)) + cout << "tnm1 has a Variance!" << endl; + if(dynamic_pointer_cast(tnm1.noiseModel_)) + cout << "tnm1 has an Isotropic!" << endl; + if(dynamic_pointer_cast(tnm1.noiseModel_)) + cout << "tnm1 has a NoiseModelBase!" << endl; + + if(dynamic_pointer_cast(tnm2.noiseModel_)) + cout << "tnm2 has a Sigma!" << endl; + if(dynamic_pointer_cast(tnm2.noiseModel_)) + cout << "tnm2 has a Variance!" << endl; + if(dynamic_pointer_cast(tnm2.noiseModel_)) + cout << "tnm2 has an Isotropic!" << endl; + if(dynamic_pointer_cast(tnm2.noiseModel_)) + cout << "tnm2 has a NoiseModelBase!" << endl; +} + +/* ************************************************************************* */ +int main() { + TestResult tr; + return TestRegistry::runAllTests(tr); +} +/* ************************************************************************* */