Python 3'e (numpy kullanarak) geriye doğru bir Euler çözücü uyguladım. Kendi rahatlığım için ve bir egzersiz olarak, degradenin sonlu fark yaklaşımını hesaplayan küçük bir işlev yazdım, böylece her zaman Jacobian'ı analitik olarak belirlemek zorunda değilim (mümkünse!).
Ascher ve Petzold 1998'de verilen açıklamaları kullanarak, verilen x noktasında gradyanı belirleyen bu fonksiyonu yazdım:
def jacobian(f,x,d=4):
'''computes the gradient (Jacobian) at a point for a multivariate function.
f: function for which the gradient is to be computed
x: position vector of the point for which the gradient is to be computed
d: parameter to determine perturbation value eps, where eps = 10^(-d).
See Ascher und Petzold 1998 p.54'''
x = x.astype(np.float64,copy=False)
n = np.size(x)
t = 1 # Placeholder for the time step
jac = np.zeros([n,n])
eps = 10**(-d)
for j in np.arange(0,n):
yhat = x.copy()
ytilde = x.copy()
yhat[j] = yhat[j]+eps
ytilde[j] = ytilde[j]-eps
jac[:,j] = 1/(2*eps)*(f(t,yhat)-f(t,ytilde))
return jac
Sarkaç için çok değişkenli bir fonksiyon alıp sembolik Jacobian'ı bir dizi nokta için sayısal olarak belirlenmiş gradyan ile karşılaştırarak bu fonksiyonu test ettim. Testin sonuçlarından memnun kaldım, hata 1e-10 civarındaydı. Sarkaç için ODE'yi yaklaşık Jacobian'ı kullanarak çözdüğümde de çok iyi çalıştı; İkisi arasında herhangi bir fark tespit edemedim.
Sonra aşağıdaki PDE (1D'de Fisher denklemi) ile test etmeyi denedim:
sonlu farklar ayrıklığı kullanarak.
Şimdi Newton'un yöntemi ilk zaman diliminde patlıyor:
/home/sfbosch/Fisher-Equation.py:40: RuntimeWarning: overflow encountered in multiply
du = (k/(h**2))*np.dot(K,u) + lmbda*(u*(C-u))
./newton.py:31: RuntimeWarning: invalid value encountered in subtract
jac[:,j] = 1/(2*eps)*(f(t,yhut)-f(t,yschlange))
Traceback (most recent call last):
File "/home/sfbosch/Fisher-Equation.py", line 104, in <module>
fisher1d(ts,dt,h,L,k,C,lmbda)
File "/home/sfbosch/Fisher-Equation.py", line 64, in fisher1d
t,xl = euler.implizit(fisherode,ts,u0,dt)
File "./euler.py", line 47, in implizit
yi = nt.newton(g,y,maxiter,tol,Jg)
File "./newton.py", line 54, in newton
dx = la.solve(A,b)
File "/usr/lib64/python3.3/site-packages/scipy/linalg/basic.py", line 73, in solve
a1, b1 = map(np.asarray_chkfinite,(a,b))
File "/usr/lib64/python3.3/site-packages/numpy/lib/function_base.py", line 613, in asarray_chkfinite
"array must not contain infs or NaNs")
ValueError: array must not contain infs or NaNs
Bu, çeşitli eps değerleri için olur, ancak garip bir şekilde, sadece PDE uzamsal adım boyutu ve zaman adımı boyutu ayarlandığında, Courant – Friedrichs – Lewy koşulu karşılanmaz. Aksi takdirde çalışır. (Bu, ileri Euler ile çözerseniz bekleyebileceğiniz davranıştır!)
Tamlık için, Newton Yönteminin işlevi şöyledir:
def newton(f,x0,maxiter=160,tol=1e-4,jac=jacobian):
'''Newton's Method.
f: function to be evaluated
x0: initial value for the iteration
maxiter: maximum number of iterations (default 160)
tol: error tolerance (default 1e-4)
jac: the gradient function (Jacobian) where jac(fun,x)'''
x = x0
err = tol + 1
k = 0
t = 1 # Placeholder for the time step
while err > tol and k < maxiter:
A = jac(f,x)
b = -f(t,x)
dx = la.solve(A,b)
x = x + dx
k = k + 1
err = np.linalg.norm(dx)
if k >= maxiter:
print("Maxiter reached. Result may be inaccurate.")
print("k = %d" % k)
return x
(La.solve işlevi scipy.linalg.solve şeklindedir.)
Geriye doğru Euler uygulamamın uygun olduğundan eminim, çünkü Jacobian için bir işlev kullanarak test ettim ve istikrarlı sonuçlar elde ettim.
Hata ayıklayıcıda newton () hata oluşmadan önce 35 yineleme yönetir görebilirsiniz. Bu sayı denediğim her eps için aynı kalıyor.
Ek bir gözlem: degradeyi FDA ve başlangıç koşulunu bir girdi olarak kullanan bir fonksiyonla hesapladığımda ve ikisini epsilon boyutunu değiştirirken karşılaştırdığımda, hata epsilon küçüldükçe büyür. İlk başta büyük olmasını beklerim, sonra küçülür, sonra tekrar epsilon küçülürken büyürüm. Bu yüzden Jacobian'ı uygulamamdaki bir hata makul bir varsayımdır, ancak eğer öyleyse, o kadar incedir ki göremiyorum. EDIT: jacobian () merkezi farklılıklar yerine ileri kullanmak için değiştirdim ve şimdi hata beklenen gelişme gözlemlemek. Ancak, newton () hala yakınsama yapamıyor. Newton yinelemesinde dx'i gözlemleyerek, sadece büyüdüğünü görüyorum, bir dalgalanma bile yok: her adımda neredeyse iki katına (faktör 1.9), faktör giderek büyüyor.
Ascher ve Petzold, Jacobian için fark yaklaşımlarının her zaman iyi çalışmadığını belirtiyor. Sonlu farklılıklara sahip yaklaşık bir Jacobian, Newton'un yönteminde kararsızlığa neden olabilir mi? Yoksa sebep başka bir yerde mi? Bu soruna başka nasıl yaklaşabilirim?