Skip to content

Commit 221bb41

Browse files
Merge pull request #77 from yash2798/ys/tolerance
Tolerance fix for interval solvers
2 parents 167db26 + 35247e6 commit 221bb41

File tree

6 files changed

+96
-6
lines changed

6 files changed

+96
-6
lines changed

lib/SimpleNonlinearSolve/src/bisection.jl

+16-2
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,21 @@ function Bisection(; exact_left = false, exact_right = false)
2020
end
2121

2222
function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Bisection, args...;
23-
maxiters = 1000,
23+
maxiters = 1000, abstol = min(eps(prob.tspan[1]), eps(prob.tspan[2])),
2424
kwargs...)
2525
f = Base.Fix2(prob.f, prob.p)
2626
left, right = prob.tspan
2727
fl, fr = f(left), f(right)
28-
2928
if iszero(fl)
3029
return SciMLBase.build_solution(prob, alg, left, fl;
3130
retcode = ReturnCode.ExactSolutionLeft, left = left,
3231
right = right)
3332
end
33+
if iszero(fr)
34+
return SciMLBase.build_solution(prob, alg, right, fr;
35+
retcode = ReturnCode.ExactSolutionRight, left = left,
36+
right = right)
37+
end
3438

3539
i = 1
3640
if !iszero(fr)
@@ -41,6 +45,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Bisection, args...
4145
retcode = ReturnCode.FloatingPointLimit,
4246
left = left, right = right)
4347
fm = f(mid)
48+
if abs((right - left) / 2) < abstol
49+
return SciMLBase.build_solution(prob, alg, mid, fm;
50+
retcode = ReturnCode.Success,
51+
left = left, right = right)
52+
end
4453
if iszero(fm)
4554
right = mid
4655
break
@@ -63,6 +72,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Bisection, args...
6372
retcode = ReturnCode.FloatingPointLimit,
6473
left = left, right = right)
6574
fm = f(mid)
75+
if abs((right - left) / 2) < abstol
76+
return SciMLBase.build_solution(prob, alg, mid, fm;
77+
retcode = ReturnCode.Success,
78+
left = left, right = right)
79+
end
6680
if iszero(fm)
6781
right = mid
6882
fr = fm

lib/SimpleNonlinearSolve/src/brent.jl

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A non-allocating Brent method
77
struct Brent <: AbstractBracketingAlgorithm end
88

99
function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Brent, args...;
10-
maxiters = 1000,
10+
maxiters = 1000, abstol = min(eps(prob.tspan[1]), eps(prob.tspan[2])),
1111
kwargs...)
1212
f = Base.Fix2(prob.f, prob.p)
1313
a, b = prob.tspan
@@ -18,6 +18,10 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Brent, args...;
1818
return SciMLBase.build_solution(prob, alg, a, fa;
1919
retcode = ReturnCode.ExactSolutionLeft, left = a,
2020
right = b)
21+
elseif iszero(fb)
22+
return SciMLBase.build_solution(prob, alg, b, fb;
23+
retcode = ReturnCode.ExactSolutionRight, left = a,
24+
right = b)
2125
end
2226
if abs(fa) < abs(fb)
2327
c = b
@@ -60,6 +64,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Brent, args...;
6064
cond = false
6165
end
6266
fs = f(s)
67+
if abs((b - a) / 2) < abstol
68+
return SciMLBase.build_solution(prob, alg, s, fs;
69+
retcode = ReturnCode.Success,
70+
left = a, right = b)
71+
end
6372
if iszero(fs)
6473
if b < a
6574
a = b
@@ -99,6 +108,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Brent, args...;
99108
left = a, right = b)
100109
end
101110
fc = f(c)
111+
if abs((b - a) / 2) < abstol
112+
return SciMLBase.build_solution(prob, alg, c, fc;
113+
retcode = ReturnCode.Success,
114+
left = a, right = b)
115+
end
102116
if iszero(fc)
103117
b = c
104118
fb = fc

lib/SimpleNonlinearSolve/src/falsi.jl

+15-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
struct Falsi <: AbstractBracketingAlgorithm end
55

66
function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Falsi, args...;
7-
maxiters = 1000,
7+
maxiters = 1000, abstol = min(eps(prob.tspan[1]), eps(prob.tspan[2])),
88
kwargs...)
99
f = Base.Fix2(prob.f, prob.p)
1010
left, right = prob.tspan
@@ -14,6 +14,10 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Falsi, args...;
1414
return SciMLBase.build_solution(prob, alg, left, fl;
1515
retcode = ReturnCode.ExactSolutionLeft, left = left,
1616
right = right)
17+
elseif iszero(fr)
18+
return SciMLBase.build_solution(prob, alg, right, fr;
19+
retcode = ReturnCode.ExactSolutionRight, left = left,
20+
right = right)
1721
end
1822

1923
i = 1
@@ -32,6 +36,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Falsi, args...;
3236
break
3337
end
3438
fm = f(mid)
39+
if abs((right - left) / 2) < abstol
40+
return SciMLBase.build_solution(prob, alg, mid, fm;
41+
retcode = ReturnCode.Success,
42+
left = left, right = right)
43+
end
3544
if iszero(fm)
3645
right = mid
3746
break
@@ -54,6 +63,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Falsi, args...;
5463
retcode = ReturnCode.FloatingPointLimit,
5564
left = left, right = right)
5665
fm = f(mid)
66+
if abs((right - left) / 2) < abstol
67+
return SciMLBase.build_solution(prob, alg, mid, fm;
68+
retcode = ReturnCode.Success,
69+
left = left, right = right)
70+
end
5771
if iszero(fm)
5872
right = mid
5973
fr = fm

