您如何合并稀疏矩阵以创建新的稀疏矩阵?

时间:2018-12-04 16:38:37

标签: algorithm matrix sparse-matrix eigen3 intel-mkl

我正在尝试实现以下目标:

Given A(m,n), B(m,q), C(p,n), D(p,q), sparse matrices.
Create E(m+p,n+q), a sparse matrix, like :
E = | A B |
    | C D |

我尝试了以下方法:

  • 本征-我发现实现此目标的唯一方法是从A,B,C和D读取所有非零值,将其存储在std::vector<Triplet>中,并使用setFromTriplets构造E。这太复杂了。
  • Intel MKL-算法是相同的,除了我使用的是块稀疏行存储表示。首先,读取非零,然后调用构造函数。同样的复杂性,无法使用。

这些库的问题在于E的构造与任何稀疏矩阵一样,而没有利用E和A,B,C,D的各个部分之间存在冗余的事实。我想应该可以通过重新索引A,B,C和D内部结构中存储的内容来构造E。

问题如下:如何用稀疏矩阵实现此合并操作?您将使用哪种软件?您将使用哪种算法?

理想的解决方案是不使用块存储方案,这样稀疏度将基于零值而不是零块。

编程语言无关紧要。

谢谢。

1 个答案:

答案 0 :(得分:0)

旧问题,新解决方案!似乎每个人都想出了一种方法,方法是创建一个新的三元组容器,并进行相应的添加。公认这是安全和直观的,但是即使您相应地保留了非零值,也没有那么快。

在这种方法中,它仅适用于主要列矩阵(您可以非常轻松地创建主要列维数),我直接使用CSC数据结构。我介绍了3种方法: 1)“垂直堆叠” 列数相同的两个稀疏矩阵, 2)“水平堆叠” 两个行数相同的稀疏矩阵和3)“就地水平堆叠” ,两个稀疏矩阵的行数相同。 3)本质上效率更高,因为左矩阵的存储未更改。

如您所见,即使直接使用原始指针,水平堆叠两个矩阵也几乎是微不足道的。垂直堆叠需要一些思考,但是不需要太多。如果有人能找到一种可以避免某些复制(或任何改进)的垂直堆叠的就地方法,请告诉我!

#include <algorithm>
#include <Eigen/Sparse>

template<typename Scalar, typename StorageIndex>
void sparse_stack_v(
    const SparseMatrix<Scalar, ColMajor, StorageIndex>& top,
    const SparseMatrix<Scalar, ColMajor, StorageIndex>& bottom,
    SparseMatrix<Scalar, ColMajor, StorageIndex>& stacked)
{
    assert(top.cols() == bottom.cols());
    stacked.resize(top.rows() + bottom.rows(), top.cols());
    stacked.resizeNonZeros(top.nonZeros() + bottom.nonZeros());

    StorageIndex i = 0;

    for (StorageIndex col = 0; col < top.cols(); col++)
    {
        stacked.outerIndexPtr()[col] = i;

        for (StorageIndex j = top.outerIndexPtr()[col]; j < top.outerIndexPtr()[col + 1]; j++, i++)
        {
            stacked.innerIndexPtr()[i] = top.innerIndexPtr()[j];
            stacked.valuePtr()[i] = top.valuePtr()[j];
        }

        for (StorageIndex j = bottom.outerIndexPtr()[col]; j < bottom.outerIndexPtr()[col + 1]; j++, i++)
        {
            stacked.innerIndexPtr()[i] = (StorageIndex)top.rows() + bottom.innerIndexPtr()[j];
            stacked.valuePtr()[i] = bottom.valuePtr()[j];
        }
    }
    stacked.outerIndexPtr()[top.cols()] = i;
}

template<typename Scalar, typename StorageIndex>
void sparse_stack_h(
    const SparseMatrix<Scalar, ColMajor, StorageIndex>& left,
    const SparseMatrix<Scalar, ColMajor, StorageIndex>& right,
    SparseMatrix<Scalar, ColMajor, StorageIndex>& stacked)
{
    assert(left.rows() == right.rows());

    stacked.resize(left.rows(), left.cols() + right.cols());
    stacked.resizeNonZeros(left.nonZeros() + right.nonZeros());

    std::copy(left.innerIndexPtr(), left.innerIndexPtr() + left.nonZeros(), stacked.innerIndexPtr());
    std::copy(right.innerIndexPtr(), right.innerIndexPtr() + right.nonZeros(), stacked.innerIndexPtr() + left.nonZeros());

    std::copy(left.valuePtr(), left.valuePtr() + left.nonZeros(), stacked.valuePtr());
    std::copy(right.valuePtr(), right.valuePtr() + right.nonZeros(), stacked.valuePtr() + left.nonZeros());

    std::copy(left.outerIndexPtr(), left.outerIndexPtr() + left.cols(), stacked.outerIndexPtr());//dont need the last entry of A.outerIndexPtr() -- total length is AB.cols() + 1 = A.cols() + B.cols() + 1
    std::transform(right.outerIndexPtr(), right.outerIndexPtr() + right.cols() + 1, stacked.outerIndexPtr() + left.cols(), [&](StorageIndex i) { return i + left.nonZeros(); });
}

template<typename Scalar, typename StorageIndex>
void sparse_stack_h_inplace(
    SparseMatrix<Scalar, ColMajor, StorageIndex>& left,
    const SparseMatrix<Scalar, ColMajor, StorageIndex>& right)
{
    assert(left.rows() == right.rows());

    const StorageIndex leftcol = (StorageIndex)left.cols();
    const StorageIndex leftnz = (StorageIndex)left.nonZeros();

    left.conservativeResize(left.rows(), left.cols() + right.cols());
    left.resizeNonZeros(left.nonZeros() + right.nonZeros());

    std::copy(right.innerIndexPtr(), right.innerIndexPtr() + right.nonZeros(), left.innerIndexPtr() + leftnz);
    std::copy(right.valuePtr(), right.valuePtr() + right.nonZeros(), left.valuePtr() + leftnz);
    std::transform(right.outerIndexPtr(), right.outerIndexPtr() + right.cols() + 1, left.outerIndexPtr() + leftcol, [&](StorageIndex i) { return i + leftnz; });
}