计算scipy稀疏矩阵

时间:2017-12-21 10:53:17

标签: python numpy scipy sparse-matrix floyd-warshall

我想在Python中计算稀疏矩阵的 transitive closure。目前我正在使用scipy稀疏矩阵。

矩阵功率(在我的情况下为**12)适用于非常稀疏的矩阵,无论它们有多大,但对于有针对性的不那么稀疏的情况,我想使用更智能的算法。

我在Floyd-Warshall algorithm找到了 scipy.sparse.csgraph (德语页面有更好的伪代码),它比它应该做的多一点:只有 Warshall的算法 - 这是一回事。

主要问题是我可以将稀疏矩阵传递给函数,但这完全没有意义,因为函数将始终返回密集矩阵,因为传递闭包中的0应该是{{1长度,有人觉得这需要明确存储。

所以我的问题是:是否有任何python模块允许计算稀疏矩阵的传递闭包并保持稀疏

我不是100%确定他使用相同的矩阵,但 Gerald Penn his comparison paper中显示出令人印象深刻的加速,这表明有可能解决问题。

编辑: 由于存在许多混淆,我将指出理论背景:

我正在寻找传递闭包(不是反身或对称)。

我将确保在布尔矩阵中编码的关系具有所需的属性,即对称反身性

我有两种关系

  1. 自反
  2. 自反对称
  3. enter image description here enter image description here

    我想对这两种关系应用传递闭包。这与矩阵功率完美匹配(仅在某些情况下它太昂贵):

    inf

    所以在第一种情况下,我们得到一个节点的所有后代(包括它自己),在第二种情况下,我们获得所有组件,即同一组件中的所有节点。

    enter image description here enter image description here

1 个答案:

答案 0 :(得分:6)

这是on SciPy issue tracker。问题不在于输出格式; Floyd-Warshall的实现是从充满无穷大的矩阵开始,然后在找到路径时插入有限值。稀疏性立即丢失。

networkx库提供了all_pairs_shortest_path_length的替代方案。它的输出是一个迭代器,它返回格式为

的元组
(source, dictionary of reachable targets) 

需要一些工作才能转换为SciPy稀疏矩阵(csr格式在这里很自然)。一个完整的例子:

import numpy as np
import networkx as nx
import scipy.stats as stats
import scipy.sparse as sparse

A = sparse.random(6, 6, density=0.2, format='csr', data_rvs=stats.randint(1, 2).rvs).astype(np.uint8)
G = nx.DiGraph(A)       # directed because A need not be symmetric
paths = nx.all_pairs_shortest_path_length(G)
indices = []
indptr = [0]
for row in paths:
  reachable = [v for v in row[1] if row[1][v] > 0]
  indices.extend(reachable)
  indptr.append(len(indices))
data = np.ones((len(indices),), dtype=np.uint8)
A_trans = A + sparse.csr_matrix((data, indices, indptr), shape=A.shape)
print(A, "\n\n", A_trans)

添加A的原因如下。 Networkx输出包括长度为0的路径,它将立即填充对角线。我们不希望这种情况发生(你想要传递闭包,而不是反身和传递闭包)。因此,行reachable = [v for v in row[1] if row[1][v] > 0]。但是我们根本没有得到任何对角线条目,即使A有它们(0长空路径击败由自循环形成的1长度路径)。所以我将A添加回结果。它现在有条目1或2,但只有它们非零的事实才有意义。

运行上述示例(为了输出的可读性,我选择6乘6尺寸)。原始矩阵:

  (0, 3)    1
  (3, 2)    1
  (4, 3)    1
  (5, 1)    1
  (5, 3)    1
  (5, 4)    1
  (5, 5)    1 

传递性关闭:

  (0, 2)    1
  (0, 3)    2
  (3, 2)    2
  (4, 2)    1
  (4, 3)    2
  (5, 1)    2
  (5, 2)    1
  (5, 3)    2
  (5, 4)    2
  (5, 5)    1

你可以看到这个工作正常:添加的条目是(0,2),(4,2)和(5,2),都是通过路径(3,2)获得的。

顺便说一句,networkx也有floyd_warshall方法,但其文档说

  

此算法最适合密集图。运行时间为O(n ^ 3),运行空间为O(n ^ 2),其中n为G中的节点数。

输出再次密集。我得到的印象是这个算法本质上被认为是密集的。似乎all_pairs_shortest_path_length是一种Dijkstra's algorithm

传递性和反身性

如果不是传递闭包(这是包含给定的传递关系的最小传递关系),你需要传递和反身闭包(包含给定的最小传递和反身关系)一),代码简化,因为我们不再担心0长度路径。

for row in paths:
  indices.extend(row[1])
  indptr.append(len(indices))
data = np.ones((len(indices),), dtype=np.uint8)
A_trans = sparse.csr_matrix((data, indices, indptr), shape=A.shape)

传递性,反身性和对称性

这意味着找到包含给定关系的最小等价关系。等效地,将顶点划分为连接的组件。为此,您不需要访问networkx,SciPy有connected_components方法。在那里设置directed=False。例如:

import numpy as np
import scipy.stats as stats
import scipy.sparse as sparse
import itertools

A = sparse.random(20, 20, density=0.02, format='csr', data_rvs=stats.randint(1, 2).rvs).astype(np.uint8)
components = sparse.csgraph.connected_components(A, directed=False)
nonzeros = []
for k in range(components[0]):
  idx = np.where(components[1] == k)[0]
  nonzeros.extend(itertools.product(idx, idx))
  row = tuple(r for r, c in nonzeros)
  col = tuple(c for r, c in nonzeros)
  data = np.ones_like(row)
B = sparse.coo_matrix((data, (row, col)), shape=A.shape)

这是随机示例的输出print(B.toarray()),20乘20:

[[1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0]
 [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0]
 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0]
 [1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0]
 [1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]]