You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现稀疏矩阵向量乘积时遇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)

错误原因

  1. 类型不匹配:scipy中COO矩阵相加other1 + other2返回的是CSR矩阵,而你的CSRMatrix.__init__只处理COO矩阵,直接访问COO独有的.row属性会触发AttributeError。
  2. 类方法定义错误:add方法既不是实例方法(无self参数)也不是类方法(无cls参数),却在内部使用self_csr这类实例变量,逻辑完全混乱,直接通过CSRMatrix.add调用也不符合面向对象规范。
  3. 未实现手动CSR转换:__init__中调用了coo_matrix.tocsr()(违反“不能用库的CSR实现”要求),且self_csr是局部变量,未赋值给实例属性,导致后续无法访问indptr等CSR核心结构。
  4. 缺失LinearOperator要求的方法:继承LinearOperator必须实现_matvec或_matmat,你写的csr_matvec是独立函数,不是类的_matvec方法,触发运行警告。
  5. 调用逻辑错误: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)

修复说明

  1. 手动CSR转换:在__init__中对COO数据按行排序,通过np.bincount计算indptr,完全手动实现COO到CSR的转换,符合要求。
  2. 正确实现__add__:将两个CSR实例转回COO相加(scipy的COO相加会自动合并相同位置的元素),再转换为CSR实例,避免转稠密矩阵。
  3. 实现_matvec方法:将numba加速的核心函数封装为_matvec,满足LinearOperator的要求,同时支持直接用@运算符调用。
  4. 规范类结构:使用super().__init__初始化父类,修正所有方法的参数规范,调用逻辑符合面向对象规则。

内容的提问来源于stack exchange,提问作者Anushka Tilekar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 02:55:55