小能豆

是否有针对非标准代数对象进行稀疏矩阵运算的 Python 库?

py

摘要:我正在寻找一种方法来对稀疏矩阵进行计算,其非零条目不是通常的整数/浮点数/等,而是代数元素,即具有加法、乘法和零元素的非标准 python 类的实例。

它对密集矩阵很有效。我通过定义一个 Python 类并重载加法和乘法来实现这个代数algebra

class algebra(object):
   ...
   __mul__(self,other):
      ...
   __add__(self,other):
      ...

numpy允许我定义向量和矩阵,其条目是类的实例algebra。它还允许我执行所有常见的操作,如矩阵乘法/加法/张量点/切片/等,因此它的工作方式与整数/浮点数/等矩阵一样。

它不适用于稀疏矩阵。 为了加快计算速度,我现在想用稀疏矩阵替换这些密集矩阵。我曾尝试使用 SciPy 的 2-D 稀疏矩阵包来实现这一点scipy.sparse,但到目前为止我失败了。我可以用我的代数元素填充这些稀疏矩阵类的实例,但每当我用它们进行计算时,我都会收到一条错误消息,例如

TypeError: no supported conversion for types: (dtype('O'),dtype('O'))

对我来说,这表明 所支持的对象类型存在限制。我看不出任何数学上的理由来解释稀疏矩阵的运算为什么应该关心对象类型。只要该类具有浮点数的所有运算,它就应该可以工作。我遗漏了什么?有没有支持任意对象类型scipy.sparse的替代方案?scipy.sparse


下面是一个最小的工作示例。请注意,我已经用通常的整数 0 来实现代数的零元素。还请注意,我感兴趣的实际代数比实数更复杂!

import numpy as np
from scipy.sparse import csr_matrix

class algebra(object): # the algebra of the real integers

    def __init__(self,num):
        self.num = num

    def __add__(self,other):
        if isinstance(other, self.__class__):
            return algebra(self.num+other.num)
        else:
            return self

    def __radd__(self,other):
        if isinstance(other, self.__class__):
            return algebra(self.num+other.num)
        else:
            return self

    def __mul__(self,other):
        if isinstance(other, self.__class__):
            return algebra(self.num*other.num)
        else:
            return 0

    def __rmul__(self,other):
        if isinstance(other, self.__class__):
            return algebra(self.num*other.num)
        else:
            return 0

    def __repr__(self):
        return "algebra:"+str(self.num)  

a=algebra(5)
print(a*a)
print(a*0)
print(0*a)
indptr = np.array([0, 2, 3, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
data = np.array([a,a,a,a,a,a])
S = csr_matrix((data, indices, indptr), shape=(3, 3))
print(S)
print("Everything works fine up to here.")
S*S    

输出为:

algebra:25
0
0
  (0, 0)    algebra:5
  (0, 2)    algebra:5
  (1, 2)    algebra:5
  (2, 0)    algebra:5
  (2, 1)    algebra:5
  (2, 2)    algebra:5
Everything works fine up to here.
Traceback (most recent call last):
  File "test", line 46, in <module>
    S*S    
  File "/usr/lib/python3/dist-packages/scipy/sparse/base.py", line 319, in __mul__
    return self._mul_sparse_matrix(other)
  File "/usr/lib/python3/dist-packages/scipy/sparse/compressed.py", line 499, in _mul_sparse_matrix
    data = np.empty(nnz, dtype=upcast(self.dtype, other.dtype))
  File "/usr/lib/python3/dist-packages/scipy/sparse/sputils.py", line 57, in upcast
    raise TypeError('no supported conversion for types: %r' % (args,))
TypeError: no supported conversion for types: (dtype('O'), dtype('O'))

我在 Linux 上使用 Python 3.5.2。


阅读 16

收藏
2025-01-03

共1个答案

小能豆

这可能更多地属于评论类别,但作为答案,我可以使其更长,并进行更多编辑。

numpy数组通过将指向对象的指针/引用存储在数组的数据缓冲区中来实现对象 dtype。数学运算是通过将任务委托给对象方法来完成的。迭代基本上是 Python 速度,可与列表理解相媲美(甚至可能更慢一点)。 numpy不会对这些对象进行快速编译数学运算。

scipy.sparse尚未开发此类功能。coo可能可以使用对象输入创建格式矩阵 - 但这是因为它没有太多作用。事实上,如果datarowcol输入具有正确的numpy数组设置,它们将用作coo属性而无需更改。

显然,csr像您对indptretc 所做的那样进行操作也只是分配属性。coo转换csr可能效果不太好,因为这涉及到重复项的求和。

无论如何,csr数学代码使用了 python 和 c(cython)的混合,并且编译部分只能处理有限数量的数字类型 - 长整数、双整数和浮点数。我认为它甚至不适用于短整数(int8, int16)。它没有实现任何对象数据类型委托ndarrays

与您的S

In [187]: S.A                                                                                                
...
ValueError: unsupported data types in input

In [188]: S.tocoo()                                                                                          
Out[188]: 
<3x3 sparse matrix of type '<class 'numpy.object_'>'
    with 6 stored elements in COOrdinate format>

不需要改变 的值tocoo。但回到csr需要对 重复项求和:

In [189]: S.tocoo().tocsr()                                                                                  
 ...
TypeError: no supported conversion for types: (dtype('O'),)

In [190]: S.tolil()                                                                                          
/usr/local/lib/python3.6/dist-packages/scipy/sparse/sputils.py:115: UserWarning: object dtype is not supported by sparse matrices
  warnings.warn("object dtype is not supported by sparse matrices")
Out[190]: 
<3x3 sparse matrix of type '<class 'numpy.object_'>'
    with 6 stored elements in LInked List format>

存储此对象数据没有问题

使用对象列表与数组进行数学运算 - 时间类似:

In [192]: alist = [a]*100                                                                                    
In [193]: arr = np.array(alist)                                                                              
In [194]: timeit [i*j for i,j in zip(alist,alist)]                                                           
77.9 µs ± 272 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [195]: timeit arr*arr                                                                                     
75.1 µs ± 2.29 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
2025-01-03