pass parameters to insided functions - julia

Suppose I have a very simple function,
function func_in(x; y, z)
println(x + z)
end
which is called by another function
function test_dict(x, y, z...)
println(x + y )
func_in(z...)
end
I would like to pass parameter in z... to func_in. How could I do it in the right way? Note in func_in there are kargs and I could not find a way to pass parameter correctly.
The following code doesn't work
test_dict(1, 2, 15, 17, 19)
Many thanks!
UPDATE
I would like to be a little more elaborate about possible scenarios that parameters in z... could be very flexible.
function func_in(x; y, z, t1, t2, t3, t4)
println(x + t4)
end
Then how do I only pass x and t4, something like:
test_dict(1, 2, (x = 15, t4 = 19))
In R language, the ... works straightforwardly. Anything in ... would be used when it is cited. Like following, ... is directly passed to fn_2 and depatached accordingly. How to get similar behivor in Julia? I hope I have made myself clear.
fn_1 <- function(x, y, ...) {
fn_2(...)
}

Positional args and kwargs must be splatted separately:
func_in(args...; kwargs...)
Right now, you only pass positional inputs.
If you want to only pass kwargs to a function, you must do (note leading ;)
foo(; kwargs...)
BTW, it is considered good practice to always distinguish kwargs with a leading ;, even when it is not strictly necessary:
foo(x, y, a="hello", b=4) # legal
foo(x, y; a="hello", b=4) # better style
foo(a="hello", b=4) # legal
foo(; a="hello", b=4) # better style
Response to update: Keyword arguments must either have a default value, or you must provide a value for it when you call the function. You can for example write:
function func_in(x; y=nothing, z=nothing, t1=nothing, t2=nothing, t3=nothing, t4=nothing)
Another problem in your function test_dict, is that you must call it with three positional arguments, two for println and one for func_in, but you are only calling it with two positional arguments.
This will work:
function func_in(x; y=nothing, z=nothing, t1=nothing, t2=nothing, t3=nothing, t4=nothing)
println(x + t4)
end
function test_dict(x, y, z; w...) # note: *three* positional args
println(x + y )
func_in(z; w...)
end
test_dict(1, 2, 3; y = 15, t4 = 19) # three positional args
You can also use a named tuple and splat it:
kwargs = (y = 15, t4 = 19) # named tuple
test_dict(1, 2, 3; kwargs...)

Is this what you have in mind? :
julia> function func_in(x; y, z)
println(x + z)
end
func_in (generic function with 1 method)
julia> function test_dict(x, y, z...)
println(x + y )
func_in(z[1]; y = z[2], z = z[3])
end
test_dict (generic function with 1 method)
julia> test_dict(1, 2, 15, 17, 19)
3
34

Related

How to compute and evaluate composite function in GP?

I found a workaround to make composite function, but I believe there should be a better way to do this:
? f = x^2
%1 = x^2
? g = x^3
%2 = x^3
? x = g
%3 = x^3
? fog = eval(f)
%4 = x^6
? x = 2
%5 = 2
? result = eval(fog)
%6 = 64
In this method, I need to assign x many times and I don't want to use eval function. The code is not readable and maintainable.
You can simplify Piotr's nice answer to
comp(f, g) = x->f(g(x));
Indeed, you do not need to assign to the (global) variable h in the comp function itself. Also, the braces are not necessary for a single-line statement, and neither are type annotations (which are meant to optimize the byte compiler output or help gp2c; in this specific case they do not help).
Finally the parentheses around the argument list are optional in the closure definition when there is a single argument, as (x) here.
I would modify the examples as follows
f(x) = x^2;
g(x) = x^3;
h = comp(f, g);
? h('x) \\ note the backquote
%1 = x^6
? h(2)
%2 = 64
The backquote in 'x makes sure we use the formal variable x and not whatever value was assigned to the GP variable with that name. For the second example, there is no need to assign the value 2 to x, we can call h(2) directly
P.S. The confusion between formal variables and GP variables is unfortunate but very common for short names such as x or y. The quote operator was introduced to avoid having to kill variables. In more complicated functions, it can be cumbersome to systematically type 'x instead of x. The idiomatic construct to avoid this is my(x = 'x). This makes sure that the x GP variable really refers to the formal variable in the current scope.
PARI/GP supports the anonymous closures. So you can define the function composition on your own like this:
comp(f: t_FUNC, g: t_FUNC) = {
h = (x) -> f(g(x))
};
Then your code can be transformed to a more readable form:
f(x) = x^2;
g(x) = x^3;
h = comp(f, g);
h(x)
? x^6
x = 2; h(x)
? 64
Hope, this helps.

