# Creating function that broadcast function over multiple keyword arguments

Dear all

I come from the R community, and try to switch to Julia )
Assume I have a function f1 (fictive function below)

f1 = function(x, y ; a = 1.5, b = 5)
coef = a * sum(x’y) + b
(coef = coef, mod = 1)
end

where x, y are vectors, e.g.

x = [1, 10, -2] ; y = [.5, 7, 22.3] ;

and that I want to replicate this function over vectors (instead of single elements)
of parameters a and b, for instance

a = [1, 2] ; b = [.5, -.5] ;

Using map, I can do

map((a, b) → f1(x, y ; a = a, b = b), a, b)

or with another syntax
(Julia: Passing keyword arguments to function through the map() function - Stack Overflow)

map(a, b) do a, b
f1(x, y ; a = a, b = b)
end

I can also build the following meta function ‘metaf’ (using the second syntax
since in real I have successive steps in the map)

metaf = function(x, y, fun ; a, b)
map(a, b) do a, b
fun(x, y ; a = a, b = b)
end
end

metaf(x, y, f1 ; a = a, b = b)

What I would like is such a metaf function but being generic for any function ‘fun’ that can have
its own arguments (different from ‘a’ and ‘b’ of function ‘f1’ above).
.
Something like

metaf(x, y, fun = f2 ; gamma, alpha, theta)
metaf(x, y, fun = OtherFun ; C)

etc.

Is it possible to do this with map in the previous metaf function?

Or may be map is not the efficient way?

I tried to incorporate things like ‘args…’ in metaf but without success

Thanks for any ideas

I think you can do

``````f1.(Ref(x),Ref(y),a=a,b=b) # note the dot
``````

In which case the function call will be broadcasted on a and b only

Edit: no, it does not work for keyword parameters.

Something on these lines could work (sorry I am not really able to test anything now)

``````f(x,args...) = args*x .+ args

x = [ 1, 2 ]
a = [ 2, 4, 6 ]
b = [ 1, 1, 2 ]

@show f(x,1,2)

@show f.(Ref(x),a,b)

``````

Quick question:

``````julia> map(+, [1,2], [3,4,5,6,7])
2-element Vector{Int64}:
4
6
``````

is this what you intend with `a` and `b`, applying `fun` on direct pairs of values, or do you want to apply `fun` on every combination of elements in `a` and `b`?

Edit: both are totally possible but neither looks particularly elegant. The problem is, that keyword arguments get passed as NamedTuples (or a pairs view thereof) and are also expected in this form by fun (NamedTuple or Dict{Symbol,_}). So you essentially want to iterate the content of each element of the named tuple while preserving the name. Not too hard but looks unwieldly:

``````transform_kwargs(; kwargs...) = [map(x->Pair(arg.first, x), arg.second) for arg in kwargs]
transform_kwargs(kwargs) = [map(x->Pair(arg.first, x), arg.second) for arg in kwargs]

``````
``````julia> transform_kwargs(a = [1,2], b = [:x, :y, :z], c = 2)
3-element Vector{Any}:
[:a => 1, :a => 2]
[:b => :x, :b => :y, :b => :z]
:c => 2
# note that this breaks down for non-iterable arguments like transform_kwargs(say = :nooooo)
# so probably not the most reasonable implementation, sorry ^^
``````

(please correct me if there’s a function for that already, couldn’t think of one)

Your `metafun` gets easy then (solved for all pairs of arguments here):

``````function metaf(x, y, fun; kwargs...)
combos = Iterators.product(transform_kwargs(kwargs)...)
return [fun(x, y; keywords...) for keywords in combos]
end
``````
``````julia> metaf([1,2,3], [4,5,6],
(x,y;alpha, beta, theta)->(theta .* y ./ exp.(alpha .+ beta .* x));
alpha = [-1, 0, 1], beta = collect(1.:0.1:2.), theta = [1.] )
3×11×1 Array{Vector{Float64}, 3}:
[:, :, 1] =
[4.0, 1.8394, 0.812012]         [3.61935, 1.50597, 0.601553]     …  [1.47152, 0.248935, 0.0404277]
[1.47152, 0.676676, 0.298722]   [1.33148, 0.554016, 0.221299]       [0.541341, 0.0915782, 0.0148725]
[0.541341, 0.248935, 0.109894]  [0.489826, 0.203811, 0.0814114]     [0.199148, 0.0336897, 0.00547129]
``````

But this looks rather opaque, so you can probably come up with a clearer and more explicit solution.

Editedit: if you put `fun` as a first argument to `metaf`, this enables do-block syntax which makes it a lot more readable.

Thanks for quoting me this syntax wuth f. and @show… I will study it.
But I think

is a constraint to me, since here it requires to know that the function f has two arguments, while f1, f2 etc. can have different numbers of arguments and I would like to automatize. Thanks anyway

Yes, direct pairs

Many thanks for all your powerfull code. I will work on it in more details and if needed will come back to you

Another solution:

``````function metaf(x, y, f; args...)
names = [a.first for a in args]
values = [a.second for a in args]
map(values...) do v...
f(x, y; Pair.(names, v)...)
end
end
``````

or even shorter:

``````function metaf(x, y, f; args...)
map(values(args)...) do v...
f(x, y; Pair.(keys(args), v)...)
end
end
``````

Example:

``````x = 1;
y = 2;
a = [1,2,3];
b = [4,5,6];

function myf(x, y; a, b)
coef = a * sum(x'y) + b
(coef = coef, mod = 1)
end

julia> metaf(x, y, myf; a, b)
3-element Vector{NamedTuple{(:coef, :mod), Tuple{Int64, Int64}}}:
(coef = 6, mod = 1)
(coef = 9, mod = 1)
(coef = 12, mod = 1)
``````
3 Likes

This is amazing! (@sudete) It seems being exactly what I had in mind when I posted my questions. Many thanks!

. also thanks again to others for the other ideas )

1 Like

@sudete well done, does the right thing and is much prettier than my version!

1 Like