파이썬 3에서 numpy를 사용하여 Back-Euler 솔버를 구현했습니다. 나 자신의 편의와 운동을 위해 그래디언트의 유한 차분 근사값을 계산하는 작은 함수를 작성하여 항상 야곱을 분석적으로 결정할 필요가 없도록했습니다.
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
나는 진자에 대한 다변량 함수를 취하고 상징적 인 Jacobian을 점의 범위에 대해 수치 적으로 결정된 기울기와 비교 하여이 기능을 테스트했습니다. 테스트 결과에 만족했으며 오류는 1e-10 정도였습니다. 근사한 Jacobian을 사용하여 진자의 ODE를 풀었을 때 매우 효과적이었습니다. 둘 사이의 차이를 감지 할 수 없었습니다.
그런 다음 다음 PDE (1D의 피셔 방정식)로 테스트 해 보았습니다.
유한 차이 이산화를 사용합니다.
이제 Newton의 방법이 첫 번째 단계에서 폭발합니다.
/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 값에 대해 발생하지만, 이상하게도 Courant-Friedrichs–Lewy 조건이 충족되지 않도록 PDE 공간 단계 크기 및 시간 단계 크기가 설정된 경우에만 발생합니다. 그렇지 않으면 작동합니다. (이것은 앞으로 Euler로 해결하면 기대되는 행동입니다!)
완성도를 높이기 위해 다음은 Newton Method의 기능입니다.
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입니다.
Jacobian의 함수를 사용하여 테스트하고 안정적인 결과를 얻었으므로 이전 Euler 구현이 순서대로 작동한다고 확신합니다.
디버거에서 newton ()이 오류가 발생하기 전에 35 개의 반복을 관리한다는 것을 알 수 있습니다. 이 숫자는 내가 시도한 모든 eps에 대해 동일하게 유지됩니다.
추가 관찰 : FDA로 그라디언트와 초기 조건을 입력으로 사용하여 함수를 계산하고 엡실론의 크기를 변경하면서 두 가지를 비교하면 엡실론이 줄어들면 오류가 커집니다. 엡실론이 줄어들면 처음에는 커질 것으로 예상하고 작아지고 다시 커집니다. 따라서 Jacobian을 구현할 때 발생하는 오류는 합리적인 가정이지만 그럴 경우 미묘하기 때문에 볼 수 없습니다. 편집 : 중심 차이 대신 앞으로 사용하도록 jacobian ()을 수정했으며 이제 예상되는 오류가 발생하는 것을 관찰했습니다. 그러나 newton ()은 여전히 수렴하지 않습니다. 뉴턴 반복에서 dx를 관찰하면, 나는 그것이 자라고, 변동조차도 없다는 것을 알 수 있습니다. 매 단계마다 거의 두 배가 (요인 1.9), 요인이 점차 커집니다.
Ascher와 Petzold는 Jacobian의 차이 근사치가 항상 제대로 작동하지는 않는다고 언급합니다. 유한 한 차이가있는 근사한 야 코비안이 뉴턴의 방법에서 불안정성을 야기 할 수 있습니까? 아니면 원인이 다른 곳입니까? 이 문제에 어떻게 접근 할 수 있습니까?