6

我正在将一个在 Matlab 中工作的算法移植到 numpy 并观察到一个奇怪的行为。相关的代码段是

P = eye(4)*1e20;
A = [1 -0.015 -0.025 -0.035; 0.015 1 0.035 -0.025; 0.025 -0.035 1 0.015; 0.035 0.025 -0.015 1];
V1 = A*(P*A')
V2 = (A*P)*A'

这段代码,当我用 Matlab 运行时,提供了以下矩阵:

V1 = 1.0021e+20            0  -8.0000e+00            0
              0   1.0021e+20            0            0
    -8.0000e+00            0   1.0021e+20            0
              0            0            0   1.0021e+20


V2 = 1.0021e+20            0  -8.0000e+00            0
              0   1.0021e+20            0            0
    -8.0000e+00            0   1.0021e+20            0
              0            0            0   1.0021e+20

请注意,正如预期的那样,V1 和 V2 是相同的。

当相同的代码在 Octave 中运行时,它提供:

V1 = 1.0021e+20   4.6172e+01  -1.3800e+02   1.8250e+02
    -4.6172e+01   1.0021e+20  -1.8258e+02  -1.3800e+02
     1.3801e+02   1.8239e+02   1.0021e+20  -4.6125e+01
    -1.8250e+02   1.3800e+02   4.6125e+01   1.0021e+20

V2 = 1.0021e+20  -4.6172e+01   1.3801e+02  -1.8250e+02
     4.6172e+01   1.0021e+20   1.8239e+02   1.3800e+02
    -1.3800e+02  -1.8258e+02   1.0021e+20   4.6125e+01
     1.8250e+02  -1.3800e+02  -4.6125e+01   1.0021e+20

在 numpy 中,段变为

from numpy import array, dot, eye
A = numpy.array([[1, -0.015, -0.025, -0.035],[0.015, 1, 0.035, -0.025],[0.025, -0.035, 1, 0.015],[0.035, 0.025, -0.015, 1]])
P = numpy.eye(4)*1e20
print numpy.dot(A,numpy.dot(P,A.transpose()))
print numpy.dot(numpy.dot(A,P),A.transpose())

哪个输出

[[  1.00207500e+20   4.61718750e+01  -1.37996094e+02   1.82500000e+02]
 [ -4.61718750e+01   1.00207500e+20  -1.82582031e+02  -1.38000000e+02]
 [  1.38011719e+02   1.82386719e+02   1.00207500e+20  -4.61250000e+01]
 [ -1.82500000e+02   1.38000000e+02   4.61250000e+01   1.00207500e+20]]
[[  1.00207500e+20  -4.61718750e+01   1.38011719e+02  -1.82500000e+02]
 [  4.61718750e+01   1.00207500e+20   1.82386719e+02   1.38000000e+02]
 [ -1.37996094e+02  -1.82582031e+02   1.00207500e+20   4.61250000e+01]
 [  1.82500000e+02  -1.38000000e+02  -4.61250000e+01   1.00207500e+20]]

因此,Octave 和 numpy 都提供了相同的答案,但它与 Matlab 的答案非常不同。第一点是 V1 != V2,这似乎不对。另一点是,虽然非对角元素比对角元素小很多数量级,但这似乎在我的算法中引起了一些问题。

有谁知道 numpy 和 Octave 的行为方式吗?

4

3 回答 3

6

他们在内部使用双精度,只有大约 15 位精度。您的数学运算可能会超过此值,这会导致数学上错误的结果。

值得一读: http: //floating-point-gui.de/

编辑:从我收集到的文档中,Numpy 没有更高的精度。SymPy似乎可以为您提供所需的精度- 如果该库也适合您。

于 2012-09-07T22:16:26.593 回答
4

不管它值多少钱,我在 64 位系统上得到与 matlab 相同的结果:

[[  1.00207500e+20   0.00000000e+00  -8.00000000e+00   0.00000000e+00]
 [  0.00000000e+00   1.00207500e+20   0.00000000e+00   0.00000000e+00]
 [ -8.00000000e+00   0.00000000e+00   1.00207500e+20   0.00000000e+00]
 [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00207500e+20]]
[[  1.00207500e+20   0.00000000e+00  -8.00000000e+00   0.00000000e+00]
 [  0.00000000e+00   1.00207500e+20   0.00000000e+00   0.00000000e+00]
 [ -8.00000000e+00   0.00000000e+00   1.00207500e+20   0.00000000e+00]
 [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00207500e+20]]
[[  1.00207500e+20   0.00000000e+00  -8.00000000e+00   0.00000000e+00]

如果您使用的是 32 位系统(或者您在 64 位系统上安装了 32 位版本的 python 和 numpy),您将遇到精度问题,并得到不同的答案,如 @Lucero 所述以下。在这种情况下,您可以尝试显式指定 64 位浮点数(但操作会更慢)。例如,尝试使用np.array(..., dtype=np.float64).

如果您认为需要额外的精度,可以使用np.longdouble(与np.float12864 位系统和np.float9632 位系统相同),但这可能并非在所有平台上都受支持,并且许多线性代数函数会将事物截断回本机精度。

另外,您使用的是什么 BLAS 库?numpy 和 octave 结果可能相同,因为它们使用相同的 BLAS 库。

最后,您可以将 numpy 代码简化为:

import numpy as np
A = np.array([[1,     -0.015, -0.025, -0.035],
              [0.015, 1,      0.035,  -0.025],
              [0.025, -0.035, 1,      0.015],
              [0.035, 0.025,  -0.015, 1]])
P = np.eye(4)*1e20
print A.dot(P.dot(A.T))
print A.dot(P).dot(A.T)
于 2012-09-08T00:08:19.977 回答
0

np.einsum更接近 MATLAB

In [1299]: print(np.einsum('ij,jk,lk',A,P,A))
[[  1.00207500e+20   0.00000000e+00  -5.07812500e-02   0.00000000e+00]
 [  0.00000000e+00   1.00207500e+20   5.46875000e-02   0.00000000e+00]
 [ -5.46875000e-02   5.46875000e-02   1.00207500e+20   0.00000000e+00]
 [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00207500e+20]]

第 2 行和第 2 列中的非对角项不同,但在其他地方具有相同的 0。

使用双点,P.dot(A.T)添加产品时会产生舍入误差。那些传播到下一个doteinsum生成所有产品,只需一个总和。我怀疑 MATLAB 解释器会识别这种情况,并执行旨在最小化舍入误差的特殊计算。

Numpy Matrix Multiplication U*B*UT Results in Non-symmetric Matrix - 是一个更新的问题,具有相同的解释。

于 2015-08-07T15:47:50.267 回答