Is there a way in Julia to smoothly define a recursive function?
function f(x_0, y_0)
x_1 = g1(x_0,y_0)
y_1 = g2(x_0,y_0)
x_2 = g1(x_1,y_1)
y_2 = g2(x_1,y_1)
x_3 = g1(x_2,y_2)
y_3 = g2(x_2,y_2)
x_4 = g1(x_3,y_3)
y_4 = g2(x_3,y_3)
return x_2,y_2
end
In particular, I want to be able to call the function and give parameter that would specify the circle of the recursion. Something like this:
f(x_0, y_0, circle = 2)
>> x_2, y_2
f(x_0, y_0, circle = 3)
>> x_3, y_3
If you define
function apply_n(f, x_0, cycle_len)
for _ in 1:cycle_len
x_0 = f(x_0)
end
return x0
end
and call apply_n((x,y)->(g1(x,y),g2(x,y)), (x_0,y_0), 3) it will work.
IterTools.jl provides an iterate method that does exatly this.
help?> iterated
…
iterated(f, x)
Iterate over successive applications of f, as in x, f(x), f(f(x)), f(f(f(x))), ...
…
julia> x_0, y_0 = 5, 10;
g1 = +;
g2 = -;
julia> using IterTools: iterated, nth
julia> nth(iterated(((x, y),) -> (g1(x, y), g2(x, y)), (x_0, y_0)), 3)
(10, 20)
As the documentation says, the result is (an iterator over) (x, f(x), f(f(x)), …), which means (x_2, y_2) from the question would be f(f(x)) which is the third element - that's why the call to nth above passes 3 as the second argument.
An advantage of this method is that it returns an iterator that you can then treat like any other iterator. So if you instead want the results of all the first 5 stages of the process:
julia> using Base.Iterators: take
julia> take(iterated(((x, y),) -> (g1(x, y), g2(x, y)), (x_0, y_0)), 5) |> collect
10-element Vector{Tuple{Int64, Int64}}:
(5, 10)
(15, -5)
(10, 20)
(30, -10)
(20, 40)
Or only want the recursion to continue while a condition is true:
julia> using Iterators: takewhile
julia> takewhile(((x, y),) -> x + y < 50,
iterated(((x, y),) -> (g1(x, y), g2(x, y)), (x_0, y_0))) |> collect
4-element Vector{Tuple{Int64, Int64}}:
(5, 10)
(15, -5)
(10, 20)
(30, -10)
Related
How to get coefficients for ALL combinations of the variables of a multivariable polynomial using sympy.jl or another Julia package for symbolic computation?
Here is an example from MATLAB,
syms a b y
[cxy, txy] = coeffs(ax^2 + by, [y x], ‘All’)
cxy =
[ 0, 0, b]
[ a, 0, 0]
txy =
[ x^2y, xy, y]
[ x^2, x, 1]
My goal is to get
[ x^2y, xy, y]
[ x^2, x, 1]
instead of [x^2, y]
I asked the same question at
https://github.com/JuliaPy/SymPy.jl/issues/482
and
https://discourse.julialang.org/t/symply-jl-for-getting-coefficients-for-all-combination-of-the-variables-of-a-multivariable-polynomial/89091
but I think I should ask if this can be done using Sympy.py.
Using Julia, I tried the following,
julia> #syms x, y, a, b
julia> ff = sympy.Poly(ax^2 + by, (x,y))
Poly(ax**2 + by, x, y, domain='ZZ[a,b]')
julia> [prod(ff.gens.^i) for i in ff.monoms()]
2-element Vector{Sym}:
x^2
y
This is a longer form rewrite of the one-liner in the comment.
It uses Pipe.jl to write expressions 'functionally', so familiarity with pipe operator (|>) and Pipe.jl will help.
using SymPy
using Pipe
#syms x, y, a, b
ff = sympy.Poly(a*x^2 + b*y, (x,y))
max_degrees =
#pipe ff.monoms() .|> collect |> hcat(_...) |>
reduce(max, _, dims=2) |> vec
degree_iter =
#pipe max_degrees .|> UnitRange(0, _) |>
tuple(_...) |> CartesianIndices
result = [prod(ff.gens.^Tuple(I)) for I in degree_iter] |>
reverse |> eachcol |> collect
or using more of the python methods:
[prod(ff.gens.^I) for
I in Iterators.product((0:d for d in ff.degree.(ff.gens))...)] |>
reverse |> eachcol |> collect
Both give the desired result:
2-element Vector{...}:
[x^2*y, x*y, y]
[x^2, x, 1]
UPDATE:
In case there are more than 2 generators, the result needs to be a Array with higher dimension. The last bits of matrix transposes is immaterial and the expressions become:
Method 1:
max_degrees =
#pipe ff.monoms() .|> collect |> hcat(_...) |>
reduce(max, _, dims=2) |> vec
degree_iter =
#pipe max_degrees .|> UnitRange(0, _) |>
tuple(_...) |> CartesianIndices
result = [prod(ff.gens.^Tuple(I)) for I in degree_iter]
Method 2:
result = [prod(ff.gens.^Tuple(I)) for I in degree_iter]
Thanks a lot #Dan Getz. Your solution works for the TOY example from MATLAB. My real case is more complicated, which has more variables and polynominals. I tried your method for 3 variables,
using SymPy
#syms x, y, z, a, b
ff = sympy.Poly(a*x^2 + b*y + z^2 + x*y + y*z, (x, y, z))
[prod(ff.gens.^Tuple(I)) for I in CartesianIndices(tuple(UnitRange.(0,vec(reduce(max, hcat(collect.(ff.monoms())...), dims=1)))...))]
I got the following error,
ERROR: LoadError: DimensionMismatch: arrays could not be broadcast to a common size; got a dimension with lengths 3 and 5
Stacktrace:
How to generate your method to any number of variables with different degrees, e.g., x^3 + y^3 + z^3 + xyz + xy^2z?
You can find the degree of each of the two variables of interest and then use them to create the matrix of generators; you can use them to get the coefficients of interest. I am not sure what you expect if the equation were like a*x**2 + b*y + c...
>>> from sympy import *
>>> from sympy.abc import a, b, x, y
>>> eq = a*x**2 + b*y
>>> deg = lambda x: Poly(eq, x).degree() # helper to give degree in "x"
>>> v = (Matrix([x**i for i in range(deg(x),-1,-1)]
... )*Matrix([y**i for i in range(deg(y),-1,-1)]).T).T; v
Matrix([[x**2*y, x*y, y], [x**2, x, 1]])
>>> Matrix(*v.shape, [eq.coeff(i) if i.free_symbols else eq.as_coeff_Add()[0]
... for i in v])
Matrix([[0, 0, b], [a, 0, 0]])
From #jverzani (thanks)
using SymPy;
#syms a b x y;
eq = a*x^2 + b*y;
deg = x -> sympy.Poly(eq, x).degree();
xs, ys = [x^i for i ∈ range(deg(x):-1:0], [y^i for i ∈ deg(y):-1:0];
v = permutedims(xs .* permutedims(ys));
M = [x^2*y x*y y; x^2 x 1];
[length(free_symbols(i)) > 0 ? eq.coeff(i) : eq.as_coeff_add()[1] for i ∈ v];
[0 0 b; a 0 0]
Given a generator:
myVec1 = rand(0:4, 2)
myVec2 = rand(0:4, 8)
myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2)
This is basically a matrix with 2 columns. It can be seen by using collect(myGen).
How can I create a generator which yields two values per call (basically a column)?
Conceptually, something equivalent of:
for myCol in eachcol(collect(myGen))
#show myCol;
end
Just without any explicit allocation of the matrix.
Can I wrap myGen for the following case:
for value1, value2 in myGen
dosomethingelse1(value1, value2)
end
In other words, I am after a way to create a generator which returns 2 (or more?) consecutive values at once and can be used in a loop to do so.
So basically, we create a 2D array in the generator and I'd like to access the whole slice at once. I could do it with eachcol and eachrow for actual array, but what about the generator?
Here is a test case:
myVec1 = rand(0:4, 2);
myVec2 = rand(0:4, 800);
#btime begin
myMat = [val1 + val2 for val1 in myVec1, val2 in myVec2];
outVec = [sum(myCol) for myCol in eachcol(myMat)];
end
#btime begin
myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2);
outVec = [sum(myCol) for myCol in Iterators.partition(myGen, 2)];
end
The solution by #Bogumił Kamiński indeed works, yet in practice, for some reason, it creates more allocations while the motivation was to reduce it.
This is supported directly by Julia syntax. Iterating over generators of tuples works like interating over atomic values.
For an example you could try:
for (a,b) in ((x, 3x) for x in 1:4)
println("a=$a, b=$b")
end
I assume you want something like:
julia> for (x1, x2) in Iterators.partition(1:10, 2)
#show x1, x2
end
(x1, x2) = (1, 2)
(x1, x2) = (3, 4)
(x1, x2) = (5, 6)
(x1, x2) = (7, 8)
(x1, x2) = (9, 10)
If this is what you want then Iterators.partition is a function you can use.
Edit: If you have two streams of sources use zip:
julia> for (x1, x2) in zip(1:5, 6:10)
#show x1, x2
end
(x1, x2) = (1, 6)
(x1, x2) = (2, 7)
(x1, x2) = (3, 8)
(x1, x2) = (4, 9)
(x1, x2) = (5, 10)
Edit 2: my first solution works already for your case:
julia> collect(myGen)
2×8 Matrix{Int64}:
3 7 5 4 6 3 4 5
1 5 3 2 4 1 2 3
julia> for (x1, x2) in Iterators.partition(myGen, 2)
#show x1, x2
end
(x1, x2) = (3, 1)
(x1, x2) = (7, 5)
(x1, x2) = (5, 3)
(x1, x2) = (4, 2)
(x1, x2) = (6, 4)
(x1, x2) = (3, 1)
(x1, x2) = (4, 2)
(x1, x2) = (5, 3)
Although the other answers are in some ways more general based on the information added by OP in their edits a more memory efficient option would be using nested generators. Something like:
function solution_nested(v1, v2)
myGen = ((val1 + val2 for val1 in v1) for val2 in v2)
[sum(myCol) for myCol in myGen]
end
When you test the solutions you should avoid using global variables and preferably wrap the solution in a function so you give Julia sufficient opportunity to optimize the code.
This solution gives the expected result of only one allocation:
julia> #btime solution_nested(myVec1, myVec2);
1.856 μs (1 allocation: 6.38 KiB)
So while this solution does not quite fit the title it seems to fit what you are describing. We use a lazy sequence of lazy columns. The reason Iterators.partition is slow and memory inefficient is that it actually allocates the intermediate vectors of values in a partition: https://github.com/JuliaLang/julia/blob/dacf9d65aff4668b8fff25957d9aaa2cf03868c8/base/iterators.jl#L1232 .
You're basically missing brackets when destructuring the tuple of values in the second loop. To be more detailed, you can just return two values (a tuple) in your dosomething function. For example:
function dosomething(element)
secondElement = element^2
element, secondElement
end
And then you can use the loop by destructuring the return value, like so:
for (value1, value2) in myGen
dosomethingelse(value1, value2)
end
If you want a full working example:
myArray = [1, 2, 3]
function dosomething(element)
secondElement = element^2
element, secondElement
end
myGen = (dosomething(myElement) for myElement in myArray)
function dosomethingelse(value1, value2)
println("Value 1: $value1 \nValue 2: $value2 \n")
end
for (value1, value2) in myGen
dosomethingelse(value1, value2)
end
I have two arrays that I want to iterate over at the same time.
I'm using this:
julia> xs = [1,2,3];
julia> ys = [4,5,6];
julia> for i in 1:length(xs)
x = xs[i]
y = ys[i]
#show x, y
end
(x, y) = (1, 4)
(x, y) = (2, 5)
(x, y) = (3, 6)
Is there a better way to iterate over multiple arrays in Julia?
Use zip along with tuple destructuring:
julia> xs = [1,2,3];
julia> ys = [4,5,6];
julia> for (x, y) in zip(xs, ys)
#show x, y
end
(x, y) = (1, 4)
(x, y) = (2, 5)
(x, y) = (3, 6)
zip will stop iteration at the shortest array:
julia> for (x, y) in zip([1,2], [0,0,0])
#show x, y
end
(x, y) = (1, 0)
(x, y) = (2, 0)
This pattern can be generalized to an arbitrary number of lists:
julia> for (x, y, z) in zip([1,2], [3,4], [5,6])
#show x, y, z
end
(x, y, z) = (1, 3, 5)
(x, y, z) = (2, 4, 6)
One possibility consists in using the eachindex function: if it is given multiple Array-like arguments, it will return a iterable set of indices suitable to iterate on all arguments at once.
This is useful in particular in the following situations:
when you need to use the index itself (for example because you don't only need to access the elements of the collections, but also set some of them), or
when you want to check that both arrays indeed have the same number of elements (this might or might not be a desired property depending on your use case).
Example 1: using the index itself to fill the first array with values coming from the second
julia> x = [1,2,3];
julia> y = [4,5,6];
julia> #inbounds for i in eachindex(x, y)
x[i] = 2*y[i]
end
julia> x
3-element Array{Int64,1}:
8
10
12
Example 2: check that the arrays have the same range
julia> x = [1,2];
julia> y = [4,5,6];
julia> #inbounds for i in eachindex(x, y)
x[i] = 2*y[i]
end
ERROR: DimensionMismatch("all inputs to eachindex must have the same indices, got [1, 2] and [1, 2, 3]")
Example 3: note that eachindex generalizes well for multi-dimensional arrays too.
julia> x = zeros(2, 3);
julia> y = ones(2, 3);
julia> #inbounds for i in eachindex(x, y)
x[i] = 2*y[i]
end
julia> x
2×3 Array{Float64,2}:
2.0 2.0 2.0
2.0 2.0 2.0
You can iterate over multiple collections using map and foreach. For example, with map:
julia> x, y = 1:3, 4:6;
julia> map(hypot, x, y)
3-element Array{Float64,1}:
4.123105625617661
5.385164807134504
6.708203932499369
For more complicated multi-line anonymous functions, you can use do-block syntax:
julia> xs, ys = 1:4, 10:10:40;
julia> map(xs, ys) do x, y
if isodd(x)
x + y
else
x * y
end
end
4-element Array{Int64,1}:
11
40
33
160
foreach is very similar to map, but is intended for use when a function is applied for its side effect, like printing or plotting, rather than its return value. An example with foreach:
julia> x, y = ["a", "b", "c"], 1:3;
julia> foreach(println ∘ ^, x, y)
a
bb
ccc
Note the use of the function composition operator in the foreach call.
How can I define an outer constructor that has same number of arguments as the field values? What I want to do is something like this:
struct data
x
y
end
function data(x, y)
return data(x-y, x*y)
end
But it obviously causes stackoverflow.
Based on the various helpful comments, thanks to all, I changed my answer. Here is an example in Julia 1.0.0 of what you may be after. I am learning Julia myself, so maybe further comments can improve this example code.
# File test_code2.jl
struct Data
x
y
Data(x, y) = new(x - y, x * y)
end
test_data = Data(105, 5)
println("Constructor example: test_data = Data(105, 5)")
println("test_data now is...: ", test_data)
#= Output
julia> include("test_code2.jl")
Constructor example: test_data = Data(105, 5)
test_data now is...: Data(100, 525)
=#
This works for me
julia> struct datatype
x
y
end
julia> function datatype_create(a,b)
datatype(a - b, a * b)
end
datatype_create (generic function with 1 method)
julia> methods(datatype_create)
# 1 method for generic function "datatype_create":
[1] datatype_create(a, b) in Main at none:2
julia> methods(datatype)
# 1 method for generic function "(::Type)":
[1] datatype(x, y) in Main at none:2
julia> a = datatype_create(105,5)
datatype(100, 525)
julia> b = datatype_create(1+2im,3-4im)
datatype(-2 + 6im, 11 + 2im)
julia> c = datatype_create([1 2;3 4],[4 5;6 7])
datatype([-3 -3; -3 -3], [16 19; 36 43])
julia> d = datatype_create(1.5,0.2)
datatype(1.3, 0.30000000000000004)
If you are absolutely Ideologically Hell Bent on using an outer constructor, then you can do something like this
julia> datatype(a,b,dummy) = datatype(a - b,a * b)
datatype
julia> e = datatype(105,5,"dummy")
datatype(100, 525)
Antman's solution using the power of MACRO
julia> macro datatype(a,b)
return :( datatype($a - $b , $a * $b) )
end
#datatype (macro with 1 method)
julia> f = #datatype( 105 , 5 )
datatype(100, 525)
You can easily pass "normal" (i.e. non-keyword) variable values to a function via a Tuple with an ellipsis, like e.g.:
julia> f(x, y, z) = x + y + z;
julia> f(0, 1, 2)
3
julia> varvalues = 0, 1, 2
(0,1,2)
julia> f(varvalues...)
3
But for keyword variables, how do you pass both the keywords and the corresponding variable values via variables? Like e.g. (forgive the silly example):
julia> function g(x, y, z; operation = "add", format = "number")
operation == "add" && format == "number" && return x + y + z
operation == "add" && format == "string" && return string(x + y + z)
operation == "times" && format == "number" && return x * y * z
operation == "times" && format == "string" && return string(x * y * z)
end; # yep, I know this not type-stable
julia> g(0, 1, 2, operation = "times", format = "string")
"0"
julia> g(varvalues..., operation = "times", format = "string") # varvalues = (0,1,2)
"0"
So I would like to define two variables, analogous to varvalues above: keywords with the keywords and keywordvarvalues with the corresponding variable values, that can be passed to function g. Something like this, but that works:
julia> keywords = :operation, :format
(:operation,:format)
julia> keywordvarvalues = "times", "string"
("times","string")
julia> g(varvalues..., keywords... = keywordvarvalues...)
ERROR: MethodError: no method matching broadcast!...
I suppose I can always compose this String from keywords and keywordvarvalues:
expressionstring = """g(varvalues..., operation = "times", format = "string")"""
and then parse-eval it, but that's prolly bad practice, no?
This works:
julia> keywords = :operation, :format
(:operation,:format)
julia> keywordvarvalues = 10, 20
(10,20)
julia> g(; operation=1, format=2) = (operation, format)
g (generic function with 1 method)
julia> g(; zip(keywords, keywordvarvalues)...)
(10,20)
You can also use dictionaries:
julia> g(; keywords...) = keywords
julia> g(a=3, b=4)
2-element Array{Any,1}:
(:a,3)
(:b,4)
julia> d = Dict(:c=>5, :d=>6)
Dict{Symbol,Int64} with 2 entries:
:c => 5
:d => 6
julia> g(; d...)
2-element Array{Any,1}:
(:c,5)
(:d,6)