Saya telah menerapkan pemecah Euler mundur dalam python 3 (menggunakan numpy). Untuk kenyamanan saya sendiri dan sebagai latihan, saya juga menulis fungsi kecil yang menghitung perkiraan perbedaan hingga dari gradien sehingga saya tidak selalu harus menentukan Jacobian secara analitis (jika itu mungkin!).
Menggunakan deskripsi yang disediakan dalam Ascher dan Petzold 1998 , saya menulis fungsi ini yang menentukan gradien pada titik x:
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
Saya menguji fungsi ini dengan mengambil fungsi multivariat untuk pendulum dan membandingkan Jacobian simbolis dengan gradien yang ditentukan secara numerik untuk berbagai titik. Saya senang dengan hasil tes, kesalahannya sekitar 1e-10. Ketika saya memecahkan ODE untuk pendulum menggunakan Jacobian yang didekati, itu juga bekerja dengan sangat baik; Saya tidak bisa mendeteksi perbedaan antara keduanya.
Kemudian saya mencoba mengujinya dengan PDE berikut (persamaan Fisher dalam 1D):
menggunakan diskritisasi perbedaan hingga.
Sekarang metode Newton meledak di catatan waktu pertama:
/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
Ini terjadi untuk berbagai nilai eps, tetapi anehnya, hanya ketika ukuran langkah spasial PDE dan ukuran langkah waktu ditetapkan sehingga kondisi Courant-Friedrichs-Lewy tidak terpenuhi. Kalau tidak berhasil. (Ini adalah perilaku yang Anda harapkan jika menyelesaikan dengan meneruskan Euler!)
Untuk kelengkapan, berikut adalah fungsi untuk Metode Newton:
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
(Fungsi la.solve adalah scipy.linalg.solve.)
Saya yakin bahwa implementasi Euler terbelakang saya adalah dalam rangka, karena saya telah mengujinya menggunakan fungsi untuk Jacobian dan mendapatkan hasil yang stabil.
Saya dapat melihat di debugger bahwa newton () mengelola 35 iterasi sebelum kesalahan terjadi. Jumlah ini tetap sama untuk setiap eps yang saya coba.
Pengamatan tambahan: ketika saya menghitung gradien dengan FDA dan fungsi menggunakan kondisi awal sebagai input dan membandingkan keduanya sambil memvariasikan ukuran epsilon, kesalahan tumbuh sebagai epsilon menyusut. Saya berharap itu menjadi besar pada awalnya, kemudian menjadi lebih kecil, kemudian lebih besar lagi saat epsilon menyusut. Jadi kesalahan dalam implementasi Jacobian saya adalah asumsi yang masuk akal, tetapi jika demikian, itu sangat halus sehingga saya tidak dapat melihatnya. EDIT: Saya memodifikasi jacobian () untuk menggunakan maju alih-alih perbedaan pusat, dan sekarang saya mengamati perkembangan kesalahan yang diharapkan. Namun, newton () masih gagal bertemu. Mengamati dx dalam iterasi Newton, saya melihat bahwa itu hanya tumbuh, bahkan tidak ada fluktuasi: hampir dua kali lipat (faktor 1.9) dengan setiap langkah, dengan faktor semakin besar semakin.
Ascher dan Petzold menyebutkan bahwa perbedaan perkiraan untuk Jacobian tidak selalu bekerja dengan baik. Bisakah seorang Jacobian yang didekati dengan perbedaan hingga menyebabkan ketidakstabilan dalam metode Newton? Atau penyebabnya di tempat lain? Bagaimana lagi saya bisa mendekati masalah ini?