I need to send reference to a subarray of 2d of Boost.MultiArray with Boost.MPI. Now I have the following code:
matrix_type::array_view<2>::type
current_process_batch = matrix[boost::indices[range(bias, finish_line)][range(0, width)]];
world.send(rank, BEGIN_TAG, current_process_batch);
But also I have following error trying to execute it:
/usr/local/include/boost/serialization/access.hpp:116:11: error: no member named 'serialize' in 'boost::detail::multi_array::multi_array_view<double, 2>'
t.serialize(ar, file_version);
Can anyone help me to send it to another process?
You need to implement serialization.
Here's a general multi_array<T. Dims>
serializer:
namespace boost::serialization {
template <typename Ar, typename T, size_t Dims>
void save(Ar& ar, boost::multi_array<T, Dims> const& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
std::copy_n(ma.shape(), Dims, shape.begin());
ar & make_nvp("shape", shape)
& make_nvp("data", make_array(ma.data(), ma.num_elements()));
}
template <typename Ar, typename T, size_t Dims>
void load(Ar& ar, boost::multi_array<T, Dims>& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
ar & make_nvp("shape", shape);
ma.resize(shape);
ar & make_nvp("data", make_array(const_cast<T*>(ma.data()), ma.num_elements()));
}
template <typename Ar, typename T, size_t Dims>
void serialize(Ar& ar, boost::multi_array<T, Dims>& ma, unsigned version) {
split_free(ar, ma, version);
}
}
See it Live On Coliru
auto make_multi_array() {
boost::multi_array<int, 2> ma(boost::extents[7][4]);
std::iota(ma.data(), ma.data() + ma.num_elements(), 10);
return ma;
}
int main() {
auto const original = make_multi_array();
{
std::ofstream os("array.txt");
boost::archive::text_oarchive oa(os);
oa << original;
}
std::cout << std::ifstream("array.txt").rdbuf() << "\n";
{
std::ifstream is("array.txt");
boost::archive::text_iarchive ia(is);
boost::multi_array<int, 2> restored;
ia >> restored;
assert(restored == original);
}
}
Prints
22 serialization::archive 17 0 0 0 0 2 7 4 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
Mmm. Already accepted? Noticing late that I didn't actually read your question that well. You wanted to serialize a view. You can't, really. You can't "deserialize a view".
The most you can do is deserialize INTO a view.
Note also that
serializing the whole array could be (much) more efficient if the size difference isn't huge
the view types are not public interface for the library, i.e. it may break in future versions of Boost. If in doubt, serialize the arrays themselves
Here's some more machinery:
adds support for detail::multi_array::multi_array_view<...>
in the sense that it can deserialize INTO an existing view of matching shape only
adds support for detail::multi_array::const_multi_array_view<...>
only for serializing. Said view can be deserialized into a non-const view only, for obvious reasons.
extends the demo with a sub-view that patches the initial array after serializing, so we can verify that reading the patch into an equivalent view of the restored array has the same effect as on the original (after checking that the patch created the expected mismatch 20 != 999
).
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/array_wrapper.hpp>
#include <boost/multi_array.hpp>
#include <iostream>
#include <fstream>
namespace boost::serialization {
template <typename Ar, typename T, size_t Dims>
void save(Ar& ar, boost::detail::multi_array::const_multi_array_view<T, Dims> const& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
std::copy_n(ma.shape(), Dims, shape.begin());
ar & make_nvp("shape", shape);
for (auto i = 0; i < shape[0]; ++i) {
for (auto j = 0; j < shape[1]; ++j) {
ar & make_nvp("item", ma[i][j]);
}
}
}
template <typename Ar, typename T, size_t Dims>
void load(Ar&, boost::detail::multi_array::const_multi_array_view<T, Dims>&, unsigned) {
static_assert(not Ar::is_loading::value, "const_multi_array_view immutable");
}
template <typename Ar, typename T, size_t Dims>
void serialize(Ar& ar, boost::detail::multi_array::const_multi_array_view<T, Dims>& ma, unsigned version) {
split_free(ar, ma, version);
}
template <typename Ar, typename T, size_t Dims>
void save(Ar& ar, boost::detail::multi_array::multi_array_view<T, Dims> const& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
std::copy_n(ma.shape(), Dims, shape.begin());
ar & make_nvp("shape", shape);
for (auto i = 0; i < shape[0]; ++i) {
for (auto j = 0; j < shape[1]; ++j) {
ar & make_nvp("item", ma[i][j]);
}
}
}
template <typename Ar, typename T, size_t Dims>
void load(Ar& ar, boost::detail::multi_array::multi_array_view<T, Dims>& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
ar & make_nvp("shape", shape);
if (!std::equal(begin(shape), end(shape), ma.shape()))
throw std::logic_error("multi_array_view shape mismatch");
for (auto i = 0; i < shape[0]; ++i) {
for (auto j = 0; j < shape[1]; ++j) {
ar & make_nvp("item", ma[i][j]);
}
}
}
template <typename Ar, typename T, size_t Dims>
void serialize(Ar& ar, boost::detail::multi_array::multi_array_view<T, Dims>& ma, unsigned version) {
split_free(ar, ma, version);
}
template <typename Ar, typename T, size_t Dims>
void save(Ar& ar, boost::multi_array<T, Dims> const& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
std::copy_n(ma.shape(), Dims, shape.begin());
ar & make_nvp("shape", shape)
& make_nvp("data", make_array(ma.data(), ma.num_elements()));
}
template <typename Ar, typename T, size_t Dims>
void load(Ar& ar, boost::multi_array<T, Dims>& ma, unsigned /*version*/) {
std::array<int, Dims> shape;
ar & make_nvp("shape", shape);
ma.resize(shape);
ar & make_nvp("data", make_array(const_cast<T*>(ma.data()), ma.num_elements()));
}
template <typename Ar, typename T, size_t Dims>
void serialize(Ar& ar, boost::multi_array<T, Dims>& ma, unsigned version) {
split_free(ar, ma, version);
}
}
auto make_multi_array() {
boost::multi_array<int, 2> ma(boost::extents[7][4]);
std::iota(ma.data(), ma.data() + ma.num_elements(), 10);
return ma;
}
int main() {
using range = boost::multi_array_types::index_range;
auto original = make_multi_array();
auto const SLICE = boost::indices[range(1,3)][range(1,3)];
{
std::ofstream os("array.txt");
boost::archive::text_oarchive oa(os);
// only serialize initial array:
oa << original;
// modify through a sub-view
auto sub = original[SLICE];
original[2][2] = 999;
// serialize the patch
oa << sub;
auto const& const_original = original;
auto const_sub = const_original[SLICE];
oa << const_sub; // compiles
}
std::cout << std::ifstream("array.txt").rdbuf() << "\n";
{
std::ifstream is("array.txt");
boost::archive::text_iarchive ia(is);
// only deserialize initial array:
boost::multi_array<int, 2> restored;
ia >> restored;
assert(restored != original); // entry overwritten with 999
assert(restored.num_elements() == original.num_elements());
auto [o_2_2, r_2_2] = std::mismatch(
original.data(), original.data()+original.num_elements(),
restored.data());
assert(*o_2_2 == 999);
assert(*r_2_2 == 20);
// now patch in the sub array at the same sub-view:
auto sub = restored[SLICE];
ia >> sub;
// now everything is equal
assert(restored == original); // now matches!
assert(*r_2_2 == 999);
auto const& const_restored = restored;
auto const_sub = const_restored[SLICE];
//ia >> const_sub; // WON'T COMPILE
}
}