处理numpy中矩阵乘法的舍入误差
Handling rounding errors in matrix multiplication in numpy
TL;DR: 状态转移矩阵的矩阵乘法应该保范,但是np.matmul
不保范。我怎样才能解决这个问题?有更好的 python 模块吗?
我有一个正确的状态转移矩阵 A,即 s(t)A(tau)=s(t+tau)
其中 s(t) 是一个总和为 1 的列矩阵。另外,我们知道 A 的每一行加起来也是 1。
我们知道A^n也是自然数中任意n的右状态转移矩阵
找到稳态分布的一种方法是在 n 趋于无穷大时计算 A^n。以下片段计算 A^(2^n):
def p_multiplier(A,n):
B=copy.deepcopy(A)
for i in range(n):
B=numpy.matmul(B,B)
return B[0]
这工作正常,但由于某种原因,行的总和开始不加到 1,即 numpy 开始泄漏 A[i] 的范数。
原因可能是舍入误差。一种快速解决方法可能是在每次迭代时强制保留每一行的范数:
def p_multiplier(A,n):
B=copy.deepcopy(A)
for i in range(n):
B=np.matmul(B,B)
for j in range(len(B)):
B[j]=B[j]/sum(B[j])
return B[0]
此修复的科学性如何? scipy 还是 mpmath 处理得更好?
编辑:为了完全满足 MWE,您可以对 A 使用以下状态转换矩阵:
A=np.asarray([[0.76554539,0.13929202,0,0,0.09516258],[0.04877058,0.76996018,0.18126925,0,0],[0,0.09516258,0.76554539,0.13929202,0],[0,0,0.13929202,0.67943873,0.18126925],[0.09516258,0,0,0.04877058,0.85606684]])
问题是因为操作数值不稳定。结果,它 迅速发散 (指数)到 0 或无穷大,即使 relatively-small 的 n
值如 70。您可以使用 对角化基于特征值的方法(有关更多信息,请参见here),它在数值上更加稳定。
def stable_multiplier(A, n):
d, P = numpy.linalg.eig(A)
return (P @ numpy.diag(d**n) @ numpy.linalg.inv(P))[0].real
丢失的(小数)位数在 O(log10(n))
中,因此误差与 n
呈线性关系。请注意,如果 n
很大,如 > 1000,那么您需要非常注意整个算法的数值稳定性,特别是如果它是一个迭代过程(具有 at of 迭代)。想了解更多请跟帖this and this.
TL;DR: 状态转移矩阵的矩阵乘法应该保范,但是np.matmul
不保范。我怎样才能解决这个问题?有更好的 python 模块吗?
我有一个正确的状态转移矩阵 A,即 s(t)A(tau)=s(t+tau)
其中 s(t) 是一个总和为 1 的列矩阵。另外,我们知道 A 的每一行加起来也是 1。
我们知道A^n也是自然数中任意n的右状态转移矩阵
找到稳态分布的一种方法是在 n 趋于无穷大时计算 A^n。以下片段计算 A^(2^n):
def p_multiplier(A,n):
B=copy.deepcopy(A)
for i in range(n):
B=numpy.matmul(B,B)
return B[0]
这工作正常,但由于某种原因,行的总和开始不加到 1,即 numpy 开始泄漏 A[i] 的范数。
原因可能是舍入误差。一种快速解决方法可能是在每次迭代时强制保留每一行的范数:
def p_multiplier(A,n):
B=copy.deepcopy(A)
for i in range(n):
B=np.matmul(B,B)
for j in range(len(B)):
B[j]=B[j]/sum(B[j])
return B[0]
此修复的科学性如何? scipy 还是 mpmath 处理得更好?
编辑:为了完全满足 MWE,您可以对 A 使用以下状态转换矩阵:
A=np.asarray([[0.76554539,0.13929202,0,0,0.09516258],[0.04877058,0.76996018,0.18126925,0,0],[0,0.09516258,0.76554539,0.13929202,0],[0,0,0.13929202,0.67943873,0.18126925],[0.09516258,0,0,0.04877058,0.85606684]])
问题是因为操作数值不稳定。结果,它 迅速发散 (指数)到 0 或无穷大,即使 relatively-small 的 n
值如 70。您可以使用 对角化基于特征值的方法(有关更多信息,请参见here),它在数值上更加稳定。
def stable_multiplier(A, n):
d, P = numpy.linalg.eig(A)
return (P @ numpy.diag(d**n) @ numpy.linalg.inv(P))[0].real
丢失的(小数)位数在 O(log10(n))
中,因此误差与 n
呈线性关系。请注意,如果 n
很大,如 > 1000,那么您需要非常注意整个算法的数值稳定性,特别是如果它是一个迭代过程(具有 at of 迭代)。想了解更多请跟帖this and this.