Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 491a632

Browse files
Merge pull request #69 from yash2798/ys/itp
Itp method
2 parents f20f5e4 + 1e4eb4b commit 491a632

File tree

3 files changed

+166
-3
lines changed

3 files changed

+166
-3
lines changed

src/SimpleNonlinearSolve.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ include("dfsane.jl")
3838
include("ad.jl")
3939
include("halley.jl")
4040
include("alefeld.jl")
41+
include("itp.jl")
4142

4243
# Batched Solver Support
4344
include("batched/utils.jl")
@@ -68,15 +69,15 @@ PrecompileTools.@compile_workload begin
6869
prob_brack = IntervalNonlinearProblem{false}((u, p) -> u * u - p,
6970
T.((0.0, 2.0)),
7071
T(2))
71-
for alg in (Bisection, Falsi, Ridder, Brent, Alefeld)
72+
for alg in (Bisection, Falsi, Ridder, Brent, Alefeld, Itp)
7273
solve(prob_brack, alg(), abstol = T(1e-2))
7374
end
7475
end
7576
end
7677

7778
# DiffEq styled algorithms
7879
export Bisection, Brent, Broyden, LBroyden, SimpleDFSane, Falsi, Halley, Klement,
79-
Ridder, SimpleNewtonRaphson, SimpleTrustRegion, Alefeld
80+
Ridder, SimpleNewtonRaphson, SimpleTrustRegion, Alefeld, Itp
8081
export BatchedBroyden, BatchedSimpleNewtonRaphson, BatchedSimpleDFSane
8182

8283
end # module

