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

What is required to use custom types in Intervals? #476

Closed
samuela opened this issue Jun 10, 2021 · 9 comments · Fixed by #593
Closed

What is required to use custom types in Intervals? #476

samuela opened this issue Jun 10, 2021 · 9 comments · Fixed by #593

Comments

@samuela
Copy link

samuela commented Jun 10, 2021

I have a custom numeric type, and I'd like to use it in conjunction (composition?) with IntervalAnalysis.jl. What function implementations are necessary in order to make this happen? Is there an API of sorts for what types can be used as the lower/upper bounds?

I'm currently facing the current error:

ERROR: LoadError: MethodError: no method matching atomic(::Type{IntervalArithmetic.Interval{TracedLeaf}}, ::IntervalArithmetic.Interval{TracedLeaf})
Closest candidates are:
  atomic(::Type{IntervalArithmetic.Interval{T}}, ::IntervalArithmetic.Interval) where T<:AbstractFloat at /Users/skainswo/.julia/packages/IntervalArithmetic/UR6Qe/src/intervals/conversion.jl:122
  atomic(::Type{IntervalArithmetic.Interval{T}}, ::S) where {T<:AbstractFloat, S<:Real} at /Users/skainswo/.julia/packages/IntervalArithmetic/UR6Qe/src/intervals/conversion.jl:95
Stacktrace:
 [1] convert(#unused#::Type{IntervalArithmetic.Interval{TracedLeaf}}, x::IntervalArithmetic.Interval{TracedLeaf})
   @ IntervalArithmetic ~/.julia/packages/IntervalArithmetic/UR6Qe/src/intervals/conversion.jl:20
 [2] Pair
   @ ./pair.jl:12 [inlined]
 [3] Pair(a::String, b::IntervalArithmetic.Interval{TracedLeaf})
   @ Base ./pair.jl:15
 [4] top-level scope
   @ ~/dev/research/julia/boxes/src/test.jl:203
in expression starting at /Users/skainswo/dev/research/julia/boxes/src/test.jl:199

But I'm not able to find the atomic function in the public API: https://juliaintervals.github.io/pages/apiDocs/apiIntervalArithmetic/index.html.

@samuela
Copy link
Author

samuela commented Jun 10, 2021

Update: atomic appears to live here:

"""
atomic(::Type{<:Interval}, x)
Construct the tightest interval of a given type that contains the value `x`.
If `x` is an `AbstractString`, the interval will be created by calling `parse`.
# Examples
Construct an `Interval{Float64}` containing a given `BigFloat`:
```julia
julia> x = big"0.1"
1.000000000000000000000000000000000000000000000000000000000000000000000000000002e-01
julia> i = IntervalArithmetic.atomic(Interval{Float64}, x)
[0.0999999, 0.100001]
julia> i isa Interval{Float64}
true
julia> i.lo <= x <= i.hi
true
julia> i.hi === nextfloat(i.lo)
true
```
Construct an `Interval{Float32}` containing a the real number 0.1 in two ways:
```julia
julia> i1 = IntervalArithmetic.atomic(Interval{Float32}, "0.1")
[0.0999999, 0.100001]
julia> i2 = IntervalArithmetic.atomic(Interval{Float32}, 1//10)
[0.0999999, 0.100001]
julia> i1 === i2
true
julia> i.lo <= 1//10 <= i.hi
true
```
"""
function atomic end
but is not exported.

@Kolaru
Copy link
Collaborator

Kolaru commented Jun 10, 2021

In principle what is needed is all arithmetic operations with rounding mode i. e. +, -, *, / rounded up and rounded down. For efficiency and precision we also have special implementations of trigonometric and other special functions.

In other words, you need a lot.

What kind of numeric type are you interested in?

If your type is build on existing one, e.g.

struct MyType
    x::Float64
end

it is tempting to try Interval{MyType}, but it may be a better choice to reverse the type parameters, that is to define

struct MyType{T}
    x::T
end

and use MyType{Interval{Float64}}.

@samuela
Copy link
Author

samuela commented Jun 10, 2021

Unfortunately, I do need to go the Interval{MyType} route. I'm ok reimplementing/rewrapping lots of +, -, sin, cos, etc as I have to do that anyhow.

Right now, the MethodError: no method matching atomic(::Type{Interval{TracedLeaf}}, ::Interval{TracedLeaf}) error is the only thing I can't wrap my head around. In particular, isn't the right convert call defined here: https://github.com/JuliaIntervals/IntervalArithmetic.jl/blob/master/src/intervals/conversion.jl#L19? It seems as though the convert call in my stack trace should be handled by the implementation on line 19, and not the one on line 20.

@samuela
Copy link
Author

samuela commented Jun 10, 2021

A simpler reproduction of what I'm trying to do is here: https://discourse.julialang.org/t/x-y-works-but-x-y-doesnt/62703

@samuela
Copy link
Author

samuela commented Jun 11, 2021

Here's an even simpler repro:

using IntervalArithmetic
struct Foo <: Real
  v
end
convert(Interval{Foo}, Interval(Foo(0.0), Foo(1.0)))
ERROR: LoadError: MethodError: no method matching atomic(::Type{Interval{Foo}}, ::Interval{Foo})
Closest candidates are:
  atomic(::Type{Interval{T}}, ::Interval) where T<:AbstractFloat at /Users/skainswo/.julia/packages/IntervalArithmetic/UR6Qe/src/intervals/conversion.jl:122
  atomic(::Type{Interval{T}}, ::S) where {T<:AbstractFloat, S<:Real} at /Users/skainswo/.julia/packages/IntervalArithmetic/UR6Qe/src/intervals/conversion.jl:95
Stacktrace:
 [1] convert(#unused#::Type{Interval{Foo}}, x::Interval{Foo})
   @ IntervalArithmetic ~/.julia/packages/IntervalArithmetic/UR6Qe/src/intervals/conversion.jl:20
 [2] top-level scope
   @ ~/dev/research/julia/boxes/src/bug3.jl:5
in expression starting at /Users/skainswo/dev/research/julia/boxes/src/bug3.jl:5

I'm just not sure why it isn't hitting the correct convert method implementation in conversion.jl... 🤔

@Kolaru
Copy link
Collaborator

Kolaru commented Jun 12, 2021

This is a separate issue (#317 and #320) that just appears here out of poor luck. Still it is a symptom of the fact that the package is currently quite brittle when it comes to type management (it was never designed to support generic types).

In other words implementing what you are proposing here might be an uphill battle.

@samuela
Copy link
Author

samuela commented Jun 12, 2021

Thanks! I ended up finding success writing a manual implementation

Base.convert(::Type{Interval{T}}, x::Interval{T}) where {T <: TracedThing} = x

where TracedThing is the type that I'd like to use as lo/hi in my intervals. It's a workaround and not particularly pretty but it does work.

@lbenet
Copy link
Member

lbenet commented Nov 3, 2021

I don't know if this is still useful or not, but changing this line to

convert(::Type{Interval{T}}, x::Interval{T}) where {T<:Real} = x

that is, explicitly stating that T is a subtype of Real, seems to solve the problem of finding the correct method.

@lbenet
Copy link
Member

lbenet commented Nov 3, 2021

This fix was already proposed in #320...

@OlivierHnt OlivierHnt mentioned this issue Dec 1, 2023
1 task
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.

3 participants