谷物 + 犰狳 + json 序列化
cereal + armadillo + json serialization
有人有基于谷物的犰狳矩阵序列化到 JSON 的示例吗?下面的二进制序列化似乎有效。
里面mat_extra_meat.hpp
template<class Archive, class eT>
typename std::enable_if<cereal::traits::is_output_serializable<cereal::BinaryData<eT>, Archive>::value, void>::type
save( Archive & ar, const Mat<eT>& m ) {
uword n_rows = m.n_rows;
uword n_cols = m.n_cols;
ar( n_rows );
ar( n_cols );
ar( cereal::binary_data(
reinterpret_cast< void * const >( const_cast< eT* >( m.memptr() ) ),
static_cast< std::size_t >( n_rows * n_cols * sizeof( eT ) ) ) );
}
template<class Archive, class eT>
typename std::enable_if<cereal::traits::is_input_serializable<cereal::BinaryData<eT>, Archive>::value, void>::type
load( Archive & ar, Mat<eT>& m ) {
uword n_rows;
uword n_cols;
ar( n_rows );
ar( n_cols );
m.resize( n_rows, n_cols );
ar( cereal::binary_data(
reinterpret_cast< void * const >( const_cast< eT* >( m.memptr() ) ),
static_cast< std::size_t >( n_rows * n_cols * sizeof( eT ) ) ) );
}
用这个测试:
int main( int argc, char** argv ) {
arma::mat xx1 = arma::randn( 10, 20 );
std::ofstream ofs( "test", std::ios::binary );
cereal::BinaryOutputArchive o( ofs );
o( xx1 );
ofs.close();
// Now load it.
arma::mat xx2;
std::ifstream ifs( "test", std::ios::binary );
cereal::BinaryInputArchive i( ifs );
i( xx2 );
}
对于 JSON 序列化,您有两种选择 - 您可以采用一种快速而肮脏的方法,这种方法不会真正成为人类可读的,或者您可以以增加序列化大小和时间为代价使其变得人类可读.
对于快速版本,您可以修改现有代码以使用 saveBinaryValue
and loadBinaryValue
,它存在于 cereal(JSON 和 XML)的文本档案中。
例如:
ar.saveBinaryValue( reinterpret_cast<void * const>( const_cast< eT* >( m.memptr() ) ),
static_cast<std::size_t>( n_rows * n_cols * sizeof( eT ) ) );
负载也类似。
这将对您的数据进行 base64 编码并将其写入字符串。您当然需要专门化该功能以仅适用于谷物中的文本档案(或仅 JSON)。
另一种方法是单独序列化每个元素。这里你又有两个选择,第一个是序列化为一个 JSON 数组(例如 myarray: [1, 2, 3, 4, 5, ...])或者作为一堆单独的名称-值-对:"array1":“1”,"array2":“2”,...
谷物中的惯例是使用 JSON 数组来动态调整大小的容器(例如矢量),但是因为我们纯粹强调这个例子的可读性,所以我将使用数组,即使你的犰狳矩阵不会是您希望用户能够使用 JSON:
添加或删除元素的东西
namespace arma
{
// Wraps a particular column in a class with its own serialization function.
// This is necessary because cereal expects actual data to follow a size_tag, and can't
// serialize two size_tags back to back without creating a new node (entering a new serialization function).
//
// This wrapper serves the purpose of creating a new node in the JSON serializer and allows us to
// then serialize the size_tag, followed by the actual data
template <class T>
struct ColWrapper
{
ColWrapper(T && m, int c, int nc) : mat(std::forward<T>(m)), col(c), n_cols(nc) {}
T & mat;
int col;
int n_cols;
template <class Archive>
void save( Archive & ar ) const
{
ar( cereal::make_size_tag( mat.n_rows ) );
for( auto iter = mat.begin_col(col), end = mat.end_col(col); iter != end; ++iter )
ar( *iter );
}
template <class Archive>
void load( Archive & ar )
{
cereal::size_type n_rows;
// Test to see if we need to resize the data
ar( cereal::make_size_tag( n_rows ) );
if( mat.n_rows != n_rows )
mat.resize( n_rows, n_cols );
for( auto iter = mat.begin_col(col), end = mat.end_col(col); iter != end; ++iter )
ar( *iter );
}
};
// Convenience function to make a ColWrapper
template<class T> inline
ColWrapper<T> make_col_wrapper(T && t, int c, int nc)
{
return {std::forward<T>(t), c, nc};
}
template<class Archive, class eT, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
inline void save( Archive & ar, const Mat<eT>& m )
{
// armadillo stored in column major order
uword n_rows = m.n_rows;
uword n_cols = m.n_cols;
// First serialize a size_tag for the number of columns. This will make expect a dynamic
// sized container, which it will output as a JSON array. In reality our container is not dynamic,
// but we're going for readability here.
ar( cereal::make_size_tag( n_cols ) );
for( auto i = 0; i < n_cols; ++i )
// a size_tag must be followed up with actual serializations that create nodes within the JSON serializer
// so we cannot immediately make a size_tag for the number of rows. See ColWrapper for more details
ar( make_col_wrapper(m, i, n_cols) );
}
template<class Archive, class eT, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
inline void load( Archive & ar, Mat<eT>& m )
{
// We're doing essentially the same thing here, but loading the sizes and performing the resize for the matrix
// within ColWrapper
cereal::size_type n_rows;
cereal::size_type n_cols;
ar( cereal::make_size_tag( n_cols ) );
for( auto i = 0; i < n_cols; ++i )
ar( make_col_wrapper(m, i, n_cols) );
}
} // end namespace arma
上面运行的示例程序:
int main(int argc, char* argv[])
{
std::stringstream ss;
std::stringstream ss2;
{
arma::mat A = arma::randu<arma::mat>(4, 5);
cereal::JSONOutputArchive ar(ss);
ar( A );
}
std::cout << ss.str() << std::endl;
{
arma::mat A;
cereal::JSONInputArchive ar(ss);
ar( A );
{
cereal::JSONOutputArchive ar2(ss2);
ar2( A );
}
}
std::cout << ss2.str() << std::endl;
return 0;
}
及其输出:
{
"value0": [
[
0.786820954867802,
0.2504803406880287,
0.7106712289786555,
0.9466678009609704
],
[
0.019271058195813773,
0.40490214481616768,
0.25131781792803756,
0.02271243862792676
],
[
0.5206431525734917,
0.34467030607918777,
0.27419560360286257,
0.561032100176393
],
[
0.14003945653337478,
0.5438560675050177,
0.5219157100717673,
0.8570772835528213
],
[
0.49977436000503835,
0.4193700240544483,
0.7442805199715539,
0.24916812957858262
]
]
}
{
"value0": [
[
0.786820954867802,
0.2504803406880287,
0.7106712289786555,
0.9466678009609704
],
[
0.019271058195813773,
0.40490214481616768,
0.25131781792803756,
0.02271243862792676
],
[
0.5206431525734917,
0.34467030607918777,
0.27419560360286257,
0.561032100176393
],
[
0.14003945653337478,
0.5438560675050177,
0.5219157100717673,
0.8570772835528213
],
[
0.49977436000503835,
0.4193700240544483,
0.7442805199715539,
0.24916812957858262
]
]
}
有人有基于谷物的犰狳矩阵序列化到 JSON 的示例吗?下面的二进制序列化似乎有效。
里面mat_extra_meat.hpp
template<class Archive, class eT>
typename std::enable_if<cereal::traits::is_output_serializable<cereal::BinaryData<eT>, Archive>::value, void>::type
save( Archive & ar, const Mat<eT>& m ) {
uword n_rows = m.n_rows;
uword n_cols = m.n_cols;
ar( n_rows );
ar( n_cols );
ar( cereal::binary_data(
reinterpret_cast< void * const >( const_cast< eT* >( m.memptr() ) ),
static_cast< std::size_t >( n_rows * n_cols * sizeof( eT ) ) ) );
}
template<class Archive, class eT>
typename std::enable_if<cereal::traits::is_input_serializable<cereal::BinaryData<eT>, Archive>::value, void>::type
load( Archive & ar, Mat<eT>& m ) {
uword n_rows;
uword n_cols;
ar( n_rows );
ar( n_cols );
m.resize( n_rows, n_cols );
ar( cereal::binary_data(
reinterpret_cast< void * const >( const_cast< eT* >( m.memptr() ) ),
static_cast< std::size_t >( n_rows * n_cols * sizeof( eT ) ) ) );
}
用这个测试:
int main( int argc, char** argv ) {
arma::mat xx1 = arma::randn( 10, 20 );
std::ofstream ofs( "test", std::ios::binary );
cereal::BinaryOutputArchive o( ofs );
o( xx1 );
ofs.close();
// Now load it.
arma::mat xx2;
std::ifstream ifs( "test", std::ios::binary );
cereal::BinaryInputArchive i( ifs );
i( xx2 );
}
对于 JSON 序列化,您有两种选择 - 您可以采用一种快速而肮脏的方法,这种方法不会真正成为人类可读的,或者您可以以增加序列化大小和时间为代价使其变得人类可读.
对于快速版本,您可以修改现有代码以使用 saveBinaryValue
and loadBinaryValue
,它存在于 cereal(JSON 和 XML)的文本档案中。
例如:
ar.saveBinaryValue( reinterpret_cast<void * const>( const_cast< eT* >( m.memptr() ) ),
static_cast<std::size_t>( n_rows * n_cols * sizeof( eT ) ) );
负载也类似。
这将对您的数据进行 base64 编码并将其写入字符串。您当然需要专门化该功能以仅适用于谷物中的文本档案(或仅 JSON)。
另一种方法是单独序列化每个元素。这里你又有两个选择,第一个是序列化为一个 JSON 数组(例如 myarray: [1, 2, 3, 4, 5, ...])或者作为一堆单独的名称-值-对:"array1":“1”,"array2":“2”,...
谷物中的惯例是使用 JSON 数组来动态调整大小的容器(例如矢量),但是因为我们纯粹强调这个例子的可读性,所以我将使用数组,即使你的犰狳矩阵不会是您希望用户能够使用 JSON:
添加或删除元素的东西namespace arma
{
// Wraps a particular column in a class with its own serialization function.
// This is necessary because cereal expects actual data to follow a size_tag, and can't
// serialize two size_tags back to back without creating a new node (entering a new serialization function).
//
// This wrapper serves the purpose of creating a new node in the JSON serializer and allows us to
// then serialize the size_tag, followed by the actual data
template <class T>
struct ColWrapper
{
ColWrapper(T && m, int c, int nc) : mat(std::forward<T>(m)), col(c), n_cols(nc) {}
T & mat;
int col;
int n_cols;
template <class Archive>
void save( Archive & ar ) const
{
ar( cereal::make_size_tag( mat.n_rows ) );
for( auto iter = mat.begin_col(col), end = mat.end_col(col); iter != end; ++iter )
ar( *iter );
}
template <class Archive>
void load( Archive & ar )
{
cereal::size_type n_rows;
// Test to see if we need to resize the data
ar( cereal::make_size_tag( n_rows ) );
if( mat.n_rows != n_rows )
mat.resize( n_rows, n_cols );
for( auto iter = mat.begin_col(col), end = mat.end_col(col); iter != end; ++iter )
ar( *iter );
}
};
// Convenience function to make a ColWrapper
template<class T> inline
ColWrapper<T> make_col_wrapper(T && t, int c, int nc)
{
return {std::forward<T>(t), c, nc};
}
template<class Archive, class eT, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
inline void save( Archive & ar, const Mat<eT>& m )
{
// armadillo stored in column major order
uword n_rows = m.n_rows;
uword n_cols = m.n_cols;
// First serialize a size_tag for the number of columns. This will make expect a dynamic
// sized container, which it will output as a JSON array. In reality our container is not dynamic,
// but we're going for readability here.
ar( cereal::make_size_tag( n_cols ) );
for( auto i = 0; i < n_cols; ++i )
// a size_tag must be followed up with actual serializations that create nodes within the JSON serializer
// so we cannot immediately make a size_tag for the number of rows. See ColWrapper for more details
ar( make_col_wrapper(m, i, n_cols) );
}
template<class Archive, class eT, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
inline void load( Archive & ar, Mat<eT>& m )
{
// We're doing essentially the same thing here, but loading the sizes and performing the resize for the matrix
// within ColWrapper
cereal::size_type n_rows;
cereal::size_type n_cols;
ar( cereal::make_size_tag( n_cols ) );
for( auto i = 0; i < n_cols; ++i )
ar( make_col_wrapper(m, i, n_cols) );
}
} // end namespace arma
上面运行的示例程序:
int main(int argc, char* argv[])
{
std::stringstream ss;
std::stringstream ss2;
{
arma::mat A = arma::randu<arma::mat>(4, 5);
cereal::JSONOutputArchive ar(ss);
ar( A );
}
std::cout << ss.str() << std::endl;
{
arma::mat A;
cereal::JSONInputArchive ar(ss);
ar( A );
{
cereal::JSONOutputArchive ar2(ss2);
ar2( A );
}
}
std::cout << ss2.str() << std::endl;
return 0;
}
及其输出:
{
"value0": [
[
0.786820954867802,
0.2504803406880287,
0.7106712289786555,
0.9466678009609704
],
[
0.019271058195813773,
0.40490214481616768,
0.25131781792803756,
0.02271243862792676
],
[
0.5206431525734917,
0.34467030607918777,
0.27419560360286257,
0.561032100176393
],
[
0.14003945653337478,
0.5438560675050177,
0.5219157100717673,
0.8570772835528213
],
[
0.49977436000503835,
0.4193700240544483,
0.7442805199715539,
0.24916812957858262
]
]
}
{
"value0": [
[
0.786820954867802,
0.2504803406880287,
0.7106712289786555,
0.9466678009609704
],
[
0.019271058195813773,
0.40490214481616768,
0.25131781792803756,
0.02271243862792676
],
[
0.5206431525734917,
0.34467030607918777,
0.27419560360286257,
0.561032100176393
],
[
0.14003945653337478,
0.5438560675050177,
0.5219157100717673,
0.8570772835528213
],
[
0.49977436000503835,
0.4193700240544483,
0.7442805199715539,
0.24916812957858262
]
]
}