Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Interval constructor fast by not including checks #26

Merged
merged 23 commits into from
Jun 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
29729f6
Fix docstring
dpsanders May 1, 2017
c0614cf
Return to error-free rounding
dpsanders Apr 23, 2017
16422ed
Add standard midpoint method if no alpha given
dpsanders Apr 23, 2017
db4d74d
Remove inner constructor for Interval. Add checked interval function
dpsanders Apr 18, 2017
37d22be
Add boolean to add or remove validity check on Interval
dpsanders Apr 22, 2017
032642d
Re-add interval function
dpsanders Apr 22, 2017
c04bf67
Change some Interval to interval in tests and @interval
dpsanders Apr 23, 2017
92d087b
More Interval->interval fixes
dpsanders Apr 23, 2017
3755f9b
Add interval(a)
dpsanders May 28, 2017
c2d0355
Add sum benchmarks
dpsanders Jun 8, 2017
62421ca
Fixup after rebase
dpsanders Jun 22, 2017
c459399
Add check of IA_VALID environment variable to determine if validity_c…
dpsanders Jun 22, 2017
ecd7f59
Change internal constructor back to allow different types
dpsanders Jun 22, 2017
edca3bd
Add test for Travis environment variable and enable validity checks i…
dpsanders Jun 22, 2017
6ba41f1
Remove incorrect rounding mode
dpsanders Jun 22, 2017
b78255a
Do not export Interval constructor
dpsanders Jun 24, 2017
6fee75e
Fix some constructor tests to use interval and make a..b use interval
dpsanders Jun 24, 2017
dc84de5
Reexport AbstractInterval
dpsanders Jun 24, 2017
2db536b
Re-add elementary function benchmarks
dpsanders Jun 24, 2017
ada3fe9
Add docs for interval and improve error message
dpsanders Jun 24, 2017
237d334
Reinstate and fix test for single NaN that now throws an error
dpsanders Jun 24, 2017
d4628a8
Remove special casing for Travis; add tests of difference between int…
dpsanders Jun 24, 2017
42124cc
Remove simple_mid
dpsanders Jun 24, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ end
end

@benchgroup "Elementary functions" begin
a = Interval(1, 2)

for op in (exp, log, sin, tan)
@bench string(op) $(op)($a)
end
Expand Down
3 changes: 2 additions & 1 deletion src/IntervalArithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import Base:
parse

export
Interval, AbstractInterval,
AbstractInterval, # Interval
interval,
@interval, @biginterval, @floatinterval, @make_interval,
diam, radius, mid, mag, mig, hull,
emptyinterval, ∅, ∞, isempty, isinterior, isdisjoint, ⪽,
Expand Down
9 changes: 5 additions & 4 deletions src/intervals/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -308,16 +308,18 @@ doc"""
mid(a::Interval, α=0.5)

Find the midpoint (or, in general, an intermediate point) at a distance α along the interval `a`. The default is the true midpoint at α=0.5.

Assumes 0 ≤ α ≤ 1.
"""
function mid(a::Interval{T}, α) where T

isempty(a) && return convert(T, NaN)
isentire(a) && return zero(a.lo)

a.lo == -∞ && return nextfloat(a.lo)
a.hi == +∞ && return prevfloat(a.hi)
a.lo == -∞ && return nextfloat(-∞)
a.hi == +∞ && return prevfloat(+∞)

@assert 0 ≤ α ≤ 1
# @assert 0 ≤ α ≤ 1

# return (1-α) * a.lo + α * a.hi # rounds to nearest
return α*(a.hi - a.lo) + a.lo # rounds to nearest
Expand All @@ -338,7 +340,6 @@ end

