确定稀疏矩阵乘法中非零数目的最佳方法是什么?


17

我想知道是否有一种快速有效的方法来预先确定稀疏矩阵乘法运算的非零数,假设两个矩阵都为CSC或CSR格式。

我知道smmp包中有一个,但是我需要已经用C或C ++实现的东西。

任何帮助将不胜感激。提前致谢。


您的矩阵的非零项位置是否具有对称性或结构?
Godric Seer 2012年

@GodricSeer ...不,我只是在谈论一般的稀疏矩阵。Matlab有nnz(A),其中A是稀疏矩阵方法,用于找出非零数。我想知道是否有这样的方法。
Recker

我个人想不出一种方法来计算该数字,而不仅仅是在不利用某种对称性或结构的情况下进行实际的矩阵乘法。我假设您要在进行乘法之前将其用于内存分配?
Godric Seer 2012年

另外,我发现本文描述了如何估计布尔矩阵乘积上的数字(与对任何矩阵乘积中的元素进行计数相同)。
Godric Seer 2012年

@GodricSeer ..是的,您是对的,我只需要确切的数字来分配结果矩阵的内存。尽管如此,感谢链接到论文的内容,这可能会使我在某个方向上有所了解。
Recker

Answers:


14

您可以通过形成两个稀疏模式的乘积来模拟矩阵矩阵乘积,即,您将稀疏模式(以CSR格式存储在单独的数组中)视为包含零或一的矩阵。每个条目。执行此模拟产品仅需要您形成对这些零和一进行运算,因此比实际的矩阵矩阵乘积要快得多-实际上,您要做的就是遍历两个矩阵的行和列,并验证在一个矩阵中至少有一个条目行和列乘以两个矩阵都不为零的位置。这是一种便宜的操作-在任何情况下都比在实际产品中实际进行浮点乘法要便宜得多,这不仅需要您进行浮点算术(昂贵),而且还需要从内存中读取实际的浮点数(甚至更昂贵,但是在乘以稀疏模式时您不需要这样做,因为矩阵的非零值分别存储在CSR中)。


6
这称为符号乘法。它不一定比数值乘法便宜,尤其是并行计算,但每个稀疏模式只需要执行一次。许多算法会使用不同的数值但使用相同的稀疏模式多次执行该运算,在这种情况下可以重复使用符号乘法。
杰德·布朗

这是一个不错的主意,但是鉴于数以百万计的晶体管并行进行float * float,我们在这里只谈论50%左右的速度节省。
Evgeni Sergeev

1
@EvgeniSergeev-关键不是节省计算,而是节省内存传输。由于您今天花费80%或更多的时间进行稀疏矩阵乘法的内存传输,因此如果您不必从内存读取/向内存写入浮点数据,则可能会获得显着收益。
Wolfgang Bangerth '16

您能明确说明方法的复杂性吗?如果m × k,在我看来您的方法需要O m k 工作,对吗?CmkO(mk)
卡尔·克里斯蒂安

@CarlChristian-我必须弄清楚细节,但是肯定不能是。它需要涉及每行的非零数。如果平均每行有p个非零,并且为了简单起见,如果有m = k,那么我想您应该能够以O m p log p 或类似的方式实现该方法。那比O m 2好得多。O(mk)pm=kO(mplogp)O(m2)
Wolfgang Bangerth '16

13

我实际上是在Matlab中为A * B(A和B稀疏)编写了原始代码。预分配结果空间确实是有趣的部分。我们观察到Godric指出的内容-知道AB中的非零数与计算AB一样昂贵。

我们在1990年左右进行了稀疏Matlab的最初实现,随后Edith Cohen的论文提出了第一种实用,快速的方法来准确估算AB的大小。我们将大小估算器放在一起,如果我们在计算中用尽了空间,则将分配加倍,并复制部分计算的结果。

我不知道Matlab现在有什么。

另一种可能性是一次计算AB一列。每列都可以临时存储在稀疏累加器中(有关这些解释,请参见稀疏Matlab论文),并分配空间以容纳结果列的确切已知大小。结果将是分散的压缩稀疏列形式-CSC中的每个列但没有列间连续性-使用2个长度为numcols的向量(col start,col length)而不是一个向量作为元数据。它的存储形式可能值得一看;它具有另一种优势-您可以在不重新分配整个矩阵的情况下增加一列。


那么我的GPU实现,我结束了第一个找到的非零结构,然后找出实际matrix.Performance是可怕的expected.I认为他们使用描述的方法本书在MATLAB有效地乘两个稀疏矩阵。
Recker

2
非常酷,感谢您的历史观点,并欢迎您访问scicomp :)
Aron Ahmadia 2012年

4

论文描述一种算法,以从两个稀疏矩阵的矩阵乘积近似所得的大小。

在稀疏矩阵乘法中找到确切数量的非零条目的问题是结果中的每个元素都取决于两个向量的相互作用,这两个向量都可能包含至少几个非零元素。因此,要计算数量,您需要为结果中的每个元素评估一对向量的逻辑运算。这样做的问题是,它需要许多运算,这些运算与计算矩阵乘积本身所需的运算数量相似。在我的评论中,我提到了利用原始矩阵的非零元素中某些结构的可能性,但是这些相同的利用也可以用来减少矩阵乘法中的工作。

您可能会更好地使用上述论文来高估内存需求,进行乘法运算,然后截断分配的内存,或者将结果矩阵移到大小更合适的数组中。同样,稀疏矩阵乘积也不是罕见的情况,我几乎可以保证这个问题之前已经得到解决。稍微研究一些开放源代码的稀疏矩阵库应该使您了解它们用于预分配内存的算法。


0

对于CSR或CSC,是否可以保证矩阵元素数组不存在零?在这种情况下,使用类似于以下内容的方法很容易找出有多少个非零元素:

int nnz = sizeof(My_Array)/sizeof(long int);

但是,如果不是这种情况(似乎有点容易),则可以尝试减少。如果矩阵元素数组很大,这可能是计算非零元素数量的最有效方法。许多并行的C / C ++库(例如Thrust(CUDA库)或OpenCL(您不需要GPU即可使用))都支持条件约简-为每个元素添加结果Condition(Element)。如果将条件设置为,Element != 0则将添加非零元素的数量。您可能还希望从元素数组,行/列索引数组中删除零值元素,并调整列/行指针。


感谢您的回复...但是我指的是A * B中的非零值,其中A和B是稀疏矩阵。我需要提前非零的数量,以便可以分配确切的内存量来存储结果矩阵。
Recker

0

实施CSR的最简单方法是尝试

std::vector< std::map<int, complex<float>> > 

代表你的矩阵。在这种情况下,您不必担心非零元素的数量,所有元素都可以通过访问

std::map< int, complex<float> >::iterator

在每一行上。最好 ..


2
STL,因为当您认为您的稀疏矩阵例程不会变得更慢时。
杰德·布朗
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.