简体   繁体   English

具有完全自定义类型的特征稀疏矩阵乘法

[英]Eigen Sparse Matrix Multiply with fully custom types

I've got as far as convincing Eigen to do this我已经说服 Eigen 这样做了

class A{...};  class B{...}; class Product{...};
auto operator*(const A&,const B&){return Product{..}} 
..operator+..
//"SparseMatrix<A>*SparseVector<B> -> SparseVector<Product>"


SparseMatrix<A> mymat_A; //...
SparseVector<B> myvec_B; //...

SparseVector<Product> result= mymat_A*myvec_B;

However what I'm really after is distinct component-Product and output-Accumulator types, eg但是我真正追求的是不同的组件-产品和输出-累加器类型,例如

class A{}class B{} class Product{} class Accumulator;
auto operator*(const A&,const B&)->Product{..}
auto operator+=(Accumulator& lhs,const Product& p){lhs+=p;return lhs;}

SparseVector<Accumulator> result = mymat_A*myvec_B

Is this possible?这可能吗? I've needed to setup eigen's type-traits for the A,B,Prod here to get this far, and setup these template<> struct Eigen::ScalarBinaryOpTraits<A,B,Eigen::internal::scalar_product_op<A, B> >{ typedef Product ReturnType;我需要在这里为 A,B,Prod 设置 eigen 的类型特征才能达到这一点,并设置这些 template<> struct Eigen::ScalarBinaryOpTraits<A,B,Eigen::internal::scalar_product_op<A, B > >{ typedef 产品返回类型; }; };

I had thought I could hint the existence of a seperate product using "..sum..<Product,Product> -> Accumulator, supplying overloads for Prod+Prod->Acc, but this didn't work.我原以为我可以使用“..sum..<Product,Product> -> 累加器来暗示存在一个单独的产品,为 Prod+Prod->Acc 提供重载,但这不起作用。

use cases= mixed precision with small products but many such that you need a larger accumulator, and doing non-arithmetic things (information flowing across graphs) that just happens to fit the storage and traversal pattern optimizations needed for sparse matrix mul.用例=混合精度与小型产品,但许多产品需要更大的累加器,并且做非算术的事情(信息在图形中流动)恰好适合稀疏矩阵 mul 所需的存储和遍历模式优化。

If this isn't possible, perhaps someone can recommend an alternate sparse-matrix library which can do this.如果这是不可能的,也许有人可以推荐一个可以做到这一点的替代稀疏矩阵库。

If I succumb to NIH and just roll my own, my design for this would have been as follows - just use decltype to infer the existence of seperate sum/product types from operator overloads (supplying a custom product type with summation overload yielding the desired accumulator).如果我屈服于 NIH 并自己动手,我的设计将如下所示 - 只需使用 decltype 从运算符重载中推断存在单独的总和/产品类型(提供具有总和重载的自定义产品类型,从而产生所需的累加器)。 but it would use Acc+=Prod internally (after declaring decltype(Prod+Prod) accumulators initialized to '0'.)但它会在内部使用 Acc+=Prod(在声明 decltype(Prod+Prod) 累加器初始化为“0”之后。)

template<typename A,typename B>
SparseVector<decltype(A()*B()+A()*B())>  operator*(const SparseMatrix<A>& ,const SparseVector<B>&){...}

// supply appropriate operator+, +=...

Current code (single source file that compiles with clang if Eigen is in the path)当前代码(如果 Eigen 在路径中,则使用 clang 编译的单个源文件)

#pragma once
#include <Eigen/Sparse>

// setup for MatElem*VecElem->Prod
// goal is to add Prod+Prod->Acc,  Acc+=Prod,  Acc(Prod) Acc=0
class MatElem {public:MatElem(){} MatElem(int x){}};
class VecElem {public:VecElem(){} VecElem(int x){}};
class Prod {public:Prod(){} Prod(int x){}};
class Acc {public:Acc(){} Acc(const Prod&){printf("Acc(Prod)");} Acc(Prod&){printf("Acc(Prod)");} Acc(int x){}};
auto operator*(const MatElem& a,const VecElem& b){printf("MatElem*VecElem\n");return Prod{};}
auto operator+(const Prod& a,const Prod& b){printf("Prod+Prod\n");return Prod{};}
auto operator+=(Prod& a,const Prod& b){printf("Prod+=Prod\n");return a;}
auto operator+=(Acc& a,const Prod& b){printf("Acc+=Prod\n");return a;}
template<>
class Eigen::NumTraits<MatElem> {
public:
    typedef MatElem Real;
    typedef MatElem NonInteger; 
    typedef MatElem Literal;
    typedef MatElem Nested;
    enum {
        IsInteger=0,
        IsSigned=1,
        RequireInitialization=1,
        IsComplex=0,
        ReadCost=1,
        AddCost=1,
        MulCost=1
        
    };
    auto static epsilon(){return MatElem();}
    auto static dummy_precision(){return MatElem();}
    auto static highest(){return MatElem();}
    auto static lowest(){return MatElem();}
    auto static digist10(){return 5;}
};

