Matlab / Octave / Numpy数值差异

时间:2012-09-07 22:08:57

标签: matlab numpy octave

我正在移植一个在Matlab中工作的算法来调整并发现一个奇怪的行为。相关的代码段是

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这样的行为?

3 个答案:

答案 0 :(得分:6)

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

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

编辑:docs我收集到Numpy没有更高的精确度。似乎SymPy虽然may give you the needed precision - 如果该库也适合您。

答案 1 :(得分: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(在64位系统上与np.float128相同,在32位上使用np.float96),但这可能不是在所有平台上都支持,许多线性代数函数会将事物截断回本机精度。

另外,您使用的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)

答案 2 :(得分: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中的非对角线项不同,但在其他地方具有相同的0。

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

Numpy Matrix Multiplication U*B*U.T Results in Non-symmetric Matrix - 是一个更近期的问题,也有相同的解释。