如何编译 Python 的 C 扩展,其中 C 函数使用 LAPACK 库?



我为Python编写了一个C扩展,并且模块成功地编译在.so文件中。但是,当我尝试在 Python 端使用包装的 C 函数(python 中调用包装的 C 函数的测试代码(时,我得到以下ImportError

ImportError: /home/username/newModule.cpython-36m-x86_64-linux-gnu.so: undefined symbol: dgetri_

我很确定导入错误中的undefined symbol: dgetri_是因为生成的.so文件找不到指向LAPACK库的链接。所以我的问题如下,

当包装的 C 函数依赖于 LAPACK 库生成.so格式的模块时,如何为 python 编译 c 扩展代码?

目前,我正在使用python的utils.core模块编译C代码。我想我需要从命令行编译 C 代码以链接LAPACK但不确切知道哪些是合适的命令?

任何帮助,不胜感激。

您可能有兴趣使用scipy.linalg.cython_lapack。它提供了对LAPACK功能dgetri等的访问。好消息是:

这使得从任何第三方使用SciPy的BLAS和LAPACK成为可能。 没有显式链接到库的 Cython 模块。这意味着 像Scikit-learn和StatsModels这样的项目不需要维护一个 对 BLAS 和 LAPACK 的单独构建依赖。

使用dger的示例可在Calling BLAS/LAPACK直接使用SciPy界面和Cython获得。另请参阅使用内部数组定义提高 Cython Lapack 性能? 我在对 MPI python-Open-MPI 的回答中详细介绍了如何使用cython_blas,所以这里是如何适应 dgetri:

  1. 代码的关键部分是用Cython编写的,在专用的文件myinverse.pyx中。

  2. 此文件由Cython转换为myinverse.c文件

  3. 这个c文件是由你最喜欢的c编译器编译的,gcc建立一个共享库myinverse.so

  4. 优化的功能可以在import myinverse后在您的程序中使用。

这是一个cython模块,要放在.pyx文件中:

import numpy
cimport numpy
cimport scipy.linalg.cython_lapack
ctypedef numpy.float64_t DTYPE_t
cimport cython
from libc.stdlib cimport malloc, free
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def invert(numpy.ndarray[DTYPE_t, ndim=2] array):
cdef  int rows = array.shape[0]
cdef   int cols = array.shape[1]
cdef  int info = 0
if cols !=rows:
return array,1,"not a square matrix"
cdef int* ipiv = <int *> malloc(rows * sizeof(int))
if not ipiv:
raise MemoryError()
scipy.linalg.cython_lapack.dgetrf(&cols,&rows,&array[0,0],&rows,ipiv,&info)
if info !=0:
free(ipiv)
return array,info,"dgetrf failed, INFO="+str(info)
#workspace query
cdef double workl
cdef int lwork=-1
scipy.linalg.cython_lapack.dgetri(&cols,&array[0,0],&rows,ipiv,&workl,&lwork,&info)
if info !=0:
free(ipiv)
return array,info,"dgetri failed, workspace query, INFO="+str(info)
#allocation workspace
lwork= int(workl)
cdef double* work = <double *> malloc(lwork * sizeof(double))
if not work:
raise MemoryError()
scipy.linalg.cython_lapack.dgetri(&cols,&array[0,0],&rows,ipiv,work,&lwork,&info)
if info !=0:
free(ipiv)
free(work)
return array,info,"dgetri failed, INFO="+str(info)
free(ipiv)
free(work)
return array,info,""

要对 .pyx 文件进行编码化和编译,可以使用以下生成文件(我希望您使用的是 Linux...

all: myinverse myinverseb

myinverse: myinverse.pyx
cython -a myinverse.pyx
myinverseb: myinverse.c
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o myinverse.so myinverse.c

新的python myinverse函数,chainng LAPACK的dgetrf()dgetri(),在主python文件中调用:

import numpy as np
import myinverse
n=42
#A=np.zeros((n,n))
#for i in range(n):
#    A[i,i]=10
A=np.random.rand(n,n)
#A=np.zeros((n,n))
Am,info,string=myinverse.invert(A.copy())
if info==0:
print np.linalg.norm(A.dot(Am)-np.identity(n), np.inf)
else :
print "inversion failed, info=",info, string

或者,我在用 Linux 编译时使用以下两行

来工作我的代码
$gcc -DNDEBUG -Wall -Wstrict-prototypes -fPIC -I/home/username/anaconda3/include/python3.6m -c stackDoc.cpp -o mydemo.o
$gcc -shared mydemo.o -o mydemo.so

以下链接我发现很有用, https://docs.python.org/2/extending/building.html

或者,您可以使模块链接到lapack。

例如

from distutils.core import setup
from distutils.extension import Extension
setup(
name='MyExtension',
version='0.1',
ext_modules=[
Extension('lib_name', ['lib_name.cpp'], extra_link_args=['-lopenblas']),
],
scripts=['lib_name.cpp', '__init__.py'],
)

相关内容

最新更新