使用LAPACK分发基于Cython的扩展
我正在写一个Python模块,这个模块里包含了Cython扩展,并且使用了 LAPACK
(和 BLAS
)。我可以选择使用 clapack
或者 lapacke
,如果有必要的话,也可以考虑一些 f2c
或 f2py
的解决方案。最重要的是,我希望能够在Cython中调用 lapack
和 blas
的函数,特别是在紧密循环中,这样就不会有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 个回答
如果我理解得没错,你可以使用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子模块的这一部分。如果我找到正确的方法,我一定会更新这个回答。