【问题标题】:Wrapping a LAPACKE function using Cython使用 Cython 包装 LAPACKE 函数
【发布时间】:2014-06-05 16:14:01
【问题描述】:

我正在尝试使用 Cython 包装 LAPACK 函数 dgtsv(三对角方程组的求解器)。

我遇到了this previous answer,但由于dgtsv 不是包装在scipy.linalg 中的LAPACK 函数之一,我认为我不能使用这种特殊方法。相反,我一直在尝试关注this example

这是我的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)

...这是我在_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_trisolve 只是在调用_solvers.TDMA_lapacke 时出现了段错误。 我很确定我的 setup.py 是正确的 - ldd _solvers.so 表明 _solvers.so 在运行时链接到正确的共享库。

我不确定如何从这里开始 - 有什么想法吗?


简短的更新

对于较小的 n 值,我往往不会立即得到段错误,但我确实得到了无意义的结果(||x - x_hat|| 应该非常接近 0): p>

In [28]: test_trisolve2.test_trisolve(10)
0
||x - x_hat|| =  6.23202576396

In [29]: test_trisolve2.test_trisolve(10)
-7
||x - x_hat|| =  3.88623414288

In [30]: test_trisolve2.test_trisolve(10)
0
||x - x_hat|| =  2.60190676562

In [31]: test_trisolve2.test_trisolve(10)
0
||x - x_hat|| =  3.86631743386

In [32]: test_trisolve2.test_trisolve(10)
Segmentation fault

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

【问题讨论】:

    标签: python numpy linear-algebra cython lapack


    【解决方案1】:

    虽然这个问题相当古老,但似乎仍然具有相关性。 观察到的行为是对参数 LDB 的误解的结果:

    • Fortran 数组是 col major,数组 B 的前导维度对应于 N。因此 LDB >= max(1,N)。
    • 主要行 LDB 对应于 NRHS,因此必须满足条件 LDB >= max(1,NRHS)。

    注释# b is (LDB, NRHS) 不正确,因为 b 的维度为 (LDB,N),在这种情况下 LDB 应该为 1。

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

    【讨论】:

      【解决方案2】:

      好的,我最终弄明白了——在这种情况下,我似乎误解了 row-major 和 column-major 的含义。

      由于 C 连续数组遵循行优先顺序,我假设我应该指定 LAPACK_ROW_MAJOR 作为 LAPACKE_dgtsv 的第一个参数。

      其实,如果我改变了

      info = LAPACKE_dgtsv(LAPACK_ROW_MAJOR, ...)
      

      info = LAPACKE_dgtsv(LAPACK_COL_MAJOR, ...)
      

      然后我的功能起作用了:

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

      这对我来说似乎很违反直觉 - 谁能解释为什么会这样?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-17
        • 2020-12-31
        • 2015-05-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多