template<>
class Eigen::NumTraits<VecElem> {
public:
    typedef VecElem Real;
    typedef VecElem NonInteger; 
    typedef VecElem Literal;
    typedef VecElem Nested;
    enum {
        IsInteger=0,
        IsSigned=1,
        RequireInitialization=1,
        IsComplex=0,
        ReadCost=1,
        AddCost=1,
        MulCost=1
        
    };
    auto static epsilon(){return VecElem{};}
    auto static dummy_precision(){return VecElem{};}
    auto static highest(){return VecElem{};}
    auto static lowest(){return VecElem{};}
    auto static digist10(){return 5;}
};
template<>
class Eigen::NumTraits<Prod> {
public:
    typedef Prod Real;
    typedef Prod NonInteger;    
    typedef Prod Literal;
    typedef Prod Nested;
    enum {
        IsInteger=0,
        IsSigned=1,
        RequireInitialization=1,
        IsComplex=0,
        ReadCost=1,
        AddCost=1,
        MulCost=1
        
    };
    auto static epsilon(){return Prod{};}
    auto static dummy_precision(){return Prod{};}
    auto static highest(){return Prod{};}
    auto static lowest(){return Prod{};}
    auto static digist10(){return 5;}
};

template<>
class Eigen::NumTraits<Acc> {
public:
    typedef Acc Real;
    typedef Acc NonInteger; 
    typedef Acc Literal;
    typedef Acc Nested;
    enum {
        IsInteger=0,
        IsSigned=1,
        RequireInitialization=1,
        IsComplex=0,
        ReadCost=1,
        AddCost=1,
        MulCost=1
        
    };
    auto static epsilon(){return Acc{};}
    auto static dummy_precision(){return Acc{};}
    auto static highest(){return Acc{};}
    auto static lowest(){return Acc{};}
    auto static digist10(){return 5;}
};

template<>
struct Eigen::ScalarBinaryOpTraits<MatElem,VecElem,Eigen::internal::scalar_product_op<MatElem, VecElem> >{
    typedef Prod ReturnType;
};

template<>
struct Eigen::ScalarBinaryOpTraits<Prod,Prod,Eigen::internal::scalar_sum_op<Prod, Prod> >{
    typedef Prod ReturnType;
};


void eigen_experiment() {
    Eigen::SparseMatrix<MatElem> mymat(3,3);
    mymat.insert(0,0)=MatElem{};
    mymat.insert(0,1)=MatElem{};
    mymat.insert(1,0)=MatElem{};
    mymat.insert(1,1)=MatElem{};
    Eigen::SparseVector<VecElem> myvec(3);
    myvec.insert(0)=VecElem{};
    myvec.insert(1)=VecElem{};
    // Can't seem to do this with "Acc", even if supplying appropriate OpTraits etc above.
    Eigen::SparseVector<Prod> tmp=mymat*myvec;
    
    for (int k=0; k<mymat.outerSize(); ++k){
        for (decltype(mymat)::InnerIterator v(mymat,k); v;++v){
            printf("%d %d\n",v.row(),v.col());
        }
    }

    for (decltype(tmp)::InnerIterator v(tmp); v;++v){
        printf("%d\n",v.index());
    }
}

int main(int argc,const char**){
    eigen_experiment();
    return 0;
}

thanks @chtz, just setting both the operator traits to "Accumulator" (the desired output) whilst the actual operator overloads return the temporary "Product" type, it just works.感谢@chtz,只需将两个运算符特征都设置为“累加器”(所需的输出),而实际的运算符重载返回临时的“产品”类型,它就可以工作。 I didn't think to try it but it seems it just uses the trait to allocate the outputs, and of course it doesn't affect the actual operator.我没想过要尝试它,但它似乎只是使用 trait 来分配输出,当然它不会影响实际的操作员。 The debug prints appear to confirm its doing what I wanted.调试打印似乎确认它在做我想要的。

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

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