src/itp.jl

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
```julia
3+
Itp(; k1::Real = 0.007, k2::Real = 1.5, n0::Int = 10)
4+
```
5+
6+
ITP (Interpolate Truncate & Project)
7+
8+
Use the [ITP method](https://en.wikipedia.org/wiki/ITP_method) to find
9+
a root of a bracketed function, with a convergence rate between 1 and 1.62.
10+
11+
This method was introduced in the paper "An Enhancement of the Bisection Method
12+
Average Performance Preserving Minmax Optimality"
13+
(https://doi.org/10.1145/3423597) by I. F. D. Oliveira and R. H. C. Takahashi.
14+
15+
# Tuning Parameters
16+
17+
The following keyword parameters are accepted.
18+
19+
- `n₀::Int = 1`, the 'slack'. Must not be negative.\n
20+
When n₀ = 0 the worst-case is identical to that of bisection,
21+
but increacing n₀ provides greater oppotunity for superlinearity.
22+
- `κ₁::Float64 = 0.1`. Must not be negative.\n
23+
The recomended value is `0.2/(x₂ - x₁)`.
24+
Lower values produce tighter asymptotic behaviour, while higher values
25+
improve the steady-state behaviour when truncation is not helpful.
26+
- `κ₂::Real = 2`. Must lie in [1, 1+ϕ ≈ 2.62).\n
27+
Higher values allow for a greater convergence rate,
28+
but also make the method more succeptable to worst-case performance.
29+
In practice, κ=1,2 seems to work well due to the computational simplicity,
30+
as κ₂ is used as an exponent in the method.
31+
32+
### Worst Case Performance
33+
34+
n½ + `n₀` iterations, where n½ is the number of iterations using bisection
35+
(n½ = ⌈log2(Δx)/2`tol`⌉).
36+
37+
### Asymptotic Performance
38+
39+
If `f` is twice differentiable and the root is simple,
40+
then with `n₀` > 0 the convergence rate is √`κ₂`.
41+
"""
42+
struct Itp{T} <: AbstractBracketingAlgorithm
43+
k1::T
44+
k2::T
45+
n0::Int
46+
function Itp(; k1::Real = 0.007, k2::Real = 1.5, n0::Int = 10)
47+
if k1 < 0
48+
error("Hyper-parameter κ₁ should not be negative")
49+
end
50+
if n0 < 0
51+
error("Hyper-parameter n₀ should not be negative")
52+
end
53+
if k2 < 1 || k2 > (1.5 + sqrt(5) / 2)
54+
ArgumentError("Hyper-parameter κ₂ should be between 1 and 1 + ϕ where ϕ ≈ 1.618... is the golden ratio")
55+
end
56+
T = promote_type(eltype(k1), eltype(k2))
57+
return new{T}(k1, k2, n0)
58+
end
59+
end
60+
61+
function SciMLBase.solve(prob::IntervalNonlinearProblem, alg::Itp,
62+
args...; abstol = 1.0e-15,
63+
maxiters = 1000, kwargs...)
64+
f = Base.Fix2(prob.f, prob.p)
65+
left, right = prob.tspan # a and b
66+
fl, fr = f(left), f(right)
67+
ϵ = abstol
68+
if iszero(fl)
69+
return SciMLBase.build_solution(prob, alg, left, fl;
70+
retcode = ReturnCode.ExactSolutionLeft, left = left,
71+
right = right)
72+
elseif iszero(fr)
73+
return SciMLBase.build_solution(prob, alg, right, fr;
74+
retcode = ReturnCode.ExactSolutionRight, left = left,
75+
right = right)
76+
end
77+
#defining variables/cache
78+
k1 = alg.k1
79+
k2 = alg.k2
80+
n0 = alg.n0
81+
n_h = ceil(log2((right - left) / (2 * ϵ)))
82+
mid = (left + right) / 2
83+
x_f = (fr * left - fl * right) / (fr - fl)
84+
xt = left
85+
xp = left
86+
r = zero(left) #minmax radius
87+
δ = zero(left) # truncation error
88+
σ = 1.0
89+
ϵ_s = ϵ * 2^(n_h + n0)
90+
i = 0 #iteration
91+
while i <= maxiters
92+
#mid = (left + right) / 2
93+
r = ϵ_s - ((right - left) / 2)
94+
δ = k1 * ((right - left)^k2)
95+
96+
## Interpolation step ##
97+
x_f = (fr * left - fl * right) / (fr - fl)
98+
99+
## Truncation step ##
100+
σ = sign(mid - x_f)
101+
if δ <= abs(mid - x_f)
102+
xt = x_f +* δ)
103+
else
104+
xt = mid
105+
end
106+
107+
## Projection step ##
108+
if abs(xt - mid) <= r
109+
xp = xt
110+
else
111+
xp = mid -* r)
112+
end
113+
114+
## Update ##
115+
yp = f(xp)
116+
if yp > 0
117+
right = xp
118+
fr = yp
119+
elseif yp < 0
120+
left = xp
121+
fl = yp
122+
else
123+
left = xp
124+
right = xp
125+
end
126+
i += 1
127+
mid = (left + right) / 2
128+
ϵ_s /= 2
129+
130+
if (right - left < 2 * ϵ)
131+
return SciMLBase.build_solution(prob, alg, mid, f(mid);
132+
retcode = ReturnCode.Success, left = left,
133+
right = right)
134+
end
135+
end
136+
return SciMLBase.build_solution(prob, alg, left, fl; retcode = ReturnCode.MaxIters,
137+
left = left, right = right)
138+
end

test/basictests.jl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,18 @@ for p in 1.1:0.1:100.0
219219
@test ForwardDiff.derivative(g, p) 1 / (2 * sqrt(p))
220220
end
221221

222+
# ITP
223+
g = function (p)
224+
probN = IntervalNonlinearProblem{false}(f, typeof(p).(tspan), p)
225+
sol = solve(probN, Itp())
226+
return sol.u
227+
end
228+
229+
for p in 1.1:0.1:100.0
230+
@test g(p) sqrt(p)
231+
@test ForwardDiff.derivative(g, p) 1 / (2 * sqrt(p))
232+
end
233+
222234
# Alefeld
223235
g = function (p)
224236
probN = IntervalNonlinearProblem{false}(f, typeof(p).(tspan), p)
@@ -246,7 +258,7 @@ end
246258
f, tspan = (u, p) -> p[1] * u * u - p[2], (1.0, 100.0)
247259
t = (p) -> [sqrt(p[2] / p[1])]
248260
p = [0.9, 50.0]
249-
for alg in [Bisection(), Falsi(), Ridder(), Brent()]
261+
for alg in [Bisection(), Falsi(), Ridder(), Brent(), Itp()]
250262
global g, p
251263
g = function (p)
252264
probN = IntervalNonlinearProblem{false}(f, tspan, p)
@@ -349,6 +361,18 @@ probB = IntervalNonlinearProblem(f, tspan)
349361
sol = solve(probB, Alefeld())
350362
@test sol.u sqrt(2.0)
351363

364+
# Itp
365+
sol = solve(probB, Itp())
366+
@test sol.u sqrt(2.0)
367+
tspan = (sqrt(2.0), 10.0)
368+
probB = IntervalNonlinearProblem(f, tspan)
369+
sol = solve(probB, Itp())
370+
@test sol.u sqrt(2.0)
371+
tspan = (0.0, sqrt(2.0))
372+
probB = IntervalNonlinearProblem(f, tspan)
373+
sol = solve(probB, Itp())
374+
@test sol.u sqrt(2.0)
375+
352376
# Garuntee Tests for Bisection
353377
f = function (u, p)
354378
if u < 2.0

0 commit comments

Comments
 (0)