【问题标题】:Can I use Eigen sparse matrices for general storage requirements我可以使用 Eigen 稀疏矩阵来满足一般存储要求吗
【发布时间】:2016-06-07 14:59:46
【问题描述】:

我需要一个模板化的稀疏矩阵实现,但只是为了减少内存占用,做任何数值求解。所以我尝试使用 Eigen,即使我不需要数学部分。为什么 ?它只是碰巧躺在我的机器上,我已经用它来做一些其他的事情了。但我肯定不是 Eigen 专家!

上下文:我有一个 T 类型(比如 struct T{int a; float b; vector<int> c; };,我需要存储它的大型矩阵(比如超过 1000x1000)并且大多数值是空值/不相关。

由于我不做任何数学运算,我认为提供一个赋值运算符来进行存储/检索操作就足够了,如下所示:

int main()
{
    Eigen::SparseMatrix<MyClass> mat(1000,1000); // 1000000 elements
    MyClass b{ 5, 1.2 };
    mat.insert( 3, 4 ) = b;
}

所以这是一个数据类型,我认为是必要的:

struct MyClass
{
    int a;
    float b;
    std::vector<int> v_things;

    MyClass( int aa, float bb ) : a(aa), b(bb) {}
    MyClass( const MyClass& other ) // copy constructor
    {
        a = other.a;
        b = other.b;
        v_things = other.v_things;
    }
    MyClass& operator=( const MyClass& arg )
    {
        if( this != &arg )
        {
            MyClass tmp(arg);
            std::swap( tmp, *this );
        }
        return *this;
    }
};

但这编译失败,因为它似乎需要某种特殊形式的赋值运算符:

/usr/include/eigen3/Eigen/src/SparseCore/SparseMatrix.h:1146:27: error: no match for ‘operator=’ (operand types are ‘Eigen::internal::CompressedStorage<MyClass, int>::Scalar {aka MyClass}’ and ‘int’)
    return (m_data.value(p) = 0);'

(编译器:带有 -std=c++11 的 GCC 5.3)

问题

  • 是否可以使用 Eigen 执行此操作?
    • 如果是,我需要在数据类型中添加什么?这是最好的方法吗?
    • 如果没有,您对另一个库有什么建议吗?

相关特征手册页:

【问题讨论】:

    标签: c++ matrix sparse-matrix eigen


    【解决方案1】:

    确实,由于它旨在存储数值,因此您的类型应该可以从文字 0 构造/分配。需要确保 insert(i,j) 返回对初始化为 0 的标量的引用。

    因此您可以通过添加一个虚拟operator= 来解决问题:

     MyClass& operator=(int x) { assert(x==0); /* ... */ return *this; }
    

    编辑:

    为了使用setFromTriplets,您还需要提供operator+=。原因是默认情况下,重复的条目被汇总在一起。在 Eigen 3.3 中,将仿函数(例如 lambda)传递给 setFromTriplets 定义应如何合并重复项更为简洁。在您的情况下,如果调用它,您可以只传递一个触发运行时断言的仿函数:

    mat.setFromTriplets(begin,end, [] (const MyClass& a,const MyClass &) {
        assert(false && "found duplicates"); return a; } );
    

    在这种情况下,您不需要定义MyClass::operator+=

    【讨论】:

    • 太好了,@ggael,我还需要添加一个空的默认构造函数并删除我原来的赋值运算符,但现在可以正常工作了。
    • 更新:写入/读取单个元素一切正常,但批量插入 (setFromTriplets()) 似乎还需要一些 += 运算符。这对我来说很奇怪:为什么在内存中存储一​​些东西需要这样?我试图添加一个但失败了......
    • 已修复,我将发布另一个包含详细信息的答案。
    • 我完成了解决setFromTriplets案例的答案。
    • 感谢您的编辑,现在对我来说更清楚了。目前,我会坚持使用 3.2.8,但添加该功能的第二个版本确实是个好主意。
    【解决方案2】:

    要完成@ggael 给出的答案以及想要做类似事情的人,这里有一个编译和运行的完整示例:

    #include <eigen3/Eigen/SparseCore>
    #include <vector>
    #include <iostream>
    struct MyClass
    {
        int a;
        float b;
        std::vector<int> v;
    
        MyClass(){}
        MyClass( int aa, float bb ) : a(aa), b(bb) {}
        MyClass( int aa): a(aa) {}
        MyClass( const MyClass& other ) // copy constructor
        {
            a = other.a;
            b = other.b;
            v = other.v;
        }
        MyClass& operator=( int x )
        {
            assert( x==0 );
            return *this;
        }
    
        MyClass& operator += ( const MyClass& x )
        {
            return *this;
        }
    };
    
    void PrintMat( const Eigen::SparseMatrix<MyClass>& mat )
    {
        std::cout << "Matrix content:\n";
        for (int k=0; k<mat.outerSize(); ++k )
            for( Eigen::SparseMatrix<MyClass>::InnerIterator it(mat,k); it; ++it )
                std::cout << "row=" << it.row() << " col=" << it.col()
                    << ": a=" << it.value().a
                    << " b=" << it.value().b
                    << " vect size=" << it.value().v.size() << "\n";
    }
    
    int main()
    {
        Eigen::SparseMatrix<MyClass> mat(1000,1000); // 1000000 positions
    
        MyClass a{ 5, 1.2 };
        a.v.resize(5);
        mat.insert( 3, 4 ) = a;   // insert single element
        PrintMat( mat );
    
        MyClass b{ 6, 2.3 };
        b.v.resize(9);
        mat.coeffRef( 3, 4 ) = b; // update single element
        PrintMat( mat );
    
        std::vector<Eigen::Triplet<MyClass>> tripletList;
        for(int i=0; i<10; i++)
        {
            MyClass a{i*2,i*3.0f};
            tripletList.push_back( Eigen::Triplet<MyClass>(i,i*10,a) );
        }
        mat.setFromTriplets(tripletList.begin(), tripletList.end());
        PrintMat( mat );
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-01
      相关资源
      最近更新 更多