Skip to content

Add support for unbounded intervals #123

@hyrodium

Description

@hyrodium

As discussed in #67 (comment), we currently don't support unbounded intervals.
This issue is a proposal for adding types such as

  • LeftUnboundedInterval{R,T} $\{x \ | \ x < a\}$
  • RightUnboundedInterval{L,T} $\{x \ | \ a < x\}$
  • ComplementInterval{L,R,T} $\{x \ | \ x < a \ \text{or} \ x > b\}$.

Define new concrete types, or allow a new type parameter?

We have the following two choices to implement unbounded intervals.

  • Define new LeftUnboundedInterval{R, T} and RightUnboundedInterval{L, T}.
  • Allow a new type parameter :unbounded. (This is the same approach as Intervals.jl)

I prefer the first, because:

julia> using Intervals

julia> i = Interval{Int,Unbounded,Unbounded}(nothing,nothing)  # (-∞,+∞)
Interval{Int64, Unbounded, Unbounded}(nothing, nothing)

julia> 3 in i
true

julia> "a" in i  # confusing
true

julia> i = Interval{Int,Open,Unbounded}(1,nothing)  # (1,+∞)
Interval{Int64, Open, Unbounded}(1, nothing)

julia> 3 in i
true

julia> "a" in i
ERROR: MethodError: no method matching isless(::String, ::Int64)
Closest candidates are:
  isless(::AbstractString, ::AbstractString) at strings/basic.jl:344
  isless(::AbstractFloat, ::Real) at operators.jl:186
  isless(::Real, ::Real) at operators.jl:434
  ...

Where the proposed type LeftUnboundedInterval{L,R,T} is a subtype of AbstractInterval{T}.
I think we need another abstract type just like IntervalSets.TypedEndpointsInterval.

What should the set operator return?

i1 = RightUnboundedInterval{:open, Int}(3) is a interval $(3, +∞)$, and i2 = LeftUnboundedInterval{:closed, Int}(5) is a interval $(-\infty, 5]$.
Then, what should i1 ∪ i2 be? Should we define a new type for $(-∞, +∞)$?
I prefer not to define the new type because:

  • The new type represents a whole real number, but we even don't have a special type for an empty set.
    • 2..1 is an example of an empty interval
  • The new type has the same problem as in(::Interval{Int64, Unbounded, Unbounded}) method, already discussed abobe.
  • If $(-∞, 1) \cup (2, \infty)$ throws an error, then the correct return is only $(-∞, ∞)$ which is really trivial.

Here, I propose adding a new type with ComplementInterval{L,R,T} which represents sets such as $\{x \ | \ x < a \ \text{or} \ x > b\}$, then the ComplementInterval(2,1) represents the whole real numbers.
With these methods and types, the following methods can be defined:

  • complement(::Interval) type-stable
  • complement(::LeftUnboundedInterval) type-stable
  • complement(::RightUnboundedInterval) type-stalbe
  • union(::LeftUnboundedInterval, ::LeftUnboundedInterval) type-stability depends on the boundaries
  • union(::RightUnboundedInterval, ::LeftUnboundedInterval) type-stable
  • union(::LeftUnboundedInterval, ::RightUnboundedInterval) type-stable
  • union(::RightUnboundedInterval, ::RightUnboundedInterval) type-stability depends on the boundaries
  • intersection(::LeftUnboundedInterval, ::LeftUnboundedInterval) type-stability depends on the boundaries
  • intersection(::RightUnboundedInterval, ::LeftUnboundedInterval) type-stable
  • intersection(::LeftUnboundedInterval, ::RightUnboundedInterval) type-stable
  • intersection(::RightUnboundedInterval, ::RightUnboundedInterval) type-stability depends on the boundaries
  • intersection(::Interval, ::LeftUnboundedInterval) type-stability depends on the boundaries
  • intersection(::Interval, ::LeftUnboundedInterval) type-stability depends on the boundaries
  • intersection(::Interval, ::RightUnboundedInterval) type-stability depends on the boundaries
  • intersection(::Interval, ::RightUnboundedInterval) type-stability depends on the boundaries
  • intersection(::LeftUnboundedInterval, ::Interval) type-stability depends on the boundaries
  • intersection(::RightUnboundedInterval, ::Interval) type-stability depends on the boundaries
  • intersection(::LeftUnboundedInterval, ::Interval) type-stability depends on the boundaries
  • intersection(::RightUnboundedInterval, ::Interval) type-stability depends on the boundaries

The following methods might throw ArgumentError just like union(1..2, 3..4).

  • union(::Interval, ::LeftUnboundedInterval) type-stability depends on the boundaries
  • union(::Interval, ::LeftUnboundedInterval) type-stability depends on the boundaries
  • union(::Interval, ::RightUnboundedInterval) type-stability depends on the boundaries
  • union(::Interval, ::RightUnboundedInterval) type-stability depends on the boundaries
  • union(::LeftUnboundedInterval, ::Interval) type-stability depends on the boundaries
  • union(::RightUnboundedInterval, ::Interval) type-stability depends on the boundaries
  • union(::LeftUnboundedInterval, ::Interval) type-stability depends on the boundaries
  • union(::RightUnboundedInterval, ::Interval) type-stability depends on the boundaries

Questions

  • Do you have any thoughts on my proposal?
  • Is it okay to have ComplementInterval <: AbstractInterval?
    • Note that a complement of an interval is not an interval.
  • What should the return type of complement(::ComplementInterval{:open,:closed}) be?
    • Interval{:open,:closed}; this implies ComplementInterval{:open,:closed} is a complement of Interval{:open,:closed}
    • Interval{:closed,:open}; this implies the endpoints of ComplementInterval are specified directly.
  • What should the type hierarchy be?
    • With abstract types TypedRightUnboundedInterval, TypedLeftUnboundedInterval, and TypedEndpointsComplementInterval
      • RightUnboundedInterval{L,T} <: TypedRightUnboundedInterval{L,T} <: AbstractInterval{T}
      • LeftUnboundedInterval{R,T} <: TypedLeftUnboundedInterval{R,T} <: AbstractInterval{T}
      • ComplementInterval{L,R,T} <: TypedEndpointsComplementInterval{L,R,T} <: AbstractInterval{T}
    • With more abstract type UnboundedInterval
      • UnboundedInterval{T} <: AbstractInterval{T}
      • RightUnboundedInterval{L,T} <: TypedRightUnboundedInterval{L,T} <: UnboundedInterval{T}
      • LeftUnboundedInterval{R,T} <: TypedLeftUnboundedInterval{R,T} <: UnboundedInterval{T}
      • ComplementInterval{L,R,T} <: TypedEndpointsComplementInterval{L,R,T} <: UnboundedInterval{T}
    • With another abstract type AbstractOrderedSet{T}
      • AbstractInterval{T} <: AbstractOrderedSet{T} <: Domain{T}
      • TypedEndpointsComplementInterval{L,R,T} <: AbstractOrderedSet{T} <: Domain{T}

Any feedback is welcomed!

(cc: @timholy @dlfivefifty @daanhb @omus)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions