Creation of function using eval

Hi

I have a small problem regarding the dynamic creation of functions from expressions. My situation is the following: A process dynamically generates a random expression which is then evaluated on given samples and an error is calculated with regard to expected outputs. This information is later used to update the creation process for the expressions etc.

Here is what I currently do:

function run(X, Y, root)
    s = sample(root) #Gives me the random expression
    f = eval(:(x -> $s))
    ev = [Base.invokelatest(f, x) for x in X]
    return  mse(ev, Y)
end

This works, but the expressions I get are pretty quick to evaluate, so I have the feeling that invokelatest adds quite a bit of overhead (Testing for some small functions it seems that invokelatest is about 10 times slower than just directly calling a function)

Is this really the best I can do in Julia? I just want to create a local function dynamically. It does not depend on any global things and it only has to be available in this scope, so I am slightly confused why this is made so difficult and also slow.

Or did I just miss something and this can be done in a fundamentally different way?

1 Like

What exactly goes wrong if you just invoke the new function?

    ev = [f(x) for x in X]

You can define a function that run the rest of the current function and invokelast that.
And no you can’t do better than that since it’s the most the compiler can infer about your code.

I get

MethodError: no method matching (::##11#12)(::Float64)
The applicable method may be too new: running in world age 31743, while current world is 31744.
Closest candidates are:
  #11(::Any) atptree.jl:205 (method too new to be called from this world context.)
run(::Array{Float64,1}, ::Array{Float64,1}, ::ChoiceNode) at ptree.jl:217
include_string(::String, ::String) at loading.jl:515
eval(::Module, ::Any) at boot.jl:235
...

Which seems to be a new restriction introduced in v0.6 (if I understood that correctly(

FWIW, since f can’t be inferred either on old versions, this is the performance you’ve got on older julia versions all along.

Hmm. Yes, that’s what I don’t really get.

I define something like f = eval(:(x -> x*x+sin(x))) and then I want to call it on a Float64. So I would just expect that it is then compiled for a Float and executed. The problem does not seem to be that the compiler is not able to infer anything but that Julia just prevents me from calling the function I just created in a normal way.

And it is possible since in Julia v0.5 it works.

But wrapping it in another function and calling that one once using invokelatest is probably a reasonable solution.

Thanks

OK. So it was just better hidden? Thanks!

It’s not hidden, the function f itself is inferred, the caller is not, that also the only difference invokelatest has.