c++ - 如何在 Armadillo 中序列化稀疏矩阵并与boost的mpi实现一起使用?

标签 c++ boost sparse-matrix armadillo

我一直在尝试序列化 armadillo 中的稀疏矩阵.cpp 库。我正在做一些大规模的数值计算,其中数据存储在稀疏矩阵中,我想使用 mpi(Boost 实现)收集这些矩阵并对来自不同节点的矩阵求和。我现在陷入困境的是如何将稀疏矩阵从一个节点发送到其他节点。 Boost 建议发送用户定义的对象(在本例中为 SpMat),需要对其进行序列化。

Boost 的 documentation提供了有关如何序列化用户定义类型的很好的教程,我可以序列化一些基本类。现在, Armadillo 的 SpMat 类对我来说非常复杂,难以理解和序列化。

我遇到了一些问题和他们非常优雅的答案

  1. This answer作者:Armadillo 的合著者和 mlpack 的作者 Ryan Curtin 展示了一种非常优雅的方法来序列化 Mat 类。
  2. This answer通过 sehe展示了一种非常简单的稀疏矩阵序列化方法。

使用第一个,我可以将 Mat 类mpi::send 发送到通信器中的另一个节点,但使用后者,我无法mpi::send

这改编自第二个链接的答案

#include <iostream>
#include <boost/serialization/complex.hpp>
#include <boost/serialization/split_member.hpp>
#include <fstream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <armadillo>
#include <boost/mpi.hpp>
namespace mpi = boost::mpi;
using namespace std;
using namespace arma;

namespace boost { 
    namespace serialization {

        template<class Archive>
            void save(Archive & ar, const arma::sp_mat &t, unsigned) {
                ar & t.n_rows;
                ar & t.n_cols;
                for (auto it = t.begin(); it != t.end(); ++it) {
                    ar & it.row() & it.col() & *it;
                }
            }

        template<class Archive>
            void load(Archive & ar, arma::sp_mat &t, unsigned) {
                uint64_t r, c;
                ar & r;
                ar & c;
                t.set_size(r, c);
                for (auto it = t.begin(); it != t.end(); ++it) {
                    double v;
                    ar & r & c & v;
                    t(r, c) = v;
                }
            }
    }}
BOOST_SERIALIZATION_SPLIT_FREE(arma::sp_mat)

int main(int argc, char *argv[])
{
    mpi::environment env(argc, argv);
    mpi::communicator world;
    arma::mat C(3,3, arma::fill::randu);
    C(1,1) = 0; //example so that a few of the components are u
    C(1,2) = 0;
    C(0,0) = 0;
    C(2,1) = 0;
    C(2,0) = 0;
    sp_mat A;
    if(world.rank() == 0) 
    {
        A = arma::sp_mat(C);
    }

    broadcast(world,A,0);

    if(world.rank() ==1 ) cout << A << endl;

    return 0;
}

我是这样编译的

$ mpicxx -L ~/boost_1_73_0/stage/lib  -lboost_mpi -lboost_serialization -I ~/armadillo-9.900.1/include -DARMA_DONT_USE_WRAPPER -lblas -llapack serialize_arma_spmat.cpp -o serialize_arma_spmat

$ mpirun -np 2 serialize_arma_spmat
[matrix size: 3x3; n_nonzero: 0; density: 0%]

作为进程号。 2 没有打印预期的 A 矩阵。所以广播不起作用。

我无法尝试以 Ryan 的答案为基础,因为我无法理解 Armadillo 中“SpMat_Meat.hpp”中的稀疏矩阵实现,这与 Mat 类非常不同。

如何在 boost 中序列化稀疏矩阵,以便可以在 boost::mpi 中使用?

最佳答案

我不想这么说,但是那个答案sehe这家伙只是有缺陷。感谢您找到它。

问题在于它在序列化期间不存储非零单元格的数量。哎呀。我不知道我在测试时怎么忽略了这一点。

(Looks like I had several versions and must have patched together a Frankenversion of it that wasn't actually properly tested).

我还进行了一个测试,矩阵被清除(这样,如果您反序列化为具有正确形状但不为空的实例,您最终不会得到新旧数据的混合。)

已修复

#include <armadillo>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/split_member.hpp>
#include <fstream>
#include <iostream>

BOOST_SERIALIZATION_SPLIT_FREE(arma::sp_mat)

namespace boost { namespace serialization {

    template<class Archive>
    void save(Archive & ar, const arma::sp_mat &t, unsigned) {
        ar & t.n_rows & t.n_cols & t.n_nonzero;

        for (auto it = t.begin(); it != t.end(); ++it) {
            ar & it.row() & it.col() & *it;
        }
    }

    template<class Archive>
    void load(Archive & ar, arma::sp_mat &t, unsigned) {
        uint64_t r, c, nz;
        ar & r & c & nz;

        t.zeros(r, c);
        while (nz--) {
            double v;
            ar & r & c & v;
            t(r, c) = v;
        }
    }
}} // namespace boost::serialization

int main() {

    arma::mat C(3, 3, arma::fill::randu);
    C(0, 0) = 0;
    C(1, 1) = 0; // example so that a few of the components are u
    C(1, 2) = 0;
    C(2, 0) = 0;
    C(2, 1) = 0;

    {
        arma::sp_mat const A = arma::sp_mat(C);
        assert(A.n_nonzero == 4);

        A.print("A: ");
        std::ofstream outputStream("bin.dat", std::ios::binary);
        boost::archive::binary_oarchive oa(outputStream);
        oa& A;
    }

    {
        std::ifstream inputStream("bin.dat", std::ios::binary);
        boost::archive::binary_iarchive ia(inputStream);

        arma::sp_mat B(3,3);
        B(0,0) = 77; // some old data should be cleared

        ia& B;

        B.print("B: ");
    }
}

现在可以正确打印

A:
[matrix size: 3x3; n_nonzero: 4; density: 44.44%]

     (1, 0)         0.2505
     (0, 1)         0.9467
     (0, 2)         0.2513
     (2, 2)         0.5206

B:
[matrix size: 3x3; n_nonzero: 4; density: 44.44%]

     (1, 0)         0.2505
     (0, 1)         0.9467
     (0, 2)         0.2513
     (2, 2)         0.5206

关于c++ - 如何在 Armadillo 中序列化稀疏矩阵并与boost的mpi实现一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67267414/

相关文章:

boost - cmake测试: Every test is run on each ctest

r - 有效地组合许多文档术语矩阵

c - 元素随机分布的稀疏矩阵的数据结构

c++ - 如何转发声明 boost::ptree::iterator

c++ - 如何将 _variant_t 数据类型转换为 wstring

c++ - 基本的shared_count变体

java - 将稀疏矩阵从 R 移至 Java

c++ - C/C++手动将string转int并判断是否溢出?

c++ - 在多线程应用程序中设置硬件断点不会触发

c++ - Boost Graph Library : How do I keep a permanant reference to a vertex?(即不受顶点重新编号的影响)