简体   繁体   中英

how to do incontinuity block operation on sparse matrix in eigen

i have this in matlab(actually A is a enormous sparse matrix,about 30k*30k. )

a=[1,3,5,...];
b=[2,4,6,...];
A(a,:)=A(a,:)+A(b,:);
A(b,:)=[]

i would like to perform this opertion (matlab) in c++.But it seems that eigen dosen't allow incontinuity block operation on sparse matrix. What i did is using igl::slice&igl::slice_into.

        SparseMatrix < double >A,A1, A2,A3;
        igl::slice(A, a, 1, A1);
        igl::slice(A, b, 1,A2);
        A3 = A1 + A2;
    
        igl::slice_into(A3, index_u, 1, A);
    
VectorXd A_rows_index = VectorXd::LinSpaced(A.rows(), 0, A.rows() - 1);
    VectorXd A_rows_index_after(A_rows_index.size());
    auto it = std::set_difference(A_rows_index.data(), A_rows_index.data() + A_rows_index.size(),
        b.data(), b.data() + b.size(),
        A_rows_index_after.data());
    A_rows_index_after.conservativeResize(std::distance(A_rows_index_after.data(), it)); // resize the result
        igl::slice(A, A_rows_index_after, 1, A);
    A.resize();

but it cost too much time . Thus i would like to know is there any other way to perform such operation in c++ which cost less time? is there anything i miss

If I understand your code correctly, you add even and odd values per column, then shrink the matrix in half. I don't think that's an operation that is supported out-of-the-box.

The poorly documented InnerIterator is your friend when building operations that are not directly supported. I think this should work reasonably fast:

#include <Eigen/Sparse>

#include <vector>


Eigen::SparseMatrix<double>
fold_even_odd(const Eigen::SparseMatrix<double>& in)
{
  using Triplet = Eigen::Triplet<double>;
  using InnerIterator = Eigen::SparseMatrix<double>::InnerIterator;
  /*
   * Initialize to upper bound of size. This is usually cheaper than
   * reserve + push_back but YMMV
   */
  std::vector<Triplet> triplets(static_cast<std::size_t>(in.nonZeros()));
  std::size_t out = 0;
  for(int col = 0, cols = in.cols(); col < cols; ++col) {
    InnerIterator nonzero(in, col);
    while(nonzero) {
      int row = nonzero.row();
      double value = nonzero.value();
      if(++nonzero && (row & 1) == 0 && nonzero.row() == row + 1) {
        // even followed by odd
        value += nonzero.value();
        ++nonzero;
      }
      triplets[out++] = Triplet(row >> 1, col, value);
    }
  }
  Eigen::SparseMatrix<double> rtrn((in.rows() + 1) >> 1, in.cols());
  rtrn.setFromTriplets(triplets.begin(), triplets.begin() + out);
  return rtrn;
}

There is a little trick we can employ. It will (probably) not make the code faster but shorter: When setting triplets, we can specify a function for duplicates. This can do the addition of successive even and odd elements.

Eigen::SparseMatrix<double>
fold_even_odd2(const Eigen::SparseMatrix<double>& in)
{
  using Triplet = Eigen::Triplet<double>;
  using InnerIterator = Eigen::SparseMatrix<double>::InnerIterator;
  std::vector<Triplet> triplets(static_cast<std::size_t>(in.nonZeros()));
  std::size_t i = 0;
  for(int col = 0, cols = in.cols(); col < cols; ++col)
    for(InnerIterator nonzero(in, col); nonzero; ++nonzero)
      triplets[i++] = Triplet(nonzero.row() >> 1, col, nonzero.value());
  Eigen::SparseMatrix<double> rtrn((in.rows() + 1) >> 1, in.cols());
  rtrn.setFromTriplets(triplets.begin(), triplets.end(),
                       [](double even, double odd) { return even + odd; });
  return rtrn;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM