JuliaApproximation / DomainSets.jl

A Julia package for describing domains as continuous sets of elements
MIT License
72 stars 12 forks source link

Domains don't mix well with Julia Set #41

Closed daanhb closed 4 years ago

daanhb commented 5 years ago

A Domain has an eltype, but it is not iterable. This leads to some problems when using functionality that is defined with sets in mind.

Here is a simple problem:

julia> using DomainSets

julia> Set(0..1)
ERROR: MethodError: Cannot `convert` an object of type Interval{:closed,:closed,Int64} to an object of type Int64
Closest candidates are:
  convert(::Type{T<:Number}, ::T<:Number) where T<:Number at number.jl:6
  convert(::Type{T<:Number}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T<:Integer}, ::Ptr) where T<:Integer at pointer.jl:23

You can not create a Set that contains a Domain this way. The way to do it is:

julia> Set([0..1])
Set(Interval{:closed,:closed,Int64}[0..1])

That is fair enough, but it leads to other issues. For example, we can not compute the intersection of three domains:

julia> using DomainSets

julia> I = 0..1
0..1

julia> intersect(I, I)
0..1

julia> intersect(I, I, I)
ERROR: MethodError: Cannot `convert` an object of type Interval{:closed,:closed,Int64} to an object of type Int64
Closest candidates are:
  convert(::Type{T<:Number}, ::T<:Number) where T<:Number at number.jl:6
  convert(::Type{T<:Number}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T<:Integer}, ::Ptr) where T<:Integer at pointer.jl:23
  ...
Stacktrace:
 [1] setindex!(::Dict{Int64,Nothing}, ::Nothing, ::Interval{:closed,:closed,Int64}) at ./dict.jl:373
 [2] push!(::Set{Int64}, ::Interval{:closed,:closed,Int64}) at ./set.jl:48
 [3] union!(::Set{Int64}, ::Interval{:closed,:closed,Int64}) at ./abstractset.jl:81
 [4] Set{Int64}(::Interval{:closed,:closed,Int64}) at ./set.jl:10
 [5] _Set(::Interval{:closed,:closed,Int64}, ::Base.HasEltype) at ./set.jl:23
 [6] Set(::Interval{:closed,:closed,Int64}) at ./set.jl:21
 [7] _shrink(::Function, ::Interval{:closed,:closed,Int64}, ::Tuple{Interval{:closed,:closed,Int64},Interval{:closed,:closed,Int64}}) at ./array.jl:2396
 [8] intersect(::Interval{:closed,:closed,Int64}, ::Interval{:closed,:closed,Int64}, ::Interval{:closed,:closed,Int64}) at ./array.jl:2400
 [9] top-level scope at none:0

The two-argument function succeeds because we defined it ourselves. The three-argument version fails because the fallback in Julia tries to make a Set out of the domains.

Also, we currently have this inconsistent behaviour of union:

julia> I = 0..1
0..1

julia> union(I)
a union of 1 domains:
        1.      : 0..1

julia> union(I,I)
0..1

julia> union(I,I,I)
a union of 3 domains:
        1.      : 0..1
        2.      : 0..1
        3.      : 0..1

Of course we could fix intersect and union ourselves, but perhaps there is a better change that would make Domain integrate better with the existing Julia functions.

daanhb commented 5 years ago

And I just realized that IntervalSets has the same problem:

julia> using IntervalSets

julia> I = 0..1
0..1

julia> intersect(I, I)
0..1

julia> intersect(I, I, I)
ERROR: MethodError: no method matching iterate(::Interval{:closed,:closed,Int64})
Closest candidates are:
  iterate(::Core.SimpleVector) at essentials.jl:568
  iterate(::Core.SimpleVector, ::Any) at essentials.jl:568
  iterate(::ExponentialBackOff) at error.jl:199
  ...
Stacktrace:
 [1] union!(::Set{Int64}, ::Interval{:closed,:closed,Int64}) at ./abstractset.jl:80
 [2] Set{Int64}(::Interval{:closed,:closed,Int64}) at ./set.jl:10
 [3] _Set(::Interval{:closed,:closed,Int64}, ::Base.HasEltype) at ./set.jl:23
 [4] Set(::Interval{:closed,:closed,Int64}) at ./set.jl:21
 [5] _shrink(::Function, ::Interval{:closed,:closed,Int64}, ::Tuple{Interval{:closed,:closed,Int64},Interval{:closed,:closed,Int64}}) at ./array.jl:2396
 [6] intersect(::Interval{:closed,:closed,Int64}, ::Interval{:closed,:closed,Int64}, ::Interval{:closed,:closed,Int64}) at ./array.jl:2400
 [7] top-level scope at none:0
daanhb commented 5 years ago

I filed this issue to remember the problem. In the code where I hit it, I replaced intersect(list_of_domains...) with reduce(intersect, list_of_domains) and that works.

daanhb commented 4 years ago

This issue is not so bad, since the problem doesn't arise when using , which only calls a two-argument intersect. Closing this for now until it comes up in a case where it is clearly a problem.