Skip to content

Commit 7dd2ab6

Browse files
authored
Allow pointwise equality (#604)
* Fix `isthininteger` for `Complex{<:Interval}` * Allow pointwise equality * Update tests * Add docstring * Remove superfluous methods * Fix broken test
1 parent a105724 commit 7dd2ab6

File tree

5 files changed

+70
-15
lines changed

5 files changed

+70
-15
lines changed

src/intervals/interval_operations/boolean.jl

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ function isinterior(x::Interval, y::Interval)
6464
return isinterior(bareinterval(x), bareinterval(y))
6565
end
6666

67-
isinterior(x::AbstractVector, y::AbstractVector) = all(t -> isinterior(t[1], t[2]), zip(x, y))
67+
function isinterior(x::AbstractVector, y::AbstractVector)
68+
n = length(x)
69+
m = length(y)
70+
n == m || return throw(DimensionMismatch("dimensions must match: x has length $n, y has length $m"))
71+
return all(t -> isinterior(t[1], t[2]), zip(x, y))
72+
end
6873

6974
isinterior(x, y, z, w...) = isinterior(x, y) & isinterior(y, z, w...)
7075
isinterior(x::Complex, y::Complex) =
@@ -86,7 +91,7 @@ isstrictsubset(x::BareInterval, y::BareInterval) = isinterior(x, y)
8691

8792
isstrictsubset(x::Interval, y::Interval) = isinterior(x, y)
8893

89-
isstrictsubset(x::AbstractVector, y::AbstractVector) = any(t -> isinterior(t[1], t[2]), zip(x, y))
94+
isstrictsubset(x::AbstractVector, y::AbstractVector) = isinterior(x, y) & any(t -> isinterior(t[1], t[2]), zip(x, y))
9095

9196
isstrictsubset(x, y, z, w...) = isstrictsubset(x, y) & isstrictsubset(y, z, w...)
9297
isstrictsubset(x::Complex, y::Complex) =
@@ -179,12 +184,13 @@ Test whether `x` is an element of `y`.
179184
180185
Implement the `isMember` function of the IEEE Standard 1788-2015 (Section 10.6.3).
181186
"""
182-
function in_interval(x::Real, y::BareInterval)
187+
function in_interval(x::Number, y::BareInterval)
183188
isinf(x) && return contains_infinity(y)
184189
return inf(y) x sup(y)
185190
end
191+
in_interval(x::Complex, y::BareInterval) = in_interval(real(x), y) & iszero(imag(x))
186192

187-
function in_interval(x::Real, y::Interval)
193+
function in_interval(x::Number, y::Interval)
188194
isnai(y) && return false
189195
return in_interval(x, bareinterval(y))
190196
end
@@ -196,8 +202,8 @@ in_interval(::Interval, ::Interval) =
196202
throw(ArgumentError("`in_interval` is purposely not supported for two interval arguments. See instead `issubset_interval`"))
197203

198204
in_interval(x::Complex, y::Complex) = in_interval(real(x), real(y)) & in_interval(imag(x), imag(y))
199-
in_interval(x::Complex, y::Real) = in_interval(real(x), y) & isthinzero(imag(x))
200-
in_interval(x::Real, y::Complex) = in_interval(x, real(y)) & in_interval(0, imag(y))
205+
in_interval(x::Complex, y::Number) = in_interval(real(x), y) & iszero(imag(x))
206+
in_interval(x::Number, y::Complex) = in_interval(x, real(y)) & in_interval(0, imag(y))
201207

202208
in_interval(x) = Base.Fix2(in_interval, x)
203209

@@ -328,13 +334,18 @@ isthin(x::Complex) = isthin(real(x)) & isthin(imag(x))
328334
Test whether `x` contains only `y`.
329335
"""
330336
isthin(x::BareInterval, y::Number) = inf(x) == sup(x) == y
337+
isthin(x::BareInterval, y::Complex) = isthin(x, real(y)) & iszero(imag(y))
331338

332339
function isthin(x::Interval, y::Number)
333340
isnai(x) && return false
334341
return isthin(bareinterval(x), y)
335342
end
336343

337-
isthin(x::Complex, y::Number) = isthin(real(x), y) & isthin(imag(x), y)
344+
isthin(x::Complex, y::Complex) = isthin(real(x), real(y)) & isthin(imag(x), imag(y))
345+
isthin(x::Complex, y::Number) = isthin(real(x), real(y)) & isthinzero(imag(x))
346+
isthin(x::Number, y::Complex) = isthin(real(x), real(y)) & iszero(imag(y))
347+
348+
isthin(x::BareInterval, y::Interval) = throw(MethodError(isthin, (x, y)))
338349

339350
"""
340351
isthinzero(x)
@@ -376,4 +387,4 @@ function isthininteger(x::Interval)
376387
return isthininteger(bareinterval(x))
377388
end
378389

379-
isthininteger(x::Complex) = isthininteger(real(x)) & isthininteger(imag(x))
390+
isthininteger(x::Complex) = isthininteger(real(x)) & isthinzero(imag(x))

src/intervals/real_interface.jl

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,6 @@ for T ∈ (:BareInterval, :Interval)
107107
Base.isnan(::$T) =
108108
throw(ArgumentError("`isnan` is purposely not supported for intervals. See instead `isnai`"))
109109

110-
Base.isinteger(::$T) =
111-
throw(ArgumentError("`isinteger` is purposely not supported for intervals. See instead `isthininteger`"))
112-
113110
Base.intersect(::$T) =
114111
throw(ArgumentError("`intersect` is purposely not supported for intervals. See instead `intersect_interval`"))
115112

@@ -130,3 +127,47 @@ for T ∈ (:BareInterval, :Interval)
130127
throw(ArgumentError("`setdiff!` is purposely not supported for intervals. See instead `interiordiff`"))
131128
end
132129
end
130+
131+
# allow pointwise equality
132+
133+
"""
134+
==(::BareInterval, ::Number)
135+
==(::Number, ::BareInterval)
136+
==(::Interval, ::Number)
137+
==(::Number, ::Interval)
138+
139+
Test whether an interval is the singleton of a given number. In other words, the
140+
result is true if and only if the interval contains only that number.
141+
142+
!!! note
143+
Comparison between intervals is purposely disallowed. Indeed, equality
144+
between non-singleton intervals has distinct properties, notably ``x = y``
145+
does not imply ``x - y = 0``. See instead [`isequal_interval`](@ref).
146+
"""
147+
Base.:(==)(x::BareInterval, y::Number) = inf(x) == sup(x) == y
148+
Base.:(==)(x::Number, y::BareInterval) = y == x
149+
function Base.:(==)(x::Interval, y::Number)
150+
isnai(x) && return false
151+
return bareinterval(x) == y
152+
end
153+
Base.:(==)(x::Number, y::Interval) = y == x
154+
Base.:(==)(x::BareInterval, y::Interval) = throw(MethodError(==, (x, y)))
155+
Base.:(==)(x::Interval, y::BareInterval) = throw(MethodError(==, (x, y)))
156+
157+
Base.iszero(x::BareInterval) = iszero(inf(x)) & iszero(sup(x))
158+
function Base.iszero(x::Interval)
159+
isnai(x) && return false
160+
return iszero(bareinterval(x))
161+
end
162+
163+
Base.isone(x::BareInterval) = isone(inf(x)) & isone(sup(x))
164+
function Base.isone(x::Interval)
165+
isnai(x) && return false
166+
return isone(bareinterval(x))
167+
end
168+
169+
Base.isinteger(x::BareInterval) = (inf(x) == sup(x)) & isinteger(inf(x))
170+
function Base.isinteger(x::Interval)
171+
isnai(x) && return false
172+
return isinteger(bareinterval(x))
173+
end

test/interval_tests/complex.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@test isequal_interval(a * a, interval(-1))
1414
@test isequal_interval(a + a, interval(2)*interval(im))
1515
@test isthin(a - a, 0)
16-
@test_broken isthin(a / a, 1)
16+
@test isthin(a / a, 1)
1717

1818
@test in_interval(3+2im, c)
1919
@test isequal_interval(hull(a, b), complex(interval(0, 3), interval(1, 4)))

test/interval_tests/consistency.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,10 @@
339339
@test_throws ArgumentError isempty(x)
340340
@test_throws ArgumentError isfinite(x)
341341
@test_throws ArgumentError isnan(x)
342-
@test_throws ArgumentError isinteger(x)
342+
@test isinteger(x)
343+
@test x == 1
344+
@test isone(x)
345+
@test !iszero(x)
343346
end
344347

345348
end

test/interval_tests/forwarddiff.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ end
4646
(t) = ForwardDiff.derivative(ψ, t)
4747
ddψ(t) = ForwardDiff.derivative(dψ, t)
4848
dddψ(t) = ForwardDiff.derivative(ddψ, t)
49-
@test ψ′(0) === (0) && !isguaranteed(ψ′(0))
49+
@test ψ′(0) === (0) && !isguaranteed(ψ′(0))
5050
@test_broken ψ′′(0) === ddψ(0) && !isguaranteed(ψ′′(0)) # rely on `Interval{T}(::Real)` being defined
5151
@test_broken ψ′′′(0) === dddψ(0) && !isguaranteed(ψ′′′(0)) # rely on `Interval{T}(::Real)` being defined
5252
t₀ = interval(0)
@@ -62,6 +62,6 @@ end
6262

6363
# g(x) = 2^x # not guaranteed
6464

65-
@test_broken f′(0) === df(0)
65+
@test f′(0) === df(0)
6666
end
6767
end

0 commit comments

Comments
 (0)