Initial commit. TOMS748 1st algorithm coded following strictly the paper.

This commit is contained in:
Jérôme 2019-04-03 08:52:54 +02:00
commit 7b2a9a6d4a
3 changed files with 193 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.pdf
__pycache__

50
findEnclosingBracket.py Normal file
View file

@ -0,0 +1,50 @@
# method to find a bracket that encloses a root of f
from math import *
def findEnclosingBracket(f, x, factor):
""" Finds a bracket [a b] that encloses a root of f : f(a)*f(b) < 0.
A typical value for the factor is 2.
The initial guess must be on the correct side of the real line : the algorithm will never cross the zero during the search.
The factor is increased every 10 iterations in order to speed up convergence for when the root is orders of magnitude away from the initial guess.
If the bracket returned by the function is too wide, try reducing the factor (while always keeping it > 1).
"""
a = x
b = x*factor # try higher
fa = f(a)
fb = f(b)
i = 0
while i in range(100) and fa*fb > 0:
if fabs(fa) < fabs(fb): # fa is closer to 0 than fb, extend the interval in the a direction (reducing it)
a = a / factor
fa = f(a)
else: # fb is closer to 0 than fa, extend the interval in the b direction (augmenting it)
b = b * factor
fb = f(b)
i = i+1
if i % 10 == 0: # every 10 iterations, bump up the factor to speed up convergence
factor = factor * 5
return (a, b, fa, fb)
f = lambda x : exp(sin(x))-2
a, b, fa, fb = findEnclosingBracket(f, 0.005, 2)
print((a,b)); print((fa, fb))
f = lambda x : exp(-x)-1e-200
a, b, fa, fb = findEnclosingBracket(f, 0.001, 2)
print((a,b)); print((fa, fb))
a, b, fa, fb = findEnclosingBracket(f, 0.001, 5)
print((a,b)); print((fa, fb))
f = lambda x : x**(1/3)-1000
a, b, fa, fb = findEnclosingBracket(f, 1e-10, 2)
print((a,b)); print((fa, fb))
# from toms748 import TOMS748_solve1
# def f(x):
# print(x)
# return x**(1/3)-1000
# TOMS748_solve1(f,a,b)

141
toms748.py Normal file
View file

@ -0,0 +1,141 @@
# 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)