Skip to content

Commit 7ba84c9

Browse files
authored
Document some edge case behaviors (#735)
* Correct and complete reference to the standard for boolean operations * Update Ill-formed intervals doc * Include a comment on hash in the philosophy * Fix typo * Change display of ill-formed intervals to NaI * Remove big splat to fix tests
1 parent 544c000 commit 7ba84c9

File tree

6 files changed

+88
-21
lines changed

6 files changed

+88
-21
lines changed

docs/src/manual/construction.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,16 @@ interval(2, 1)
126126
interval(NaN)
127127
```
128128

129-
These are all examples of ill-formed intervals, resulting in the decoration `ill`.
129+
These are all examples of ill-formed intervals,
130+
also known as `NaI`, resulting in the decoration `ill`.
130131

131-
!!! danger
132-
The decoration `ill` is an indicator that an error has occured. Therefore, any interval marked by this decoration cannot be trusted and the code needs to be debugged.
132+
Similarly to the floating point `NaN`,
133+
all boolean operations on an ill-formed interval return `false`.
133134

135+
!!! danger
136+
The decoration `ill` is an indicator that an error has occured.
137+
Therefore, when an ill-formed interval is created, a warning is raised.
138+
Any interval marked by this decoration cannot be trusted and the code needs to be debugged.
134139

135140

136141
### More constructors

docs/src/philosophy.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,24 @@ because our intervals are always closed,
155155
while the result of `setdiff` can be open.
156156

157157

158+
## `hash`
159+
160+
The function `hash` is the only case where we do not define the value of
161+
a function based on its interval extension.
162+
163+
`hash` return a single hash, and not an interval bounding the image
164+
of the function.
165+
166+
```julia
167+
julia> hash(interval(1, 2))
168+
0xda823f68b9653b1a
169+
170+
julia> hash(1.2) in hash(interval(-10, 10))
171+
false
172+
```
173+
174+
This is justified as `hash` is not a mathematical function,
175+
and julia requires that `hash` returns a `UInt`.
158176

159177
# Summary
160178

@@ -165,3 +183,4 @@ while the result of `setdiff` can be open.
165183
| Boolean operations | `==`, `<`, `<=`, `iszero`, `isnan`, `isinteger`, `isfinite` | Error if the result can not be guaranteed to be either `true` or `false` | See [`isequal_interval`](@ref) to test equality of intervals, and [`isbounded`](@ref) to test the finiteness of the elements |
166184
| Set operations | `in`, `issubset`, `isdisjoint`, `issetequal`, `isempty`, `union`, `intersect` | Always error | Use the `*_interval` function instead (e.g. [`in_interval`](@ref))
167185
| Exceptions | ``, `setdiff` | Always error | No meaningful interval extension |
186+
| Hash | `hash` | Hash the interval as a julia object | |

src/display.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ function _str_repr(a::BareInterval{T}, format::Symbol) where {T<:NumTypes}
120120
end
121121

122122
function _str_repr(a::Interval{T}, format::Symbol) where {T<:NumTypes}
123+
isnai(a) && return "NaI"
124+
123125
# `format` is either `:infsup`, `:midpoint` or `:full`
124126
str_interval = _str_basic_repr(a.bareinterval, format) # use `a.bareinterval` to not print a warning if `a` is an NaI
125127
if format === :full && str_interval != ""

src/intervals/interval_operations/boolean.jl

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# flavor in Section 10.5.9
44
# Some other (non required) related functions are also present, as well as some
55
# of the "Recommended operations" (Section 10.6.3)
6+
# The requirement for decorated intervals are described in Chapter 12,
7+
# mostly sections 12.12.9 and 12.13.3.
68

79
# used internally, equivalent to `<` but with `(Inf < Inf) == true`
810
_strictlessprime(x::Real, y::Real) = (x < y) | ((isinf(x) | isinf(y)) & (x == y))
@@ -12,7 +14,8 @@ _strictlessprime(x::Real, y::Real) = (x < y) | ((isinf(x) | isinf(y)) & (x == y)
1214
1315
Test whether `x` and `y` are identical.
1416
15-
Implement the `equal` function of the IEEE Standard 1788-2015 (Table 9.3).
17+
Implement the `equal` function of the IEEE Standard 1788-2015
18+
(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9).
1619
"""
1720
isequal_interval(x::BareInterval, y::BareInterval) = (inf(x) == inf(y)) & (sup(x) == sup(y))
1821

@@ -49,7 +52,8 @@ const issetequal_interval = isequal_interval
4952
5053
Test whether `x` is contained in `y`.
5154
52-
Implement the `subset` function of the IEEE Standard 1788-2015 (Table 9.3).
55+
Implement the `subset` function of the IEEE Standard 1788-2015
56+
(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9).
5357
5458
See also: [`isstrictsubset`](@ref) and [`isinterior`](@ref).
5559
"""
@@ -100,7 +104,8 @@ isstrictsubset(x) = Base.Fix2(isstrictsubset, x)
100104
101105
Test whether `x` is in the interior of `y`.
102106
103-
Implement the `interior` function of the IEEE Standard 1788-2015 (Table 9.3).
107+
Implement the `interior` function of the IEEE Standard 1788-2015
108+
(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9).
104109
105110
See also: [`issubset_interval`](@ref) and [`isstrictsubset`](@ref).
106111
"""
@@ -129,7 +134,8 @@ isinterior(x, y, z, w...) = isinterior(x, y) & isinterior(y, z, w...)
129134
130135
Test whether the given intervals have no common elements.
131136
132-
Implement the `disjoint` function of the IEEE Standard 1788-2015 (Table 9.3).
137+
Implement the `disjoint` function of the IEEE Standard 1788-2015.
138+
(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9).
133139
"""
134140
isdisjoint_interval(x::BareInterval, y::BareInterval) =
135141
isempty_interval(x) | isempty_interval(y) | _strictlessprime(sup(y), inf(x)) | _strictlessprime(sup(x), inf(y))
@@ -161,7 +167,8 @@ _isdisjoint_interval(x, y, z, w...) = _isdisjoint_interval(x, y) && _isdisjoint_
161167
Test whether `inf(x) ≤ inf(y)` and `sup(x) ≤ sup(y)`, where `<` is replaced by
162168
`≤` for infinite values.
163169
164-
Implement the `less` function of the IEEE Standard 1788-2015 (Table 10.3).
170+
Implement the `less` function of the IEEE Standard 1788-2015
171+
(Table 10.3, and Sections 10.5.10 and 12.12.9).
165172
"""
166173
isweakless(x::BareInterval, y::BareInterval) = (inf(x) inf(y)) & (sup(x) sup(y))
167174

@@ -176,7 +183,8 @@ end
176183
Test whether `inf(x) < inf(y)` and `sup(x) < sup(y)`, where `<` is replaced by
177184
`≤` for infinite values.
178185
179-
Implement the `strictLess` function of the IEEE Standard 1788-2015 (Table 10.3).
186+
Implement the `strictLess` function of the IEEE Standard 1788-2015
187+
(Table 10.3, and Sections 10.5.10 and 12.12.9).
180188
"""
181189
isstrictless(x::BareInterval, y::BareInterval) = # this may be flavor dependent? Should _strictlessprime be < for cset flavor?
182190
_strictlessprime(inf(x), inf(y)) & _strictlessprime(sup(x), sup(y))
@@ -191,7 +199,8 @@ end
191199
192200
Test whether any element of `x` is lesser or equal to every elements of `y`.
193201
194-
Implement the `precedes` function of the IEEE Standard 1788-2015 (Table 10.3).
202+
Implement the `precedes` function of the IEEE Standard 1788-2015
203+
(Table 10.3, and Sections 10.5.10 and 12.12.9).
195204
"""
196205
precedes(x::BareInterval, y::BareInterval) = sup(x) inf(y)
197206

@@ -205,7 +214,8 @@ end
205214
206215
Test whether any element of `x` is strictly lesser than every elements of `y`.
207216
208-
Implement the `strictPrecedes` function of the IEEE Standard 1788-2015 (Table 10.3).
217+
Implement the `strictPrecedes` function of the IEEE Standard 1788-2015
218+
(Table 10.3, and Sections 10.5.10 and 12.12.9).
209219
"""
210220
strictprecedes(x::BareInterval, y::BareInterval) = isempty_interval(x) | isempty_interval(y) | (sup(x) < inf(y))
211221

@@ -219,7 +229,8 @@ end
219229
220230
Test whether `x` is an element of `y`.
221231
222-
Implement the `isMember` function of the IEEE Standard 1788-2015 (Section 10.6.3).
232+
Implement the `isMember` function of the IEEE Standard 1788-2015
233+
(Sections 10.6.3 and 12.13.3).
223234
"""
224235
function in_interval(x::Number, y::BareInterval)
225236
isinf(x) && return contains_infinity(y)
@@ -246,7 +257,8 @@ in_interval(x) = Base.Fix2(in_interval, x)
246257
247258
Test whether `x` contains no elements.
248259
249-
Implement the `isEmpty` function of the IEEE Standard 1788-2015 (Section 10.6.3).
260+
Implement the `isEmpty` function of the IEEE Standard 1788-2015
261+
(Sections 10.5.10 and 12.12.9).
250262
"""
251263
isempty_interval(x::BareInterval{T}) where {T<:NumTypes} = (inf(x) == typemax(T)) & (sup(x) == typemin(T))
252264

@@ -263,7 +275,8 @@ isempty_interval(x::AbstractVector) = any(isempty_interval, x)
263275
264276
Test whether `x` is the entire real line.
265277
266-
Implement the `isEntire` function of the IEEE Standard 1788-2015 (Section 10.6.3).
278+
Implement the `isEntire` function of the IEEE Standard 1788-2015
279+
(Sections 10.5.10 and 12.12.9).
267280
"""
268281
isentire_interval(x::BareInterval{T}) where {T<:NumTypes} = (inf(x) == typemin(T)) & (sup(x) == typemax(T))
269282

@@ -277,6 +290,8 @@ isentire_interval(x::Complex{<:Interval}) = isentire_interval(real(x)) & isentir
277290
isnai(x)
278291
279292
Test whether `x` is an NaI (Not an Interval).
293+
294+
Implement the `isNaI` function of the IEEE Standard 1788-2015 (Section 12.12.9).
280295
"""
281296
isnai(::BareInterval) = false
282297

@@ -314,8 +329,11 @@ isunbounded(x::Complex{<:Interval}) = isunbounded(real(x)) | isunbounded(imag(x)
314329
315330
Test whether `x` is not empty and bounded.
316331
332+
Implement the `isCommonInterval` function of the IEEE Standard 1788-2015
333+
(Sections 10.6.3 and 12.13.3).
334+
317335
!!! note
318-
This is does not take into consideration the decoration of the interval.
336+
This does not take into consideration the decoration of the interval.
319337
"""
320338
iscommon(x::BareInterval) = !(isentire_interval(x) | isempty_interval(x) | isunbounded(x))
321339

@@ -346,7 +364,8 @@ isatomic(x::Complex{<:Interval}) = isatomic(real(x)) & isatomic(imag(x))
346364
347365
Test whether `x` contains only a real.
348366
349-
Implement the `isSingleton` function of the IEEE Standard 1788-2015 (Table 9.3).
367+
Implement the `isSingleton` function of the IEEE Standard 1788-2015
368+
(Sections 10.6.3 and 12.13.3).
350369
"""
351370
isthin(x::BareInterval) = inf(x) == sup(x)
352371

test/interval_tests/display.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
setprecision(BigFloat, 256) do
2+
@testset "Ill-formed interval" begin
3+
@test sprint(show, MIME("text/plain"), interval(1, -1)) == "NaI"
4+
end
5+
26
@testset "BareInterval" begin
37
a = bareinterval(-floatmin(Float64), 1.3)
48
large_expo = bareinterval(0, big"1e123456789") # use "small" exponent, cf. JuliaLang/julia#48678

test/interval_tests/multidim.jl

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,43 @@ end
104104
[(-1 .. -0.5), (0.5 .. 1)], [(-0.5 .. 0), (0.5 .. 1)],
105105
[(0 .. 0.5), (0.5 .. 1)], [(0.5 .. 1), (0.5 .. 1)]]
106106
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, vb2, vv)
107-
@test all(isequal_interval.(hull.(vb2...), ib2))
107+
@test all(enumerate(ib2)) do (i, Ib)
108+
hulled = reduce(hull, [vb2[k][i] for k in eachindex(vb2)])
109+
isequal_interval(hulled, Ib)
110+
end
108111
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (4, 4)), vb2)
109112
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (1,4)),
110113
[[(-1 .. 1), (-1 .. -0.5)], [(-1 .. 1), (-0.5 .. 0)], [(-1 .. 1), (0 .. 0.5)], [(-1 .. 1), (0.5 .. 1)]])
111-
@test all(isequal_interval.(hull.(mince(ib2, (1,4))...), ib2))
114+
115+
vb2bis = mince(ib2, (1,4))
116+
@test all(enumerate(ib2)) do (i, Ib)
117+
hulled = reduce(hull, [vb2bis[k][i] for k in eachindex(vb2bis)])
118+
isequal_interval(hulled, Ib)
119+
end
112120

113121
ib3 = fill(-1..1, 3)
114122
vb3 = mince(ib3, 4)
115123
@test length(vb3) == 4^3
116-
@test all(isequal_interval.(hull.(vb3...), ib3))
124+
@test all(enumerate(ib3)) do (i, Ib)
125+
hulled = reduce(hull, [vb3[k][i] for k in eachindex(vb3)])
126+
isequal_interval(hulled, Ib)
127+
end
117128
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (4,4,4)), vb3)
118129
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (2,1,1)),
119130
[[(-1 .. 0), (-1 .. 1), (-1 .. 1)], [(0 .. 1), (-1 .. 1), (-1 .. 1)]])
120-
@test all(isequal_interval.(hull.(mince(ib3, (2,1,1))...), ib3))
131+
vb3bis = mince(ib3, (2,1,1))
132+
@test all(enumerate(ib3)) do (i, Ib)
133+
hulled = reduce(hull, [vb3bis[k][i] for k in eachindex(vb3bis)])
134+
isequal_interval(hulled, Ib)
135+
end
121136

122137
ib4 = fill(-1..1, 4)
123138
vb4 = mince(ib4, 4)
124139
@test length(vb4) == 4^4
125-
@test all(isequal_interval.(hull.(vb4...), ib4))
140+
@test all(enumerate(ib4)) do (i, Ib)
141+
hulled = reduce(hull, [vb4[k][i] for k in eachindex(vb4)])
142+
isequal_interval(hulled, Ib)
143+
end
126144
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (4,4,4,4)), vb4)
127145
@test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (1,1,1,1)), (ib4,))
128146
end

0 commit comments

Comments
 (0)