Search code examples
c++templatesserializationboosteigen

Eigen and boost::serialize


I tried to write a generic serialize function which takes any dense matrix and serializes it: Some other questions which help but not to the end are here: Question1 Question2

I tried the following which should work:

namespace boost {
namespace serialization {
    template<class Archive, typename Derived> void serialize(Archive & ar,  Eigen::EigenBase<Derived> & g, const unsigned int version)
    {
        ar & boost::serialization::make_array(g.derived().data(), g.size());
    }
    }; // namespace serialization
}; // namespace boost

When I try to serialize an Eigen::Matrix<double,4,4>

Eigen::Matrix<double,4,4> a; 
boost::serialize(ar, a);

The compiler can somehow not match the template above? And the following errors are given :

/usr/local/include/boost/serialization/access.hpp|118|error: 'class Eigen::Matrix' has no member named 'serialize'|


Solution

  • I make use of Eigen's plugin based extension:

    /**
     * @file EigenDenseBaseAddons.h
     */
    #ifndef EIGEN_DENSE_BASE_ADDONS_H_
    #define EIGEN_DENSE_BASE_ADDONS_H_
    
    friend class boost::serialization::access;
    template<class Archive>
    void save(Archive & ar, const unsigned int version) const {
      derived().eval();
      const Index rows = derived().rows(), cols = derived().cols();
      ar & rows;
      ar & cols;
      for (Index j = 0; j < cols; ++j )
        for (Index i = 0; i < rows; ++i )
          ar & derived().coeff(i, j);
    }
    
    template<class Archive>
    void load(Archive & ar, const unsigned int version) {
      Index rows, cols;
      ar & rows;
      ar & cols;
      if (rows != derived().rows() || cols != derived().cols() )
        derived().resize(rows, cols);
      ar & boost::serialization::make_array(derived().data(), derived().size());
    }
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int file_version) {
      boost::serialization::split_member(ar, *this, file_version);
    }
    
    #endif // EIGEN_DENSE_BASE_ADDONS_H_
    

    Configure Eigen to use this pulgin:(simply define the macro before including any Eigen header)

    #ifndef EIGEN_CONFIG_H_
    #define EIGEN_CONFIG_H_
    
    #include <boost/serialization/array.hpp>
    #define EIGEN_DENSEBASE_PLUGIN "EigenDenseBaseAddons.h"
    
    #include <Eigen/Core>
    
    #endif // EIGEN_CONFIG_H_
    

    Though I have not really tested this throughly, it works well and can also deal with Array or any other dense Eigen objects. It also works perfectly for expressions like vec.tail<4>() but may fail (without any compile error) for expression like mat.topRows<2>() or block operations. (See update: now works for sub matrices also)

    In comparison to the other current answer, this works for more set of expression and might avoid some temporary. A non-intrusive version is also probably possible by passing PlainObjectBase<Derived> objects to the serialize functions..


    /// Boost Serialization Helper
    
    template <typename T>
    bool serialize(const T& data, const std::string& filename) {
      std::ofstream ofs(filename.c_str(), std::ios::out);
      if (!ofs.is_open())
        return false;
      {
        boost::archive::binary_oarchive oa(ofs);
        oa << data;
      }
      ofs.close();
      return true;
    }
    
    template <typename T>
    bool deSerialize(T& data, const std::string& filename) {
      std::ifstream ifs(filename.c_str(), std::ios::in);
      if (!ifs.is_open())
        return false;
      {
        boost::archive::binary_iarchive ia(ifs);
        ia >> data;
      }
      ifs.close();
      return true;
    }
    

    And some test code:

    VectorXf vec(100);
    vec.setRandom();
    serializeText(vec.tail<5>(), "vec.txt");
    
    MatrixXf vec_in;
    deSerialize(vec_in, "vec.bin");
    assert(vec_in.isApprox(vec.tail<5>()));
    
    serialize(Vector2f(0.5f,0.5f), "a.bin");
    Vector2f a2f;
    deSerializeBinary(a2f, "a.bin");
    assert(a2f.isApprox(Vector2f(0.5f,0.5f)));
    VectorXf axf;
    deSerialize(axf, "a.bin");
    assert(aXf.isApprox(Vector2f(0.5f,0.5f)));
    
    boost::shared_ptr<Vector4f> b = boost::make_shared<Vector4f>(Vector4f::Random());
    serialize(b, "b.tmp");
    boost::shared_ptr<Vector4f> b_in;
    deSerialize(b_in, "b.tmp");
    BOOST_CHECK_EQUAL(*b, *b_in);
    
    Matrix4f m(Matrix4f::Random());
    serialize(m.topRows<2>(), "m.bin");
    deSerialize(m_in, "m.bin");
    

    Update: I made some minor modifications,now serialization of sub matrices also works.