实现稀疏矩阵向量乘积时遇AttributeError: 'csr_matrix'无'row'属性
稀疏矩阵相加与CSR转换错误排查与修复
错误信息
<ipython-input-13-b43c92e5efc9>:50: RuntimeWarning: LinearOperator subclass should implement at least one of _matvec and _matmat. final_add__csr = CSRMatrix(final_add) --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-13-b43c92e5efc9> in <cell line: 89>() 87 #final_add_data, final_add_indices, final_add_indptr, final_add_shape = p1.add(A_coo,B_coo) 88 ---> 89 final_add_data, final_add_indices, final_add_indptr, final_add_shape = CSRMatrix.add(A_coo,B_coo) 90 91 matvec1 = p1.matvec(final_add_data, final_add_indices, final_add_indptr, final_add_shape,x) 1 frames <ipython-input-13-b43c92e5efc9> in add(other1, other2) 48 final_add = other1 + other2 49 #self_csr = CSRMatrix(final_add) ---> 50 final_add__csr = CSRMatrix(final_add) 51 print("The final addition matrix in the CSR fomrat is:-") 52 print(self_csr) <ipython-input-13-b43c92e5efc9> in __init__(self, coo_matrix) 10 self.shape = coo_matrix.shape 11 self.dtype = coo_matrix.dtype ---> 12 self.row = coo_matrix.row 13 self.col = coo_matrix.col 14 self.data = coo_matrix.data AttributeError: 'csr_matrix' object has no attribute 'row'
给定模板代码
from scipy.sparse.linalg import LinearOperator class CSRMatrix(LinearOperator): def __init__(self, coo_matrix): self.shape = coo_matrix.shape self.dtype = coo_matrix.dtype # You'll need to put more code here def __add__(self, other): """Add the CSR matrix other to this matrix.""" pass def _matvec(self, vector): """Compute a matrix-vector product.""" pass
需求要求
__init__方法接收COO矩阵作为输入,手动转换为CSR结构(禁止使用scipy的CSR实现),提取并计算CSR的data、indices、indptr__add__方法重载+运算符,实现两个CSR矩阵相加,禁止转换为稠密矩阵,可通过转COO相加再转CSR实现- 实现
_matvec方法,作为矩阵-向量乘积的核心逻辑,供scipy稀疏求解器调用
自行编写的代码
import scipy from scipy.sparse.linalg import LinearOperator import numba import numpy as np from scipy.sparse import coo_matrix from scipy.sparse import coo_array class CSRMatrix(LinearOperator): def __init__(self, coo_matrix): self.shape = coo_matrix.shape self.dtype = coo_matrix.dtype self.row = coo_matrix.row self.col = coo_matrix.col self.data = coo_matrix.data self.indices = coo_matrix.col self_csr = coo_matrix.tocsr() def add(other1, other2): if np.shape(other1) == np.shape(other2) : final_add = other1 + other2 final_add__csr = CSRMatrix(final_add) print("The final addition matrix in the CSR fomrat is:-") print(self_csr) self = self_csr return self.data, self.indices, self.indptr, self.shape else : print("Error : Shape of matrix A is not equal to shape of matrix B. Thus, cannot proceed towards addition to generate a final addition matrix and cannot produce matrix vector multiplication result using the final addition matrix.") @numba.jit(nopython=True, parallel=True) #def matvec(self.data, self.indices, self.indptr, self.shape, x): def csr_matvec(data, indices, indptr, shape, x): m, n = shape y = np.zeros(m, dtype=np.float64) for row_index in numba.prange(m): col_start = indptr[row_index] col_end = indptr[row_index + 1] for col_index in range(col_start, col_end): y[row_index] += data[col_index] * x[indices[col_index]] return y A = np.array([[1, 0],[3, 4],[6, 0]]) #3rows,2columns B = np.array([[1, 2],[5, 6],[7, 8]]) #3rows,2columns A_coo = coo_matrix(A) B_coo = coo_matrix(B) # Generate a random vector rand = np.random.RandomState(0) N = 5 x = rand.randn(2 * N) #2rows,Ncolumns final_add_data, final_add_indices, final_add_indptr, final_add_shape = CSRMatrix.add(A_coo,B_coo) matvec1 = p1.matvec(final_add_data, final_add_indices, final_add_indptr, final_add_shape,x) print(matvec1)
错误原因
- 类型不匹配:scipy中COO矩阵相加
other1 + other2返回的是CSR矩阵,而你的CSRMatrix.__init__只处理COO矩阵,直接访问COO独有的.row属性会触发AttributeError。 - 类方法定义错误:
add方法既不是实例方法(无self参数)也不是类方法(无cls参数),却在内部使用self_csr这类实例变量,逻辑完全混乱,直接通过CSRMatrix.add调用也不符合面向对象规范。 - 未实现手动CSR转换:
__init__中调用了coo_matrix.tocsr()(违反“不能用库的CSR实现”要求),且self_csr是局部变量,未赋值给实例属性,导致后续无法访问indptr等CSR核心结构。 - 缺失LinearOperator要求的方法:继承
LinearOperator必须实现_matvec或_matmat,你写的csr_matvec是独立函数,不是类的_matvec方法,触发运行警告。 - 调用逻辑错误:
matvec1 = p1.matvec(...)中的p1未定义,且csr_matvec不是实例方法,无法直接通过实例调用。
修复后完整代码
import numba import numpy as np from scipy.sparse import coo_matrix from scipy.sparse.linalg import LinearOperator @numba.jit(nopython=True, parallel=True) def _csr_matvec_core(data, indices, indptr, m, x): y = np.zeros(m, dtype=np.float64) for row_idx in numba.prange(m): start = indptr[row_idx] end = indptr[row_idx + 1] for idx in range(start, end): y[row_idx] += data[idx] * x[indices[idx]] return y class CSRMatrix(LinearOperator): def __init__(self, coo_matrix): super().__init__(dtype=coo_matrix.dtype, shape=coo_matrix.shape) # 手动从COO转换为CSR结构,不调用scipy的tocsr m, n = coo_matrix.shape # 按行排序COO数据 sorted_indices = np.lexsort((coo_matrix.col, coo_matrix.row)) sorted_rows = coo_matrix.row[sorted_indices] sorted_cols = coo_matrix.col[sorted_indices] sorted_data = coo_matrix.data[sorted_indices] # 计算indptr(行指针) self.indptr = np.zeros(m + 1, dtype=np.int32) np.cumsum(np.bincount(sorted_rows, minlength=m), out=self.indptr[1:]) self.indices = sorted_cols self.data = sorted_data def __add__(self, other): if self.shape != other.shape: raise ValueError("Matrices must have the same shape for addition") # 将两个CSR转成COO相加,再转回CSR self_coo = coo_matrix((self.data, (np.repeat(np.arange(self.shape[0]), np.diff(self.indptr)), self.indices)), shape=self.shape) other_coo = coo_matrix((other.data, (np.repeat(np.arange(other.shape[0]), np.diff(other.indptr)), other.indices)), shape=other.shape) add_coo = self_coo + other_coo return CSRMatrix(add_coo) def _matvec(self, vector): m, _ = self.shape return _csr_matvec_core(self.data, self.indices, self.indptr, m, vector) # 测试代码 A = np.array([[1, 0],[3, 4],[6, 0]]) B = np.array([[1, 2],[5, 6],[7, 8]]) A_coo = coo_matrix(A) B_coo = coo_matrix(B) # 转换为CSR实例 csr_A = CSRMatrix(A_coo) csr_B = CSRMatrix(B_coo) # 矩阵相加 csr_sum = csr_A + csr_B # 生成测试向量 rand = np.random.RandomState(0) x = rand.randn(csr_sum.shape[1]) # 计算矩阵-向量乘积 matvec_result = csr_sum @ x print("矩阵-向量乘积结果:") print(matvec_result)
修复说明
- 手动CSR转换:在
__init__中对COO数据按行排序,通过np.bincount计算indptr,完全手动实现COO到CSR的转换,符合要求。 - 正确实现__add__:将两个CSR实例转回COO相加(scipy的COO相加会自动合并相同位置的元素),再转换为CSR实例,避免转稠密矩阵。
- 实现_matvec方法:将numba加速的核心函数封装为
_matvec,满足LinearOperator的要求,同时支持直接用@运算符调用。 - 规范类结构:使用
super().__init__初始化父类,修正所有方法的参数规范,调用逻辑符合面向对象规则。
内容的提问来源于stack exchange,提问作者Anushka Tilekar
相关产品推荐
相关产品推荐

