Vectorize eigenvalue calculation in Numpy
我想要一种向量化特征值计算的numpy-sh方式,这样我就可以为其输入矩阵矩阵,并且它将返回各个特征值的矩阵。
例如,在下面的代码中,B是由4个3x3矩阵A的副本组成的块6x6矩阵。
C是我希望看到的输出,即维度(2,2,3)的数组(因为A具有3个特征值)。
这当然是一个非常简化的示例,在一般情况下,矩阵A可以具有任何大小(尽管它们仍然是正方形),矩阵B不一定由A的副本形成,而是不同的A1,A2,等等(大小相同,但包含不同元素)。
1 2 3 4 5 6 7
| import numpy as np
A = np.array([[0, 1, 0],
[0, 2, 0],
[0, 0, 3]])
B = np.bmat([[A, A], [A,A]])
C = np.array([[np.linalg.eigvals(B[0:3,0:3]),np.linalg.eigvals(B[0:3,3:6])],
[np.linalg.eigvals(B[3:6,0:3]),np.linalg.eigvals(B[3:6,3:6])]]) |
编辑:如果您使用的是numpy> = 1.8.0版本,则np.linalg.eigvals在所处理的任何数组的最后两个维度上运行,因此,如果将输入重塑为(n_subarrays, nrows, ncols)数组,则只需调用一次eigvals:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import numpy as np
A = np.array([[0, 1, 0],
[0, 2, 0],
[0, 0, 3]])
# the input needs to be an array, since matrices can only be 2D.
B = np.repeat(A[np.newaxis,...], 4, 0)
# for arbitrary input arrays you could do something like:
# B = np.vstack(a[np.newaxis,...] for a in input_arrays)
# but for this to work it will be necessary for each element in
# 'input_arrays' to have the same shape
# eigvals will operate over the last two dimensions of the array and return
# a (4, 3) array of eigenvalues
C = np.linalg.eigvals(B)
# reshape this output so that it matches your original example
C.shape = (2, 2, 3) |
如果输入数组的维数都不相同,例如input_arrays[0].shape == (2, 2),input_arrays[1].shape == (3, 3)等。那么您只能跨尺寸匹配的子集对计算进行矢量化。
如果您使用的是numpy的旧版本,那么不幸的是,我认为没有任何方法可以向量化多个输入数组上特征值的计算-您只需要在Python中循环输入即可。<铅>
- 您正在使用哪个版本的numpy? C = np.linalg.eigvals(B)给出LinAlgError: 3-dimensional array given. Array must be two-dimensional 。 eigval给出一个平方矩阵的值。
-
您是对的-它必须是一个相对较新的功能。我正在使用1.9.0.dev-f665c61起作用,但是1.7.1失败并出现该LinAlgError异常。
-
该功能已添加到1.8.0rc2中。这是提交:github.com/numpy/numpy/commit/…
-
那么,对于numpy 1.7有什么解决方案吗?
-
@AndreiBerceanu不幸的是,我认为没有任何方法可以向量化1.7中的特征值分解。我认为您要么必须升级到numpy的较新版本,要么就只能循环遍历您的输入数组并在每个数组上调用eigvals。您有多少个输入数组,它们有多大?
-
如果所有子矩阵均为3x3(或2x2),则可以对代数解进行矢量化处理。
-
我的矩阵" B"的尺寸为(6,6,512,512),即由我想对角化的512 ** 2 6x6矩阵形成。
-
至于升级numpy,我很乐意这样做,但无法找到v1.8的任何ubuntu二进制文件或有关如何从源代码进行实现的切实可行的说明。
-
与512x512矩阵的实际处理时间相比,36次迭代的开销可以忽略不计:使用几个嵌套的for循环,或使用np.vectorize。如果您使用的是512x512x6x6,则需要切换到1.8。
-
但这实际上是我所拥有的。 A是6x6,B是512x512xA,所以我需要调用np.eigenvalues 512 ** 2次。
-
在ubuntu上安装numpy的主要困难是确保已安装所有依赖项。我已经安装了1.7,因此直接升级到1.9。
您可以做这样的事情
1 2 3
| C = np.array([[np.linalg.eigvals(B[i:i+3, j:j+3])
for i in xrange(0, B.shape[0], 3)]
for j in xrange(0, B.shape[1], 3)]) |
也许更好的方法是使用https://stackoverflow.com/a/5078155/1352250中的block_view函数:
1 2
| B_blocks = block_view(B)
C = np.array([[np.linalg.eigvals(m) for m in v] for v in B_blocks]) |
更新
ali_m指出,此方法是语法糖的一种形式,不会减少因多次调用eigvals而引起的开销。如果要应用的每个矩阵的开销都很大,那么此开销应该很小,但对于OP感兴趣的6x6矩阵,它并不是微不足道的(请参阅下面的注释;根据ali_m,可能是三分之一)我上面给出的版本与他发布的使用Numpy> = 1.8.0的版本之间的差异。
-
从语法上讲这更好一些,但是您并没有真正向量化特征值分解-您仍然需要在每个子矩阵上分别调用np.linalg.eigvals
-
我不确定你是什么意思。我认为除了实际计算每个块的特征值外,没有其他方法可以获取矩阵的每个块的特征值-不管这是通过内置函数在后台完成的。我认为不存在这样的Numpy内置函数,因此以上是实现它的一种方法。请注意,实际上没有任何性能问题,因为切片和block_view的跨步技巧不会深度复制子矩阵。
-
np.linalg.eigvals是内部调用dgeev LAPACK函数的package器,该函数实际上在输入数组的(N, N) 2D切片上运行。但是,在C中循环遍历M切片具有显着的性能优势,这是在调用(M ,N, N)数组(至少使用numpy> = 1.8)时发生的情况,而不是像示例中那样使用Python循环。"向量化"通常仅是指将循环下推到诸如C或Fortran之类的低级语言的情况,而不是指诸如Python之类的高级语言,它们会带来更多的开销。
-
这是真的?开销有多少?我没有Numpy足够新的版本来测试这一点。
-
开销的重要性将取决于要循环的矩阵与它们之间的比例。如果您有少量的大型矩阵,那么您将花费更多的时间来计算特征值而不是循环,因此矢量化的好处将是微不足道的。但是,如果您有大量的小型矩阵,则可能会有更大的优势-对于(500, 6, 6)随机数组,循环遍历每个(6, 6)子矩阵并在其上调用eigvals所花费的时间大约是调用eigvals所花费时间的两倍。在整个数组上。
-
是的,这就是我的意思-我想知道OP对6x6矩阵感兴趣的开销有多大。当然,这取决于大小。有两个因数确实很重要,但是如果无法更新到较新的Numpy,我认为没有其他选择(除非编写自己的C模块)。
-
实际上,像OP一样,对于(512**2, 6, 6)数组,它更像是3的因数。