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?
Jawaban:
Lebih dari komentar panjang daripada yang lain:
Lihatlah kode untuk selisih perkiraan SUNDIALS 'untuk mendapatkan ide yang lebih baik dari apa yang harus Anda lakukan dalam implementasi. Ascher dan Petzold adalah buku yang bagus untuk memulai, tetapi SUNDIALS sebenarnya digunakan dalam pekerjaan produksi, dan karenanya telah diuji lebih baik. (Juga, SUNDIALS terkait dengan DASPK, yang digarap Petzold.)
Secara empiris, perkiraan Jacobian dapat menyebabkan kegagalan konvergensi dalam metode Newton. Saya tidak tahu bahwa saya akan menganggap mereka sebagai "ketidakstabilan"; dalam beberapa kasus, tidak mungkin untuk mencapai toleransi kesalahan yang diinginkan dalam kriteria terminasi. Dalam kasus lain, itu bisa bermanifestasi sebagai ketidakstabilan. Saya hampir yakin ada hasil yang lebih kuantitatif tentang fenomena ini dalam buku metode numerik Higham, atau diskusi Hairer dan Wanner tentang metode-W.
Tergantung di mana Anda pikir kesalahannya. Jika Anda sangat percaya diri dalam penerapan Euler mundur Anda, saya tidak akan mulai dari sana. Pengalaman telah membuat saya paranoid dalam implementasi metode numerik saya, jadi jika itu saya, saya akan mulai dengan mengkodekan beberapa masalah tes yang sangat mendasar (beberapa masalah linear kaku dan kaku, persamaan panas dengan perkiraan perbedaan hingga terpusat, hal-hal seperti itu) dan saya akan menggunakan metode solusi buatan untuk meyakinkan diri saya bahwa saya tahu akan menjadi apa solusinya, dan apa yang harus saya bandingkan.
Namun, Anda sudah melakukan itu:
Itu akan menjadi hal berikutnya yang akan saya uji: gunakan Jacobian analitik. Setelah itu, Anda mungkin juga melihat nilai eigen ekstrem dari perbedaan terbatas Anda Jacobian jika Anda berada di wilayah yang tidak stabil dari Euler terbelakang. Melihat nilai eigen ekstrem dari Jacobian analitis Anda sebagai dasar perbandingan mungkin memberi Anda wawasan. Dengan asumsi mereka semua memeriksa, masalahnya mungkin ada di Newton.
sumber