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

Reply via email to