How can I properly combine map() and rand() in Julia? - arrays

I am using Zygote in Julia, and I have some code that is supposed to loop through two objects of the same dimensions and, for every item of length less than <= 1, randomly choose between that part of/for either the first or the second object. The code may better explain what I am attempting to do:
function rangeThrough(parent1, parent2)
child = map(x -> if length(x[1]) > 1 rangeThrough(x[1], x[2]) else rand([x[1] x[2]]) end, zip(parent1, parent2))
return child
end
aa = [Float32[0. 0. 0. 0.], Float32[0. 0. 0.], Float32[0.]]
bb = [Float32[1. 1. 1. 1.], Float32[1. 1. 1.], Float32[1.]]
Zygote.Params(rangeThrough(aa, bb))
The output that I get for the last line of code is something like the following:
Params([Float32[1.0 1.0 1.0 0.0], Float32[0.0 0.0 1.0], 0.0])
However, what I am trying to achieve is:
Params([Float32[1.0 1.0 1.0 0.0], Float32[0.0 0.0 1.0], Float32[0.0]])
I have tried a few things but wasn't able to get it working yet. I am not sure why it is that the last number is not contained in a Float32[] array. What could I do differently to correct this?
Edit:
By simplifying the code to only accept one list as input, like so:
function tempRangeThrough(parent1)
child = map(x -> if length(x) > 1 tempRangeThrough(x) else rand([x]) end, parent1)
return child
end
Zygote.Params(tempRangeThrough(aa))
I get the intended output:
Params([Float32[0.0 0.0 0.0 0.0], Float32[0.0 0.0 0.0], Float32[0.0]])
So, I believe this is related to the properties of zip(), but I'm not positive how to fix this.

You might want something like this:
julia> rangeThrough(p1, p2) = [length(a) > 1 ? a : rand((a,b)) for (a, b) in zip(p1, p2)]
rangeThrough (generic function with 1 method)
julia> rangeThrough(aa, bb)
3-element Array{Array{Float32,N} where N,1}:
[0.0 0.0 0.0 0.0]
[0.0 0.0 0.0]
[1.0]

I didn't really get what behaviour you seek exactly, but wanted to mention that map can take n-ary functions, too -- so you don't need zip here. And combining with a do-block, the function could look as follows:
julia> range_through(parent1, parent2) = map(parent1, parent2) do x, y
if length(x) > 1
return x
else
return rand((x, y))
end
end
range_through (generic function with 1 method)
julia> range_through(aa, bb)
3-element Array{Array{Float32,N} where N,1}:
[0.0 0.0 0.0 0.0]
[0.0 0.0 0.0]
[1.0]

Related

Ways of taking care of JSON with Matrix data on Julia