How to define tuple of types in julia type declaration's

How do I declare a tuple of specific types for a julia function?
This works:
function f(x, y)::Int8
x+y
end
julia> f(2, 3)
5
This works too:
function g(x, y)::Tuple
x+y, x*y
end
julia> g(2, 3)
(5, 6)
But I can't figure out how to define the types in the tuple.
For example, this throws an error:
function h(x, y)::Tuple(::Int8, ::Int8)
x+y, x*y
end
ERROR: syntax: invalid "::" syntax around REPL[48]:2
An this too:
function k(x, y)::Tuple(Int8, Int8)
x+y, x*y
end
julia> k(2, 3)
ERROR: MethodError: no method matching Tuple(::Type{Int8}, ::Type{Int8})
Use curly braces and omit the :: for the tuple's elements' types:
function k(x, y)::Tuple{Int8, Int8}
x + y, x * y
end
julia> k(2, 3)
(5, 6)

Using ForwardDiff.jl for a function of many variables and parameters Julia

The github repo for ForwardDiff.jl has some examples. I am trying to extend the example to take in addition to a vector of variables, a parameter. I cannot get it to work.
This is the example (it is short so I will show it rather than linking)
using ForwardDiff
x = rand(5)
f(x::Vector) = sum(sin, x) .+ prod(tan, x) * sum(sqrt, x);
g = x -> ForwardDiff.gradient(f, x);
g(x) # this outputs the gradient.
I want to modify this since I use functions with multiple parameters as well as variables. As a simple modification I have tried adding a single parameter.
f(x::Vector, y) = (sum(sin, x) .+ prod(tan, x) * sum(sqrt, x)) * y;
I have tried the following to no avail:
fp = x -> ForwardDiff.gradient(f, x);
fp = x -> ForwardDiff.gradient(f, x, y);
y = 1
println("test grad: ", fp(x, y))
I get the following error message:
ERROR: LoadError: MethodError: no method matching (::var"#73#74")(::Array{Float64,1}, ::Int64)
A similar question was not answered in 2017. A comment led me to here and it seems the function can only accept one input?
The target function must be unary (i.e., only accept a single argument). ForwardDiff.jacobian is an exception to this rule.
Has this changed? It seems very limited to only be able to differentiate unary functions.
A possible workaround would be to concatenate the list of variables and parameters and then just slice the returned gradient to not include the gradients with respect to the parameters, but this seems silly.
I personally think it makes sense to have this unary-only syntax for ForwardDiff. In your case, you could just pack/unpack x and y into a single vector (nominally x2 below):
julia> using ForwardDiff
julia> x = rand(5)
5-element Array{Float64,1}:
0.4304735670747184
0.3939269364431113
0.7912705403776603
0.8942024934250143
0.5724373306715196
julia> f(x::Vector, y) = (sum(sin, x) .+ prod(tan, x) * sum(sqrt, x)) * y;
julia> y = 1
1
julia> f(x2::Vector) = f(x2[1:end-1], x2[end]) % unpacking in f call
f (generic function with 2 methods)
julia> fp = x -> ForwardDiff.gradient(f, x);
julia> println("test grad: ", fp([x; y])) % packing in fp call
test grad: [2.6105844240785796, 2.741442601659502, 1.9913192377198885, 1.9382805843854594, 2.26202717745402, 3.434350946190029]
But my preference would be to explicitly name the partial derivatives differently:
julia> ∂f∂x(x,y) = ForwardDiff.gradient(x -> f(x,y), x)
∂f∂x (generic function with 1 method)
julia> ∂f∂y(x,y) = ForwardDiff.derivative(y -> f(x,y), y)
∂f∂y (generic function with 1 method)
julia> ∂f∂x(x, y)
5-element Array{Float64,1}:
2.6105844240785796
2.741442601659502
1.9913192377198885
1.9382805843854594
2.26202717745402
julia> ∂f∂y(x, y)
3.434350946190029
Here's a quick attempt at a function which takes multiple arguments, the same signature as Zygote.gradient:
julia> using ForwardDiff, Zygote
julia> multigrad(f, xs...) = ntuple(length(xs)) do i
g(y) = f(ntuple(j -> j==i ? y : xs[j], length(xs))...)
xs[i] isa AbstractArray ? ForwardDiff.gradient(g, xs[i]) :
xs[i] isa Number ? ForwardDiff.derivative(g, xs[i]) : nothing
end;
julia> f1(x,y,z) = sum(x.^2)/y;
julia> multigrad(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)
julia> Zygote.gradient(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)
For a function with several scalar arguments, this evaluates each derivative separately, and perhaps it would be more efficient to use one evaluation with some Dual(x, (dx, dy, dz)). With large-enough array arguments, ForwardDiff.gradient will already perform multiple evaluations, each with some number of perturbations (the chunk size, which you can control).

