My general application of interest requires a vector of functions of the form f(x, y, p), for some known parameters p. I put the functions in a Tuple so that their types are known, and define for example the following struct:
struct Point{T,N}
x::T
y::T
F::Tuple{Vararg{Function,N}}
F_params::Tuple{Vararg{Union{Nothing, AbstractVector{T}}, N}}
end
The F_params is given the type shown so that a user can provide a Tuple with an element for each function, and either provide a vector of parameters or no parameters at all (nothing). I need to have functions that take in a Point as input, for example:
function TestFnc(pt::Point{T,N}) where {T,N}
res = 0.0
for (f, p) in zip(pt.F, pt.F_params)
res = res + f(pt.x, pt.y, p)
end
return res
end
F1 = (x, y, p) -> x^2 + y^2 - p[1]
F2 = (x, y, p) -> x*y
F3 = (x, y, p) -> x + y + p[2] - p[3]*p[1]
p₁ = [1.0]; p₂ = nothing; p₃ = [1.0, 2.0, 3.0]
x = 0.2; y = 0.7
pt = Point(x, y, (F1, F2, F3), (p₁, p₂, p₃))
When I use these definitions, I get type instabilities according to @code_warntype (see below). How can I improve my struct so that all types are properly inferred?
@code_warntype TestFnc(pt)
MethodInstance for TestFnc(::Point{Float64, 3})
from TestFnc(pt::Point{T, N}) where {T, N} in Main at c:\Users\licer\testjl.jl:7
Static Parameters
T = Float64
N = 3
Arguments
#self#::Core.Const(TestFnc)
pt::Point{Float64, 3}
Locals
@_3::Union{Nothing, Tuple{Tuple, Tuple}}
res::Any
@_5::Int64
p::Any
f::Any
Body::Any
1 ─ (res = 0.0)
│ %2 = Base.getproperty(pt, :F)::Tuple{Function, Function, Function}
│ %3 = Base.getproperty(pt, :F_params)::Tuple{Union{Nothing, AbstractVector{Float64}}, Union{Nothing, AbstractVector{Float64}}, Union{Nothing, AbstractVector{Float64}}}
│ %4 = Main.zip(%2, %3)::Base.Iterators.Zip
│ (@_3 = Base.iterate(%4))
│ %6 = (@_3 === nothing)::Bool
│ %7 = Base.not_int(%6)::Bool
└── goto #4 if not %7
2 ┄ %9 = @_3::Tuple{Tuple, Tuple}
│ %10 = Core.getfield(%9, 1)::Tuple
│ %11 = Base.indexed_iterate(%10, 1)::Core.PartialStruct(Tuple{Any, Int64}, Any[Any, Core.Const(2)])
│ (f = Core.getfield(%11, 1))
│ (@_5 = Core.getfield(%11, 2))
│ %14 = Base.indexed_iterate(%10, 2, @_5::Core.Const(2))::Core.PartialStruct(Tuple{Any, Int64}, Any[Any, Core.Const(3)])
│ (p = Core.getfield(%14, 1))
│ %16 = Core.getfield(%9, 2)::Tuple
│ %17 = res::Any
│ %18 = Base.getproperty(pt, :x)::Float64
│ %19 = Base.getproperty(pt, :y)::Float64
│ %20 = (f)(%18, %19, p)::Any
│ (res = %17 + %20)
│ (@_3 = Base.iterate(%4, %16))
│ %23 = (@_3 === nothing)::Bool
│ %24 = Base.not_int(%23)::Bool
└── goto #4 if not %24
3 ─ goto #2
4 ┄ return res