Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistency with in(x, ::Interval) and iterate(::Interval) #425

Closed
goretkin opened this issue Nov 24, 2020 · 4 comments · Fixed by #571
Closed

Inconsistency with in(x, ::Interval) and iterate(::Interval) #425

goretkin opened this issue Nov 24, 2020 · 4 comments · Fixed by #571

Comments

@goretkin
Copy link

(originally from JuliaReach/ReachabilityAnalysis.jl#373 (comment))
From doing ?in

  in(item, collection) -> Bool
  (item, collection) -> Bool
  (collection, item) -> Bool

  Determine whether an item is in the given collection, in the sense that it is == to one of the values generated by iterating over the collection. Returns a Bool value,
  except if item is missing or collection contains missing but not item, in which case missing is returned (three-valued logic
  (https://en.wikipedia.org/wiki/Three-valued_logic), matching the behavior of any and ==).

  Some collections follow a slightly different definition. For example, Sets check whether the item isequal to one of the elements. Dicts look for key=>value pairs, and the
  key is compared using isequal. To test for the presence of a key in a dictionary, use haskey or k in keys(dict). For these collections, the result is always a Bool and
  never missing.

To help explain, I'll define

my_in(element, collection) = any(==(element),  collection)

which perhaps is more clear as

my_in(element, collection) = any( element == x for x in collection)

and "consistency", and following the documentation of in, means that one of two should be the case:

  1. my_in errors
  2. my_in returns the same answer as in

Interval of IntervalArithmetic.jl wants to both be set and a <:Number:

julia> supertypes(typeof(Interval(0,1)))
(Interval{Float64}, AbstractInterval{Float64}, Real, Number, Any)

and it's a fact of life (unfortunately? fortunately?) that <:Numbers are singleton collections of themselves: https://github.com/JuliaLang/julia/blob/6614645892f03915e4f10b051df9a228c980abc8/base/number.jl#L233 and so

julia> in(0.5, Interval(0, 1))
true

julia> my_in(0.5, Interval(0, 1))
false

One possibility is to just document this. I'd be curious to see if it wreaks any havoc to define new methods on Interval (iterate, length, first, last, maybe copy, ...) that error. This is safer and stricter, and ensures consistency with in.

@goretkin goretkin changed the title Inconsistency with in(x, ::Interval) and iterate(::interval) Inconsistency with in(x, ::Interval) and iterate(::Interval) Nov 24, 2020
@dpsanders
Copy link
Member

OK, fair point I guess.
In some sense we're punning on the meaning of in. But clearly anybody would expect 0.5 ∈ (0..1) to be true.
This is another instantiation of #2 I guess. See also https://github.com/gwater/NumberIntervals.jl

@goretkin
Copy link
Author

That is the only reasonable definition of in, I agree, and furthermore it's consistent with a bunch of other set operations defined on Interval, like intersect (and to a lesser extent union and setdiff).

I don't propose another definition of in, just possibly another definition of iterate, etc. so that those methods error.

@goretkin
Copy link
Author

goretkin commented Nov 24, 2020

Thanks for pointing me to https://github.com/gwater/NumberIntervals.jl . I suspected it would have the same inconsistency since NumberInterval <: Number (by way of <: AbstractFloat), and indeed:

julia> using NumberIntervals: NumberInterval

julia> in(0.5, NumberInterval(0.0, 1.0))
true

julia> my_in(element, collection) = any(==(element),  collection)
my_in (generic function with 1 method)

julia> my_in(0.5, NumberInterval(0.0, 1.0))
missing

@goretkin
Copy link
Author

I was curious about other interval packages, and it seems like the three others I found are not <:Number and so do not run into this inconsistency.

script:

import Intervals
import IntervalSets
import ClosedIntervals
import NumberIntervals
import IntervalArithmetic

my_in(element, collection) = any(==(element),  collection)

function exception_wrap(f)
    function wrapped(args...)
        try
            return (Some(f(args...)), nothing)
        catch e
            return (nothing, e)
        end
    end
    return wrapped
end

function test_in(needle, haystack)
    _my_in = exception_wrap(my_in)
    mi = _my_in(needle, haystack)
    i = in(needle, haystack)
    return (i, mi)
end

intervals = Any[ # avoid https://github.com/JuliaIntervals/IntervalArithmetic.jl/issues/426
    Intervals.Interval(0, 1),
    IntervalSets.Interval(0, 1),
    ClosedIntervals.ClosedInterval(0, 1),
    NumberIntervals.NumberInterval(0, 1),
    IntervalArithmetic.Interval(0, 1),
]

println(stdout, "\n-----\n")
show(stdout, "text/plain", test_in.(Ref(0.5), intervals))
println(stdout, "\n-----\n")
show(stdout, "text/plain", test_in.(Ref(1.5), intervals))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants