Python优化:使用矢量技术查找numpy数组中每个矩阵的幂

时间:2019-01-01 12:54:19

标签: python numpy

3D numpy数组A包含一系列D形状的2D numpy数组2 x 2(在此示例中,我选择3)。 D矩阵如下:

D = np.array([[1,2],[3,4]])

A的初始化和分配如下:

idx = np.arange(3)
A = np.zeros((3,2,2))
A[idx,:,:] = D           # This gives A = [[[1,2],[3,4]],[[1,2],[3,4]],\
                         # [[1,2],[3,4]]]
                         # In mathematical notation: A = {D, D, D}

现在,执行代码后基本上需要的是:

从数学上讲,A = {D^0, D^1, D^2} = {D0, D1, D2} 其中D0 = [[1,0],[0,1]]D1 = [[1,2],[3,4]]D2=[[7,10],[15,22]]

是否可以在不使用for循环的情况下为A中的每个矩阵元素加电?我将在更大的矩阵中处理​​更多的序列。

我已经定义了n = np.array([0,1,2])#对应于0、1和2的幂并尝试了

Result = np.power(A,n),但没有得到所需的输出。

是否有一种有效的方法?

完整代码:

D = np.array([[1,2],[3,4]])
idx = np.arange(3)
A = np.zeros((3,2,2))
A[idx,:,:] = D           # This gives A = [[[1,2],[3,4]],[[1,2],[3,4]],\
                         # [[1,2],[3,4]]]
                         # In mathematical notation: A = {D, D, D}
n = np.array([0,1,2])
Result = np.power(A,n)   # ------> Not the desired output.

4 个答案:

答案 0 :(得分:3)

一个累积乘积以numpy存在,但不适用于矩阵。因此,您需要创建自己的“ matcumprod”功能。您可以为此使用np.dot,但是np.matmul(或@)专用于矩阵乘法。

由于您声明自己的功率始终从0变为some_power,因此建议使用以下函数:

def matcumprod(D, upto):
  Res = np.empty((upto, *D.shape), dtype=A.dtype)
  Res[0, :, :] = np.eye(D.shape[0])
  Res[1, :, :] = D.copy()
  for i in range(1,upto):
    Res[i, :, :] = Res[i-1,:,:] @ D

  return Res

顺便说一句,如果内置的numpy函数占用大量内存,则循环通常会比内置的numpy函数性能好,因此,如果您的能力不在限制之内,请不要担心...

答案 1 :(得分:2)

好的,我在这个问题上花了很多时间,但是似乎找不到您想要的矢量化解决方案。因此,我想先提出一个基本的解决方案,然后在需要连续功率的情况下进行优化。

您要查找的函数称为numpy.linalg.matrix_power

import numpy as np

D = np.matrix([[1,2],[3,4]])
idx = np.arange(3)
A = np.zeros((3,2,2))
A[idx,:,:] = D           # This gives A = [[[1,2],[3,4]],[[1,2],[3,4]],\
                         # [[1,2],[3,4]]]
                         # In mathematical notation: A = {D, D, D}
np.zeros(A.shape)
n = np.array([0,1,2])
result = [np.linalg.matrix_power(D, i) for i in n]
np.array(result)
#Output:
array([[[ 1,  0],
        [ 0,  1]],

       [[ 1,  2],
        [ 3,  4]],

       [[ 7, 10],
        [15, 22]]])

但是,如果您注意到,最终将为同一基本矩阵计算多个功效。我们可以改用中间结果,然后使用numpy.linalg.multi_dot

def all_powers_arr_of_matrix(A): 
    result = np.zeros(A.shape)
    result[0] = np.linalg.matrix_power(A[0], 0)
    for i in range(1, A.shape[0]):
        result[i] = np.linalg.multi_dot([result[i - 1], A[i]])
    return result
    result = all_powers_arr_of_matrix(A)
#Output:
array([[[ 1.,  0.],
        [ 0.,  1.]],

       [[ 1.,  2.],
        [ 3.,  4.]],

       [[ 7., 10.],
        [15., 22.]]])

此外,我们可以避免完全创建矩阵A,从而节省一些时间。

    def all_powers_matrix(D, *rangeargs): #end exclusive
        ''' Expects 2D matrix. 
        Use as all_powers_matrix(D, end) or
        all_powers_matrix(D, start, end)
        '''
        if len(rangeargs) == 1:
            start = 0
            end = rangeargs[0]
        elif len(rangeargs) == 2:
            start = rangeargs[0]
            end = rangeargs[1]
        else:
            print("incorrect args")
            return None
        result = np.zeros((end - start, *D.shape))
        result[0] = np.linalg.matrix_power(A[0], start)
        for i in range(start + 1, end):
            result[i] = np.linalg.multi_dot([result[i - 1], D])
        return result

            return result
result = all_powers_matrix(D, 3)
#Output:
array([[[ 1.,  0.],
        [ 0.,  1.]],

       [[ 1.,  2.],
        [ 3.,  4.]],

       [[ 7., 10.],
        [15., 22.]]])

请注意,如果您决定按原样使用这些功能,则需要添加错误处理。

答案 2 :(得分:0)

我没有完整的解决方案,但是有些事情我想提一下,这些评论太长了。

如果您要计算大型矩阵的强大功率,则可能首先要研究addition chain exponentiation。这基本上是在询问给定A^k的计算k需要多少个矩阵乘法。例如A^5 = A(A^2)^2,因此您只需要三个矩阵乘法:A^2(A^2)^2A(A^2)^2。这可能是提高效率的最简单方法,但是您可能仍然必须使用显式循环。

您的问题还与针对给定的Ax, A^2x, ... , A^kxA计算x的问题有关。这是当前研究的一个活跃领域(搜索“矩阵幂核”),因为有效地计算此类序列对于避免Krylov子空间方法的并行/通信很有用。如果您正在寻找一种非常有效的解决方案,那么值得研究一下相关结果。

答案 3 :(得分:0)

要计算矩阵A的幂,一种方法可能是使用np.linalg.eig查找矩阵的特征值和右特征向量,然后提高对角矩阵的幂,因为它更容易,然后经过操作,您可以使用两个np.einsum来计算#get eigvalues and eigvectors eigval, eigvect = np.linalg.eig(D) # to check how it works, you can do: print (np.dot(eigvect*eigval,np.linalg.inv(eigvect))) #[[1. 2.] # [3. 4.]] # so you get back on D #use power as ufunc of outer with n on the eigenvalues to get all the one you want arrp = np.power.outer( eigval, n).T #apply_along_axis to create the diagonal matrix along the last axis diagp = np.apply_along_axis( np.diag, axis=-1, arr=arrp) #finally use two np.einsum to calculate with the subscript to get what you want A = np.einsum('lij,jk -> lik', np.einsum('ij,kjl -> kil',eigvect,diagp), np.linalg.inv(eigvect)).round() print (A) print (A.shape) #[[[ 1. 0.] # [-0. 1.]] # # [[ 1. 2.] # [ 3. 4.]] # # [[ 7. 10.] # [15. 22.]]] # #(3, 2, 2)

  A connection to myapicall was leaked. Did you forget to close a response body?