Calculation of Functions in a loop inside another Function

Hi, I’m looking to calculate nested functions of form f(g(h(...k(x)...)) and have written a function that takes in two arguments. One is called chain which is the vector of Function defined in the order of their calculation and the input array x.

function chain_cal(chain:: Vector{Function}, x:: CuArray)
    n = size(chain)[1]
    
    for i in 1:n
        x = chain[i](x)
    end
    return x
end

I defined a variable chain with two functions named square

chain = Vector([square, square])
2-element Array{typeof(square),1}:
 square (generic function with 1 method)
 square (generic function with 1 method)

and when I run chain_cal, I get an error as follows

chain_cal(chain, x)
MethodError: no method matching chain_cal(::Array{typeof(square),1}, ::CuArray{Int64,1})
Closest candidates are:
  chain_cal(!Matched::Array{Array{Function,1},1}, ::CuArray) at In[16]:1
  chain_cal(!Matched::Array{Function,1}, ::CuArray) at In[31]:1

Stacktrace:
 [1] top-level scope at In[35]:1
 [2] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091

Is there a way where I can modify chain_cal such that it accepts all functions or do I have to modify my earlier defined square function such that it’s type is Function?

#=
Square each element in the input CUDA Array
=#
function square(x:: CuArray)
    return x.^2
end

The signature does not match since:

julia> typeof([square,square]) <: Vector{Function}
false

You can either update:

to chain_cal(chain::Vector{<:Function}, x::CuArray) since

julia> typeof([square,square]) <: Vector{<:Function}
true

or update

to Function[square, square] which would create a Vector{Function} and thus match your signature since

julia> typeof(Function[square,square]) <: Vector{Function}
true

See relevant manual section.

2 Likes

Thanks.

Can you tell me why the user defined functions are not of type Function?

They are subtypes of Function:

julia> f() = 1
f (generic function with 1 method)

julia> typeof(f) <: Function
true
4 Likes

Creating a unique type for every function allows the compiler to specialize higher-order functions (e.g. map) based on the types of their function arguments. You pass an anonymous function to map and it may be inlined inside map.

1 Like

For a different implementation you could also use (though I haven’t worked with CuArrays, maybe there’s something special about them that interferes?)

I mean something like this:

square(x) = x.^2
x = [1,2,3]
chain = [square, square]

julia> ∘(chain...)(x)
3-element Array{Int64,1}:
  1
 16
 81

We can also chain scalar functions and make a broadcast call on the result:

chain = [x->x^2, sqrt, inv];   # These functions don't work on vectors...

julia> ∘(chain...).(x)         # but we can use a broadcast call
3-element Array{Float64,1}:
 1.0
 0.5000000000000001
 0.3333333333333333
2 Likes