Isbits functions with parameter arguments

Imagine, I have a function that depends on some parameters, like step functions in DIfferentialEuations.jl. I would like to define these parameters once and avoid passing them as arguments at every function call. As a solution I can use function-like objects:

struct PFunction <: Function
    func :: Function
    p :: Tuple
end

function (pfunc::PFunction)(x...)
    pfunc.func(x..., pfunc.p)
end

function foo(x, y, p)
    a, b = p
    return a * x + b * y
end

p1 = (1, 2)
pfoo1 = PFunction(foo, p1)

p2 = (3, 4)
pfoo2 = PFunction(foo, p2)

@show pfoo1(1, 1)
@show pfoo2(1, 1)
pfoo1(1, 1) = 3
pfoo2(1, 1) = 7

Thus, my goal is reached: instead of calling foo as foo(x, y, p), I can use just pfoo(x, y).

However, since the type of PFunction is not isbits type, I can not use such objects in CUDA kernels. Can you please suggest any solution that will allow me to obtain the same functions behavior inside CUDA kernels. I guess, I need to look towards metaprogramming, but I have no experience with macros so far.

How about:

julia> struct PFunction{T<:Tuple} <: Function
       p::T
       end

julia> function (pfunc::PFunction)(x,y)
         foo(x, y, pfunc.p)
       end

julia> function foo(x, y, p)
           a, b = p
           return a * x + b * y
       end
foo (generic function with 1 method)

julia> p1 = (1, 2)
(1, 2)

julia> pfoo1 = PFunction(p1)
(::PFunction{Tuple{Int64,Int64}}) (generic function with 1 method)

julia> pfoo1(1,1)
3

julia> isbits(pfoo1)

Note, that the type-parameter of PFunction is needed to make it isbits, as Tuple is not a concrete type.

2 Likes

Oh, thank you very much! With your approach even the original code seems to work:

struct PFunction{F<:Function, T<:Tuple} <: Function
    func :: F
    p :: T
end

function (pfunc::PFunction)(x...)
    pfunc.func(x..., pfunc.p)
end

function foo(x, y, p)
    a, b = p
    return a * x + b * y
end

p = (1, 2)
pfoo = PFunction(foo, p)

@show pfoo(1, 1)
@show isbits(pfoo)
pfoo(1, 1) = 3
isbits(pfoo) = true

Now I have to understand why type-parameters work this way. Looks a bit counterintuitive to me.

Ok, I assumed that functions are not isbits, but they are and thus parameterizing them works.

See https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-fields-with-abstract-type-1

Thank you. Now its clear.