Can I use Eigen sparse matrices for general storage requirements

柔情痞子 提交于 2019-12-23 15:11:23

问题


I need a templated sparse matrix implementation but only to reduce memory footprint, not do any numerical solving. So I tried to use Eigen, even though I don't need the math part. Why ? It just happened to be lying on my machine, and I already had used it a little for other stuff. But I am surely no Eigen expert!

Context: I have a type T (say struct T{int a; float b; vector<int> c; }; and I need to store large matrices of this (say more than 1000x1000) and most of the values are null/irrelevant.

Since I don't do any math, I though it would be enough to provide an assignement operator to do storage / retrieval operations, like the following:

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

So here is a datatype, with what I thought was necessary:

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

But this fails to compile, because it seems to request some special form of the assignment operator:

/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);'

(compiler: GCC 5.3 with -std=c++11)

Questions:

  • Is it possible to do this with Eigen ?
    • If yes, what do I need to add to the data type ? Is this the best approach ?
    • If no, would you have suggestion on another libray?

Relevant Eigen manual pages:

  • http://eigen.tuxfamily.org/dox/group__TutorialSparse.html
  • http://eigen.tuxfamily.org/dox/classEigen_1_1SparseMatrix.html
  • http://eigen.tuxfamily.org/dox/group__SparseQuickRefPage.html

回答1:


Indeed, since it is designed to store numerical values, your type is supposed to be constructible/assignable from the literal 0. It is needed to make sure that insert(i,j) returns a reference to a scalar initialized to 0.

So you can workaround by adding a dummy operator= :

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

Edit:

In order to use setFromTriplets, you also need to provide operator+=. The reason is that by default, duplicate entries are summed together. In Eigen 3.3, it is cleaner to pass a functor (e.g.,a lambda) to setFromTriplets defining how duplicates should be merge. In your case, you could just pass a functor triggering a runtime assertion if it is called:

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

In this case, you don't need to define MyClass::operator+=




回答2:


To complete the answer given by @ggael and for those who would want to do something similar, here is a complete sample that compiles and runs:

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


来源:https://stackoverflow.com/questions/37682868/can-i-use-eigen-sparse-matrices-for-general-storage-requirements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!