Повысить сериализацию разреженной матрицы от Armadillo - PullRequest
1 голос
/ 12 апреля 2020

Я пытаюсь использовать функцию разреженной матрицы в Armadillo, и у меня возникают некоторые проблемы с ее сериализацией. Матрицы, с которыми я имею дело, очень большие и в основном содержат нули в компонентах, поэтому имеет смысл использовать sp_mat. Вот код:

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

BOOST_SERIALIZATION_SPLIT_FREE(arma::sp_mat)

namespace boost { 
namespace serialization {

template<class Archive>
void save(Archive & ar, const arma::sp_mat &t, unsigned int version)
{
    ar & t.n_rows;
    ar & t.n_cols;
    const double *data = t.memptr();
    for(int K=0; K<t.n_elem; ++K)
        ar & data[K];

}

template<class Archive>
void load(Archive & ar, arma::sp_mat &t, unsigned int version)
{
    int rows, cols;
    ar & rows;
    ar & cols;
    t.set_size(rows, cols);
    double *data = t.memptr();
    for(int K=0; K<t.n_elem; ++K)
        ar & data[K];}
}}
int main() {

  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;
  arma::sp_mat A = arma::sp_mat(C);

  std::ofstream outputStream;
  outputStream.open("bin.dat");
  std::ostringstream oss;
  boost::archive::binary_oarchive oa(outputStream);
  oa & A;
  outputStream.close();

  arma::sp_mat B;
  std::ifstream inputStream;
  inputStream.open("bin.dat", std::ifstream::in);
  boost::archive::binary_iarchive ia(inputStream);
  ia & B;
  return 0;
}

В настоящее время проблема в том, что sp_mat не имеет члена memptr (), поэтому сериализация компонентов, которые выполняются, например, в строках 10-12, не работает для sp_mat. Мне любопытно, если кто-нибудь знает обходной путь? Я нахожу странным, что когда я печатаю все компоненты А по отдельности, даже нули остаются в памяти, даже если разреженная матрица игнорирует нули. Например, я напечатал A (1,1) и получил 0. Вот также, как выглядит 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

1 Ответ

0 голосов
/ 20 апреля 2020

Количество элементов в матрице всегда n × m, независимо от стратегии хранения (разреженной или плотной).

Поэтому вы не должны удивляться, что сможете прочитать ячейки «0» - они не могут быть сохранены, но очевидно, что они важны для вычислений, поэтому вы должны иметь возможность получить их значение.

В свете этого, ваш эскиз (с memptr(), который я предполагаю, был Копирование / вставка из некоторого кода, указанного c в не разреженные матрицы) всегда будет хранить не разреженные данные (вы повторяете все n_elems). Но data не может указывать на какое-то смежное хранилище, потому что как матрица узнает, какие ячейки существуют, если только разметка памяти не совпадает с размерами матрицы напрямую (плотное хранилище, основной ряд или основной столбец).

На основе на информацию из Возвращение местоположений и значений разреженной матрицы в Armadillo C ++ вот фиксированная реализация:

  • НЕ пытается использовать недокументированные детали реализации
  • использует документированный интерфейс (it.col (), it.row ()) для редкой сериализации
  • работает

Полный код (проверено на моей машине):

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

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

int main() {

  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;

  {
      arma::sp_mat const A = arma::sp_mat(C);

      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;
      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
...