Eigen и boost:: serialize

Я попытался написать обобщенную функцию сериализации, которая берет любую плотную матрицу и сериализует ее: Некоторые другие вопросы, которые помогают, но не до конца, здесь: Question1 Question2

Я пробовал следующее, которое должно работать:

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
</derived></class>

Когда я пытаюсь сериализовать Eigen::Matrix

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

Компилятор может как-то не соответствовать шаблону выше? Приводятся следующие ошибки:

/usr/local/include/boost/serialization/access.hpp|118|error: "класс Eigen:: Matrix" не имеет члена с именем "serialize" |

3 ответа

Я использую Расширение на основе плагина Eigen:

/**
 * @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_
</class></class></class>

Настроить Eigen для использования этого pulgin: (просто определите макрос, прежде чем включать любой заголовок Eigen)

#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_
</eigen></boost>

Хотя я на самом деле не проверял это на самом деле, он работает хорошо и может также иметь дело с Array или любыми другими плотными Eigen-объектами. Он также отлично работает для выражений типа vec.tail < 4> (), но может завершиться неудачей (без какой-либо ошибки компиляции) для выражения типа mat.topRows < 2> () или операций с блоками. (См. обновление: теперь также работает и для подматриц)

По сравнению с другим текущим ответом, это работает для большего количества выражений и может избежать некоторых временных. Неинтрузивная версия также, вероятно, возможна путем передачи PlainObjectBase объектов в функции сериализации.

/// 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;
}
</typename></typename>

И некоторый тестовый код:

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");
</vector4f></vector4f></vector4f>

Обновление: я внес некоторые незначительные изменения, теперь также работает сериализация подматриц.


Я проверил ваш код, и он также не работал, когда я пытался его скомпилировать. Однако на основе документации для Boost Serialize у меня создается впечатление, что она предназначена для использования с оператором потока <. Следующий код отлично работает для меня:

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 <******, 4="" 4,=""> a; out << a; return 0; } </******,> </derived> </class>

Файл my_archive создается в рабочей папке с ненулевыми значениями (просто неинициализированный мусор в памяти, с приведенным выше упрощенным кодом).

EDIT: Я попытался использовать точный код выше в моем собственном приложении и обнаружил, что получил ту же ошибку, что и вы. Я честно не понимаю, почему это, прямо сейчас. Самое простое исправление, которое я нашел, заключалось в замене Eigen::EigenBase на фактический тип матрицы. Текущий код, который я использую:

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

Вышеприведенный код работает для любого скалярного типа (float, ******, int) и для динамических/среднесрочных матриц. Для статического размера проверьте и обновите параметры шаблона соответствующим образом.

РЕДАКТИРОВАТЬ № 2 (09 апреля 2014 года):

Несмотря на появление вышеописанного кода, когда я попытался полностью интегрировать его в свой код и осуществить его с помощью соответствующего модульного тестирования, он перестает работать. К сожалению, сообщения об ошибках, которые мне давали - как из Visual Studio, так и из clang - были бесполезны. К счастью, gcc похоронили в ужасном беспорядке сообщений об ошибках, ссылку на CV-несоответствие, которая, похоже, позволила мне полностью решить эту проблему.

Появится следующий код для компиляции и запуска. Я попытался сделать форматирование разборчивым (без боковой прокрутки) - надеюсь, код ниже ясен:

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
</scalar,></s,></s,>

Несколько критических точек над приведенным выше кодом:

  • Этот код теперь имеет шаблонные параметры для всех параметров собственной матрицы. Это должно позволить ему работать со всеми типами матриц и векторов, независимо от того, заданы ли они во время компиляции или во время выполнения. Это существенное улучшение по сравнению с приведенным выше кодом.

  • Важно, чтобы код сериализации был разделен на отдельные функции сохранения и загрузки. В противном случае код десериализации не будет изменять размер матрицы для хранения исходных данных. Я считаю, что Boost:: Serialize предоставляет некоторые дополнительные функции, которые могут быть перегружены для выполнения операций по сериализации или десериализации, но этот подход был проще реализовать.

  • Квалификатор const по методу save имеет важное значение. Это было источником моих проблем, прежде чем ошибка неясного g++ ввела меня в заблуждение.

  • Я не могу сказать, что я полностью проверил этот код. Если вы (или кто-либо еще) найдете больше проблем с ним, сообщите мне об этом, и я попытаюсь следить за чем-либо еще, что я нахожу.

Доверяйте, что это помогает.

Шмуэль


Вот более общая и более короткая версия со следующими функциями:

  • работает правильно, является ли матрица строкой или столбцом.
  • использует пары имя-значение, поэтому он работает с xml-архивами и т.д.
  • не требуется разделение между версией сохранения и загрузки
  • добавляет функцию обертки для Eigen:: Transform (********, Isometry3f и т.д.).
  • unit тест для различных комбинаций, например. он работает, когда вы сохраняете основной размер столбца 2x2 MatrixXd и загружаете его в качестве основной строки Eigen:: Matrix при использовании XML-архивов. Однако это, видимо, не работает для двоичных или текстовых архивов! В этих случаях типы должны точно соответствовать

код:

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
</s,></s,>

licensed under cc by-sa 3.0 with attribution.