获取密集Eigen::Matrix对象的所有非零值



假设您有一个动态大小的Eigen::Matrix对象,并希望仅对非零值进行一些计算,您如何获得所有非零值的向量或列表表示?

Matrix3f m;
m << 1, 0, 0,
0, 5, 6,
0, 0, 9;
VectorXf v = get_non_zero_values(m);
cout << v;

应该给你

1 5 6 9

如何用Eigen(最有效地)做到这一点?

在网上做了大量的研究,并受到这篇stackoverflow帖子的启发,我想出了我自己的解决方案

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

下面是一个使用Eigen 3.4.0版本的解决方案。

如果需要额外的并行性,可以为STL算法使用#include <execution>

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

输出:

1 5 6 9

解决这个问题的另一种可能性是对Eigen的稀疏模块进行一个小偏移:

Eigen::Matrix3f m;
m << 1, 0, 0, 
0, 5, 6,
0, 0, 9;
// Construct a sparse matrix
Eigen::SparseMatrix<float> sparseM(m.sparseView());

你现在可以做任何你想要的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);

使用临时std::vector可以应用push_back方法存储非零值。std::vector的内容可以被Map传送到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; 
}

最新更新