Я реалізував зворотний рішень-ейлер у python 3 (використовуючи numpy). Для власної зручності і як вправи я також написав невелику функцію, яка обчислює наближення градієнта кінцевою різницею, так що мені не завжди доведеться аналітично визначати якобіанські (якщо це навіть можливо!).
Використовуючи описи, наведені в Ascher і Petzold 1998 , я написав цю функцію, яка визначає градієнт у заданій точці 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
Я перевірив цю функцію, взявши багатоваріантну функцію для маятника і порівняв символічний якобіанський з числово визначеним градієнтом для діапазону точок. Я був задоволений результатами тесту, помилка склала близько 1е-10. Коли я вирішив ODE для маятника за допомогою апроксимованого якобіанського, він також спрацював дуже добре; Я не міг виявити різниці між ними.
Потім я спробував тестувати його за допомогою наступного PDE (рівняння Фішера в 1D):
з використанням обмеженої різниці дискретизації.
Тепер метод Ньютона вибухає в перший часовий крок:
/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
Це трапляється для різних значень eps, але, як не дивно, лише тоді, коли розмір просторового кроку PDE та розмір кроку часу встановлені таким чином, що умова Курант – Фрідріх – Льюї не виконується. Інакше це працює. (Така поведінка ви очікуєте, якщо вирішувати з форвардом Ейлера!)
Для повноти ось функція методу Ньютона:
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 - scipy.linalg.solve.)
Я впевнений, що моя відстала реалізація Ейлера в порядку, тому що я протестував її за допомогою функції для якобіанців і отримав стабільні результати.
Я можу побачити на відладчику, що Newton () управляє 35 ітераціями до появи помилки. Це число залишається однаковим для кожного eps, який я пробував.
Додаткове спостереження: коли я обчислюю градієнт з FDA і функцією, використовуючи початкову умову в якості вхідного даних, і порівнюю два, змінюючи розмір epsilon, помилка зростає, коли epsilon скорочується. Я б очікував, що спочатку він буде великим, потім стане меншим, потім знову більшим, коли епсилон скорочується. Тож помилка в моєму здійсненні якобіанців є розумним припущенням, але якщо так, то воно настільки тонке, що я не в змозі його побачити. EDIT: Я змінив jacobian (), щоб використовувати вперед замість центральних відмінностей, і тепер спостерігаю очікуваний розвиток помилки. Однак, newton () все ще не вдається конвергуватися. Спостерігаючи dx в ітерації Ньютона, я бачу, що він тільки зростає, навіть немає коливань: він майже подвоюється (фактор 1,9) з кожним кроком, при цьому коефіцієнт стає прогресивно більшим.
Ашер і Петцольд згадують, що різницькі наближення якобіанців не завжди працюють добре. Чи може наближений якобійський з скінченними відмінностями викликати нестабільність методу Ньютона? Або причина десь ще? Як інакше я можу підійти до цієї проблеми?