Wiki

Clone wiki

gtsam / Migrating from GTSAM 3.X to GTSAM 4.0

Contents

Vector and Matrix Related

Vector/Matrix comma initializer

From GTSAM 3.0 we have convenient comma initializer to initialize Vector and Matrix objects in built-in Eigen library. But this makes GTSAM is no longer compatible to official Eigen release. In GTSAM 4.0 we remove this comma initializer and few changes needed

// this is no longer correct
gtsam::Vector v = gtsam::Vector(3) << 0, 0, 0;
gtsam::Matrix m = gtsam::Matrix(2,2) << 1, 0, 0, 1;
// this is correct
gtsam::Vector v = (gtsam::Vector(3) << 0, 0, 0).finished();
gtsam::Matrix m = (gtsam::Matrix(2,2) << 1, 0, 0, 1).finished();

Fixed size matrices

FIxed sized matrices will decided the allocate memory size during compile time, so there will be performance boosting by using fixed sized matrices if the size is pre-decidable.

// syntactic sugar of Eigen fixed style: 
Matrix35 m35;      // MatrixMN = Matrix(M,N), M,N <= 9;
Matrix3 m33;       // MatrixM = Matrix(M,M), M <= 9;

// Identity Matrix:
Matrix3 I = I_3x3;      // MatrixM I_MxN, M,N <= 9;

// Zero Matrix:
Matrix3 z = Z_3x3;      // MatrixM Z_MxN; //M,N <= 9;

OptionalJacobian

If you are working on your own factors, writing error function with known size jacobian matrices, note that GTSAM has a OptionalJacobian class now, which is used to replace old boost::optional<Matrix&> class. The old format is still compatible but no longer recommended.

// old style, not recommended now
Vector YourOwnFactor::evaluateError(const Type& somevar, boost::optional<Matrix&> H) const; 
// new style, with known size jacobian MxN
Vector YourOwnFactor::evaluateError(const Type& somevar, OptionalJacobian<M,N> H) const; 

LieVector/LieScalar

GTSAM traits and Values type

One of the main change from GTSAM 3.X to GTSAM 4.0 is the use of gtsam::traits. gtsam::traits are a step towards making GTSAM more modern and more efficient, by defining type properties such as dimensionality, group-ness, etc with boost::traits style meta-functions. Our core data structure Values can now take any type, provided the necessary gtsam::traits are defined. This allows for getting rid of those ugly LieScalar/LieVector types.

We are trying to keep user interfaces unchanged, but since this is a fundamental change in GTSAM underlay data structure, some code are not compilable anymore. Here we have brief tutorial of how to fix the related issues and take advantages of the latest techniques.

Custom Value types

In GTSAM 3.X, all Value types had to be derived from gtsam::DerivedValue, which in turn derived from gtsam::Value. In GTSAM 4.0, we remove this requirement in favor of type traits. A minimal example for a generic Value type may then look like this:

class ExampleValue {
 public:
  ExampleValue() { ... }

  // Tangent space dimensionality
  enum {
    dimension = N
  };

  inline size_t dim() const {}

  /// Retract delta to manifold
  ExampleValue retract(const VectorN &v) const {}

  /// Compute the coordinates in the tangent space
  VectorN localCoordinates(const ExampleValue &value) const {}

  /// The print function
  void print(const std::string &s = std::string()) const {}

  /// The equals function with tolerance
  bool equals(const ExampleValue &s, double tol = 1e-9) const {}
};

// required traits - could alternatively be VectorSpace, LieGroup, etc.
template<>
struct traits<ExampleValue> : public internal::Manifold<ExampleValue> {};

template<>
struct traits<const ExampleValue> : public internal::Manifold<ExampleValue> {};

Type traits not only add necessary machinery for custom types to function as Values, but also enforce a number of concept checks. Examples of built-in classes implementing this are mainly in geometry, including Rot3, Pose2, etc.

Type Casting

Old GTSAM geometry classes are derived from gtsam::DerivedValue class, which is derived from gtsam::Value. But now all geometry classes are not derived from gtsam::DerivedValue class anymore. So we cannot cast a gtsam::Value object to geometry object anymore, or vice versa. The following code will get error

gtsam::Value value;
// let value contains a gtsam::Pose2 object here

// static_cast is not compilable
gtsam::Pose2 pose_static = static_cast<const gtsam::Pose2&>(value);
// dynamic_cast will throw runtime std::bad_cast
gtsam::Pose2 pose_dynamic = dynamic_cast<const gtsam::Pose2&>(value);

The modern way to do this is using the Value::cast() function to get cast object

// use cast() provided by gtsam::Value
gtsam::Pose2 pose_cast = value.cast<gtsam::Pose2>();

Const Type Reference in at() and exists()

Since the implementations of gtsam::Values::at() and gtsam::Values::exists() are changed, there's no need to put any const or reference typename in the template parameters. Such as

gtsam::Values values;
gtsam::Key key;
// values contains a Pose2 object at key 

// these will be not compilable
gtsam::Pose2 pose_at = values.at<const gtsam::Pose2&>(key);
gtsam::Pose2 pose_exists = values.exists<const gtsam::Pose2&>(key);

To fix just remove any const and reference

// these are fine
gtsam::Pose2 pose_at = values.at<gtsam::Pose2>(key);
gtsam::Pose2 pose_exists = values.exists<gtsam::Pose2>(key);

Typeid

If you are using typeid keyword to identify the type of object contained in a gtsam::Value object, note that now the typeid of a value is no longer the same as the typeid of the Typename, but gtsam::ChartValue<Typename>.

gtsam::Value value;
// let value contains a gtsam::Pose2 object here

// this will return false
typeid(value) == typeid(gtsam::Pose2);
// this will return true
typeid(value) == typeid(gtsam::ChartValue<gtsam::Pose2>);

Expression Factor

Updated