c++boostmpiboost-multi-arrayboost-mpi

How to send subarray of 2d Boost.MultiArray via Boost.MPI?


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?


Solution

  • 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
    

    UPDATE: VIEWS

    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:

    Live On Coliru

    #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
        }
    }