newton-raphson方法的python实现中的奇异矩阵

时间:2016-09-10 20:12:34

标签: python python-2.7 numpy numerical-methods newtons-method

我在python中编写了一个代码,它实现了Newton-Raphson方法来解决多个非线性方程。

我所采取的具体问题来自 Mark Newman's - 计算物理,练习6.17非线性电路

import numpy as np
from numpy.linalg import solve, norm
from math import e

#DATA
vp= 5. #V_plus in volts
r1, r2, r3, r4 = 1., 4., 3., 2. #k-ohm resistances
i = 3. #A constant originally in nA
vt = 0.05 #V_t in volts

def f(x):
'''
evaluates f(x) for where x is a 2-dim vector of voltage v1 and v2
'''
    v1, v2 = x[0], x[1]
    y = np.array([(v1-vp)/r1 + v1/r2 + i*(e**((v1-v2)/vt)-1), (vp-v2)/r3 -    v2/r4 + i*(e**((v1-v2)/vt)-1)])
    print y
    return y

def gradf(x):
'''
n-Derivative of f(x) where x is a vector of n-dimensions
'''
    v1, v2 = x[0], x[1]
    m = np.array([[1./r1 + 1./r2 + (i/vt)*e**((v1-v2)/vt), (i/vt)*e**((v1-v2)/vt)],\
    [(-i/vt)*e**((v1-v2)/vt), -1*(1./r3 +1./r4 +(i/vt)*e**((v1-v2)/vt))]], dtype = np.float64)#the matrix for the 'grad' f function
    print m
    return m

def cls_newton(x):
'''
Classroom implementation of the newton raphson method
'''
    v1, v2 = x[0], x[1]
    f_v1 = 1./r1 + 1./r2 + (i/vt)*e**((v1-v2)/vt)
    f_v2 = (-i/vt)*e**((v1-v2)/vt)
    g_v1 = (i/vt)*e**((v1-v2)/vt)
    g_v2 = -1*(1./r3 +1./r4 +(i/vt)*e**((v1-v2)/vt))

    f = (v1-vp)/r1 + v1/r2 + i*(e**((v1-v2)/vt)-1)
    g = (vp-v2)/r3 - v2/r4 + i*(e**((v1-v2)/vt)-1)

    print f
    print g
    print f_v2, g_v1, g_v1, f_v1  
    v1n = v1 - (f*g_v2 - g*f_v2)/(f_v1*g_v2 - g_v1*f_v2)
    v2n = v2 - (g*f_v1 - f*g_v1)/(f_v1*g_v2 - g_v1*f_v2)
    print v1n
    print v2n
    return np.array([v1n,v2n])

x1 = np.array([4., 5.]) #initial guess of roots are 4. and 5. volts
error = 1e-6 # permissible error
i = 0 # iteration counter

while norm(x1)>error and i < 50:
    delta = solve(gradf(x1), f(x1))
    x2 = x1 - delta
    print x2
    print 'x1 = {0}, x2 = {1}'.format(x1, x2)# test line
    x1 = x2
    print x1
    i+=1 

rt = x1 # estimated root of the equation

print 'The root of the equation is ' + str(rt) + '\n' + 'f(root) = ' +   str(f(rt))
print 'No. of iterations: ' + str(i)

在这段代码中,我已经为多个根的方法的两个不同实现编写了函数。

我在这个程序中使用的那个是我在 gradf(x)之间解决方程的那个(它产生 雅可比矩阵 )和 f(x )(这给了我一个带有我用Kirchoff定律得到的方程的向量)。

它的作用类似于 gradf(x).delta = f(x) 所以我们使用solve()函数找到delta 然后我们从x1(我们的初始v1和v2)中减去delta来找到x2

我对矩阵有问题,当我在Ipython中调用函数gradf([4.,5。])时,它给了我一个像

这样的矩阵
array([[  1.25000004e+00,   4.12230724e-08],
   [ -4.12230724e-08,  -8.33333375e-01]])

但在程序正常运行期间打印时的相同矩阵类似于

[[ 1.25        0.        ]
[ 0.         -0.83333333]]

无论v1和v2(或x1)的初始猜测如何,我在第一次迭代中都得到了这个矩阵。下一次迭代给我一个错误,如

LinAlgError: Singular matrix .

我不认为这是由于Python中的四舍五入,因为当我单独打印矩阵中的第一个数组元素的值(比如运行脚本时),它给了我一个零应该给出像4.12230724e-08这样的东西。

课堂实施或cls_newton(x)简化了方程式并直接给了我x2似乎做了同样的事情,但我无法说明原因,它给了我一个通过Ipython和执行期间的不同答案给出了不同的答案。

另外,当我写下f_v1时,我指的是f相对于v1和g_v2的偏导数,是g相对于v2的偏导数等等。

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我知道这是一个很晚的回应。如果您仍然对获得答案感兴趣,那么我认为这段代码应该可以解决问题。它为问题的最后一部分提供了正确的答案。如果您有任何疑问,请告诉我。

import numpy as np

Vp = 5 #V
R1 = 1e3 # ohms
R2 = 4e3 # ohms
R3 = 3e3 # ohms
R4 = 2e3 # ohms
I0 = 3e-9 # A
VT = 0.05 # V

tol = 1e-6

def f1(V):
    v1 = V[0]; v2 = V[1]
    return( (v1-Vp)/R1 + v1/R2 + I0*(np.exp((v1-v2)/VT)-1) )

def f2(V):
    v1 = V[0]; v2 = V[1]
    return( -(v2-Vp)/R3 - v2/R4 + I0*(np.exp((v1-v2)/VT)-1) )

def j11(V):
    v1 = V[0]; v2 = V[1]
    return 1/R1 + 1/R2 + I0/VT * np.exp((v1-v2)/VT)

def j12(V):
    v1 = V[0]; v2 = V[1]
    return -I0/VT * np.exp((v1-v2)/VT)

def j21(V):
    v1 = V[0]; v2 = V[1]
    return I0/VT * np.exp((v1-v2)/VT)

def j22(V):
    v1 = V[0]; v2 = V[1]
    return -1/R3 - 1/R4 - I0/VT * np.exp((v1-v2)/VT)

# initial guesses
v1 = 0.5
v2 = 0.5

V = np.array( [v1,v2] )
F = np.array( [ f1( V ) , f2( V ) ] )
J = np.array( [ [ j11( V ) , j12( V ) ] , [ j21( V ) , j22( V )] ] )
DV = np.dot( np.linalg.inv(J) , F )
estimate = V - DV
err = np.abs(estimate - V)
while ( err > tol ).any():
    F = np.array( [ f1(estimate) , f2(estimate) ] )
    J = np.array( [ [ j11(estimate) , j12(estimate) ] , [ j21(estimate) , j22(estimate) ] ] )
    DV = np.dot( np.linalg.inv(J) , F ) # f(x)/f'(x)
    new_estimate = estimate - DV
    err = np.abs(new_estimate - estimate)
    estimate = new_estimate

print("V1={:.4E}V\tV2={:.4E}V".format(*new_estimate))
print("Voltage across forward biased diode: {:.4E}V".format(new_estimate[0]-new_estimate[1]))