如何在Armadillo中序列化稀疏矩阵并与Boost的MPI实现结合使用?
我一直在尝试序列化armadillocpp库中的稀疏矩阵。我正在进行一些大规模的数值计算,其中的数据存储在一个稀疏矩阵中,我希望使用MPI(Boost实现)收集该矩阵,并对来自不同节点的矩阵求和。我现在的问题是如何将稀疏矩阵从一个节点发送到其他节点。Boost建议,要发送用户定义的对象(在本例中为
SpMat
),需要将其序列化。
Boost的documentation提供了关于如何序列化用户定义类型的很好的教程,我可以序列化一些基本类。现在,Aradillo的SpMat类对我来说非常复杂,难以理解和序列化。
我遇到了几个问题和他们非常优雅的回答
- This answer,Armadillo的合著者和mlpack的作者Ryan Curtin展示了一种非常优雅的方法来序列化
Mat
类。 - This answerbysehe显示了一种非常简单的稀疏矩阵序列化方法。
使用第一个mpi::send
我可以将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
中使用?
推荐答案
我不想这么说,但是<[3-4]那个人的回答是有缺陷的。感谢您找到它。
问题是它在序列化期间没有存储非零单元格的数量。哎呀。我不知道我在测试时怎么会忽略了这一点。
(看起来我有几个版本,一定是把它的Franken版拼凑在一起了,但实际上没有正确测试)。
我还抛出了一个测试,矩阵将被清除(因此,如果您将其反序列化为具有正确形状但不为空的实例,则不会得到新旧数据的混合。)
已修复
#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
相关文章