How to update function coefficients during run time

I need to define a function:

ddx(v) = ((v[4] + v[6]) / 2 - v[5]) / X.^ 2

where X is only known at run time and changes with each high level iteration of a loop.

I’ve tried using @eval:
@eval ddx(v) = ((v[4] + v[6]) / 2 - v[5]) / $(X.^ 2)

but I get a applicable method may be too new error when running the program.

What is the proper way to create a compilable function where the coefficients change at run time?

1 Like

Are you sure the original version you wrote (without @eval) doesn’t already do what you want? I would expect it is, making a closure over X so that any changes to the variable X are seen in its run-time evaluation.

If not, you could do something like the following:

_ddx(v,X) = ((v[4] + v[6]) / 2 - v[5]) / X.^ 2 # define once

ddx = let X=X;
    v -> _ddx(v,X)
end # run this every loop to capture the updated X
3 Likes

In case you really do want to invoke the compiler (which might be the case if a high-level iteration of your loop takes a long time to execute, otherwise it wouldn’t be worth it performance-wise), here are two options.

Note that isbits(X) has to hold if you want to move X into the type domain, so in practice it probably has to be a static array, as in StaticArrays.jl.

Untested:

# Example function

some_function_scalar(x, a, b, c) = ((a + b)/2 - c) / x .^ 2

function some_function_vector(x, v)
  (a, b, c) = v
  some_function_scalar(x, a, b, c)
end

# Option 1, move `X` into the type domain

compiled1_impl(f::F, ::Val{x}) where {F, x} = let f = f
  v -> f(x, v)
end

compiled1(f::F, x) where {F} = compiled1_impl(f, Val(x))

# Option 2, move the entire function into the type domain

closure(f::F, x) where {F} = let f = f, x = x
  v -> f(x, v)
end

compiled2_impl(::Val{f}) where {f} = v -> f(v)
compiled2(f::F) where {F} = compiled2_impl(Val(f))
compiled2(f::F, x) where {F} = compiled2(closure(f, x))

Then you can create your compileable function like compiled1(some_function_vector, x) or compiled2(some_function_vector, x), and it will get compiled (with x as a compile-time constant) the first time you run it.

Keep in mind that you also need to use function barriers properly (see the Performance tips page in the Manual), so your inner loops will have to be in their own functions.

See these two previous threads for more:

2 Likes