-
Notifications
You must be signed in to change notification settings - Fork 71
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
Rewrite IntervalBox to contain an SVector of Intervals #152
Conversation
Codecov Report
@@ Coverage Diff @@
## master #152 +/- ##
=========================================
- Coverage 92.24% 92.2% -0.05%
=========================================
Files 21 22 +1
Lines 1006 1026 +20
=========================================
+ Hits 928 946 +18
- Misses 78 80 +2
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot! In mi opinion, this is ready to be merged. I guess this closes #149, since it is not anymore needed, except perhaps for the tests.
src/multidim/arithmetic.jl
Outdated
/(a::IntervalBox, b::Real) = IntervalBox( a.v ./ b ) | ||
|
||
Base.broadcast(f, X::IntervalBox) = IntervalBox(f.(X.v)) | ||
Base.broadcast(f, X::IntervalBox, Y::IntervalBox) = IntervalBox( f.(X.v, Y.v) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I truly think that this broadcasting change is very nice, I just noticed that, in order to use a function (say exp
) on an IntervalBox
, you do require the dot-notation. The following uses the current commit (93bb8e0) of this PR:
julia> A = IntervalBox(1..2, 3..4)
[1, 2] × [3, 4]
julia> exp.(A) # this is implemented here
[2.71828, 7.38906] × [20.0855, 54.5982]
julia> exp(A) # throwing an error here is uncomfortable!
ERROR: MethodError: no method matching exp(::IntervalArithmetic.IntervalBox{2,Float64})
Closest candidates are:
exp(::Float16) at math.jl:950
exp(::Complex{Float16}) at math.jl:951
exp(::BigFloat) at mpfr.jl:517
...
This is sort of uncomfortable, because you force the user to write whatever functions using the dot notation instead of the usual functions. This is actually not a problem, a priori, but then it should be explicitly written somewhere. My idea with #151 was to avoid this, i.e., use directly exp(A)
.
Should we allow exp(A)
to simply return exp.(A)
, or shall we leave it as it is now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Julia has moved to requiring exp.(v)
to make explicit that you want to apply exp
element-wise, since exp
of a vector does not make sense.
I don't understand the context where exp
of an IntervalBox
makes sense. I don't actually understand your Taylor
example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Julia has moved to requiring exp.(v) to make explicit that you want to apply exp element-wise, since exp of a vector does not make sense.
Fair enough.
src/multidim/intervalbox.jl
Outdated
|
||
IntervalBox(x::Interval) = IntervalBox( (x,) ) # single interval treated as tuple with one element | ||
|
||
Base.getindex(X::IntervalBox, i) = X.v[i] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This replaces what it was in line 11, before; do we need Base.@propagate_inbounds
here, or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. To be honest I'm not sure, but it probably doesn't hurt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure either... I've never understood why it was needed. Proceed as you prefer.
Just added it. |
The tests from #151 are included here I believe? |
Yes, but using broadcasting, i.e., requiring using the dot notation. See this comment. |
The kind of subtleties seems important in what I'm doing in TaylorModels. There, from time to time I need to evaluate expressions of the form julia> x0
IntervalBox(Interval(0.0, 0.0), Interval(1.0, 1.0))
julia> v
2-element Array{TaylorSeries.TaylorN{IntervalArithmetic.Interval{Float64}},1}:
Interval(1.0, 1.0) x + 𝒪(‖x‖³)
Interval(1.0, 1.0) y + 𝒪(‖x‖³)
julia> x0 + v
ERROR: MethodError: no method matching +(::IntervalArithmetic.IntervalBox{2,Float64}, ::Array{TaylorSeries.TaylorN{IntervalArithmetic.Interval{Float64}},1})
...
julia> x0 .+ v
ERROR: MethodError: no method matching +(::IntervalArithmetic.IntervalBox{2,Float64}, ::TaylorSeries.TaylorN{IntervalArithmetic.Interval{Float64}})
... Can we add methods that at least one of these works? |
I think we would want |
I'm not sure if we actually need to have In any case, it would be nice to have, at least, |
I still do not understand broadcasting properly, so for now the easiest thing will be to define + for an IntervalBox and a Taylor. |
I think that defining Base.broadcast(f, X::IntervalBox, v::AbstractVector) = IntervalBox( f.(X, v)... ) does the trick for all number-interval operations, since it is more specific than something like Base.broadcast(f, A::Any, v::AbstractVector) which is probably currently called when using this PR with |
Good point. Certainly it is worth checking. |
I just checked the proposal of @Kolaru , and it does the work. Thanks for the suggestion! Beside that method, it would be needed as well the one with the arguments exchanged, i.e. |
Currently things like (Unfortunately there were no tests for these.) |
The solution seems to be the following: explicitly say how to broadcast those functions:
|
Other functions: Also variants of |
Another solution is to define a function which turn vector of choose_type(v::SVector{Interval}) = IntervalBox(v)
choose_type(v) = v Then broadcasting becomes Base.broadcast(f, X::IntervalBox) = choose_type(f.(X.v)) and it should work as expected. |
@Kolaru That sounds like a great solution, thanks! |
The failure on 32-bit Windows seems to be a Julia bug: JuliaLang/julia#27402 So I think this is ready to merge. |
Merging! Thanks a lot! |
Fixes #150.
Rewrites
IntervalBox
to contain anSVector
.(Previously
IntervalBox
was a subtype ofStaticVector
.Incorporates #151.
cc @lbenet