我正在寻找用于解决复杂 floats 类型(又称为np.complex64
)的线性方程组的最准确的类似于C的实现。正要进行LU分解。我想先用numpy实现它,看看一切顺利,然后将实现转换为C。
这是我目前得到的:
import scipy.linalg as la
import numpy as np
def lu_factor(A):
L, U = np.empty_like(A), np.empty_like(A)
n = A.shape[0]
for k in range(n):
L[k, k] = 1
U[k, k] = A[k, k] - L[k, :k] @ U[:k, k]
for j in range(k + 1, n):
U[k, j] = A[k, j] - L[k, :k] @ U[:k, j]
for i in range(k + 1, n):
L[i, k] = (A[i, k] - L[i, :k] @ U[:k, k]) / U[k, k]
return L, U
def forward_sub(L, b):
x = np.empty_like(b)
for i in range(b.size):
x[i] = (b[i] - L[i, :i] @ x[:i]) / L[i, i]
return x
def backward_sub(U, b):
x = np.empty_like(b)
for i in reversed(range(b.size)):
x[i] = (b[i] - U[i, i + 1:] @ x[i + 1:]) / U[i, i]
return x
def lu_solve(A, b):
L, U = lu_factor(A)
return backward_sub(U, forward_sub(L, b))
b = np.array([6 + 1j, -4 + 2j, 27 + 3j], dtype=np.complex128)
A = np.array([
[1 + 4j, 1 + 5j, 1 + 6j],
[0 + 7j, 2 + 8j, 5 + 9j],
[2 + 1j, 5 + 2j, -1 + 3j],
], dtype=np.complex128)
x_expected = la.lu_solve(la.lu_factor(A), b)
x = lu_solve(A, b)
np.testing.assert_allclose(x_expected, x)
A
和b
是随机选择的。注意-它们的类型为np.complex128
(复数双精度),“天真的”实现的结果与scipy的实现足够接近。
将类型更改为np.complex64
(复杂浮点数)时,我们得到:
Mismatch: 66.7%
Max absolute difference: 1.0612305e-06
Max relative difference: 6.8692873e-07
x: array([ 1.387071-0.680237j, 3.673277+1.09019j , -3.683192-1.225474j], dtype=complex64)
y: array([ 1.387072-0.680236j, 3.673277+1.090189j, -3.683192-1.225474j], dtype=complex64)
e-06
对我来说似乎很高。除了“双精度浮点精度”之外,还有很好的解释吗?我到底不是什么numpy / BLAS?可以使用基本操作复制吗?
注意:性能对我来说不是问题,我只关心精度。
答案 0 :(得分:1)
编码鲁棒且准确的数值线性代数过程可能非常棘手。 我的第一个建议是使用像Lapack
这样的成熟库如果您要编写自己的LU,则绝对必须至少使用"partial pivoting"来建立可靠的过程,否则:
如果矩阵中没有适当的顺序或排列,则 因式分解可能无法实现。
注意: wikepedia建议确实可以作为一个很好的起点的实现。您只需要修改它即可支持复数而不是双精度数。
一旦解决了系统问题,就可以通过执行iterative refinement的一两个步骤来提高精度
Compute the residual: r = b − A.x
Solve the system: A.d = r
Add the correction: x = x + d
只需使用您的LU代码应用前面的过程即可解决A.d = r系统。如果您要追求高精度,请不要忽略最后一步,通常在实践中效果很好。