我在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的偏导数等等。
提前感谢您的帮助!
答案 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]))