I think you should assume you have to perform branching at run-time whenever you want to condition behavior on values (like -1 vs. +1) rather than on types (like Float64 vs. Int64).
So I think it’s going to be very difficult to achieve the goal you have in mind. — John On Jul 30, 2014, at 3:00 AM, Jan Drugowitsch <jdr...@gmail.com> wrote: > Hi all, > > I've been recently running into a presumably frequently occurring problem, > and was wondering what the best way is to solve this in Julia. The problem is > how to dynamically choose amongst a set of functions based on some constant > parameter value (without the use of macros, which usually make my head hurt). > A use case would be the sampler for a probability distribution for which > several algorithms exist, and whose choice depends on the parameters of this > distribution. Given that this distribution is implemented as an immutable > class, one would want to pick the best algorithm at construction of a class > instance rather than evaluating it at every time the sampler is called. > > More specifically, an abstract example is to have some function f(a::BaseA, > x::Number) that chooses among > > f1(x::Number) = x + 10 > f2(x::Number) = x - 10 > > based on if some parameter y, used to construct BaseA(y), is positive of > negative. In addition, we would like to add generic vectorization, akin to > > abstract BaseA > f{T <: Number}(a::BaseA, x::Vector{T}) = [f(a, xi) for xi in x] > > The fastest approach might be > > immutable A0 <: BaseA > y::Float64 > A0(y::Number) = new(float(y)) > end > f(a::A0, x::Number) = a.y >= 0.0 ? f1(x) : f2(x) > f{T <: Number}(a::A0, x::Vector{T}) = a.y >= 0.0 ? [f1(xi) for xi in x] : > [f2(xi) for xi in x] > > that is, re-implementing the vectorization, to avoid a.y >= 0.0 to be called > for every vector element. This seems to defeat the purpose of writing generic > methods. A second possibility is to perform the comparison of y at every > iteration by > > immutable A1 <: BaseA > y::Float64 > A1(y::Number) = new(float(y)) > end > f(a::A1, x::Number) = a.y >= 0.0 ? f1(x) : f2(x) > > For both implementation I find around the same run-time (vector of size 10^8, > no gc): > > A0: elapsed time: 0.687105729 seconds (1600000096 bytes allocated) > A1: elapsed time: 0.696713456 seconds (1600000096 bytes allocated) > > However, I don't find either of them particularly satisfactory. A third > option would be to choose the function at construction of a: > > immutable A2 <: BaseA > f::Function > A2(y::Number) = new(y >= zero(y) ? f1 : f2) > end > f(a::A2, x::Number) = a.f(x) > > However, this is slow (elapsed time: 25.70402182 seconds (7999991920 bytes > allocated)), and defeats type inference (returns Array{Any,10^8}). As last > option would be to construct instances of different classes, depending on y: > > immutable _A3f1 <: BaseA; end > f(a::_A3f1, x::Number) = f1(x) > immutable _A3f2 <: BaseA; end > f(a::_A3f2, x::Number) = f2(x) > A3(y::Number) = y >= zero(y) ? _A3f1() : _A3f2() > > This is (surprisingly) still slower than A0 and A1 (elapsed time: 1.360536247 > seconds (1600000096 bytes allocated)), and just shifts the problem of type > inference one level higher. > > Are there any better solutions to tackle this, or are macros the only way? > > Thanks for your helps, > Jan