使用LAPACK分发基于Cython的扩展

68 投票
1 回答
2600 浏览
提问于 2025-04-17 15:49

我正在写一个Python模块,这个模块里包含了Cython扩展,并且使用了 LAPACK(和 BLAS)。我可以选择使用 clapack 或者 lapacke,如果有必要的话,也可以考虑一些 f2cf2py 的解决方案。最重要的是,我希望能够在Cython中调用 lapackblas 的函数,特别是在紧密循环中,这样就不会有Python调用的额外开销。

我找到一个例子,在这里。不过,这个例子依赖于SAGE。我希望我的模块可以在不安装SAGE的情况下安装,因为我的用户可能不需要SAGE做其他事情。我的用户可能已经安装了像numpy、scipy、pandas和scikit-learn这样的包,所以这些依赖是合理的。我想知道使用哪些接口组合最好,以及最简化的setup.py文件应该是什么样的,以便能够获取编译所需的信息(来自numpy、scipy等)。

编辑:这是我最后做的事情。它在我的macbook上可以工作,但我不知道它的可移植性如何。肯定还有更好的方法。

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy
from Cython.Build import cythonize
from numpy.distutils.system_info import get_info

# TODO: This cannot be the right way
blas_include = get_info('blas_opt')['extra_compile_args'][1][2:]
includes = [blas_include,numpy.get_include()]

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = cythonize([Extension("cylapack", ["cylapack.pyx"],
                                       include_dirs = includes,
                                       libraries=['blas','lapack'])
                   ])
)

之所以可行,是因为在我的macbook上, clapack.h 这个头文件和 cblas.h 在同一个目录下。然后我可以在我的pyx文件中这样做:

ctypedef np.int32_t integer

cdef extern from "cblas.h":
    double cblas_dnrm2(int N,double *X, int incX)
cdef extern from "clapack.h":
    integer dgelsy_(integer *m, integer *n, integer *nrhs, 
    double *a, integer *lda, double *b, integer *ldb, integer *
    jpvt, double *rcond, integer *rank, double *work, integer *
    lwork, integer *info)

1 个回答

6

如果我理解得没错,你可以使用SciPy中的Cython包装器来调用BLAS和LAPACK的功能。这些包装器的使用说明可以在这里找到:

根据说明,你需要确保传给这些函数的数组在Fortran的要求下是对齐的。你可以在你的.pyx文件中直接导入并使用这些函数。例如:

from scipy.linalg.cython_blas cimport dnrm2 
from scipy.linalg.cython_lapack cimport dgelsy 

考虑到这些代码经过了充分测试,并且在不同平台上广泛使用,我认为它们是一个很好的选择,可以可靠地分发直接调用BLAS和LAPACK的Cython扩展。


如果你不想让你的代码依赖整个SciPy库,你可以在SciPy的linalg目录中找到许多相关的包装函数文件,链接在这里这里。一个有用的参考是setup.py中的这些行,它列出了源文件和头文件。请注意,需要一个Fortran编译器!

理论上,你应该可以只提取出编译BLAS和LAPACK Cython包装器所需的源文件,然后将它们打包成一个独立的扩展模块。

但实际上,这个过程非常繁琐。linalg子模块的构建过程需要一些Python函数来帮助在不同平台上进行编译(例如,来自这里)。构建还依赖于其他C和Fortran源文件(这里),这些文件的路径是硬编码在这些Python函数里的。

显然,很多工作都投入到确保SciPy能够在不同操作系统和架构上正确编译。

我相信这是可以做到的,但在移动文件和调整路径后,我还没有找到正确的方法来独立构建linalg子模块的这一部分。如果我找到正确的方法,我一定会更新这个回答。

撰写回答