2014-04-21 113 views
5

我正在嘗試使用Cython來包裝LAPACK函數dgtsv(三方對稱方程組的求解器)。使用Cython包裝LAPACKE函數

我碰到了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) 

...這裏是我的薄用Cython包裝在_solvers.pyx

#!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只是se grup打電話給_solvers.TDMA_lapacke。 我很確定我的setup.py是正確的 - ldd _solvers.so顯示_solvers.so正在運行時鏈接到正確的共享庫。

我不太確定如何從這裏開始 - 任何想法?


的簡要更新

因爲我往往不會立即得到段錯誤的n較小的值,但我得到的廢話結果(|| X - x_hat ||應該是非常接近0):

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很小,我也會最終遇到段錯誤。

回答

3

好的,我終於明白了 - 似乎我誤解了在這種情況下行列專業所指的。

由於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 

這似乎非常反直覺我 - 任何人都可以解釋爲什麼是這樣嗎?

1

雖然相當古老,但問題似乎仍然相關。 所觀察到的行爲是參數LDB的曲解的結果:

  • Fortran數組是COL主要和陣列B的主導尺寸對應於N.因此LDB> = MAX(1,N)。
  • 行重大LDB對應於NRHS,因此必須滿足條件LDB> = max(1,NRHS)。

評論#b爲(LDB,NRHS)是不正確的,因爲B具有尺寸(LDB,N)和LDB應在這種情況下是1。

只要NRHS等於1,從LAPACK_ROW_MAJOR切換到LAPACK_COL_MAJOR就可以解決問題。col col(N,1)的內存佈局與row major(1,N)相同。然而,如果NRHS大於1,它將會失敗。