Reputation: 1
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
Upvotes: 0
Views: 111
Reputation: 1
I find a much more efficient way to deal with the problem. Instead of using any subroutine in any library to perform this operation at once. We can use a permutation matrix to swap the order of the row/col of the matrix.Than reshape the matrix.
Upvotes: 0
Reputation: 13295
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;
}
Upvotes: 1