【问题标题】:How to compile C extension for Python where C function uses LAPACK library?如何为 C 函数使用 LAPACK 库的 Python 编译 C 扩展?
【发布时间】:2018-08-31 00:25:42
【问题描述】:

我为 Python 编写了一个 C 扩展,并且该模块成功编译为 .so 文件。但是,当我尝试在 Python 端使用包装的 C 函数(调用包装的 C 函数的 python 中的测试代码)时,我得到以下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 但不完全知道要使用哪些合适的命令?

感谢任何帮助。

【问题讨论】:

    标签: python c wrapper


    【解决方案1】:

    您可能有兴趣使用scipy.linalg.cython_lapack 它提供对 LAPACK 函数dgetri 等的访问。而the good news 是:

    这使得使用来自任何第 3 方的 SciPy 的 BLAS 和 LAPACK 成为可能 Cython 模块没有明确地与库链接。这表示 scikit-learn 和 statsmodels 等项目不需要维护 对 BLAS 和 LAPACK 的单独构建依赖。

    Calling BLAS / LAPACK directly using the SciPy interface and Cython 提供了使用 dger 的示例。另见Improving Cython Lapack performance with internal array definitions? 我在对 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文件进行cythonize和编译,可以使用以下makefile(希望你使用的是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
    

    【讨论】:

    • 作为额外的优势:scipy 为已安装的不同版本的 BLAS/LAPACK 库提供了一些兼容性代码,例如scipy 是用 MKL、OpenBLAS 还是其他工具构建的。这意味着当使用来自 scipy 的 cython 包装器时,调用代码不需要担心使用了哪个 linalg 库。
    • 感谢您提供所有详细信息。它们是有趣的读物。但是,在 linux 中编译时,我使用以下两行代码完成了我的代码
    【解决方案2】:

    另外,我在 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

    【讨论】:

      【解决方案3】:

      或者,您可以使模块与 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'],
      )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-04
        • 1970-01-01
        • 2019-04-22
        • 2010-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多