lib/SimpleNonlinearSolve/src/itp.jl

+7-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct ITP{T} <: AbstractBracketingAlgorithm
5959
end
6060

6161
function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::ITP,
62-
args...; abstol = 1.0e-15,
62+
args...; abstol = min(eps(prob.tspan[1]), eps(prob.tspan[2])),
6363
maxiters = 1000, kwargs...)
6464
f = Base.Fix2(prob.f, prob.p)
6565
left, right = prob.tspan # a and b
@@ -111,6 +111,12 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::ITP,
111111
xp = mid -* r)
112112
end
113113

114+
if abs((left - right) / 2) < ϵ
115+
return SciMLBase.build_solution(prob, alg, mid, f(mid);
116+
retcode = ReturnCode.Success,
117+
left = left, right = right)
118+
end
119+
114120
## Update ##
115121
tmin, tmax = minmax(left, right)
116122
xp >= tmax && (xp = prevfloat(tmax))

lib/SimpleNonlinearSolve/src/ridder.jl

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A non-allocating ridder method
77
struct Ridder <: AbstractBracketingAlgorithm end
88

99
function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Ridder, args...;
10-
maxiters = 1000,
10+
maxiters = 1000, abstol = min(eps(prob.tspan[1]), eps(prob.tspan[2])),
1111
kwargs...)
1212
f = Base.Fix2(prob.f, prob.p)
1313
left, right = prob.tspan
@@ -17,6 +17,10 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Ridder, args...;
1717
return SciMLBase.build_solution(prob, alg, left, fl;
1818
retcode = ReturnCode.ExactSolutionLeft, left = left,
1919
right = right)
20+
elseif iszero(fr)
21+
return SciMLBase.build_solution(prob, alg, right, fr;
22+
retcode = ReturnCode.ExactSolutionRight, left = left,
23+
right = right)
2024
end
2125

2226
xo = oftype(left, Inf)
@@ -37,6 +41,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Ridder, args...;
3741
x = mid + (mid - left) * sign(fl - fr) * fm / s
3842
fx = f(x)
3943
xo = x
44+
if abs((right - left) / 2) < abstol
45+
return SciMLBase.build_solution(prob, alg, mid, fm;
46+
retcode = ReturnCode.Success,
47+
left = left, right = right)
48+
end
4049
if iszero(fx)
4150
right = x
4251
fr = fx
@@ -66,6 +75,11 @@ function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Ridder, args...;
6675
retcode = ReturnCode.FloatingPointLimit,
6776
left = left, right = right)
6877
fm = f(mid)
78+
if abs((right - left) / 2) < abstol
79+
return SciMLBase.build_solution(prob, alg, mid, fm;
80+
retcode = ReturnCode.Success,
81+
left = left, right = right)
82+
end
6983
if iszero(fm)
7084
right = mid
7185
fr = fm

lib/SimpleNonlinearSolve/test/basictests.jl

+28
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,34 @@ probB = IntervalNonlinearProblem(f, tspan)
373373
sol = solve(probB, ITP())
374374
@test sol.u sqrt(2.0)
375375

376+
# Tolerance tests for Interval methods
377+
f, tspan = (u, p) -> u .* u .- 2.0, (1.0, 10.0)
378+
probB = IntervalNonlinearProblem(f, tspan)
379+
tols = [0.1, 0.01, 0.001, 0.0001, 1e-5, 1e-6, 1e-7]
380+
ϵ = eps(1.0) #least possible tol for all methods
381+
382+
for atol in tols
383+
sol = solve(probB, Bisection(), abstol = atol)
384+
@test abs(sol.u - sqrt(2)) < atol
385+
@test abs(sol.u - sqrt(2)) > ϵ #test that the solution is not calculated upto max precision
386+
sol = solve(probB, Falsi(), abstol = atol)
387+
@test abs(sol.u - sqrt(2)) < atol
388+
@test abs(sol.u - sqrt(2)) > ϵ
389+
sol = solve(probB, ITP(), abstol = atol)
390+
@test abs(sol.u - sqrt(2)) < atol
391+
@test abs(sol.u - sqrt(2)) > ϵ
392+
end
393+
394+
tols = [0.1] # Ridder and Brent converge rapidly so as we lower tolerance below 0.01, it converges with max precision to the solution
395+
for atol in tols
396+
sol = solve(probB, Ridder(), abstol = atol)
397+
@test abs(sol.u - sqrt(2)) < atol
398+
@test abs(sol.u - sqrt(2)) > ϵ
399+
sol = solve(probB, Brent(), abstol = atol)
400+
@test abs(sol.u - sqrt(2)) < atol
401+
@test abs(sol.u - sqrt(2)) > ϵ
402+
end
403+
376404
# Garuntee Tests for Bisection
377405
f = function (u, p)
378406
if u < 2.0

0 commit comments

Comments
 (0)