From 3dcec42f34bee24d60b373958e9cf8e4a8bf8d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 6 Oct 2025 12:17:03 +0200 Subject: [PATCH 1/6] Correct and complete reference to the standard for boolean operations --- src/intervals/interval_operations/boolean.jl | 43 ++++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/intervals/interval_operations/boolean.jl b/src/intervals/interval_operations/boolean.jl index 4e49ae57..42c96c1b 100644 --- a/src/intervals/interval_operations/boolean.jl +++ b/src/intervals/interval_operations/boolean.jl @@ -3,6 +3,8 @@ # flavor in Section 10.5.9 # Some other (non required) related functions are also present, as well as some # of the "Recommended operations" (Section 10.6.3) +# The requirement for decorated intervals are described in Chapter 12, +# mostly sections 12.12.9 and 12.13.3. # used internally, equivalent to `<` but with `(Inf < Inf) == true` _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) Test whether `x` and `y` are identical. -Implement the `equal` function of the IEEE Standard 1788-2015 (Table 9.3). +Implement the `equal` function of the IEEE Standard 1788-2015 +(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9). """ isequal_interval(x::BareInterval, y::BareInterval) = (inf(x) == inf(y)) & (sup(x) == sup(y)) @@ -49,7 +52,8 @@ const issetequal_interval = isequal_interval Test whether `x` is contained in `y`. -Implement the `subset` function of the IEEE Standard 1788-2015 (Table 9.3). +Implement the `subset` function of the IEEE Standard 1788-2015 +(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9). See also: [`isstrictsubset`](@ref) and [`isinterior`](@ref). """ @@ -100,7 +104,8 @@ isstrictsubset(x) = Base.Fix2(isstrictsubset, x) Test whether `x` is in the interior of `y`. -Implement the `interior` function of the IEEE Standard 1788-2015 (Table 9.3). +Implement the `interior` function of the IEEE Standard 1788-2015 +(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9). See also: [`issubset_interval`](@ref) and [`isstrictsubset`](@ref). """ @@ -129,7 +134,8 @@ isinterior(x, y, z, w...) = isinterior(x, y) & isinterior(y, z, w...) Test whether the given intervals have no common elements. -Implement the `disjoint` function of the IEEE Standard 1788-2015 (Table 9.3). +Implement the `disjoint` function of the IEEE Standard 1788-2015. +(Tables 9.3 and 10.3, and Sections 9.5, 10.5.10 and 12.12.9). """ isdisjoint_interval(x::BareInterval, y::BareInterval) = 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_ Test whether `inf(x) ≤ inf(y)` and `sup(x) ≤ sup(y)`, where `<` is replaced by `≤` for infinite values. -Implement the `less` function of the IEEE Standard 1788-2015 (Table 10.3). +Implement the `less` function of the IEEE Standard 1788-2015 +(Table 10.3, and Sections 10.5.10 and 12.12.9). """ isweakless(x::BareInterval, y::BareInterval) = (inf(x) ≤ inf(y)) & (sup(x) ≤ sup(y)) @@ -176,7 +183,8 @@ end Test whether `inf(x) < inf(y)` and `sup(x) < sup(y)`, where `<` is replaced by `≤` for infinite values. -Implement the `strictLess` function of the IEEE Standard 1788-2015 (Table 10.3). +Implement the `strictLess` function of the IEEE Standard 1788-2015 +(Table 10.3, and Sections 10.5.10 and 12.12.9). """ isstrictless(x::BareInterval, y::BareInterval) = # this may be flavor dependent? Should _strictlessprime be < for cset flavor? _strictlessprime(inf(x), inf(y)) & _strictlessprime(sup(x), sup(y)) @@ -191,7 +199,8 @@ end Test whether any element of `x` is lesser or equal to every elements of `y`. -Implement the `precedes` function of the IEEE Standard 1788-2015 (Table 10.3). +Implement the `precedes` function of the IEEE Standard 1788-2015 +(Table 10.3, and Sections 10.5.10 and 12.12.9). """ precedes(x::BareInterval, y::BareInterval) = sup(x) ≤ inf(y) @@ -205,7 +214,8 @@ end Test whether any element of `x` is strictly lesser than every elements of `y`. -Implement the `strictPrecedes` function of the IEEE Standard 1788-2015 (Table 10.3). +Implement the `strictPrecedes` function of the IEEE Standard 1788-2015 +(Table 10.3, and Sections 10.5.10 and 12.12.9). """ strictprecedes(x::BareInterval, y::BareInterval) = isempty_interval(x) | isempty_interval(y) | (sup(x) < inf(y)) @@ -219,7 +229,8 @@ end Test whether `x` is an element of `y`. -Implement the `isMember` function of the IEEE Standard 1788-2015 (Section 10.6.3). +Implement the `isMember` function of the IEEE Standard 1788-2015 +(Sections 10.6.3 and 12.13.3). """ function in_interval(x::Number, y::BareInterval) isinf(x) && return contains_infinity(y) @@ -246,7 +257,8 @@ in_interval(x) = Base.Fix2(in_interval, x) Test whether `x` contains no elements. -Implement the `isEmpty` function of the IEEE Standard 1788-2015 (Section 10.6.3). +Implement the `isEmpty` function of the IEEE Standard 1788-2015 +(Sections 10.5.10 and 12.12.9). """ isempty_interval(x::BareInterval{T}) where {T<:NumTypes} = (inf(x) == typemax(T)) & (sup(x) == typemin(T)) @@ -263,7 +275,8 @@ isempty_interval(x::AbstractVector) = any(isempty_interval, x) Test whether `x` is the entire real line. -Implement the `isEntire` function of the IEEE Standard 1788-2015 (Section 10.6.3). +Implement the `isEntire` function of the IEEE Standard 1788-2015 +(Sections 10.5.10 and 12.12.9). """ isentire_interval(x::BareInterval{T}) where {T<:NumTypes} = (inf(x) == typemin(T)) & (sup(x) == typemax(T)) @@ -277,6 +290,8 @@ isentire_interval(x::Complex{<:Interval}) = isentire_interval(real(x)) & isentir isnai(x) Test whether `x` is an NaI (Not an Interval). + +Implement the `isNaI` function of the IEEE Standard 1788-2015 (Section 12.12.9). """ isnai(::BareInterval) = false @@ -314,6 +329,9 @@ isunbounded(x::Complex{<:Interval}) = isunbounded(real(x)) | isunbounded(imag(x) Test whether `x` is not empty and bounded. +Implement the `isCommonInterval` function of the IEEE Standard 1788-2015 +(Sections 10.6.3 and 12.13.3). + !!! note This is does not take into consideration the decoration of the interval. """ @@ -346,7 +364,8 @@ isatomic(x::Complex{<:Interval}) = isatomic(real(x)) & isatomic(imag(x)) Test whether `x` contains only a real. -Implement the `isSingleton` function of the IEEE Standard 1788-2015 (Table 9.3). +Implement the `isSingleton` function of the IEEE Standard 1788-2015 +(Sections 10.6.3 and 12.13.3). """ isthin(x::BareInterval) = inf(x) == sup(x) From 517740e56e335f0c7ce4111fa168523b9c44ca2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 6 Oct 2025 12:36:57 +0200 Subject: [PATCH 2/6] Update Ill-formed intervals doc --- docs/src/manual/construction.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/src/manual/construction.md b/docs/src/manual/construction.md index d716bde0..2954b4a4 100644 --- a/docs/src/manual/construction.md +++ b/docs/src/manual/construction.md @@ -126,11 +126,16 @@ interval(2, 1) interval(NaN) ``` -These are all examples of ill-formed intervals, resulting in the decoration `ill`. +These are all examples of ill-formed intervals, +also known as `NaI`, resulting in the decoration `ill`. -!!! danger - 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. +Similarly to the floating point `NaN`, +all boolean operations on an ill-formed interval return `false`. +!!! danger + The decoration `ill` is an indicator that an error has occured. + Therefore, when an ill-formed interval is created, a warning is raised. + Any interval marked by this decoration cannot be trusted and the code needs to be debugged. ### More constructors From c7bb37e2dba6179c537828b2e36c9f7bff9fa131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 6 Oct 2025 13:08:47 +0200 Subject: [PATCH 3/6] Include a comment on hash in the philosophy --- docs/src/philosophy.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/src/philosophy.md b/docs/src/philosophy.md index a121ba96..1a3b8ceb 100644 --- a/docs/src/philosophy.md +++ b/docs/src/philosophy.md @@ -155,6 +155,24 @@ because our intervals are always closed, while the result of `setdiff` can be open. +## `hash` + +The function `hash` is the only case where we do not define the value of +a function based on its interval extension. + +`hash` return a single hash, and not an interval bounding the image +of the function. + +```julia +julia> hash(interval(1, 2)) +0xda823f68b9653b1a + +julia> hash(1.2) in hash(interval(-10, 10)) +false +``` + +This is justified as `hash` is not a mathematical function, +and julia requires that `hash` returns a `UInt`. # Summary @@ -165,3 +183,4 @@ while the result of `setdiff` can be open. | 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 | | Set operations | `in`, `issubset`, `isdisjoint`, `issetequal`, `isempty`, `union`, `intersect` | Always error | Use the `*_interval` function instead (e.g. [`in_interval`](@ref)) | Exceptions | `≈`, `setdiff` | Always error | No meaningful interval extension | +| Hash | `hash` | Hash the interval as a julia object | | From 98b1ab4fb854a84d740bab70ebd71786e558e788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Fri, 10 Oct 2025 09:21:11 +0200 Subject: [PATCH 4/6] Fix typo --- src/intervals/interval_operations/boolean.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intervals/interval_operations/boolean.jl b/src/intervals/interval_operations/boolean.jl index 42c96c1b..16dd3b8b 100644 --- a/src/intervals/interval_operations/boolean.jl +++ b/src/intervals/interval_operations/boolean.jl @@ -333,7 +333,7 @@ Implement the `isCommonInterval` function of the IEEE Standard 1788-2015 (Sections 10.6.3 and 12.13.3). !!! note - This is does not take into consideration the decoration of the interval. + This does not take into consideration the decoration of the interval. """ iscommon(x::BareInterval) = !(isentire_interval(x) | isempty_interval(x) | isunbounded(x)) From 223b0fbaab664df1f9567d02a886182ca0d97587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Fri, 10 Oct 2025 10:01:43 +0200 Subject: [PATCH 5/6] Change display of ill-formed intervals to NaI --- src/display.jl | 2 ++ test/interval_tests/display.jl | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/display.jl b/src/display.jl index 3d704739..166f8e00 100644 --- a/src/display.jl +++ b/src/display.jl @@ -120,6 +120,8 @@ function _str_repr(a::BareInterval{T}, format::Symbol) where {T<:NumTypes} end function _str_repr(a::Interval{T}, format::Symbol) where {T<:NumTypes} + isnai(a) && return "NaI" + # `format` is either `:infsup`, `:midpoint` or `:full` str_interval = _str_basic_repr(a.bareinterval, format) # use `a.bareinterval` to not print a warning if `a` is an NaI if format === :full && str_interval != "∅" diff --git a/test/interval_tests/display.jl b/test/interval_tests/display.jl index 779ed48f..f5c1cdd0 100644 --- a/test/interval_tests/display.jl +++ b/test/interval_tests/display.jl @@ -1,4 +1,8 @@ setprecision(BigFloat, 256) do + @testset "Ill-formed interval" begin + @test sprint(show, MIME("text/plain"), interval(1, -1)) == "NaI" + end + @testset "BareInterval" begin a = bareinterval(-floatmin(Float64), 1.3) large_expo = bareinterval(0, big"1e123456789") # use "small" exponent, cf. JuliaLang/julia#48678 From b176d42e6c5afbd66aa0d9588da844b8fc083401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Fri, 10 Oct 2025 10:02:01 +0200 Subject: [PATCH 6/6] Remove big splat to fix tests --- test/interval_tests/multidim.jl | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/test/interval_tests/multidim.jl b/test/interval_tests/multidim.jl index 89443e82..4d053de9 100644 --- a/test/interval_tests/multidim.jl +++ b/test/interval_tests/multidim.jl @@ -104,25 +104,43 @@ end [(-1 .. -0.5), (0.5 .. 1)], [(-0.5 .. 0), (0.5 .. 1)], [(0 .. 0.5), (0.5 .. 1)], [(0.5 .. 1), (0.5 .. 1)]] @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, vb2, vv) - @test all(isequal_interval.(hull.(vb2...), ib2)) + @test all(enumerate(ib2)) do (i, Ib) + hulled = reduce(hull, [vb2[k][i] for k in eachindex(vb2)]) + isequal_interval(hulled, Ib) + end @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (4, 4)), vb2) @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (1,4)), [[(-1 .. 1), (-1 .. -0.5)], [(-1 .. 1), (-0.5 .. 0)], [(-1 .. 1), (0 .. 0.5)], [(-1 .. 1), (0.5 .. 1)]]) - @test all(isequal_interval.(hull.(mince(ib2, (1,4))...), ib2)) + + vb2bis = mince(ib2, (1,4)) + @test all(enumerate(ib2)) do (i, Ib) + hulled = reduce(hull, [vb2bis[k][i] for k in eachindex(vb2bis)]) + isequal_interval(hulled, Ib) + end ib3 = fill(-1..1, 3) vb3 = mince(ib3, 4) @test length(vb3) == 4^3 - @test all(isequal_interval.(hull.(vb3...), ib3)) + @test all(enumerate(ib3)) do (i, Ib) + hulled = reduce(hull, [vb3[k][i] for k in eachindex(vb3)]) + isequal_interval(hulled, Ib) + end @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (4,4,4)), vb3) @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (2,1,1)), [[(-1 .. 0), (-1 .. 1), (-1 .. 1)], [(0 .. 1), (-1 .. 1), (-1 .. 1)]]) - @test all(isequal_interval.(hull.(mince(ib3, (2,1,1))...), ib3)) + vb3bis = mince(ib3, (2,1,1)) + @test all(enumerate(ib3)) do (i, Ib) + hulled = reduce(hull, [vb3bis[k][i] for k in eachindex(vb3bis)]) + isequal_interval(hulled, Ib) + end ib4 = fill(-1..1, 4) vb4 = mince(ib4, 4) @test length(vb4) == 4^4 - @test all(isequal_interval.(hull.(vb4...), ib4)) + @test all(enumerate(ib4)) do (i, Ib) + hulled = reduce(hull, [vb4[k][i] for k in eachindex(vb4)]) + isequal_interval(hulled, Ib) + end @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (4,4,4,4)), vb4) @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (1,1,1,1)), (ib4,)) end