计算几何级数上三角矩阵的最快方法(Python)

时间:2015-02-24 20:42:42

标签: python numpy vectorization linear-algebra

并提前感谢您的帮助。

使用Python(主要是numpy),我试图计算一个上三角矩阵,其中每一行“j”是几何系列的第一个j项,所有行都使用相同的参数。

例如,如果我的参数是B(其中abs(B)=< 1,即[-1,1]中的B),那么第1行将是[1 BB ^ 2 B ^ 3 ... B ^(N-1)],第2行将是[0 1 BB ^ 2 ... B ^(N-2)] ...行N将是[0 0 0 ... 1]。

这个计算是贝叶斯Metropolis-Gibbs采样器的关键,因此需要对“B”的新值进行数千次。

我目前尝试过这两种方式:

方法1 - 主要是矢量化:

B_Matrix = np.triu(np.dot(np.reshape(B**(-1*np.array(range(N))),(N,1)),np.reshape(B**(np.array(range(N))),(1,N))))

基本上,这是Nx1和1xN矩阵集的乘积的上三角部分:

上三角形([1 B ^( - 1)B ^( - 2)... B ^( - (N-1))]'* [1 BB ^ 2 B ^ 3 ... B ^( N-1)])

这适用于小N(代数上是正确的),但对于大N,它会出错。并且它产生B = 0的错误(应该允许)。我相信这是因为小B和大N取B ^( - N)~inf。

方法2:

B_Matrix = np.zeros((N,N))
B_Row_1 = B**(np.array(range(N)))
for n in range(N):
    B_Matrix[n,n:] = B_Row_1[0:N-n]

因此,只是逐行填充矩阵,但使用一个减慢速度的循环。

我想知道是否有人之前碰到过这个问题,或者对如何以更快的方式计算这个矩阵有更好的想法。

我以前从未在stackoverflow上发布过,但是在任何地方都没有看到这个问题,并且我想我会问。

让我知道是否有更好的地方可以提出这个问题,以及我是否应该提供更多细节。

1 个答案:

答案 0 :(得分:2)

您可以使用scipy.linalg.toeplitz

In [12]: n = 5

In [13]: b = 0.5

In [14]: toeplitz(b**np.arange(n), np.zeros(n)).T
Out[14]: 
array([[ 1.    ,  0.5   ,  0.25  ,  0.125 ,  0.0625],
       [ 0.    ,  1.    ,  0.5   ,  0.25  ,  0.125 ],
       [ 0.    ,  0.    ,  1.    ,  0.5   ,  0.25  ],
       [ 0.    ,  0.    ,  0.    ,  1.    ,  0.5   ],
       [ 0.    ,  0.    ,  0.    ,  0.    ,  1.    ]])

如果您对数组的使用严格“只读”,则可以使用numpy步长来快速创建仅使用2 * n-1个元素(而不是n ^ 2)的数组:

In [55]: from numpy.lib.stride_tricks import as_strided

In [56]: def make_array(b, n):
   ....:     vals = np.zeros(2*n - 1)
   ....:     vals[n-1:] = b**np.arange(n)
   ....:     a = as_strided(vals[n-1:], shape=(n, n), strides=(-vals.strides[0], vals.strides[0]))
   ....:     return a
   ....: 

In [57]: make_array(0.5, 4)
Out[57]: 
array([[ 1.   ,  0.5  ,  0.25 ,  0.125],
       [ 0.   ,  1.   ,  0.5  ,  0.25 ],
       [ 0.   ,  0.   ,  1.   ,  0.5  ],
       [ 0.   ,  0.   ,  0.   ,  1.   ]])

如果要就地修改数组,请复制make_array(b, n)返回的结果。也就是arr = make_array(b, n).copy()

函数make_array2包含@Jaime在评论中提出的建议:

In [30]: def make_array2(b, n):
   ....:     vals = np.zeros(2*n-1)
   ....:     vals[n-1] = 1
   ....:     vals[n:] = b
   ....:     np.cumproduct(vals[n:], out=vals[n:])
   ....:     a = as_strided(vals[n-1:], shape=(n, n), strides=(-vals.strides[0], vals.strides[0]))
   ....:     return a
   ....: 

In [31]: make_array2(0.5, 4)
Out[31]: 
array([[ 1.   ,  0.5  ,  0.25 ,  0.125],
       [ 0.   ,  1.   ,  0.5  ,  0.25 ],
       [ 0.   ,  0.   ,  1.   ,  0.5  ],
       [ 0.   ,  0.   ,  0.   ,  1.   ]])

make_array2的速度是make_array的两倍多:

In [35]: %timeit make_array(0.99, 600)
10000 loops, best of 3: 23.4 µs per loop

In [36]: %timeit make_array2(0.99, 600)
100000 loops, best of 3: 10.7 µs per loop