Home > Software design >  Make a 2-dimensional lattice in Julia
Make a 2-dimensional lattice in Julia

Time:12-14

Context

Let us consider 5 x 5 lattice with each point indexed as (1,1),(1,2),...(1,5),(2,1),...,(5,5), and call this lattice L.

What I want to do

I want to make a 5 x 5 matrix with each element having a value which indicates each point of L like this:

5×5 Matrix{Vector{Int64}}:
 [1, 1]  [1, 2]  [1, 3]  [1, 4]  [1, 5]
 [2, 1]  [2, 2]  [2, 3]  [2, 4]  [2, 5]
 [3, 1]  [3, 2]  [3, 3]  [3, 4]  [3, 5]
 [4, 1]  [4, 2]  [4, 3]  [4, 4]  [4, 5]
 [5, 1]  [5, 2]  [5, 3]  [5, 4]  [5, 5]

What I tried

I just tried the following:

X1 = [1,2,3,4,5]
X2 = copy(X1)
Lattice = Matrix{Vector{Int64}}(undef, length(X1), length(X2)) # what I want to make
for x1 in X1
    for x2 in X2
        Lattice[x1,x2] = [X1[x1],X2[x2]]
    end
end

Lattice

Question

  • Is there other ways to make the code simple or short?
  • I'm afraid if the performance get worse when increasing the lattice size like 50 x 50. Any better way?
  • Whatever better practice?

Any information would be appreciated.

CodePudding user response:

You can use an array comprehension:

julia> N = 5;

julia> L = [[i, j] for i in 1:N, j in 1:N]
5×5 Matrix{Vector{Int64}}:
 [1, 1]  [1, 2]  [1, 3]  [1, 4]  [1, 5]
 [2, 1]  [2, 2]  [2, 3]  [2, 4]  [2, 5]
 [3, 1]  [3, 2]  [3, 3]  [3, 4]  [3, 5]
 [4, 1]  [4, 2]  [4, 3]  [4, 4]  [4, 5]
 [5, 1]  [5, 2]  [5, 3]  [5, 4]  [5, 5]

CodePudding user response:

It's not a Matrix of Vectors, but CartesianIndices serves this purpose.

L = zeros((5,5)) # example 5x5 Matrix

Li = CartesianIndices(size(L))
#=
5×5 CartesianIndices{2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}:
 CartesianIndex(1, 1)  …  CartesianIndex(1, 5)
 CartesianIndex(2, 1)     CartesianIndex(2, 5)
 CartesianIndex(3, 1)     CartesianIndex(3, 5)
 CartesianIndex(4, 1)     CartesianIndex(4, 5)
 CartesianIndex(5, 1)     CartesianIndex(5, 5)
=#

If you must have the indices matrix like in your post, you could make a method that converts a CartesianIndex to a Vector and broadcast that method over the CartesianIndices:

Li .|> (el -> collect(Tuple(el)) )
#=
5×5 Array{Array{Int64,1},2}:
 [1, 1]  [1, 2]  [1, 3]  [1, 4]  [1, 5]
 [2, 1]  [2, 2]  [2, 3]  [2, 4]  [2, 5]
 [3, 1]  [3, 2]  [3, 3]  [3, 4]  [3, 5]
 [4, 1]  [4, 2]  [4, 3]  [4, 4]  [4, 5]
 [5, 1]  [5, 2]  [5, 3]  [5, 4]  [5, 5]
=#

But I recommend sticking with CartesianIndices because it doesn't allocate memory, and CartesianIndex is tailor-made for array indexing, which seems to be your intent.

P.S. Directly making a Matrix of Vectors necessarily allocates memory, too, but it will probably allocate less memory than how I converted the CartesianIndices. collect(Tuple(el)) must be type-unstable or something. So if you must have a Matrix of Vectors, try to find the way that allocates the least. In the 5x5 case, you will at minimum have 26 allocations.

CodePudding user response:

What BatWannaBe recommends is the way I would do it, but just as a reference here is how you could get what you asked for using broadcasted vcat:

julia> vcat.(1:5, (1:5)')
5×5 Matrix{Vector{Int64}}:
 [1, 1]  [1, 2]  [1, 3]  [1, 4]  [1, 5]
 [2, 1]  [2, 2]  [2, 3]  [2, 4]  [2, 5]
 [3, 1]  [3, 2]  [3, 3]  [3, 4]  [3, 5]
 [4, 1]  [4, 2]  [4, 3]  [4, 4]  [4, 5]
 [5, 1]  [5, 2]  [5, 3]  [5, 4]  [5, 5]
  • Related