Как скомпилировать расширение C для Python, где функция C использует библиотеку LAPACK? - PullRequest
0 голосов
/ 31 августа 2018

Я написал расширение C для Python, и модуль успешно скомпилирован в файл .so. Однако, когда я пытаюсь использовать упакованную функцию C на стороне Python (тестовый код на python, который вызывает упакованную функцию C), я получаю следующее ImportError

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

Я почти уверен, что undefined symbol: dgetri_ в ошибке импорта, потому что сгенерированный файл .so не нашел ссылку на библиотеку LAPACK. Итак, мой вопрос заключается в следующем,

Как мне скомпилировать код расширения c для python, когда обернутая функция C зависит от библиотеки LAPACK для генерации модуля в формате .so?

В настоящее время я компилирую код C, используя модуль utils.core python. Я думаю, что мне нужно скомпилировать код C из командной строки для ссылки LAPACK, но точно не знаю, какие соответствующие команды использовать?

Любая помощь приветствуется.

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Кроме того, мой код работал с использованием следующих двух строк при компиляции в 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

0 голосов
/ 02 сентября 2018

Вас может заинтересовать использование scipy.linalg.cython_lapack . Это обеспечивает доступ к функции LAPACK dgetri среди других. И хорошие новости это:

Это позволяет использовать BLAS и LAPACK SciPy от любого третьего лица Модуль Cython без явного связывания с библиотеками. Это означает что такие проекты, как scikit-learn и statsmodels, не нуждаются в поддержке отдельная зависимость сборки от BLAS и LAPACK.

Пример использования dger доступен на Вызов BLAS / LAPACK напрямую с использованием интерфейса SciPy и Cython . См. Также Повышение производительности Cython Lapack с помощью внутренних определений массивов? Я подробно описал использование cython_blas в своем ответе на MPI python-Open-MPI , поэтому вот как его можно адаптировать к dgetri:

  1. Критическая часть кода записана в Cython, в выделенном файле myinverse.pyx.

  2. Этот файл превращен в myinverse.c файл Cython

  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 можно использовать следующий make-файл (надеюсь, вы используете 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, цепочка 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
...