在 Cython 中调用点积和线性代数运算?

2025-04-10 09:45:00
admin
原创
21
摘要:问题描述:我正在尝试使用点积、矩阵求逆和其他可在 Cython 中从 numpy 获得的基本线性代数运算。函数如numpy.linalg.inv(求逆)、numpy.dot(点积)、X.t(矩阵/数组的转置)。从 Cython 函数调用会产生很大的开销,numpy.*而其余函数都是用 Cython 编写的,所...

问题描述:

我正在尝试使用点积、矩阵求逆和其他可在 Cython 中从 numpy 获得的基本线性代数运算。函数如numpy.linalg.inv(求逆)、numpy.dot(点积)、X.t(矩阵/数组的转置)。从 Cython 函数调用会产生很大的开销,numpy.*而其余函数都是用 Cython 编写的,所以我想避免这种情况。

如果我假设用户已经numpy安装,有没有办法做类似的事情:

#include "numpy/npy_math.h"

作为extern,并调用这些函数?或者直接调用 BLAS(或 numpy 为这些核心操作调用的任何函数)?

举个例子,假设你在 Cython 中有一个函数,它可以做很多事情,最后需要进行涉及点积和矩阵逆的计算:

cdef myfunc(...):
  # ... do many things faster than Python could
  # ...
  # compute one value using dot products and inv
  # without using 
  #   import numpy as np 
  #   np.*
  val = gammaln(sum(v)) - sum(gammaln(v)) + dot((v - 1).T, log(x).T)

如何做到这一点?如果 Cython 中已经有一个实现这些的库,我也可以使用它,但还没有找到任何东西。即使这些程序的优化程度不如 BLAS 直接优化,但无需numpy从 Cython 调用 Python 模块的开销仍会使整体速度更快。

我想要调用的示例函数:

  • 点积 ( np.dot)

  • 矩阵求逆 ( np.linalg.inv)

  • 矩阵乘法

  • 采取转置(相当于x.T在 numpy 中)

  • gammaln 函数(类似scipy.gammaln等效函数,在 C 中可用)

