简体   繁体   English

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

[英]Get all non-zero values of a dense Eigen::Matrix object

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?假设您有一个动态大小的 Eigen::Matrix 对象并且只想对非零值进行一些计算,您如何获得所有非零值的向量或列表表示?

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)?如何使用 Eigen(最有效)做到这一点?

After a lot of research in the web and inspired by this stackoverflow post I came up with my own solution在网上进行了大量研究并受到这篇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;
}

Another possibility to solve this issue is a small excursion to Eigen's Sparse Module :解决此问题的另一种可能性是对Eigen 的 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 :你现在可以用sparseM做任何你想做的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);

Using a temporary std::vector makes it possible to apply the push_back method for storing the non-zero values.使用临时std::vector可以应用push_back方法来存储非零值。 The content of the std::vector can then be Map ped to an Eigen::Vector .然后可以将std::vector的内容MapEigen::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; 
}

Here is a solution that uses Eigen ver 3.4.0.这是一个使用 Eigen 版本 3.4.0 的解决方案。

You can #include <execution> for the STL algorithms if desired for extra parallelism.如果需要额外的并行性,您可以#include <execution>用于 STL 算法。

#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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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