简体   繁体   English

Matlab / Octave / Numpy数值差异

[英]Matlab/Octave/Numpy numeric difference

I'm porting an algorithm that works in Matlab to numpy and observed a strange behavior. 我正在移植一个在Matlab中工作的算法,以便numpy并观察到一个奇怪的行为。 The relevant segment of code is 相关的代码段是

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'

This code, when I run with Matlab, provides the following matrices: 当我使用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

Note that V1 and V2 are the same, as expected. 请注意,V1和V2与预期相同。

When the same code runs in Octave, it provides: 当相同的代码在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

In numpy, the segment becomes 在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())

which outputs 哪个输出

[[  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]]

So, both Octave and numpy provides the same answer, but it's very different from Matlab's. 所以,Octave和numpy都提供了相同的答案,但它与Matlab的非常不同。 The first point is that V1 != V2, which doesn't seem right. 第一点是V1!= V2,这似乎不对。 The other point is that, although the non-diagonal elements are many orders of magnitude smaller than the diagonal ones, this seems to be causing some problem in my algorithm. 另一点是,尽管非对角线元素比对角线元素小许多个数量级,但这似乎在我的算法中引起了一些问题。

Does anyone knows way numpy and Octave behave this way? 有谁知道方式numpy和Octave这样做?

They use doubles internally, which have only about 15 digits precision. 它们在内部使用双精度,只有大约15位数的精度。 Your math operations likely exceed this, which causes the mathematically wrong results. 您的数学运算可能超过此值,这会导致数学上错误的结果。

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

Edit: From the docs I gather that there is no higher precision available for Numpy. 编辑:从我收集的文档中可以看出Numpy没有更高的精度。 It seems that SymPy though may give you the needed precision - if that library works for you as well. 似乎SymPy 可能会给你所需的精度 - 如果该库也适合你。

For whatever it's worth, I get identical results to matlab on a 64-bit system: 无论它值多少,我在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]

If you're using a 32-bit system (or you have a 32-bit version of python and numpy installed on a 64-bit system) you'll run into precision problems, and get a different answer, as noted by @Lucero below. 如果您使用的是32位系统(或者您在64位系统上安装了32位版本的python和numpy),那么您将遇到精确问题,并获得不同的答案,如@Lucero所述下面。 You might try explicitly specifying 64-bit floats in that case (the operations will be slower, though). 在这种情况下,您可能会尝试显式指定64位浮点数(但操作会更慢)。 For example, try using np.array(..., dtype=np.float64) . 例如,尝试使用np.array(..., dtype=np.float64)

If you think you need additional precison, you can use np.longdouble (Same as np.float128 on a 64-bit system and np.float96 on 32-bit), but this may not be supported on all platforms and many linear algebra functions will truncate things back to the native precision. 如果你认为你需要额外的精密度,你可以使用np.longdouble (同np.float128 64位系统和np.float96 32位),但是这可能不会在所有平台上和许多线性代数函数的支持将事物截断回本机精度。

Also, what BLAS library are you using? 另外,你使用什么BLAS库? The numpy and octave results are likely the same because they're using the same BLAS library. numpy和octave结果可能相同,因为它们使用相同的BLAS库。

Finally, you can simplify your numpy code down to: 最后,您可以将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)

np.einsum is a lot closer to the MATLAB 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]]

The off diagonal terms in row and column 2 are different, but it has the same 0s elsewhere. 行和列2中的非对角线项不同,但在其他地方具有相同的0。

With the double dot, P.dot(AT) creates rounding errors when it adds the products. 使用双点, P.dot(AT)在添加产品时会产生舍入误差。 Those propagate into the next dot . 那些传播到下一个dot einsum generates all the products, with just one summation. einsum生成所有产品,只需一次总结。 I suspect the MATLAB interpreter recognizes this situation, and performs a special calculation designed to minimize the rounding errors. 我怀疑MATLAB解释器能够识别这种情况,并执行一项特殊的计算,旨在最大限度地减少舍入误差。

Numpy Matrix Multiplication U*B*UT Results in Non-symmetric Matrix - is a more recent question with the same explanation. Numpy矩阵乘法U * B * UT结果在非对称矩阵中 - 是一个更近期的问题,具有相同的解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM