-
Notifications
You must be signed in to change notification settings - Fork 17
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
Recommended way of lifting monotonic functions #242
Comments
There was actually an issue about this: What do you think a good syntax would be? Just
to define the lifted |
I would need to lift a callable object (currently done manually here) but I guess |
Hmmm, I'm not sure that this is as trivial as it seems. For example, what do you want to happen if someone calls your function with negative values? Maybe you need to specify the domain. If you have many functions like this, you can use metaprogramming, something like
However, note that the function as written will not give correctly rounded values. |
I guess that using julia> function monot_interval(f, a::Interval)
res = try
Interval(f(a.lo), f(a.hi))
catch
Interval(f(a.hi), f(a.lo))
end
return res
end
monot_interval (generic function with 1 method)
julia> monot_interval( x-> x^3, 0..2)
[0, 8]
julia> monot_interval( x-> -x^3, 0..2)
[-8, -0]
(Awful function name, I know, but you can change it.) I guess this could be wrapped nicely in a macro. |
@dpsanders: of course I will add domain checks, but that is orthogonal. How would I define a version with correct rounding, using a macro? I tried "Apply a (monotone) transformation to an interval by endpoints."
macro monotone_interval(S)
:((f::$S)(x::Interval) = ValidatedNumerics.@interval(f(x.lo), f(x.hi)))
end but julia> macroexpand(quote @monotone_interval Logistic end)
quote # REPL[17], line 1:
(f::Logistic)(#13#x::Interval) = begin # REPL[16], line 2:
(ValidatedNumerics.Interval)(f(((ValidatedNumerics.convert)((ValidatedNumerics.esc
)(ValidatedNumerics.T)),#13#x.lo)).lo,f(((ValidatedNumerics.convert)((ValidatedNumerics.esc)(V
alidatedNumerics.T)),#13#x.hi)).hi)
end
end and those free (I would like to avoid using |
Good question. Indeed it seems to be a macro hygiene issue in |
Does |
Please also update to the latest version of the package that was released yesterday! (0.7) |
With the latest version of the package, the following seems to solve your problem. Note the use of julia v0.5> immutable InvOddsRatio end
julia v0.5> (::Type{InvOddsRatio})(x::Number) = x == Inf ? one(x) : x/(1+x)
julia v0.5> InvOddsRatio(3)
0.75
julia v0.5> macro monotone_interval(S)
:((::Type{$S})(x::Interval) = ValidatedNumerics.@interval($S(x.lo), $S(x.hi)))
end
julia v0.5> @monotone_interval InvOddsRatio
julia v0.5> InvOddsRatio(3..4)
[0.75, 0.800001] By the way, if the functions you need are simple one-liners, it would be possible to define a macro to do
and have it automatically generate everything! |
Hmm, this is almost there but is giving a stack overflow. I'll have to think about this some more. |
My current solution is here. I wonder if you could look at it and tell me if I am doing the right thing. Also, I imagine that rounding is overdone for expressions like |
I don't actually think that's completely right, since if you want to round down The way to get around this problem is to use intervals. The best solution that I can think of is something like this: f(x, dummy) = x == Inf ? one(x) : x/(1+x)
f(x::Number) = f(x, true)
f{T}(x::Interval{T}) = Interval(f(convert(Interval{T}, x.lo), true).lo,
f(convert(Interval{T}, x.hi), true).hi) The idea is that the correct rounding is done by passing in an julia> x = convert(Interval{Float64}, 3.1)
[3.09999, 3.10001]
julia> f(x, true) # gives correct rounding
[0.756097, 0.756098] We pass in separately the Compare the following: your method and my method give slightly different results: julia> setrounding(Float64, RoundDown) do
f(0.1, true)
end
0.09090909090909091
julia> x = convert(Interval{Float64}, 0.1)
[0.0999999, 0.100001]
julia> f(x, true)
[0.090909, 0.0909091]
julia> setdisplay(:full)
6
julia> f(x, true)
Interval(0.09090909090909088, 0.09090909090909093) I have a guaranteed lower and upper bound, whereas with your method I'm not sure if it's actually a lower bound. Indeed, it actually isn't: julia> f(big"0.1", true)
9.09090909090909090909090909090909090909090909090909090909090909090909090909085e-02 |
Thanks! I found that I can use using ValidatedNumerics
f(x::Real) = x == Inf ? one(x) : x/(1+x)
function f_singleton{T <: Real}(x::T)
println("singleton interval around $x")
invoke(f, Tuple{Real}, convert(Interval{T}, x))
end
f(x::Interval) = Interval(f_singleton(x.lo).lo, f_singleton(x.hi).hi) and then julia> f(1.0)
0.5
julia> f(3.1)
0.7560975609756099
julia> f(Interval(3.1))
singleton interval around 3.1
singleton interval around 3.1
[0.756097, 0.756098] I am still experimenting with this so I may have missed some corner case of dispatch, but this seems robust. What do you think? |
I had thought of using |
It's kind of annoying that it's necessary to write 3 methods for each function, but I don't see a way around that. And these functions can definitely be auto-generated with metaprogramming. |
I'm not sure if there's anything to add to We could add a |
I have realized that |
OK, no problem. What is it that you're trying to do? If you just need intervals as sets, and you don't need to worry about all the rounding issues, then you might want to look at Hopefully with traits it will be possible at some point to unify some of these ideas. |
The rigorous way of checking that a function is monotonic involves some tricks which involve the derivatives, with methods rather well established. I thought the idea was that the user takes responsibility, something like using |
Yes of course if we delegate responsibility to the user, that is much easier ;) Do you have a reference for those techniques? I don't see that it would ever be possible to prove that eg. x/(1+x) is monotone over the real line. |
I guess one has to be careful around -1, but elsewhere not including it, I think it should be possible. If I have some time this afternoon, I work on this. |
Actually that particular function I believe is only supposed to be for positive x. |
Then there should be no problem... |
@dpsanders answering the "what am I trying to do" question: initially I wanted "intervals as sets", then read about validated numerics and thought I could get lots of useful things like error analysis for free. Realized that I would like to dispatch on intervals being finite or infinite (one and two-sided), so I wrote yet another way of dealing with the problem. Hopefully with traits one day we can unify, but as things are at the moment working with either |
If I have a univariate monotonic function, what's the recommended way to define it for
Interval
? Is there a utility function for lifting? EgNaturally
works but I am wondering if there is a shorthand (writing many functions).
The text was updated successfully, but these errors were encountered: