I'm trying to implement the deflation method for finding multiple roots of a polynomial on Julia. In the deflation method, new functions are generated from the actual function divided by x - x_roots[i], and the root of new generated function must be found. But g(x) = g(x) / (x - x_root) gives me a Stack Overflow error, as probably it generated an infinite recursive relation. How may I generate a new function in each step?
function deflation(f::Function, order)
roots=[]
n=1
g(x)=f(x)
x_root=Muller_method(f,-5,0,5,1e-5,50)
while n<=order
x_root=Muller_method(a,-5,0,5,1e-5,50)
g(x)=g(x)/(x-x_root)
append!(roots,x_root)
n=n+1
end
return (roots)
Something like this causes infinite recursion:
julia> g(x) = x
g (generic function with 1 method)
julia> g(1)
1
julia> g(x) = g(x) / 2
g (generic function with 1 method)
julia> g(1)
ERROR: StackOverflowError:
Stacktrace:
[1] g(::Int64) at ./REPL[3]:1 (repeats 79984 times)
This is because function (or method) definitions do not work like variable assignment: each re-definition of g(x) overwrites the previous one (note how g above only ever has one method). When a method definition refers to itself, it is recursion, i.e. it refers to its own version at the time when the function gets called.
As for your deflation method, perhaps you could define a new function that closes over the vector of currently found roots. Consider the following example to see how things would work:
julia> f(x) = (x-1) * (x-2)
f (generic function with 1 method)
julia> roots = Float64[]
Float64[]
# g is defined once, and accounts for all currently found roots
julia> g(x) = f(x) / reduce(*, x-root for root in roots; init=one(x))
g (generic function with 1 method)
# No roots are identified at the beginning
julia> g(1+eps())
-2.2204460492503126e-16
julia> g(2+eps())
0.0
# But the results produced by g update as roots are identified
julia> push!(roots, 1.)
1-element Array{Float64,1}:
1.0
julia> g(1+eps())
-0.9999999999999998
julia> g(2+eps())
0.0
Related
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).
I can alias a single function like so:
julia> f(y;x=1) = x * y
f (generic function with 2 methods)
julia> const g = f
f (generic function with 1 method)
julia> g(3,x=2)
6
But if I try to do that with a composite function, the kwarg causes trouble:
julia> const gg= sqrt ∘ f
#62 (generic function with 1 method)
julia> gg(3,x=2)
ERROR: MethodError: no method matching (::Base.var"#62#63"{typeof(sqrt),typeof(f)})(::Int64; x=2)
Closest candidates are:
#62(::Any...) at operators.jl:875 got unsupported keyword argument "x"
Is there a solution to this? I am trying to pass the arguments to f and then transform that result via a shortcut (gg in the above MWE).
You have the following definition:
∘(f, g) = (x...)->f(g(x...))
and as you can see it does not support keyword arguments. Probably you can open an issue to discuss allowing them.
For now you can define your own version of it like this:
julia> ⋄(f, g) = (x...; kw...)->f(g(x...; kw...))
⋄ (generic function with 1 method)
julia> f(y;x=1) = x * y
f (generic function with 1 method)
julia> const gg = sqrt ⋄ f
#1 (generic function with 1 method)
julia> gg(3,x=2)
2.449489742783178
EDIT:
I have switched to the other OS, and I see that ⋄ does not render very sharp here. I used the following symbol:
help?> ⋄
"⋄" can be typed by \diamond<tab>
which is defined to be allowed to be an infix operator and is relatively easy to remember while not overshadowing ∘.
I'm trying to build a function that will output an expression to be assigned to a new in-memory function. I might be misinterpreting the capability of metaprogramming but, I'm trying to build a function that generates a math series and assigns it to a function such as:
main.jl
function series(iter)
S = ""
for i in 1:iter
a = "x^$i + "
S = S*a
end
return chop(S, tail=3)
end
So, this will build the pattern and I'm temporarily working with it in the repl:
julia> a = Meta.parse(series(4))
:(x ^ 1 + x ^ 2 + x ^ 3 + x ^ 4)
julia> f =eval(Meta.parse(series(4)))
120
julia> f(x) =eval(Meta.parse(series(4)))
ERROR: cannot define function f; it already has a value
Obviously eval isn't what I'm looking for in this case but, is there another function I can use? Or, is this just not a viable way to accomplish the task in Julia?
The actual error you get has to do nothing with metaprogramming, but with the fact that you are reassigning f, which was assigned a value before:
julia> f = 10
10
julia> f(x) = x + 1
ERROR: cannot define function f; it already has a value
Stacktrace:
[1] top-level scope at none:0
[2] top-level scope at REPL[2]:1
It just doesn't like that. Call either of those variables differently.
Now to the conceptual problem. First, what you do here is not "proper" metaprogramming in Julia: why deal with strings and parsing at all? You can work directly on expressions:
julia> function series(N)
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :(x ^ $i))
end
return S
end
series (generic function with 1 method)
julia> series(3)
:(x ^ 1 + x ^ 2 + x ^ 3)
This makes use of the fact that + belongs to the class of expressions that are automatically collected in repeated applications.
Second, you don't call eval at the appropriate place. I assume you meant to say "give me the function of x, with the body being what series(4) returns". Now, while the following works:
julia> f3(x) = eval(series(4))
f3 (generic function with 1 method)
julia> f3(2)
30
it is not ideal, as you newly compile the body every time the function is called. If you do something like that, it is preferred to expand the code once into the body at function definition:
julia> #eval f2(x) = $(series(4))
f2 (generic function with 1 method)
julia> f2(2)
30
You just need to be careful with hygiene here. All depends on the fact that you know that the generated body is formulated in terms of x, and the function argument matches that. In my opinion, the most Julian way of implementing your idea is through a macro:
julia> macro series(N::Int, x)
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :($x ^ $i))
end
return S
end
#series (macro with 1 method)
julia> #macroexpand #series(4, 2)
:(2 ^ 1 + 2 ^ 2 + 2 ^ 3 + 2 ^ 4)
julia> #series(4, 2)
30
No free variables remaining in the output.
Finally, as has been noted in the comments, there's a function (and corresponding macro) evalpoly in Base which generalizes your use case. Note that this function does not use code generation -- it uses a well-designed generated function, which in combination with the optimizations results in code that is usually equal to the macro-generated code.
Another elegant option would be to use the multiple-dispatch mechanism of Julia and dispatch the generated code on type rather than value.
#generated function series2(p::Val{N}, x) where N
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :(x ^ $i))
end
return S
end
Usage
julia> series2(Val(20), 150.5)
3.5778761722367333e43
julia> series2(Val{20}(), 150.5)
3.5778761722367333e43
This task can be accomplished with comprehensions. I need to RTFM...
https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions
Is it possible to access function defined in another function in julia? For example:
julia> function f(x)
function g(x)
x^2
end
x * g(x)
end
f (generic function with 1 method)
julia> f(2)
8
julia> f.g(2)
ERROR: type #f has no field g
in eval_user_input(::Any, ::Base.REPL.REPLBackend) at ./REPL.jl:64
in macro expansion at ./REPL.jl:95 [inlined]
in (::Base.REPL.##3#4{Base.REPL.REPLBackend})() at ./event.jl:68
No. In julia, it is often more ideomatic to use a module for local functions
module F
function g(x)
x^2
end
function f(x)
x * g(x)
end
export f
end
using F
f(2)
F.g(2)
What's the use case? You can define a custom type, give it a function field, and then make the type callable (a closure) to achieve the behaviour you want. But whether that is the best way of solving your issue in julia is a different question.
You can do this if your function f is an instance of a callable type, containing a function g as an accessible field:
julia> type F
g::Function
end
julia> function(p::F)(x) # make type F a callable type
x * p.g(x)
end
julia> f = F(function (x) return x.^2 end) # initialise with a function
F(#1)
julia> f(2)
8
julia> f.g
(::#1) (generic function with 1 method)
If g is always a fixed function, then you can introduce it via an internal constructor.
But to echo Lyndon's comments above, a better question is, why would you do this instead of something relying more on julia's dynamic dispatch features?
Got a function that takes three arguments.
f(a, b, c) = # do stuff
And another function that returns a tuple.
g() = (1, 2, 3)
How do I pass the tuple as function arguments?
f(g()) # ERROR
Using Nanashi's example, the clue is the error when you call f(g())
julia> g() = (1, 2, 3)
g (generic function with 1 method)
julia> f(a, b, c) = +(a, b, c)
f (generic function with 1 method)
julia> g()
(1,2,3)
julia> f(g())
ERROR: no method f((Int64,Int64,Int64))
This indicates that this gives the tuple (1, 2, 3) as the input to f without unpacking it. To unpack it use an ellipsis.
julia> f(g()...)
6
The relevant section in the Julia manual is here: http://julia.readthedocs.org/en/latest/manual/functions/#varargs-functions
Answer outdated as of Julia version 0.4, released in 2015:
In modern versions of Julia, use the ... operator f(g()...).
Use apply.
julia> g() = (1,2,3)
g (generic function with 1 method)
julia> f(a,b,c) = +(a,b,c)
f (generic function with 1 method)
julia> apply(f,g())
6
Let us know if this helps.