Python 使用Cython包装LAPACKE函数

Python 使用Cython包装LAPACKE函数,python,numpy,linear-algebra,cython,lapack,Python,Numpy,Linear Algebra,Cython,Lapack,我试图用Cython包装LAPACK函数(三对角方程组的求解器) 我遇到过,但是由于dgtsv不是包装在scipy.linalg中的LAPACK函数之一,我认为我不能使用这种特殊的方法。相反,我一直在努力跟随 以下是我的lapacke.pxd文件的内容: ctypedef int lapack_int cdef extern from "lapacke.h" nogil: int LAPACK_ROW_MAJOR int LAPACK_COL_MAJOR lapac

我试图用Cython包装LAPACK函数(三对角方程组的求解器)

我遇到过,但是由于
dgtsv
不是包装在
scipy.linalg
中的LAPACK函数之一,我认为我不能使用这种特殊的方法。相反,我一直在努力跟随

以下是我的
lapacke.pxd
文件的内容:

ctypedef int lapack_int

cdef extern from "lapacke.h" nogil:

    int LAPACK_ROW_MAJOR
    int LAPACK_COL_MAJOR

    lapack_int LAPACKE_dgtsv(int matrix_order,
                             lapack_int n,
                             lapack_int nrhs,
                             double * dl,
                             double * d,
                             double * du,
                             double * b,
                             lapack_int ldb)
…这是我在
\u solvers.pyx
中的薄Cython包装:

#!python

cimport cython
from lapacke cimport *

cpdef TDMA_lapacke(double[::1] DL, double[::1] D, double[::1] DU,
                   double[:, ::1] B):

    cdef:
        lapack_int n = D.shape[0]
        lapack_int nrhs = B.shape[1]
        lapack_int ldb = B.shape[0]
        double * dl = &DL[0]
        double * d = &D[0]
        double * du = &DU[0]
        double * b = &B[0, 0]
        lapack_int info

    info = LAPACKE_dgtsv(LAPACK_ROW_MAJOR, n, nrhs, dl, d, du, b, ldb)

    return info
…下面是一个Python包装器和测试脚本:

import numpy as np
from scipy import sparse
from cymodules import _solvers


def trisolve_lapacke(dl, d, du, b, inplace=False):

    if (dl.shape[0] != du.shape[0] or dl.shape[0] != d.shape[0] - 1
            or b.shape != d.shape):
        raise ValueError('Invalid diagonal shapes')

    if b.ndim == 1:
        # b is (LDB, NRHS)
        b = b[:, None]

    # be sure to force a copy of d and b if we're not solving in place
    if not inplace:
        d = d.copy()
        b = b.copy()

    # this may also force copies if arrays are improperly typed/noncontiguous
    dl, d, du, b = (np.ascontiguousarray(v, dtype=np.float64)
                    for v in (dl, d, du, b))

    # b will now be modified in place to contain the solution
    info = _solvers.TDMA_lapacke(dl, d, du, b)
    print info

    return b.ravel()


def test_trisolve(n=20000):

    dl = np.random.randn(n - 1)
    d = np.random.randn(n)
    du = np.random.randn(n - 1)

    M = sparse.diags((dl, d, du), (-1, 0, 1), format='csc')
    x = np.random.randn(n)
    b = M.dot(x)

    x_hat = trisolve_lapacke(dl, d, du, b)

    print "||x - x_hat|| = ", np.linalg.norm(x - x_hat)
不幸的是,
test\u trisolve
只是在调用
\u solvers.TDMA\u lapacke
时对故障进行分段。 我非常确定我的
setup.py
是正确的-
ldd\u解算器。因此
显示了
\u解算器。因此
在运行时被链接到正确的共享库

我真的不知道如何从这里开始-有什么想法吗


简短更新

对于
n
的较小值,我倾向于不立即得到segfaults,但我确实得到了无意义的结果(| | x-x|u hat | | | |应该非常接近0):


通常
LAPACKE_dgtsv
返回代码
0
(这应该表示成功),但偶尔我会得到
-7
,这意味着参数7(
b
)的值非法。发生的情况是,只有
b
的第一个值实际上被修改到位。如果我继续调用
test\u trisolve
,即使
n
很小,我最终也会遇到一个segfault。

好的,我最终找到了答案-似乎我误解了本例中的行和列major指的是什么

由于C-连续数组遵循行主顺序,我假设应该指定
LAPACK\u row\u major
作为
LAPACKE\u dgtsv
的第一个参数

事实上,如果我改变

info = LAPACKE_dgtsv(LAPACK_ROW_MAJOR, ...)

那么我的功能就起作用了:

test_trisolve2.test_trisolve()
0
||x - x_hat|| =  6.67064747632e-12

这似乎与我的直觉背道而驰——有人能解释为什么会出现这种情况吗?

尽管这个问题已经很老了,但似乎仍然是相关的。 观察到的行为是对参数LDB的错误解释的结果:

  • Fortran数组是主列,数组B的前导维度对应于N。因此LDB>=max(1,N)
  • 行主LDB对应于NRHS,因此必须满足LDB>=max(1,NRHS)的条件
注释#b is(LDB,NRHS)不正确,因为b具有维度(LDB,N),在这种情况下,LDB应为1

从LAPACK_ROW_MAJOR切换到LAPACK_COL_MAJOR可以修复问题,只要NRHS等于1。列主(N,1)的内存布局与行主(1,N)相同。但是,如果NRHS大于1,它将失败

info = LAPACKE_dgtsv(LAPACK_COL_MAJOR, ...)
test_trisolve2.test_trisolve()
0
||x - x_hat|| =  6.67064747632e-12