Reputation: 162
Asuming you have a dynamic size Eigen::Matrix object and want to do some computation on only non-zero values, how can you get a vector or list representation of all non-zero values?
Matrix3f m;
m << 1, 0, 0,
0, 5, 6,
0, 0, 9;
VectorXf v = get_non_zero_values(m);
cout << v;
should give you
1 5 6 9
How can this be done with Eigen (most efficiently)?
Upvotes: 0
Views: 1940
Reputation: 2568
Another possibility to solve this issue is a small excursion to Eigen's Sparse Module:
Eigen::Matrix3f m;
m << 1, 0, 0,
0, 5, 6,
0, 0, 9;
// Construct a sparse matrix
Eigen::SparseMatrix<float> sparseM(m.sparseView());
You can now do whatever you want with sparseM
:
// Print non-zeros
for (int i = 0; i < sparseM.nonZeros(); ++i)
std::cout << *(sparseM.valuePtr() + i) << "\n";
// Construct a dense map
Eigen::Map<Eigen::VectorXf> denseMap(sparseM.valuePtr(), sparseM.nonZeros());
// Construct a dense vector
Eigen::VectorXf denseVector(denseMap);
Upvotes: 1
Reputation: 1002
Here is a solution that uses Eigen ver 3.4.0.
You can #include <execution>
for the STL algorithms if desired for extra parallelism.
#include <Eigen/Dense>
#include <algorithm>
#include <functional>
#include <iostream>
int main()
{
Eigen::MatrixXf const m{{1, 0, 0},
{0, 5, 6},
{0, 0, 9}};
auto const size = m.size();
// create 1D view
auto const view = m.reshaped().transpose();
// create boolean markers for nonzeros
auto const mask = view.array() != 0;
// create index list and set useless elements to sentinel value
auto constexpr sentinel = std::numeric_limits<int>::lowest();
auto idxs = mask.select(Eigen::RowVectorXi::LinSpaced(size, 0, size), sentinel).eval();
// sort to remove sentinel values
std::partial_sort(idxs.begin(), idxs.begin() + size, idxs.end(), std::greater{});
idxs.conservativeResize(mask.count());
auto const nonzeros = view(idxs.reverse()).eval();
std::cout << nonzeros << std::endl;
}
Output:
1 5 6 9
Upvotes: 1
Reputation: 23788
Using a temporary std::vector
makes it possible to apply the push_back
method for storing the non-zero values. The content of the std::vector
can then be Map
ped to an Eigen::Vector
.
VectorXf get_non_zero_values(const MatrixXf& m) {
std::vector<float> nzv;
for (int i = 0; i < m.size(); ++i) {
if (m(i)) nzv.push_back(m(i));
}
Map<VectorXf> nzm(nzv.data(), nzv.size());
return nzm;
}
Upvotes: 0
Reputation: 162
After a lot of research in the web and inspired by this stackoverflow post I came up with my own solution
template <typename T>
Eigen::Matrix<T, Eigen::Dynamic, 1> get_non_zeros(Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& _input)
{
Eigen::Matrix<T, Eigen::Dynamic, 1> reduced(Eigen::Map<Eigen::Matrix<T, Eigen::Dynamic, 1>>(_input.data(), _input.size()));
Eigen::Matrix<bool, Eigen::Dynamic, 1> empty = (reduced.array() == 0).rowwise().all();
size_t last = reduced.rows() - 1;
for ( size_t i = 0; i < last + 1;) {
if ( empty(i) ) {
reduced.row(i).swap(reduced.row(last));
empty.segment<1>(i).swap(empty.segment<1>(last));
--last;
}
else {
++i;
}
}
reduced.conservativeResize(last + 1, reduced.cols());
return reduced;
}
Upvotes: 1