Java中稀疏矩阵行的有效修改

Java中稀疏矩阵行的有效修改,java,matrix,sparse-matrix,performance,memory-efficient,Java,Matrix,Sparse Matrix,Performance,Memory Efficient,我需要“规范化”一个大的稀疏矩阵,以便每行中的条目之和为1。对于具有一些非零项的行,每个项都除以行和。对于全为零的行,每个条目将替换为1/numberOfColumns(使矩阵的稀疏性大大降低) 如果有关系的话,我的矩阵是正方形的,对称的。我首先使用的是一个“小”测试样本,我的矩阵大小约为32K x 32K,但我们最终需要它来处理更大的矩阵。时间和内存效率都很重要(内存比时间更重要)。在标准化之前,大约1%的条目是非零的 规范化n x n矩阵后,需要将其乘以一系列n x k密集矩阵,其中k是一个

我需要“规范化”一个大的稀疏矩阵,以便每行中的条目之和为1。对于具有一些非零项的行,每个项都除以行和。对于全为零的行,每个条目将替换为1/numberOfColumns(使矩阵的稀疏性大大降低)

如果有关系的话,我的矩阵是正方形的,对称的。我首先使用的是一个“小”测试样本,我的矩阵大小约为32K x 32K,但我们最终需要它来处理更大的矩阵。时间和内存效率都很重要(内存比时间更重要)。在标准化之前,大约1%的条目是非零的


规范化n x n矩阵后,需要将其乘以一系列n x k密集矩阵,其中k是一个小数字(我是la4j库的作者。我确实看到您的代码中有几个地方需要改进。以下是我的建议:

调用
getRow
(以及
setRow
)总是一个坏主意(特别是对于稀疏矩阵),因为它会启动矩阵行的完整复制。我建议您避免此类调用。因此,w/o
getRow
/
setRow
代码应该如下所示:

SparseMatrix t = new CRSMatrix(n, n);

double uniformWeight = (double) 1 / n; // used when the rowSum is zero
for (int i = 0; i < n; i++) {
  double rowSum = t.foldRow(i, Matrices.asSumAccumulator(0.0));
  if (rowSum > 0.0) {
    MatrixFunction divider = Matrices.asDivFunction(rowSum);
    for (int j = 0; j < n; j++) {
      // TODO: I should probably think about `updateRow` method
      //       in order to avoid this loop
      t.update(i, j, divider);
    }
  } else {
    for (int j = 0; j < n; j++) {
      t.set(i, j, uniformWeight);
    }
  }
}
SparseMatrix t=新的crs矩阵(n,n);
double uniformWeight=(double)1/n;//行和为零时使用
对于(int i=0;i0.0){
MatrixFunction除法器=矩阵.asDivFunction(行和);
对于(int j=0;j
请试试这个。我没有编译它,但它应该可以工作

更新

使用布尔数组来跟踪相同的行是一个非常好的主意。这里的主要瓶颈是循环:

for (int j = 0; j < n; j++) {
  t.set(i, j, uniformWeight);
}
for(int j=0;j
在这里,我们完全破坏了稀疏矩阵的性能/占用空间,因为将整行分配给了相同的值。因此,我要说的是,将这两个想法结合在一起:避免
getRow/setRow
+带标志的额外数组(我将使用位集,它在占用空间方面非常有效)应该给你一个很棒的表演


感谢您使用la4j库,请向邮件列表或GitHub页面报告任何性能/功能问题。此处提供了所有参考资料:。

如果您不使用la4j,该软件包(我维护)有一些工具可以非常高效地执行这类操作。这是可能的,因为有两个功能:

  • 数据的稀疏存储
  • 轻量级可变“视图”,因此您可以将矩阵的行作为向量进行适当的变换
我会采用的策略是:

  • 创建
    VectorMatrixMN
    以将矩阵存储为稀疏向量行的集合
  • 对每一行使用
    SparseIndexedVector
    ,这对于大多数零数据来说是一种有效的格式
然后可以使用以下代码对矩阵行进行归一化:

VectorMatrixMN m = ....

for (int i=0; i<SIZE; i++) {
    AVector row=m.getRow(i);
    double sum=row.elementSum();
    if (sum>0) {
        row.divide(sum);
    } else {
        m.setRow(i, new RepeatedElementVector(SIZE,1.0/SIZE));
    }
}
VectorMatrixMN m=。。。。
对于(int i=0;i0){
行。除(和);
}否则{
m、 setRow(i,新的RepeatedElementVector(大小,1.0/大小));
}
}
请注意,这段代码在适当的位置修改行,因此不需要像“setRow”这样的操作就可以将数据返回到矩阵中

使用32000 x 32000稀疏矩阵和每行100个非零值的密度的这种配置,我将其计时在不到32ms的时间,以使用此代码对整个矩阵进行归一化(即,每个非零元素大约10ns==每个矩阵元素0.03ns:因此,利用稀疏性显然可以获得很大的好处)

您还可以选择对全为零的行使用
ZeroVector
(这会更快,但会施加一些额外的约束,因为ZeroVectors是不可变的……)

编辑:

我编写了一个完整的示例,演示了如何将稀疏矩阵用于与此问题非常类似的用例:


如果您的大多数条目最终都是非稀疏的,那么您几乎可以肯定使用非稀疏矩阵会更好。虽然我从未实际使用过这两种矩阵,但我希望它会快得多,所以我不能肯定。考虑到您已经有超过10亿个单元格,并且需要“大得多”数据集你需要一些专门的库。如果你对数据集做了大量的数学处理,你甚至可以考虑使用图形卡,因为它们可以以大规模并行的方式处理(虽然你所谈论的数据集的大小可能仍然是个问题)。@TimB感谢您的回复。我想知道,出于内存效率的原因,我们是否最好尝试选项#3并保持稀疏矩阵,但同时跟踪零行,以便在执行乘法时,我们可以细分为恒定的均匀向量。想法?关于:“专家库”--我认为这就是la4j和类似软件包的功能?我会将每个零行替换为包含相同对象的相同预构建行n次。这会为您节省大量空间。请确保您的条目和行是不可变的。例如,不要使用
row.assign(uniformWeight);row.setRow(I,row);
使用
row.setRow(uniformRow)
where
uniformRow
是根据
n
预先构建的。感谢您的回复和帮助!我要到下周才有机会玩这个游戏,但我非常感谢您的建议,一个一个地更新单个单元格,而不是使用
setRow
foldRow
方法来进行行的更新嗯,我确实实施了我的方案
for (int j = 0; j < n; j++) {
  t.set(i, j, uniformWeight);
}
VectorMatrixMN m = ....

for (int i=0; i<SIZE; i++) {
    AVector row=m.getRow(i);
    double sum=row.elementSum();
    if (sum>0) {
        row.divide(sum);
    } else {
        m.setRow(i, new RepeatedElementVector(SIZE,1.0/SIZE));
    }
}