I am in a Julia project and I am trying to write code for saving Matrix data as .json file.
The problem I am facing is when I read the json string from the file and parse it, the Matrix is changed into "vector of vectors".
In [1]:
n=10
mat = zeros(n,n)
data = Dict(
"n" => n,
"mat" => mat
)
using JSON
output_text = JSON.json(data)
out = open("jsonTest.json","w")
println(out,output_text)
close(out)
In [2]:
data
Out[2]:
Dict{String, Any} with 2 entries:
"mat" => [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.…
"n" => 10
In [3]:
op = JSON.parsefile("jsonTest.json")
Out[3]:
Dict{String, Any} with 2 entries:
"mat" => Any[Any[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Any[0.0, …
"n" => 10
According to this question, one simple solution is using hcat function to re-convert "vector of vectors" to "matrix" when reading the json.
It works, but it is a bit messy(and might time-consuming) to take care of each matrix in the json. Is there simpler way?
Any information would be appreciated.
Since JSON (the spec) does not know about Matrix, you need to either do nested vectors (as you're right now), or reshape.
I personally prefer reshape because its memory layout is the same as an concrete Matrix in Julia and reshap has no allocation and less overhead over hcat
julia> a = rand(2,3)
2×3 Matrix{Float64}:
0.534246 0.282277 0.140581
0.841056 0.443697 0.427142
julia> as = JSON3.write(a)
"[0.5342463881378705,0.8410557102859995,0.2822771326129221,0.44369703601566,0.1405805564055571,0.4271417199755423]"
julia> reshape(JSON3.read(as), (2,3))
2×3 reshape(::JSON3.Array{Float64, Base.CodeUnits{UInt8, String}, Vector{UInt64}}, 2, 3) with eltype Float64:
0.534246 0.282277 0.140581
0.841056 0.443697 0.427142

Julia: eigs() function doesn't return all eigenvalues

I am using the eigs() function (from the Arpack package) to find the eigenvalues of a sparse matrix (eigen() doesn't work for spare matrices). Apparently, eigs() fails to find all the eigenvalues, even in a very simple case:
using Arpack
M = spdiagm(0 => [1,2,3])
eigs(M, nev = 3)
The output of the last line is a vector contacting only 2 eigenvalues, "2" and "3" ("1" is missing). What am I missing? Is there a different function for computing the eigenvalues of a sparse matrix (the actual sparse matrix is much large than the above M).
It actually shows at warning:
julia> eigs(Matrix(M), nev = 3);
┌ Warning: Adjusting nev from 3 to 2
└ # Arpack c:\JuliaPkg\Julia1.5.0\packages\Arpack\o35I5\src\Arpack.jl:82
Looking at the source code this can return a maximum of LinearAlgebra.checksquare(M) - 1 values.
What you could try to do is to use a BandedMatrix instead which is also sparse:
julia> m=BandedMatrix(0=>1:3)
3×3 BandedMatrix{Int64,Array{Int64,2},Base.OneTo{Int64}}:
1 ⋅ ⋅
⋅ 2 ⋅
⋅ ⋅ 3
julia> eigen(m)
Eigen{Float64,Float64,Array{Float64,2},Array{Float64,1}}
values:
3-element Array{Float64,1}:
1.0
2.0
3.0
vectors:
3×3 Array{Float64,2}:
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
eigs uses an iterative method that is only practical or encouraged for large sparse matrices and when you only want a small number of eigenvalues (small compared to the size of the matrix).
The error message you got in eigen is a little unfortunate. Preferably, it would have also mentioned that if your array is small, you should just collect it into a dense matrix, e.g.
julia> using SparseArrays, LinearAlgebra
julia> M = spdiagm(0 => [1,2,3])
3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries:
[1, 1] = 1
[2, 2] = 2
[3, 3] = 3
julia> eigen(collect(M))
Eigen{Float64,Float64,Array{Float64,2},Array{Float64,1}}
values:
3-element Array{Float64,1}:
1.0
2.0
3.0
vectors:
3×3 Array{Float64,2}:
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
If your matrix is too big to fit into memory and you must use a sparse matrix, then I suspect you don't need all the eigenvalues.

Why isn't dot-assignment updating multiple arrays?

I want to assign some computation into a pair of arrays, with the top portion going into array x, and the bottom portion going into y. I attempted the following, but neither x nor y were updated:
x = zeros(2)
y = zeros(3)
[x;y] .= [1.2, 4.5, 2.3, 4.5, 5.6]
In general1, the .= operator just assigns into whatever the result of the left hand side evaluates to — and in this case the result is a brand new array with the contents of x and y vertically concatenated. You can see that [x; y] creates a new array decoupled from x and y by just trying it by itself:
x = zeros(2)
y = zeros(3)
r = [x;y]
r[1] = 1
julia> r
5-element Array{Float64,1}:
1.0
0.0
0.0
0.0
0.0
julia> x
2-element Array{Float64,1}:
0.0
0.0
julia> y
3-element Array{Float64,1}:
0.0
0.0
0.0
julia> r .= [1.2, 4.5, 2.3, 4.5, 5.6] # just changes `r`, not `x` or `y`
5-element Array{Float64,1}:
1.2
4.5
2.3
4.5
5.6
julia> all(iszero, x) && all(iszero, y)
true
Now, you can update x and y if they're put into a special "lazy" container from LazyArrays.jl that emulates a concatenation operation:
julia> using LazyArrays
julia> ApplyArray(vcat, x, y) .= [1.2, 4.5, 2.3, 4.5, 5.6]
5-element ApplyArray{Float64,1,typeof(vcat),Tuple{Array{Float64,1},Array{Float64,1}}}:
1.2
4.5
2.3
4.5
5.6
julia> x
2-element Array{Float64,1}:
1.2
4.5
julia> y
3-element Array{Float64,1}:
2.3
4.5
5.6
1 There's one important exception to this general rule: we support indexed assignment with multiple selected indices in combination with .= to update the original array. In other words, the syntax y[1:2] .= [3.4, 5.6] will indeed update the first two elements of y, even though y[1:2] elsewhere will allocate a brand new 2-element array decoupled from y. In other words, when you use indexing on the left-hand side of .=, it automatically uses a view when necessary.

How can I find a value's location in a matrix in Julia?

I was using find with a 3D matrix A like this:
julia> find(A.==1)
2-element Array{Int64,1}:
1
234
4567
Julia gives me the location as an index instead of as an array of indices. For example, it returns 234 instead of (1,2,1).
I looked at this question, but my matrix is very large and has a shape of (360,360,360). I can't use the method suggested there.
I tried to study its index pattern and transform it using a function that I coded:
function cmf_p(matrix)
for a=1:length(matrix);
aa=matrix[a]
rd_u_m=ceil(aa/(360^2))
rd_d_m=floor(aa/(360^2)-1)
rd_d_t=(aa-rd_d_m*360)/360^2
rd_d_p=aa-rd_d_m*360^2-floor(rd_d_t)*360
println(rd_u_m);
println(ceil(rd_d_t)*360);
println(ceil(aa-rd_d_m*360^2-floor(rd_d_t)*360))
end
end
But it gives me the wrong result.
How can I use the index and transform it to the location I want?
You are looking for ind2sub:
julia> A = eye(3)
3x3 Array{Float64,2}:
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
julia> inds = find(A.==1.0)
3-element Array{Int64,1}:
1
5
9
julia> [ind2sub(size(A), ind) for ind in inds]
3-element Array{Any,1}:
(1,1)
(2,2)
(3,3)

Multiple assignment in multidimensional array

I have a 4x4 array of zeros.
julia> X = zeros(4,4)
4x4 Array{Float64,2}:
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
I have an 2xN array containing indices of elements in X that I want to assign a new value.
julia> ind = [1 1; 2 2; 3 3]
3x2 Array{Int64,2}:
1 1
2 2
3 3
What is the simplest way to assign a value to all elements in X whose indices are rows in ind? (something like X[ind] = 2.0).
julia> X
2.0 0.0 0.0 0.0
0.0 2.0 0.0 0.0
0.0 0.0 2.0 0.0
0.0 0.0 0.0 0.0
I'm not sure there is a non-looping way to do this. What's wrong with this?
for i=[1:size(ind)[1]]
a, b = ind[i, :]
X[a, b] = 2.0
end
user3467349's answer is correct, but inefficient, because it allocates an Array for the indices. Also, the notation [a:b] is deprecated as of Julia 0.4. Instead, you can use:
for i = 1:size(ind, 1)
a, b = ind[i, :]
X[a, b] = 2.0
end

Resources