我意识到,正如 numpy 邮件列表 ( https://groups.google.com/forum/?fromgroups=#!topic/cython-users/XZjMVSIQnTE ) 上所说,如果在大型矩阵上调用这些函数,那么从 Cython 执行此操作是没有意义的,因为从 numpy 调用它只会导致大部分时间花在 numpy 调用的优化 C 代码上。但是,就我而言,我在小矩阵上多次调用这些线性代数运算- 在这种情况下,反复从 Cython 返回 numpy 并返回 Cython 所带来的开销将远远超过实际从 BLAS 计算操作所花费的时间。因此,对于这些简单的操作,我想将所有内容保留在 C/Cython 级别,而不是通过 python。

我宁愿不通过 GSL,因为这会增加另一个依赖项,而且不清楚 GSL 是否得到积极维护。由于我假设代码的用户已经安装了 scipy/numpy,我可以放心地假设他们拥有与这些库相关的所有 C 代码,所以我只想能够利用该代码并从 Cython 调用它。

编辑:我找到了一个在 Cython 中包装 BLAS 的库(https://github.com/tokyo/tokyo),它很接近但不是我想要的。我想直接调用 numpy/scipy C 函数(我假设用户已经安装了这些函数。)


解决方案 1:

调用与 Scipy 捆绑在一起的 BLAS“相当”简单,这里有一个调用 DGEMM 来计算矩阵乘法的示例:https://gist.github.com/pv/5437087 请注意,BLAS 和 LAPACK 期望所有数组都是 Fortran 连续的(模数 lda/b/c 参数),因此order="F"double[::1,:]对于正确运行是必需的。

通过在单位矩阵上应用 LAPACK 函数,可以类似地计算逆矩阵dgesv。有关签名,请参见此处。所有这些都需要降到相当低级的编码,您需要自己分配临时工作数组等等。---但是,这些可以封装到您自己的便利函数中,或者只需通过用上述方式从 Scipy 获得的函数指针tokyo替换函数来重用代码。lib_*

如果您使用 Cython 的 memoryview 语法 ( double[::1,:]),您的转置与平常相同x.T。或者,您可以通过编写自己的函数来计算转置,该函数在对角线上交换数组的元素。Numpy 实际上不包含此操作,x.T只会更改数组的步幅,而不会移动数据。

可能可以重写tokyo模块以使用 Scipy 导出的 BLAS/LAPACK 并将其捆绑在 中scipy.linalg,这样您就可以直接执行from scipy.linalg.blas cimport dgemm。如果有人想深入研究它,可以接受拉取请求。


如您所见,这一切都归结为传递函数指针。如上所述,Cython 确实提供了自己的函数指针交换协议。例如,考虑from scipy.spatial import qhull; print(qhull.__pyx_capi__)--- 这些函数可以通过 Cython 访问from scipy.spatial.qhull cimport XXXX(但它们是私有的,所以不要这样做)。

但是,目前scipy.special不提供此 C-API。不过,提供它其实相当简单,因为 scipy.special 中的接口模块是用 Cython 编写的。

我认为目前还没有任何合理且可移植的方法来访问执行繁重工作的函数gamln(尽管您可以窥探 UFunc 对象,但这不是一个合理的解决方案:),因此目前最好只是从 scipy.special 中获取相关部分源代码并将其与您的项目捆绑在一起,或者使用 GSL。

解决方案 2:

如果您确实接受使用 GSL,那么最简单的方法可能是使用此 GSL->cython 接口https://github.com/twiecki/CythonGSL并从那里调用 BLAS(参见示例https://github.com/twiecki/CythonGSL/blob/master/examples/blas2.pyx)。它还应该处理 Fortran 与 C 的排序。GSL 的新特性并不多,但您可以放心地认为它是积极维护的。与 tokyo 相比,CythonGSL 更完整;例如,它具有 numpy 中没有的对称矩阵乘积。

解决方案 3:

由于我刚刚遇到了同样的问题,并编写了一些其他函数,因此我会将它们包含在这里,以防其他人发现它们有用。我编写了一些矩阵乘法,还调用了 LAPACK 函数进行矩阵求逆、行列式和 Cholesky 分解。但是,如果有循环,您应该考虑尝试在任何循环之外进行线性代数运算,就像我在这里做的那样。顺便说一句,如果您有建议,这里的行列式函数不太管用。另外,请注意,我不会检查输入是否符合要求。

from scipy.linalg.cython_lapack cimport dgetri, dgetrf, dpotrf

cpdef void double[:, ::1] inv_c(double[:, ::1] A, double[:, ::1] B, 
                          double[:, ::1] work, double[::1] ipiv):
    '''invert float type square matrix A

    Parameters
    ----------
    A : memoryview (numpy array)
        n x n array to invert
    B : memoryview (numpy array)
        n x n array to use within the function, function
        will modify this matrix in place to become the inverse of A
    work : memoryview (numpy array)
        n x n array to use within the function
    ipiv : memoryview (numpy array)
        length n array to use within function
    '''

    cdef int n = A.shape[0], info, lwork
    B[...] = A

    dgetrf(&n, &n, &B[0, 0], &n, &ipiv[0], &info)

    dgetri(&n, &B[0,0], &n, &ipiv[0], &work[0,0], &lwork, &info)

cpdef double det_c(double[:, ::1] A, double[:, ::1] work, double[::1] ipiv):
    '''obtain determinant of float type square matrix A

    Notes
    -----
    As is, this function is not yet computing the sign of the determinant
    correctly, help!

    Parameters
    ----------
    A : memoryview (numpy array)
        n x n array to compute determinant of
    work : memoryview (numpy array)
        n x n array to use within function
    ipiv : memoryview (numpy array)
        length n vector use within function

    Returns
    -------
    detval : float
        determinant of matrix A
    '''

    cdef int n = A.shape[0], info
    work[...] = A

    dgetrf(&n, &n, &work[0,0], &n, &ipiv[0], &info)

    cdef double detval = 1.
    cdef int j

    for j in range(n):
        if j != ipiv[j]:
            detval = -detval*work[j, j]
        else:
            detval = detval*work[j, j]

    return detval

cdef void chol_c(double[:, ::1] A, double[:, ::1] B):
    '''cholesky factorization of real symmetric positive definite float matrix A

    Parameters
    ----------
    A : memoryview (numpy array)
        n x n matrix to compute cholesky decomposition
    B : memoryview (numpy array)
        n x n matrix to use within function, will be modified
        in place to become cholesky decomposition of A. works
        similar to np.linalg.cholesky
    '''
    cdef int n = A.shape[0], info
    cdef char uplo = 'U'
    B[...] = A

    dpotrf(&uplo, &n, &B[0,0], &n, &info)

    cdef int i, j
    for i in range(n):
        for j in range(n):
            if j > i:
                B[i, j] = 0  

cpdef void dotmm_c(double[:, :] A, double[:, :] B, double[:, :] out):
    '''matrix multiply matrices A (n x m) and B (m x l)

    Parameters
    ----------
    A : memoryview (numpy array)
        n x m left matrix
    B : memoryview (numpy array)
        m x r right matrix
    out : memoryview (numpy array)
        n x r output matrix
    '''
    cdef Py_ssize_t i, j, k
    cdef double s
    cdef Py_ssize_t n = A.shape[0], m = A.shape[1]
    cdef Py_ssize_t l = B.shape[0], r = B.shape[1]

    for i in range(n):
        for j in range(r):
            s = 0
            for k in range(m):
                s += A[i, k]*B[k, j]

            out[i, j] = s
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用