Numba nopython模式的三对角矩阵算法

时间:2019-03-25 02:06:23

标签: python python-3.x performance numpy numba

我正在尝试在nopython模式下用numba编写一个TDMA algorithm。这是我的代码:

@jit(nopython=True)
def TDMA(a,b,c,d):
  n = len(d)
  x = np.zeros(n)
  w = np.zeros(n)
  #   ac, bc, cc, dc = map(np.copy, (a, b, c, d)) # copy arrays
  ac = np.copy(a)
  bc = np.copy(b)
  cc = np.copy(c)
  dc = np.copy(d)
  for i in range(1,n):
    w[i] = ac[i-1]/bc[i-1]
    bc[i] = bc[i] - w[i]*cc[i-1]
    dc[i] = dc[i] - w[i]*dc[i-1]

  x[n-1] = dc[n-1]/bc[n-1]
  for k in range(n-2,-1,-1):
    x[k] = (dc[k]-cc[k]*x[k+1])/bc[k]
  return np.array(x)

然后测试此求解器:

A = np.array([[5, 2, 0, 0],[1, 5, 2, 0],[0, 1, 5, 2],[0, 0, 1, 5]],float)
B = np.array([[15],[2],[7],[20]],float)
a = A.diagonal(-1)
b = A.diagonal()
c = A.diagonal(1)
x1 = np.linalg.solve(A,B)
x2 = TDMA(a,b,c,B)
print('by default solver, x1 = ',x1)
print('by TDMA, x2 = ',x2)

但是,我的TDMA函数失败并显示TypingError

TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot resolve setitem: array(float64, 1d, C)[int64] = array(float64, 1d, C)

File "<ipython-input-20-e25cda7246bd>", line 16:
def TDMA(a,b,c,d):
    <source elided>

  x[n-1] = dc[n-1]/bc[n-1]
  ^

它可以与@jit装饰器一起正常使用,但在nopython模式下不能使用。我应该如何修改此TDMA函数以使其与nopyhon兼容?

我评论的那一行:

ac, bc, cc, dc = map(np.copy, (a, b, c, d)) # copy arrays

也不兼容nopython。可以在map模式下使用nopython函数吗?

我了解我的TDMA可能仍然很慢。那么,有没有最快的代码使用python 3语言实现Tridiagonal Matrix Algorithm?

1 个答案:

答案 0 :(得分:1)

问题是您有2D数组,但对其进行索引和分配就像它们是1D数组一样。因此,您可以在将它们传递给numba函数之前ravel()。我不确定这是否正确-但是出于这个答案的目的,我认为是正确的。

此外,您无需复制ac,因为您无需修改​​它们,而实际上您只需要复制b和{{1}的第一个元素}。

因此工作功能可能如下所示:

d

您这样称呼它:

import numba as nb
import numpy as np

@nb.njit
def TDMA(a,b,c,d):
    n = len(d)
    x = np.zeros(n)
    bc = np.zeros(len(b))
    bc[0] = b[0]
    dc = np.zeros(len(d))
    dc[0] = d[0]

    for i in range(1, n):
        w = a[i - 1] / bc[i - 1]
        bc[i] = b[i] - w * c[i - 1]
        dc[i] = d[i] - w * dc[i - 1]

    x[n - 1] = dc[n - 1] / bc[n - 1]
    for k in range(n - 2, -1, -1):
        x[k] = (dc[k] - c[k] * x[k + 1]) / bc[k]
    return x

因为我使用了TDMA(a.ravel(), b.ravel(), c.ravel(), B.ravel()) ,所以结果与ravel()的形状不同:

np.linalg.solve

但是我真的不会重新实现NumPy函数,除非您可以利用数据中的一些NumPy函数不知道的结构。 NumPy是一个高性能库,已经使用了经过微调的实现,因此,临时重新实现可能仅对于极小的数据集或在您可以利用有关数据的某些事实(允许使用性能更高的算法)时更快。 )。

我必须承认我不知道“三对角矩阵算法”,但我知道某些BLAS libraries(通常是令人难以置信的快速数学库)实现了它。 NumPy使用BLAS。

但是SciPy为特殊矩阵类型提供了一些(非常快的)特殊线性代数求解器:

  

Basics

     
      
  • by default solver, x1 = [[ 3.05427975] [-0.13569937] [-0.18789144] [ 4.03757829]] by TDMA, x2 = [ 3.05427975 -0.13569937 -0.18789144 4.03757829] 计算矩阵的逆。
  •   
  • inv(a[, overwrite_a, check_finite])求解线性方程组a * x = b,用于将矩阵x平方的未知x。
  •   
  • solve(a, b[, sym_pos, lower, overwrite_a, …])假设a是带状矩阵,则对x求解方程a x = b。
  •   
  • solve_banded(l_and_u, ab, b[, overwrite_ab, …])求解方程a x = b。
  •   
  • solveh_banded(ab, b[, overwrite_ab, …])对x求解C x = b,其中C是循环矩阵。
  •   
  • solve_circulant(c, b[, singular, tol, …])假设a是三角矩阵,则对x求解方程a x = b。
  •   
  • solve_triangular(a, b[, trans, lower, …])使用Levinson递归求解Toeplitz系统
  •   
     

[...]

关于您对solve_toeplitz(c_or_cr, b[, check_finite])的提问:当前的官员list of supported built-in functions不包括map。因此,您不能在Numbas nopython模式下使用map