Specializing method calls in order in meta-programming

I have issue after calling my macro:
#introspectable square(x) = x * x
Then when calling
square(3)
i should be able to get 9, cause the function call has been specialized to execute an attribute of the structure which is Julia code, however when I enter the macro, the code seems to be directly evaluated.
What i have tried:
struct IntrospectableFunction
name
parameters
native_function
end
(f::IntrospectableFunction)(x) = f.native_function(x)
macro introspectable(expr)
name = expr.args[1].args[1]
parameters = tuple(expr.args[1].args[2:end]...)
body = expr.args[2].args[2]
:( global $name = IntrospectableFunction( :( name ), $parameters, :( body ) ))
end
#introspectable square(x) = x * x
square(3)
The answer should be 9 , however i get "Object of type symbol are not callable ". However if i replace :( body ) with x -> x * x i get the desired result, my objective is generalizing the macro-call.
I usually find it easier to work with expressions in macros (it is not the shortest way to write things, but, from my experience, it is much easier to control what gets generated).
Therefore I would rewrite your code as:
macro introspectable(expr)
name = expr.args[1].args[1]
parameters = expr.args[1].args[2:end]
anon = Expr(Symbol("->"), Expr(:tuple, parameters...), expr.args[2].args[2])
constr = Expr(:call, :IntrospectableFunction, QuoteNode(name), Tuple(parameters), anon)
esc(Expr(:global, Expr(Symbol("="), name, constr)))
end
Now, as you said you wanted generality I would define your functor like this:
(f::IntrospectableFunction)(x...) = f.native_function(x...)
(in this way you allow multiple positional arguments to be passed).
Now let us test our definitions:
julia> #introspectable square(x) = x * x
IntrospectableFunction(:square, (:x,), getfield(Main, Symbol("##3#4"))())
julia> square(3)
9
julia> #macroexpand #introspectable square(x) = x * x
:(global square = IntrospectableFunction(:square, (:x,), ((x,)->x * x)))
julia> #introspectable toarray(x,y) = [x,y]
IntrospectableFunction(:toarray, (:x, :y), getfield(Main, Symbol("##5#6"))())
julia> toarray("a", 10)
2-element Array{Any,1}:
"a"
10
julia> #macroexpand #introspectable toarray(x,y) = [x,y]
:(global toarray = IntrospectableFunction(:toarray, (:x, :y), ((x, y)->[x, y])))
julia> function localscopetest()
#introspectable globalfun(x...) = x
end
localscopetest (generic function with 1 method)
julia> localscopetest()
IntrospectableFunction(:globalfun, (:(x...),), getfield(Main, Symbol("##9#10"))())
julia> globalfun(1,2,3,4,5)
(1, 2, 3, 4, 5)
julia> function f()
v = 100
#introspectable localbinding(x) = (v, x)
end
f (generic function with 1 method)
julia> f()
IntrospectableFunction(:localbinding, (:x,), getfield(Main, Symbol("##11#12")){Int64}(100))
julia> localbinding("x")
(100, "x")
(note that it is useful to use #macroexpand to make sure our macro works as expected)
EDIT - how to handle a minimal multiple dispatch
I am writing a non-macro example because it is related to the data structure:
Use e.g. such a definition:
struct IntrospectableFunction
name::Symbol
method_array::Vector{Pair{Type{<:Tuple}, Function}}
end
function (f::IntrospectableFunction)(x...)
for m in f.method_array
if typeof(x) <: first(m)
return last(m)(x...)
end
end
error("signature not found")
end
and now you can write:
julia> square = IntrospectableFunction(:square, [Tuple{Any}=>x->x*x,Tuple{Any,Any}=>(x,y)->x*y])
IntrospectableFunction(:square, Pair{DataType,Function}[Tuple{Any}=>##9#11(), Tuple{Any,Any}=>##10#12()])
julia> square(3)
9
julia> square(2,3)
6
Keep in mind that the approach I present is not perfect and universal - it just serves to give a very simple example how you could do it.

Can I pass keyword args to do-blocks in julia?

Below is an example:
def(f::Function) = f
f1 = def() do x
x
end
f2 = def() do x, y
x, y
end
f3 = def() do x, y; z # this syntax is accepted
x, y, z
end
f1(1)
f2(1, 2)
f3(1, 2, z=3) # ERROR: LoadError: function #13 does not accept keyword arguments
The example below is not my actual use case, but it illustrates the question. How should I handle kwargs in a do-block?
As far as I know this is not possible. If you look at /src/julia-parser.scm file you can find that parsing of a do block is defined in parse-do here. And you can see that do-block is rewritten to an anonymous function that can only accept positional arguments in doargs variable separated by commas.
In this line you can see that ; explicitly terminates parsing of arguments of do-block. This means that:
julia> z = "hello"
"hello"
julia> f3 = def() do x, y; println(z) # this syntax is accepted
x, y, z
end
#24 (generic function with 1 method)
julia> f3(100, 1000)
hello
(100, 1000, "hello")
will work as the code past ; is treated as a part of do-block body.
You could try to put ( and ) around the arguments like this:
julia> f3 = def() do (x, y; z)
x, y, z
end
ERROR: syntax: invalid assignment location "; z"
julia> f3 = def() do (y; z)
y, z
end
ERROR: syntax: "begin
y
# REPL[52], line 1
z
end" is not a valid function argument name
This will go through parser, but then julia-syntax.scm will throw an error. This probably could be improved as:
f3 = def(function(x, y; z)
x, y, z
end)
works correctly (you can consider opening an issue for this on https://github.com/JuliaLang/julia/issues).
Until this is fixed the only thing you can do is to use anonymous function definition approach I have given above (although it is not very clean).
You can do this, you just need to pass the kwargs before the do:
julia> f(g, xs; kwargs...) = begin
println(kwargs...)
map(g,xs)
end
f (generic function with 1 method)
julia> f(1:5; thisworks=true) do x x^2 end
:thisworks => true
5-element Array{Int64,1}:
1
4
9
16
25

Resources