TOMS748/toms748.py

142 lines
4 KiB
Python
Raw Normal View History

# Implementation of TOMS748
# https://na.math.kit.edu/alefeld/download/1995_Algorithm_748_Enclosing_Zeros_of_Continuous_Functions.pdf
from math import sin, fabs, cos
# finite differences 1st derivative given a, b, fa, fb
def fbracket1(a, b, fa, fb):
return (fb-fa)/(b-a)
# finite differences 2nd derivative given a, b, d, fa, fb, fd
def fbracket2(a, b, d, fa, fb, fd):
return (fbracket1(b, d, fb, fd) - fbracket1(a, b, fa, fb))/(d-a)
# standard bracketing routine
# returns ahat, bhat, d
# d is a point outside the new interval
def bracket(a, b, c, fa, fb, fc):
# print('bracket')
if fabs(fc) < 1e-12:
print("root found : %f ; f(root) = %f" % (c, fc))
if fa*fc < 0:
return (a, c, b, fa, fc, fb)
else:
return (c, b, a, fc, fb, fa)
# finds an approximate solution to the quadratic P(x) = fa + f[a,b]*(x-a) + f[a,b,d]*(x-a)(x-b)
# with f[a,b] = fbracket1(a,b) and f[a,b,d] = fbracket2(a,b,d)
def NewtonQuadratic(a, b, d, fa, fb, fd, k):
A = fbracket2(a,b,d,fa,fb,fd)
B = fbracket1(a,b,fa,fb)
if A == 0:
return a - fa/B
if A*fa > 0:
r = a
else:
r = b
for i in range(k):
r = r - B*r/(B + A*(2*r - a - b))
return r
# Inverse cubic interpolation evaluated at 0 (modified Aitken-Neville interpolation)
def ipzero(a, b, c, d, fa, fb, fc, fd):
Q11 = (c-d)*fc/(fd-fc)
Q21 = (b-c)*fb/(fc-fb)
Q31 = (a-b)*fa/(fb-fa)
D21 = (b-c)*fc/(fc-fb)
D31 = (a-b)*fb/(fb-fa)
Q22 = (D21-Q11)*fb/(fd-fb)
Q32 = (D31-Q21)*fa/(fc-fa)
D32 = (D31-Q21)*fc/(fc-fa)
Q33 = (D32-Q22)*fa/(fd-fa)
return a + Q31 + Q32 + Q33
def TOMS748_solve1(f, a, b):
mu = 0.5
fa, fb = f(a), f(b)
c = a - fa/fbracket1(a, b, fa, fb) # 4.1.1 secant method
fc = f(c)
a, b, d, fa, fb, fd = bracket(a, b, c, fa, fb, fc) # 4.1.2
e = d
fe = fd
# ---
for n in range(2,10): # 4.1.3
if n == 2 or (fa == fb or fb == fd or fd == fe):
c = NewtonQuadratic(a, b, d, fa, fb, fd, 2)
else:
c = ipzero(a, b, d, e, fa, fb, fd, fe)
if (c-a)*(c-b) >= 0:
c = NewtonQuadratic(a, b, d, fa, fb, fd, 2)
# ---
fc = f(c)
a, b, dbar, fa, fb, fdbar = bracket(a, b, c, fa, fb, fc) # 4.1.4
# ---
if fabs(fa) < fabs(fb): # 4.1.5
u = a
fu = fa
else:
u = b
fu = fb
# ---
c = u - 2*fu/fbracket1(a, b, fa, fb) # 4.1.6
# ---
if fabs(c - u) > 0.5*(b - a): # 4.1.7
c = 0.5*(b + a)
# ---
fc = f(c)
a, b, dhat, fa, fb, fdhat = bracket(a, b, c, fa, fb, fc) # 4.1.8
# ---
if b - a < mu*(b - a): # 4.1.9
d = dhat
e = dbar
fd = fdhat
fe = fdbar
else:
e = dhat
fe = fdhat
c = 0.5*(a+b)
fc = f(c)
a, b, d, fa, fb, fd = bracket(a, b, c, fa, fb, fc)
# TEST
f = lambda E : E - 0.5*sin(E) - 0.3
a, b, c, d = 0, 1, 0.3, 1.2
fa, fb, fc, fd = f(a), f(b), f(c), f(d)
print(fbracket1(a, b, fa, fb))
print(fbracket2(a, b, d, fa, fb, fd))
print(bracket(a, b, c, fa, fb, fc))
print(NewtonQuadratic(a, b, d, fa, fb, fd, 2))
print(ipzero(a, b, c, d, fa, fb, fc, fd))
# solution using bracket only :
print('bracket only')
for i in range(50):
c = (a+b)/2
fc = f(c)
a, b, d, fa, fb, fd = bracket(a, b, c, fa, fb, fc)
if fabs(b-a) < 1e-12:
break
print(i)
print((a,b), (a+b)/2, b-a)
print('Newton with derivative')
df = lambda E : 1 - 0.5*cos(E)
x = 0.5
for i in range(50):
delta = f(x)/df(x)
x = x - delta
if fabs(delta) < 1e-12:
break
print(i)
print(x)
print('TOMS748_solve1 only')
def fct1(E):
print("x = %20.15f" % E)
return E - 0.5*sin(E) - 0.3
TOMS748_solve1(fct1, 0, 1)