使用 scipy.sparse.bmat 从子块创建非常大的稀疏矩阵时出错

Error creating very large sparse matrix from sub-blocks using scipy.sparse.bmat

我正在尝试使用 scipy.sparse.bmat 从较小的 csr 矩阵创建一个矩阵 - 我的函数调用在这里:sparse.bmat(HList, format='csr')。生成的矩阵将是一个约有 26 亿 columns/rows 的方阵。但是,当我尝试构造此矩阵时出现以下错误:

    Traceback (most recent call last):
      [...]/lib/python3.7/site-packages/scipy/sparse/construct.py", line 623, in bmat
        return coo_matrix((data, (row, col)), shape=shape).asformat(format)
      [...]/lib/python3.7/site-packages/scipy/sparse/coo.py", line 192, in __init__
        self._check()
      [...]/lib/python3.7/site-packages/scipy/sparse/coo.py", line 283, in _check
        raise ValueError('negative row index found')
    ValueError: negative row index found 

当组合矩阵转换为 coo 格式时,问题似乎出现了。我认为这个问题与索引溢出有关,因为整个矩阵的索引不适合 32 位格式(26 亿 > 2^31)。我已经针对这个问题的较小版本测试了我的矩阵构造脚本,它工作正常。

This post 有一个与我非常相似的问题,但是,那里列出的解决方案不适用于我的情况。 运行 那里描述的测试,

>>> scipy.sparse.sputils.get_index_dtype((np.array(10**10),))
<class 'numpy.int64'>

我可以确认 numpy 使用的是 64 位索引。

我的程序是否有其他部分导致溢出?还是问题的根源完全不同?

非常感谢任何帮助!

import numpy as np
from scipy.sparse import coo_matrix, csr_matrix, bmat

a = coo_matrix(([1], ([int(1e9)], [int(1e9)])))
blocks = [a.copy() for i in range(200)]
blocks = [blocks for i in range(20)]

arr = bmat(blocks, format='coo')

首先 - 这绝对是可重现的(我正在使用 COO 数组,因为我不想分配 1e11 indptr 数组)。

ValueError: negative row index found

它也无助于将 a 数组索引从 int32 转换为 int64。事实上,问题似乎完全出在 bmat function

内部
# convert everything to COO format
for i in range(M):
    for j in range(N):
        if blocks[i,j] is not None:
            A = coo_matrix(blocks[i,j])

首先,它将您所有的块转换为 COO 矩阵。如果行和列索引适合 int32s,它将使用 int32s(我假设你的索引这样做)。稍后它通过添加偏移量(基于块所在的位置)来计算新的行值。不幸的是,这是它溢出的地方:

for i, j in zip(ii, jj):
    B = blocks[i, j]
    ...
    row[idx] = B.row + row_offsets[i]
    col[idx] = B.col + col_offsets[j]

>>> blocks[2, 0].row
array([1000000000], dtype=int32)

>>> blocks[2, 0].row + 2000000002
array([-1294967294], dtype=int32)

由于该溢出(并且因为它位于您无法从外部访问的 bmat 中的代码中),所以这是一个 scipy 错误。也就是说,如果您复制 scipy bmat 函数并重新键入块索引数组,则可以非常简单地修复它,如下所示:

for i, j in zip(ii, jj):
    B = blocks[i, j]
    ...
    row[idx] = B.row.astype(idx_dtype) + row_offsets[i]
    col[idx] = B.col.astype(idx_dtype) + col_offsets[j]