Skip to content

Commit ee8fb32

Browse files
committed
(feat) add falsi and bisection methods
0 parents  commit ee8fb32

File tree

5 files changed

+166
-0
lines changed

5 files changed

+166
-0
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name = "NonlinearSolve"
2+
uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
3+
authors = ["Kanav Gupta <[email protected]>"]
4+
version = "0.1.0"

src/NonlinearSolve.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module NonlinearSolve
2+
3+
include("utils.jl")
4+
include("bisection.jl")
5+
include("falsi.jl")
6+
7+
export bisection, falsi
8+
end # module

src/bisection.jl

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
bisection(f, tup ; maxiters=1000)
3+
4+
Uses bisection method to find the root of the function `f` between a tuple `tup` of values.
5+
"""
6+
function bisection(f, tup ; maxiters=1000)
7+
x0, x1 = tup
8+
fx0, fx1 = f(x0), f(x1)
9+
fx0x1 = fx0 * fx1
10+
fzero = zero(fx0x1)
11+
12+
(fx0x1 > fzero) && error("Non bracketing interval passed in bisection method.")
13+
# NOTE: fx0x1 = 0 can mean that both fx0 and fx1 are very small and multiplication of them
14+
# could be less than the smallest float, hence could be zero.
15+
16+
fx0 == fzero && return x0 # should replace with some tolerance compare i.e. ≈
17+
fx1 == fzero && return x1
18+
19+
left = x0
20+
right = x1
21+
22+
iter = 0
23+
while true
24+
iter += 1
25+
26+
if iter == maxiters
27+
return left
28+
end
29+
30+
fl = f(left)
31+
fr = f(right)
32+
33+
fl * fr >= fzero && error("Bracket became non-containing in between iterations. This could mean that"
34+
+ " input function crosses the x axis multiple times. Bisection is not the right method to solve this.")
35+
36+
mid = (left + right) / 2.0
37+
fm = f(mid)
38+
if iszero(fm)
39+
# we are in the region of zero, inner loop
40+
right = mid
41+
while true
42+
iter += 1
43+
44+
if iter == maxiters
45+
return left
46+
end
47+
48+
mid = (left + right) / 2.0
49+
(left == mid || right == mid) && return left
50+
fm = f(mid)
51+
52+
if iszero(fm)
53+
if !iszero(f(prevfloat_tdir(mid, x0, x1)))
54+
return prevfloat_tdir(mid, x0, x1)
55+
end
56+
right = mid
57+
else
58+
left = mid
59+
end
60+
61+
end
62+
end
63+
64+
(left == mid || right == mid) && return left
65+
if sign(fm) == sign(fl)
66+
left = mid
67+
else
68+
right = mid
69+
end
70+
end
71+
end

src/falsi.jl

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""
2+
falsi(f, tup ; maxiters=1000)
3+
4+
Uses Regula-Falsi method to find the root of the function `f` between a tuple `tup = (x0, x1)` of values.
5+
It doesn't find the exact value at which f(x) = 0, but finds an x where the next float in the direction of `x1`
6+
gives the evaluation of `f` to be zero. If `f` is zero for a region of x, it returns the left-most (in the
7+
direction of x0) such value.
8+
"""
9+
function falsi(f, tup ; maxiters=1000)
10+
x0, x1 = tup
11+
fx0, fx1 = f(x0), f(x1)
12+
fx0x1 = fx0 * fx1
13+
fzero = zero(fx0x1)
14+
15+
(fx0x1 > fzero) && error("Non bracketing interval passed in bisection method.")
16+
# NOTE: fx0x1 = 0 can mean that both fx0 and fx1 are very small and multiplication of them
17+
# could be less than the smallest float, hence could be zero.
18+
19+
fx0 == fzero && return x0 # should replace with some tolerance compare i.e. ≈
20+
fx1 == fzero && return x1
21+
22+
left = x0
23+
right = x1
24+
25+
iter = 0
26+
while true
27+
iter += 1
28+
29+
if iter == maxiters
30+
return left
31+
end
32+
33+
fl = f(left)
34+
fr = f(right)
35+
36+
fl * fr >= fzero && error("Bracket became non-containing in between iterations. This could mean that"
37+
+ " input function crosses the x axis multiple times. Bisection is not the right method to solve this.")
38+
39+
mid = (fr * left - fl * right) / (fr - fl)
40+
fm = f(mid)
41+
if iszero(fm)
42+
# we are in the region of zero, inner loop
43+
right = mid
44+
while true
45+
iter += 1
46+
47+
if iter == maxiters
48+
return left
49+
end
50+
51+
mid = (left + right) / 2.0
52+
(left == mid || right == mid) && return left
53+
fm = f(mid)
54+
55+
if iszero(fm)
56+
if !iszero(f(prevfloat_tdir(mid, x0, x1)))
57+
return prevfloat_tdir(mid, x0, x1)
58+
end
59+
right = mid
60+
else
61+
left = mid
62+
end
63+
64+
end
65+
end
66+
67+
(left == mid || right == mid) && return left
68+
if sign(fm) == sign(fl)
69+
left = mid
70+
else
71+
right = mid
72+
end
73+
end
74+
end

src/utils.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""
2+
prevfloat_tdir(x, x0, x1)
3+
4+
Move `x` one floating point towards x0.
5+
"""
6+
function prevfloat_tdir(x::T, x0::T, x1::T)::T where {T}
7+
x1 > x0 : prevfloat(x) : nextfloat(x)
8+
end
9+

0 commit comments

Comments
 (0)