Eigen and boost :: serialize

I tried to write a generalized serialization function that takes any dense matrix and serializes it: Some other questions that 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 Eigen::Matrix<double,4,4>

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

Can the compiler somehow not match the pattern above? The following errors are listed:

/usr/local/include/boost/serialization/access.hpp|118|error: "class Eigen :: Matrix" does not have a member named "serialize" |

+6
source share
3 answers

I am using the Eigen plugin 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: (just define a 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_ 

Although I have not actually tested this in reality, it works well and can also deal with Array or any other dense Eigen objects. It also works great for expressions like vec.tail <4> (), but it may fail (without any compilation error) for expressions like mat.topRows <2> () or block operations. (See update : now also works for sub-matrices)

Compared to the other current answer, this works for more expressions and can avoid some temporary ones. A non-intrusive version is also probably possible by passing PlainObjectBase<Derived> objects in serialization 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 changes, now the serialization of submatrices also works.

+2
source

I checked your code and it also did not work when I tried to compile it. However, based on the documentation for Boost Serialize, I get the impression that it is intended for use with the stream operator <. The following code works fine for me:

 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()); } } } int main (int argc, char* argv[]){ std::ofstream out("my_archive"); boost::archive::text_oarchive oa (out); Eigen::Matrix <double, 4, 4> a; out << a; return 0; } 

The my_archive file is created in the working folder with non-zero values ​​(just uninitialized garbage in memory, with the simplified code above).

EDIT: I tried using the exact code above in my own application and found that I got the same error as you. I honestly don't understand why this is, right now. The simplest fix I found was to replace Eigen::EigenBase<Derived> with the actual matrix type. Current code I'm using:

 namespace boost{ namespace serialization { template <class Archive, typename Scalar> void serialize ( Archive & ar, Eigen::Matrix<Scalar, -1, -1, 0, -1, -1> & g, const unsigned int version ){ /* ... */ } } } 

The above code works for any scalar type (float, double, int) and for dynamic / medium-term matrices. For static size, check and update the template settings accordingly.

EDIT No. 2 (April 09, 2014):

Despite the appearance of the above code, when I tried to fully integrate it into my code and implement it using the appropriate unit testing, it stops working. Unfortunately, the error messages I was given - both from Visual Studio and from clang - were useless. Fortunately, gcc was buried in a terrible mess of error messages, a CV mismatch link that seemed to allow me to completely solve this problem.

The following code will appear to compile and run. I tried to make the formatting legible (without side scrolling) - I hope the code below is clear:

 namespace boost{ namespace serialization{ template< class Archive, class S, int Rows_, int Cols_, int Ops_, int MaxRows_, int MaxCols_> inline void save( Archive & ar, const Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g, const unsigned int version) { int rows = g.rows(); int cols = g.cols(); ar & rows; ar & cols; ar & boost::serialization::make_array(g.data(), rows * cols); } template< class Archive, class S, int Rows_, int Cols_, int Ops_, int MaxRows_, int MaxCols_> inline void load( Archive & ar, Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g, const unsigned int version) { int rows, cols; ar & rows; ar & cols; g.resize(rows, cols); ar & boost::serialization::make_array(g.data(), rows * cols); } template< class Archive, class S, int Rows_, int Cols_, int Ops_, int MaxRows_, int MaxCols_> inline void serialize( Archive & ar, Eigen::Matrix<scalar, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g, const unsigned int version) { split_free(ar, g, version); } } // namespace serialization } // namespace boost 

A few critical points over the above code:

  • This code now has template parameters for all parameters of its own matrix. This should allow it to work with all types of matrices and vectors, regardless of whether they are specified at compile time or at run time. This is a significant improvement over the code above.

  • It is important that the serialization code is divided into separate save and load functions. Otherwise, the deserialization code will not resize the matrix to store the original data. I believe that Boost :: Serialize provides some additional features that can be overloaded to perform serialization or deserialization operations, but this approach was easier to implement.

  • The const qualifier in the save method is important. This was the source of my problems before the obscure g ++ error misled me.

  • I can not say that I have fully verified this code. If you (or anyone else) find more problems with him, let me know and I will try to keep track of anything else that I find.

Trust this helps.

Shmuel

+6
source

Here is a more general and shorter version with the following features:

  • works correctly whether the matrix is ​​a row or column.
  • uses name-value pairs, so it works with xml archives, etc.
  • no separation between save and load version required
  • Adds a wrapper function for Eigen :: Transform (Affine3d, Isometry3f, etc.).
  • unit test for various combinations, for example. it works when you save the main size of a 2x2 MatrixXd column and load it as the main row of Eigen :: Matrix when using XML archives. However, this apparently does not work for binary or text archives! In these cases, the types must match exactly

code:

 namespace boost { namespace serialization { template< class Archive, class S, int Rows_, int Cols_, int Ops_, int MaxRows_, int MaxCols_> inline void serialize(Archive & ar, Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & matrix, const unsigned int version) { int rows = matrix.rows(); int cols = matrix.cols(); ar & make_nvp("rows", rows); ar & make_nvp("cols", cols); matrix.resize(rows, cols); // no-op if size does not change! // always save/load row-major for(int r = 0; r < rows; ++r) for(int c = 0; c < cols; ++c) ar & make_nvp("val", matrix(r,c)); } template< class Archive, class S, int Dim_, int Mode_, int Options_> inline void serialize(Archive & ar, Eigen::Transform<S, Dim_, Mode_, Options_> & transform, const unsigned int version) { serialize(ar, transform.matrix(), version); } }} // namespace boost::serialization 
+4
source

Source: https://habr.com/ru/post/952287/


All Articles