A family of function and its arguments as argument of other custom function

I am trying to define a function which one of its arguments is a class of other functions, with diverse parameters (for example, distribution functions). So, I don’t know how to pass these parameters because the parameters, according to the distribution, doesn’t have the same pattern. For example

function foo(;distribution::Function, x = 0, parameters) 
    y = x + rand(distribution (parameters) )
    return y
end

In this case, I need to execute, for example:

# For normal(0,1)
foo(Normal, x = 2, 0,1)
# Or for normal(1,2)
foo(Normal, x = 2, 1,2)

Or

# For TDist(ν)
foo(TDist, x = 4, ν = 2 )

I spent some time looking for a solution, but I couldn’t find it. I would like a solution to include as arguments not only distributions and their parameters, but any kind of functions with several parameters.

julia> using Distributions

julia> function foo(distribution, x, parameters...)
           y = x + rand(distribution(parameters...))
           return y
       end

julia> foo(Normal, 2, 0, 1)
0.7412695175676529

julia> foo(TDist, 4, 2)
1.5158055345656356
1 Like

Why I can not do this?

function foo(distribution; x=0, parameters... )  
    y = x + rand(distribution(parameters...) ) 
    return y
end

Note that

foo(TDist,2 ,1)
0.010202203826929823

But

foo(TDist,x=2 ,1)
UndefKeywordError: keyword argument distribution not assigned

On the other hand

foo(TDist,2,ν=1)
UndefKeywordError: keyword argument distribution not assigned

distributions’ constructors don’t take keyword arguments, don’t use them

2 Likes

Right. How about:

foo(TDist,x=2 ,1)

Something is not working. A concrete example: firts I am definig a function that simulate an autoregressive time series:

using Distributions

function simulateAR1(;ρ,σ = 1,x₀ = 0,μ = 0,T=1000)
    #noise
    η = rand( Normal(0,σ), T) 
    # My X
    X = zeros(T)
    # Looping 
    X[1] = x₀
    for t in 2:T
        X[t] = ρ*X[t-1] + η[t]
    end    
    return (X .+ μ)
end    

Test Ok

ξ = simulateAR1(ρ = 0.9)

Now, I want to do some Compound Sum of any distribution (it can be the the AR(1) distribution or other)

function CP(distribution, λ,parameters...)
    N  = rand(Poisson(λ))
    A =  zeros(T,N)
    for j =1:N
        aux = distribution(parameters...)
        for t=1:T
            A[t,j]=aux[t]
        end
    end
    Y=sum(A,dims=2)
    return Y
end

But

# In this case N poisson with lambda =2
CP(simulateAR1 ,2,  0.9)
UndefVarError: T not defined

Moreover

CP(simulateAR1 ,2,  0.9, T =100)
MethodError: no method matching CP(::typeof(simulateAR1), ::Int64, ::Float64; T=100)
Closest candidates are:
  CP(::Any, ::Any, ::Any...) at In[4]:1 got unsupported keyword argument "T"

Note, that I want input any other distributions with any kind of arguments. For example:

function non_ergodic1(; p₀ = .5 , p=.5 , T = 1000)
    moeda = rand(Bernoulli(p))
    if (moeda == 0)
        p = 1
    end
    ξ = [  rand(Bernoulli(p)) for t in 1:T ]
    return ξ
end

Defining function has been a headache for me. Help!

It will be hard to interface with every function like that. One alternative is to keep the internal interface fixed and use closures:

function foo(distribution::F, x) where {F<:Function}
    y = x + rand(distribution())
end

# call with
x = 2.0
foo(() -> Normal(0,1), x) 

Thus, your function expects, always, a function without parameters (or whatever), and interfacing with it is done by defining anonymous functions which adapt to that interface.

3 Likes

Your solution works:

function CP(distribution::F, λ;T=1000) where {F<:Function}
    N  = rand(Poisson(λ))
    A =  zeros(T,N)
    for j =1:N
        aux = distribution()
        for t=1:T
            A[t,j]=aux[t]
        end
    end
    Y=sum(A,dims=2)
    return Y
end

Notice that simulateAR1 runs ok according to the function defined above:

simulateAR1(ρ=0.9);

Moreover

#Ok
CP(() -> simulateAR1(ρ=0.9), 2) ;

But why this does not work?

CP(simulateAR1(ρ=0.9), 2)

Thanks!

This notation define a new function, without parameters, that returns the result of the right side. You are passing the function, not the result of the evaluation of the function, which is what you do in the second case:

() -> Normal(1.0)

# or

f() = Normal(1.0)

are function definitions, the first anonymous, and you are using that in that case, the second one just a regular function, without parameters.

Normal(1.0) is the call to the Normal function and returns a value (the distribution), not a function.

1 Like