Skip to content

Commit 4153a9d

Browse files
eeshan9815dpsanders
authored andcommitted
newton1d - debug mode, robustness, harder test cases (#50)
* Add debug mode and bug fixes * Add tests * Major robustness improvements and hard test cases added * Documentation and minor fixes
1 parent d80403e commit 4153a9d

File tree

3 files changed

+155
-44
lines changed

3 files changed

+155
-44
lines changed

src/IntervalRootFinding.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export
2323
export isunique, root_status
2424

2525

26-
import IntervalArithmetic.interval
26+
import IntervalArithmetic: interval, wideinterval
2727

2828

2929

src/newton1d.jl

Lines changed: 116 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,151 @@ Optional keyword arguments give the tolerances `reltol` and `abstol`.
55
and a `debug` boolean argument that prints out diagnostic information."""
66

77
function newton1d{T}(f::Function, f′::Function, x::Interval{T};
8-
reltol=eps(T), abstol=eps(T), debug=false)
8+
reltol=eps(T), abstol=eps(T), debug=false, debugroot=false)
99

10-
L = Interval{T}[]
10+
L = Interval{T}[] # Array to hold the intervals still to be processed
1111

12-
R = Root{Interval{T}}[]
12+
R = Root{Interval{T}}[] # Array to hold the `root` objects obtained
13+
reps = reps1 = 0
1314

14-
push!(L, x)
15+
push!(L, x) # Initialize
16+
initial_width =
17+
X = emptyinterval(T) # Initialize
18+
while !isempty(L) # Until all intervals have been processed
19+
X = pop!(L) # Process next interval
20+
21+
debug && (print("Current interval popped: "); @show X)
1522

16-
while !isempty(L)
17-
X = pop!(L)
1823
m = mid(X)
1924
if (isempty(X))
2025
continue
2126
end
2227

23-
if 0 f′(X)
28+
if 0 f′(X) # if 0 ∉ f′(X), Newton steps can be made normally until either X becomes empty, or is known to contain a unique root
29+
30+
debug && println("0 ∉ f′(X)")
31+
2432
while true
33+
2534
m = mid(X)
26-
N = m - (f(Interval(m)) / f′(X))
35+
N = m - (f(interval(m)) / f′(X)) # Newton step
36+
37+
debug && (print("Newton step1: "); @show (X, X N))
38+
if X == X N # Checking if Newton step was redundant
39+
reps1 += 1
40+
if reps1 > 20
41+
reps1 = 0
42+
break
43+
end
44+
end
2745
X = X N
2846

29-
if isempty(X)
47+
if (isempty(X)) # No root in X
3048
break
3149

32-
elseif 0 f(Interval(prevfloat(m), nextfloat(m)))
33-
push!(R, Root(X, :unique))
50+
elseif 0 f(wideinterval(mid(X))) # Root guaranteed to be in X
51+
n = fa = fb = 0
52+
root_exist = true
53+
while (n < 4 && (fa == 0 || fb == 0)) # Narrowing the interval further
54+
if fa == 0
55+
if 0 f(wideinterval(X.lo))
56+
fa = 1
57+
else
58+
N = X.lo - (f(interval(X.lo)) / f′(X))
59+
X = X N
60+
if (isempty(X))
61+
root_exist = false
62+
break
63+
end
64+
end
65+
end
66+
if fb == 0
67+
if 0 f(wideinterval(X.hi))
68+
fb = 1
69+
else
70+
if 0 f(wideinterval(mid(X)))
71+
N = X.hi - (f(interval(X.hi)) / f′(X))
72+
else
73+
N = mid(X) - (f(interval(mid(X))) / f′(X))
74+
end
75+
X = X N
76+
if (isempty(X))
77+
root_exist = false
78+
break
79+
end
80+
end
81+
end
82+
N = mid(X) - (f(interval(mid(X))) / f′(X))
83+
X = X N
84+
if (isempty(X))
85+
root_exist = false
86+
break
87+
end
88+
n += 1
89+
end
90+
if root_exist
91+
push!(R, Root(X, :unique))
92+
debugroot && @show "Root found", X # Storing determined unique root
93+
end
94+
3495
break
3596
end
3697
end
3798

38-
else
39-
# 0 ∈ f'(X)
99+
else # 0 ∈ f′(X)
100+
if diam(X) == initial_width # if no improvement occuring for a number of iterations
101+
reps += 1
102+
if reps > 10
103+
push!(R, Root(X, :unknown))
104+
debugroot && @show "Repeated root found", X
105+
reps = 0
106+
continue
107+
end
108+
end
109+
initial_width = diam(X)
110+
debug && println("0 ∈ f'(X)")
111+
40112
expansion_pt = Inf
41113
# expansion point for the newton step might be m, X.lo or X.hi according to some conditions
42114

43-
if 0 f(Interval(mid(X)))
115+
if 0 f(wideinterval(mid(X))) # if root in X, narrow interval further
44116
# 0 ∈ fⁱ(x)
45-
# Step 7
46117

47-
if 0 f(Interval(X.lo))
118+
debug && println("0 ∈ fⁱ(x)")
119+
120+
if 0 f(wideinterval(X.lo))
48121
expansion_pt = X.lo
49122

50-
elseif 0 f(Interval(X.hi))
123+
elseif 0 f(wideinterval(X.hi))
51124
expansion_pt = X.hi
52125

53126
else
54-
x1 = mid(Interval(X.lo, mid(X)))
55-
x2 = mid(Interval(mid(X), X.hi))
56-
if 0 f(Interval(x1)) || 0 f(Interval(x2))
57-
push!(L, Interval(X.lo, m))
58-
push!(L, Interval(m, X.hi))
127+
x1 = mid(interval(X.lo, mid(X)))
128+
x2 = mid(interval(mid(X), X.hi))
129+
if 0 f(wideinterval(x1)) || 0 f(wideinterval(x2))
130+
push!(L, interval(X.lo, m))
131+
push!(L, interval(m, X.hi))
59132
continue
60133

61134
else
62-
push!(R, Root(X, :unique))
135+
push!(R, Root(X, :unknown))
136+
137+
debugroot && @show "Multiple root found", X
138+
63139
continue
64140
end
65141
end
66142

67143
else
68144
# 0 ∉ fⁱ(x)
69145

70-
if (diam(X)/mag(X)) < reltol && diam(f(X)) < abstol
146+
debug && println("0 ∉ fⁱ(x)")
147+
148+
if (diam(X)/mag(X)) < reltol && diam(f(X)) < abstol # checking if X is still within tolerances
71149
push!(R, Root(X, :unknown))
150+
151+
debugroot && @show "Tolerance root found", X
152+
72153
continue
73154
end
74155
end
@@ -78,26 +159,28 @@ function newton1d{T}(f::Function, f′::Function, x::Interval{T};
78159
expansion_pt = mid(X)
79160
end
80161

81-
initial_width = diam(X)
82162

83-
a = f(Interval(expansion_pt))
163+
a = f(interval(expansion_pt))
84164
b = f′(X)
85-
165+
# Newton steps with extended division creating two intervals
86166
if 0 < b.hi && 0 > b.lo && 0 a
87167
if a.hi < 0
88-
push!(L, X (expansion_pt - Interval(-Inf, a.hi / b.hi)))
89-
push!(L, X (expansion_pt - Interval(a.hi / b.lo, Inf)))
168+
push!(L, X (expansion_pt - interval(-Inf, a.hi / b.hi)))
169+
push!(L, X (expansion_pt - interval(a.hi / b.lo, Inf)))
90170

91171
elseif a.lo > 0
92-
push!(L, X (expansion_pt - Interval(-Inf, a.lo / b.lo)))
93-
push!(L, X (expansion_pt - Interval(a.lo / b.hi, Inf)))
172+
push!(L, X (expansion_pt - interval(-Inf, a.lo / b.lo)))
173+
push!(L, X (expansion_pt - interval(a.lo / b.hi, Inf)))
94174

95175
end
96176

97177
continue
98178

99179
else
100-
N = expansion_pt - (f(Interval(expansion_pt))/f′(X))
180+
N = expansion_pt - (f(interval(expansion_pt))/f′(X))
181+
182+
debug && (print("Newton step2: "); @show (X, X N))
183+
101184
X = X N
102185
m = mid(X)
103186

@@ -106,12 +189,7 @@ function newton1d{T}(f::Function, f′::Function, x::Interval{T};
106189
end
107190
end
108191

109-
if diam(X) > initial_width/2
110-
push!(L, Interval(m, X.hi))
111-
X = Interval(X.lo, m)
112-
end
113-
114-
push!(L, X)
192+
push!(L, X) # Pushing X into L to be processed again
115193
end
116194
end
117195

test/newton1d.jl

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,69 @@ three_halves_pi = 3*big_pi/2
1818

1919
@testset "Testing newton1d" begin
2020

21-
f(x) = exp(x^2) - cos(x)
21+
f(x) = exp(x^2) - cos(x) #double root
2222
f′(x) = 2*x*exp(x^2) + sin(x)
23-
f1(x) = x^4 - 10x^3 + 35x^2 - 50x + 24
23+
f1(x) = x^4 - 10x^3 + 35x^2 - 50x + 24 #four unique roots
2424
f1′(x) = 4x^3 - 30x^2 + 70x - 50
25-
25+
f2(x) = 4567x^2 - 9134x + 4567 #double root
26+
f2′(x) = 9134x - 9134
27+
f3(x) = (x^2 - 2)^2 #two double roots
28+
f3′(x) = 4x * (x^2 - 2)
29+
f4(x) = sin(x) - x #triple root at 0
30+
f4′(x) = cos(x) - 1
31+
f5(x) = (x^2 - 1)^4 * (x^2 - 2)^4 #two quadruple roots
32+
f5′(x) = 8x * (-3 + 2x^2) * (2 - 3x^2 + x^4)^3
2633
for autodiff in (false, true)
2734
if autodiff
2835
rts1 = newton1d(sin, -5..5)
2936
rts2 = newton1d(f, -..∞)
3037
rts3 = newton1d(f1, -10..10)
38+
rts4 = newton1d(f2, -10..11)
39+
rts5 = newton1d(f3, -10..10)
40+
rts6 = newton1d(f4, -10..10)
41+
rts7 = newton1d(f5, -10..10)
3142

3243
else
3344
rts1 = newton1d(sin, cos, -5..5)
3445
rts2 = newton1d(f, f′, -..∞)
3546
rts3 = newton1d(f1, f1′, -10..10)
47+
rts4 = newton1d(f2, f2′, -10..11)
48+
rts5 = newton1d(f3, f3′, -10..10)
49+
rts6 = newton1d(f4, f4′, -10..10)
50+
rts7 = newton1d(f5, f5′, -10..10, reltol=0)
3651
end
3752

3853
@test length(rts1) == 3
3954
L = [ -pi_interval(Float64), 0..0, pi_interval(Float64)]
4055
for i = 1:length(rts1)
41-
@test L[i] == rts1[i].interval && :unique == rts1[i].status
56+
@test L[i] in rts1[i].interval && :unique == rts1[i].status
4257
end
4358

4459
@test length(rts2) == 1
45-
@test (0..0) == rts2[1].interval && :unique == rts2[1].status
60+
@test (0..0) == rts2[1].interval && :unknown == rts2[1].status
4661

4762
@test length(rts3) == 4
4863
L = [1, 2, 3, 4]
4964
for i = 1:length(rts3)
5065
@test L[i] in rts3[i].interval && :unique == rts3[i].status
5166
end
67+
68+
@test length(rts4) == 1
69+
@test 1 in rts4[1].interval && :unknown == rts4[1].status
70+
71+
L1 = [-sqrt(2), sqrt(2)]
72+
for i = 1:length(rts5)
73+
@test L1[i] in rts5[i].interval && :unknown == rts5[i].status
74+
end
75+
76+
@test length(rts6) == 1
77+
@test 0 in rts6[1].interval && :unknown == rts6[1].status
78+
79+
@test length(rts7) == 4
80+
L = [-sqrt(2), -1, 1, sqrt(2)]
81+
for i = 1:length(rts7)
82+
@test L[i] in rts7[i].interval && :unknown == rts7[i].status
83+
end
84+
5285
end
5386
end

0 commit comments

Comments
 (0)