mid(a::Interval{Rational{T}}) where T = (1//2) * (a.lo + a.hi)


doc"""
diam(a::Interval)

Expand Down
87 changes: 68 additions & 19 deletions src/intervals/intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,38 @@

## Interval type

if haskey(ENV, "IA_VALID") == true
const validity_check = true
else
const validity_check = false
end

abstract type AbstractInterval{T} <: Real end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This was not there when I began reviewing.)
My suggestion (see elsewhere) is that the default is to have the validity being checked.


struct Interval{T<:Real} <: AbstractInterval{T}
lo :: T
hi :: T

function Interval{T}(a::Real, b::Real) where T
function Interval{T}(a::Real, b::Real) where T<:Real

if isnan(a) || isnan(b)
return new(NaN, NaN) # nai
end
if validity_check

if a > b
# empty interval = [∞,-∞]
(isinf(a) && isinf(b)) && return new(a, b)
throw(ArgumentError("Must have a ≤ b to construct Interval(a, b)."))
end
if is_valid_interval(a, b)
new(a, b)

else
throw(ArgumentError("Interval of form [$a, $b] not allowed. Must have a ≤ b to construct interval(a, b)."))
end

# The IEEE Std 1788-2015 states that a < Inf and b > -Inf;
# see page 6, "bounds".
if a == Inf || b == -Inf
throw(ArgumentError(
"Must satisfy `a < Inf` and `b > -Inf` to construct Interval(a, b)."))
end

return new(a,b)
new(a, b)

end
end



## Outer constructors

Interval(a::T, b::T) where T<:Real = Interval{T}(a, b)
Expand All @@ -57,6 +58,54 @@ Interval{T}(x) where T = Interval(convert(T, x))

Interval{T}(x::Interval) where T = convert(Interval{T}, x)

"""
is_valid_interval(a::Real, b::Real)

Check if `(a, b)` constitute a valid interval
"""
function is_valid_interval(a::Real, b::Real)

# println("isvalid()")

if isnan(a) || isnan(b)
if isnan(a) && isnan(b)
return true
else
return false
end
end

if a > b
if isinf(a) && isinf(b)
return true # empty interval = [∞,-∞]
else
return false
end
end

if a == Inf || b == -Inf
return false
end

return true
end

"""
interval(a, b)

`interval(a, b)` checks whether [a, b] is a valid `Interval`, which is the case if `-∞ <= a <= b <= ∞`, using the (non-exported) `is_valid_interval` function. If so, then an `Interval(a, b)` object is returned; if not, then an error is thrown.
"""
function interval(a::Real, b::Real)
if !is_valid_interval(a, b)
throw(ArgumentError("`[$a, $b]` is not a valid interval. Need `a ≤ b` to construct `interval(a, b)`."))
end

return Interval(a, b)
end

interval(a::Real) = interval(a, a)

Copy link
Member

@lbenet lbenet Jun 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand interval always checks the validity of the construction, while
Interval does not (currently as defualt) unless IA_VALID=true. Is this correct?

Yes, exactly: the user is supposed to use the interval function to build an Interval, rather than the Interval constructor itself. Since the interval function does perform checks, I don't think there is any problem with standards compliance.

In fact, my idea would be that the Interval constructor should not even be exported, so that it cannot be used "accidentally".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add some docstrings here?


## Include files
include("special.jl")
include("macros.jl")
Expand All @@ -75,11 +124,11 @@ include("hyperbolic.jl")

# a..b = Interval(convert(Interval, a).lo, convert(Interval, b).hi)

..(a::Integer, b::Integer) = Interval(a, b)
..(a::Integer, b::Real) = Interval(a, nextfloat(float(b)))
..(a::Real, b::Integer) = Interval(prevfloat(float(a)), b)
..(a::Integer, b::Integer) = interval(a, b)
..(a::Integer, b::Real) = interval(a, nextfloat(float(b)))
..(a::Real, b::Integer) = interval(prevfloat(float(a)), b)

..(a::Real, b::Real) = Interval(prevfloat(float(a)), nextfloat(float(b)))
..(a::Real, b::Real) = interval(prevfloat(float(a)), nextfloat(float(b)))

macro I_str(ex) # I"[3,4]"
@interval(ex)
Expand Down
2 changes: 1 addition & 1 deletion src/intervals/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,5 @@ function make_interval(T, expr1, expr2)

expr2 = transform(expr2[1], :convert, :(Interval{$T}))

:(Interval(($expr1).lo, ($expr2).hi))
:(interval(($expr1).lo, ($expr2).hi))
end
1 change: 0 additions & 1 deletion src/multidim/intervalbox.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# This file is part of the IntervalArithmetic.jl package; MIT licensed


doc"""An `IntervalBox` is an $N$-dimensional rectangular box, given
by a Cartesian product of $N$ `Interval`s.
"""
Expand Down
11 changes: 11 additions & 0 deletions test/interval_tests/consistency.jl
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,15 @@ c = @interval(0.25, 4.0)
@test !iszero(Interval(0.0, nextfloat(0.0)))
end

@testset "Difference between Interval and interval" begin
@test interval(1, 2) == Interval(1, 2)

@test inf(Interval(3, 2)) == 3
@test_throws ArgumentError interval(3, 2)

@test sup(Interval(Inf, Inf)) == Inf
@test_throws ArgumentError interval(Inf, Inf)

end

end
28 changes: 14 additions & 14 deletions test/interval_tests/construction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,20 @@ using Base.Test

# Disallowed conversions with a > b

@test_throws ArgumentError Interval(2, 1)
@test_throws ArgumentError Interval(big(2), big(1))
@test_throws ArgumentError Interval(BigInt(1), 1//10)
@test_throws ArgumentError Interval(1, 0.1)
@test_throws ArgumentError Interval(big(1), big(0.1))
@test_throws ArgumentError interval(2, 1)
@test_throws ArgumentError interval(big(2), big(1))
@test_throws ArgumentError interval(BigInt(1), 1//10)
@test_throws ArgumentError interval(1, 0.1)
@test_throws ArgumentError interval(big(1), big(0.1))

Copy link
Member

@lbenet lbenet Jun 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my comment about interval is correct (interval checks the validity and Interval may not), I think it is a good idea to test the differences among Interval and interval when their behavior differs.

EDIT: Fix the 's

@test_throws ArgumentError @interval(2, 1)
@test_throws ArgumentError @interval(big(2), big(1))
@test_throws ArgumentError @interval(big(1), 1//10)
@test_throws ArgumentError @interval(1, 0.1)
@test_throws ArgumentError @interval(big(1), big(0.1))
@test_throws ArgumentError Interval(Inf)
@test_throws ArgumentError Interval(-Inf, -Inf)
@test_throws ArgumentError Interval(Inf, Inf)
@test_throws ArgumentError interval(Inf)
@test_throws ArgumentError interval(-Inf, -Inf)
@test_throws ArgumentError interval(Inf, Inf)

# Conversion to Interval without type
@test convert(Interval, 1) == Interval(1.0)
Expand Down Expand Up @@ -254,12 +254,8 @@ end

# issue 192:
@testset "Disallow a single NaN in an interval" begin
a = Interval(NaN, 2)
@test isnan(a.lo) && isnan(a.hi)

a = Interval(Inf, NaN)
@test isnan(a.lo) && isnan(a.hi)

@test_throws ArgumentError interval(NaN, 2)
@test_throws ArgumentError interval(Inf, NaN)
end

# issue 206:
Expand Down Expand Up @@ -305,3 +301,7 @@ end
x = 3..4
@test @interval(f(x.lo), f(x.hi)) == Interval(0.75, 0.8)
end

@testset "a..b with a > b" begin
@test_throws ArgumentError 3..2
end
2 changes: 1 addition & 1 deletion test/interval_tests/numeric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ end
@test Interval(1,2) ^ -3 == Interval(1/8, 1.0)
@test Interval(0,3) ^ -3 == @interval(1/27, Inf)
@test Interval(-1,2) ^ -3 == entireinterval()
@test_throws ArgumentError Interval(-1, -2) ^ -3 # wrong way round
@test_throws ArgumentError interval(-1, -2) ^ -3 # wrong way round
@test Interval(-3,2) ^ (3//1) == Interval(-27, 8)
@test Interval(0.0) ^ 1.1 == Interval(0, 0)
@test Interval(0.0) ^ 0.0 == emptyinterval()
Expand Down
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using IntervalArithmetic
using Base.Test

const Interval = IntervalArithmetic.Interval

# Interval tests:

setformat(:full)
Expand Down