Skip to content

Commit 5a0027a

Browse files
eeshan9815dpsanders
authored andcommitted
Automatic evaluation of slope expansions (#69)
* Add automatic slope evaluation * Add functions which return derivative for completeness (outer bound) * Add benchmarking and data * Add tests * Incorporate review comments * Use concrete types and add BigFloat tests * unified tests * Minor fixes
1 parent 5b7a8ab commit 5a0027a

File tree

6 files changed

+337
-1
lines changed

6 files changed

+337
-1
lines changed

perf/slopes.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using BenchmarkTools, Compat, DataFrames, IntervalRootFinding, IntervalArithmetic, StaticArrays
2+
3+
function benchmark_results()
4+
f = [] # Example functions from Dietmar Ratz - An Optimized Interval Slope Arithmetic and its Application
5+
push!(f, x->((x + sin(x)) * exp(-x^2)))
6+
push!(f, x->(x^4 - 10x^3 + 35x^2 - 50x + 24))
7+
push!(f, x->((log(x + 1.25) - 0.84x) ^ 2))
8+
push!(f, x->(0.02x^2 - 0.03exp(-(20(x - 0.875))^2)))
9+
push!(f, x->(exp(x^2)))
10+
push!(f, x->(x^4 - 12x^3 + 47x^2 - 60x - 20exp(-x)))
11+
push!(f, x->(x^6 - 15x^4 + 27x^2 + 250))
12+
push!(f, x->(atan(cos(tan(x)))))
13+
push!(f, x->(asin(cos(acos(sin(x))))))
14+
15+
s = interval(0.75, 1.75)
16+
df = DataFrame()
17+
18+
df[:Method] = ["Automatic Differentiation", "Slope Expansion"]
19+
for n in 1:length(f)
20+
21+
t1 = ForwardDiff.derivative(f[n], s)
22+
t2 = slope(f[n], s, mid(s))
23+
df[Symbol("f" * "$n")] = [t1, t2]
24+
end
25+
a = []
26+
for i in 1:length(f)
27+
push!(a, Symbol("f" * "$i"))
28+
end
29+
df1 = stack(df, a)
30+
dfnew = unstack(df1, :variable, :Method, :value)
31+
dfnew = rename(dfnew, :variable => :Function)
32+
println(dfnew)
33+
dfnew
34+
end
35+
36+
function benchmark_time()
37+
f = []
38+
push!(f, x->((x + sin(x)) * exp(-x^2)))
39+
push!(f, x->(x^4 - 10x^3 + 35x^2 - 50x + 24))
40+
push!(f, x->((log(x + 1.25) - 0.84x) ^ 2))
41+
push!(f, x->(0.02x^2 - 0.03exp(-(20(x - 0.875))^2)))
42+
push!(f, x->(exp(x^2)))
43+
push!(f, x->(x^4 - 12x^3 + 47x^2 - 60x - 20exp(-x)))
44+
push!(f, x->(x^6 - 15x^4 + 27x^2 + 250))
45+
push!(f, x->(atan(cos(tan(x)))))
46+
push!(f, x->(asin(cos(acos(sin(x))))))
47+
48+
s = interval(0.75, 1.75)
49+
df = DataFrame()
50+
df[:Method] = ["Automatic Differentiation", "Slope Expansion"]
51+
for n in 1:length(f)
52+
53+
t1 = @belapsed ForwardDiff.derivative($f[$n], $s)
54+
t2 = @belapsed slope($f[$n], $s, mid($s))
55+
df[Symbol("f" * "$n")] = [t1, t2]
56+
end
57+
a = []
58+
for i in 1:length(f)
59+
push!(a, Symbol("f" * "$i"))
60+
end
61+
df1 = stack(df, a)
62+
dfnew = unstack(df1, :variable, :Method, :value)
63+
dfnew = rename(dfnew, :variable => :Function)
64+
println(dfnew)
65+
dfnew
66+
end

perf/slopes_tabular.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
Results
2+
3+
│ Row │ Function │ Automatic Differentiation │ Slope Expansion │
4+
├─────┼──────────┼───────────────────────────┼───────────────────────┤
5+
│ 1 │ f1 │ [-5.44573, 0.886249] │ [-2.79917, 0.0521429] │
6+
│ 2 │ f2 │ [-87.6875, 77.0625] │ [-43.875, 38.25] │
7+
│ 3 │ f3 │ [-0.474861, 0.787211] │ [-0.159199, 0.432842] │
8+
│ 4 │ f4 │ [-2.97001, 21.0701] │ [0.04, 0.326667] │
9+
│ 5 │ f5 │ [2.63258, 74.8333] │ [6.03135, 33.2205] │
10+
│ 6 │ f6 │ [-94.5871, 115.135] │ [-38.9908, 65.5595] │
11+
│ 7 │ f7 │ [-279.639, 167.667] │ [-146.852, 67.0665] │
12+
│ 8 │ f8 │ [-∞, ∞] │ [1, 2] │
13+
│ 9 │ f9 │ [-∞, ∞] │ [1.3667, ∞] │
14+
15+
Time
16+
<Using abstract types>
17+
│ Row │ Function │ Automatic Differentiation │ Slope Expansion │
18+
├─────┼──────────┼───────────────────────────┼─────────────────┤
19+
│ 1 │ f1 │ 8.847e-6 │ 2.4192e-5 │
20+
│ 2 │ f2 │ 3.0109e-5 │ 7.6821e-5 │
21+
│ 3 │ f3 │ 6.1046e-6 │ 9.447e-6 │
22+
│ 4 │ f4 │ 1.6145e-5 │ 3.8827e-5 │
23+
│ 5 │ f5 │ 8.29233e-6 │ 2.2186e-5 │
24+
│ 6 │ f6 │ 3.0895e-5 │ 7.9524e-5 │
25+
│ 7 │ f7 │ 3.0847e-5 │ 7.6188e-5 │
26+
<After using concrete types>
27+
│ Row │ Function │ Automatic Differentiation │ Slope Expansion │
28+
├─────┼──────────┼───────────────────────────┼─────────────────┤
29+
│ 1 │ f1 │ 8.34067e-6 │ 2.1679e-5 │
30+
│ 2 │ f2 │ 2.9766e-5 │ 7.194e-5 │
31+
│ 3 │ f3 │ 6.0278e-6 │ 7.827e-6 │
32+
│ 4 │ f4 │ 1.7114e-5 │ 3.5235e-5 │
33+
│ 5 │ f5 │ 8.36567e-6 │ 2.0747e-5 │
34+
│ 6 │ f6 │ 3.0061e-5 │ 7.273e-5 │
35+
│ 7 │ f7 │ 3.0198e-5 │ 7.2014e-5 │
36+
│ 8 │ f8 │ 7.43125e-6 │ 8.046e-6 │
37+
│ 9 │ f9 │ 7.118e-6 │ 7.8705e-6 │

src/IntervalRootFinding.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export
2121
bisect, newton1d, quadratic_roots,
2222
gauss_seidel_interval, gauss_seidel_interval!,
2323
gauss_seidel_contractor, gauss_seidel_contractor!,
24-
gauss_elimination_interval, gauss_elimination_interval!
24+
gauss_elimination_interval, gauss_elimination_interval!,
25+
slope
2526

2627
export isunique, root_status
2728

@@ -47,6 +48,7 @@ include("roots.jl")
4748
include("newton1d.jl")
4849
include("quadratic.jl")
4950
include("linear_eq.jl")
51+
include("slopes.jl")
5052

5153

5254
gradient(f) = X -> ForwardDiff.gradient(f, X[:])

src/slopes.jl

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Reference : Dietmar Ratz - An Optimized Interval Slope Arithmetic and its Application
2+
using IntervalArithmetic, ForwardDiff
3+
import Base: +, -, *, /, ^, sqrt, exp, log, sin, cos, tan, asin, acos, atan
4+
import IntervalArithmetic: mid, interval
5+
6+
function slope(f::Function, x::Interval, c::Number)
7+
f(slope_var(x, c)).s
8+
end
9+
10+
struct Slope{T}
11+
x::Interval{T} # Interval on which slope is evaluated
12+
c::Interval{T} # Point about which slope is evaluated (Interval to get bounded rounding errors)
13+
s::Interval{T} # Variable which propogates the slope information
14+
end
15+
16+
Slope(c) = Slope(c, c, 0)
17+
Slope(a, b, c) = Slope(promote(convert(Interval, a), b, c)...)
18+
19+
function slope_var(v::Number)
20+
Slope(v, v, 1)
21+
end
22+
23+
function slope_var(v::Interval, c::Number)
24+
Slope(v, c, 1)
25+
end
26+
27+
function interval(u::Slope)
28+
u.x
29+
end
30+
31+
function mid(u::Slope)
32+
u.c
33+
end
34+
35+
function slope(u::Slope)
36+
u.s
37+
end
38+
39+
function +(u::Slope, v::Slope)
40+
Slope(u.x + v.x, u.c + v.c, u.s + v.s)
41+
end
42+
43+
function -(u::Slope, v::Slope)
44+
Slope(u.x - v.x, u.c - v.c, u.s - v.s)
45+
end
46+
47+
function *(u::Slope, v::Slope)
48+
Slope(u.x * v.x, u.c * v.c, u.s * v.c + u.x * v.s)
49+
end
50+
51+
function /(u::Slope, v::Slope)
52+
Slope(u.x / v.x, u.c / v.c, (u.s - (u.c / v.c) * v.s) / v.x)
53+
end
54+
55+
function +(u, v::Slope)
56+
Slope(u + v.x, u + v.c, v.s)
57+
end
58+
59+
function -(u, v::Slope)
60+
Slope(u - v.x, u - v.c, -v.s)
61+
end
62+
63+
function *(u, v::Slope)
64+
Slope(u * v.x, u * v.c, u * v.s)
65+
end
66+
67+
function /(u, v::Slope)
68+
Slope(u / v.x, u / v.c, -(u / v.c) * (v.s / v.x))
69+
end
70+
71+
+(v::Slope, u) = u + v
72+
73+
*(v::Slope, u) = u * v
74+
75+
function -(u::Slope, v)
76+
Slope(u.x - v, u.c - v, u.s)
77+
end
78+
79+
function -(u::Slope)
80+
Slope(-u.x, -u.c, -u.s)
81+
end
82+
83+
function /(u::Slope, v)
84+
Slope(u.x / v, u.c / v, u.s / v)
85+
end
86+
87+
function sqr(u::Slope)
88+
Slope(u.x ^ 2, u.c ^ 2, (u.x + u.c) * u.s)
89+
end
90+
91+
function ^(u::Slope, k::Integer)
92+
if k == 0
93+
return Slope(1)
94+
elseif k == 1
95+
return u
96+
elseif k == 2
97+
return sqr(u)
98+
else
99+
hxi = interval(u.x.lo) ^ k
100+
hxs = interval(u.x.hi) ^ k
101+
hx = hull(hxi, hxs)
102+
103+
if (k % 2 == 0) && (0 u.x)
104+
hx = interval(0, hx.hi)
105+
end
106+
107+
hc = u.c ^ k
108+
109+
i = u.x.lo - u.c.lo
110+
s = u.x.hi - u.c.hi
111+
112+
if ((i == 0) || (s == 0) || (k % 2 == 1 && zero(u.x) u.x))
113+
h1 = k * (u.x ^ (k - 1))
114+
else
115+
if k % 2 == 0 || u.x.lo >= 0
116+
h1 = interval((hxi.hi - hc.lo) / i, (hxs.hi - hc.lo) / s)
117+
else
118+
h1 = interval((hxs.lo - hc.hi) / s, (hxi.lo - hc.hi) / i)
119+
end
120+
end
121+
return Slope(hx, hc, h1 * u.s)
122+
end
123+
end
124+
125+
function sqrt(u::Slope)
126+
Slope(sqrt(u.x), sqrt(u.c), u.s / (sqrt(u.x) + sqrt(u.c)))
127+
end
128+
129+
function exp(u::Slope)
130+
hx = exp(u.x)
131+
hc = exp(u.c)
132+
133+
i = u.x.lo - u.c.lo
134+
s = u.x.hi - u.c.hi
135+
136+
if (i == 0 || s == 0)
137+
h1 = hx
138+
else
139+
h1 = interval((hx.lo - hc.lo) / i, (hx.hi - hc.hi) / s)
140+
end
141+
142+
Slope(hx, hc, h1 * u.s)
143+
end
144+
145+
function log(u::Slope)
146+
hx = log(u.x)
147+
hc = log(u.c)
148+
149+
i = u.x.lo - u.c.lo
150+
s = u.x.hi - u.c.hi
151+
152+
if (i == 0 || s == 0)
153+
h1 = 1 / u.x
154+
else
155+
h1 = interval((hx.hi - hc.hi) / s, (hx.lo - hc.lo) / i)
156+
end
157+
Slope(hx, hc, h1 * u.s)
158+
end
159+
160+
function sin(u::Slope) # Using derivative to upper bound the slope expansion for now
161+
hx = sin(u.x)
162+
hc = sin(u.c)
163+
hs = cos(u.x)
164+
Slope(hx, hc, hs)
165+
end
166+
167+
function cos(u::Slope) # Using derivative to upper bound the slope expansion for now
168+
hx = cos(u.x)
169+
hc = cos(u.c)
170+
hs = -sin(u.x)
171+
Slope(hx, hc, hs)
172+
end
173+
174+
function tan(u::Slope) # Using derivative to upper bound the slope expansion for now
175+
hx = tan(u.x)
176+
hc = tan(u.c)
177+
hs = (sec(u.x)) ^ 2
178+
Slope(hx, hc, hs)
179+
end
180+
181+
function asin(u::Slope)
182+
hx = asin(u.x)
183+
hc = asin(u.c)
184+
hs = 1 / sqrt(1 - (u.x ^ 2))
185+
Slope(hx, hc, hs)
186+
end
187+
188+
function acos(u::Slope)
189+
hx = acos(u.x)
190+
hc = acos(u.c)
191+
hs = -1 / sqrt(1 - (u.x ^ 2))
192+
Slope(hx, hc, hs)
193+
end
194+
195+
function atan(u::Slope)
196+
hx = atan(u.x)
197+
hc = atan(u.c)
198+
hs = 1 / 1 + (u.x ^ 2)
199+
Slope(hx, hc, hs)
200+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ include("newton1d.jl")
88
include("quadratic.jl")
99
include("test_smiley.jl")
1010
include("linear_eq.jl")
11+
include("slopes.jl")

test/slopes.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using IntervalArithmetic, IntervalRootFinding
2+
using ForwardDiff
3+
using Base.Test
4+
5+
struct Slopes{T}
6+
f::Function
7+
x::Interval{T}
8+
c::T
9+
sol::Interval{T}
10+
end
11+
12+
@testset "Automatic slope expansion" begin
13+
for T in [Float64, BigFloat]
14+
s = interval(T(0.75), T(1.75))
15+
example = Slopes{T}[]
16+
push!(example, Slopes(x->((x + sin(x)) * exp(-x^2)), s, mid(s), interval(T(-2.8), T(0.1))))
17+
push!(example, Slopes(x->(x^4 - 10x^3 + 35x^2 - 50x + 24), s, mid(s), interval(T(-44), T(38.5))))
18+
push!(example, Slopes(x->((log(x + 1.25) - 0.84x) ^ 2), s, mid(s), interval(T(-0.16), T(0.44))))
19+
push!(example, Slopes(x->(0.02x^2 - 0.03exp(-(20(x - 0.875))^2)), s, mid(s), interval(T(0.03), T(0.33))))
20+
push!(example, Slopes(x->(exp(x^2)), s, mid(s), interval(T(6.03), T(33.23))))
21+
push!(example, Slopes(x->(x^4 - 12x^3 + 47x^2 - 60x - 20exp(-x)), s, mid(s), interval(T(-39), T(65.56))))
22+
push!(example, Slopes(x->(x^6 - 15x^4 + 27x^2 + 250), s, mid(s), interval(T(-146.9), T(67.1))))
23+
push!(example, Slopes(x->(atan(cos(tan(x)))), s, mid(s), interval(T(1), T(2))))
24+
push!(example, Slopes(x->(asin(cos(acos(sin(x))))), s, mid(s), interval(T(1.36), T(∞))))
25+
26+
for i in 1:length(example)
27+
@test slope(example[i].f, example[i].x, example[i].c) example[i].sol
28+
end
29+
end
30+
end

0 commit comments

Comments
 (0)