Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two different types of arrays of arrays

Tags:

arrays

julia

I'm confused about the different types of arrays of arrays. Consider these two examples

a = Array{Float64}[]
push!(a,[1, 2])
push!(a,[3, 4])
push!(a,[1 2; 3 4])

b = Array[[1.0, 2.0], [3.0,4.0], [1.0 2.0; 3.0 4.0]]

I'm not sure how a and b differ. Suppose I intend to run a for loop over each of the elements in a or b, and multiple each element by 2. That is,

for i in 1:3 a[i] = a[i]*2 end

for i in 1:3 b[i] = b[i]*2 end

I time the run time of both lines respectively, but they are equally fast. Are a and b the same? If so, why does a even exist? It looks fairly complicated, because typeof(a) yields "Array{Array{Float64,N} where N,1}". What does where do here?

like image 257
user1691278 Avatar asked Jan 25 '23 12:01

user1691278


1 Answers

Both a and b are vectors but they allow different type of elements. You can check this by writing:

julia> typeof(a)
Array{Array{Float64,N} where N,1}

julia> typeof(b)
Array{Array,1}

Now Array isa parametric type taking two parameters. The first parameter is type of elements that it allows. The second parameter is the dimension. You can see that in both cases the dimension is 1 which means both a and b are vectors. You can also check it using the ndims function:

julia> ndims(a)
1

julia> ndims(b)
1

The first parameter is allowed element type. In the case of a it is Array{Float64,N} where N while in the case of b just Array is printed. Before I explain how to read them note that the first parameter can be extracted using the eltype function:

julia> eltype(a)
Array{Float64,N} where N

julia> eltype(b)
Array

You can see that both a and b allow Array to be stored. First let me explain how to read Array{Float64, N} where N. It means that a allows storing of arrays of Float64 of any dimension. Actually you could have written in a shorter way like this Array{Float64} as you can check that:

julia> (Array{Float64,N} where N) === Array{Float64}
true

The reason is that if you do not put a restriction on a tail parameter it can be dropped in syntax. The where N part is a restriction on the parameter. In this case there is no restriction on the second parameter.

Now we can turn to b. You see its eltype is just Array, so both parameters are dropped, thus there are no restrictions on them as explained above. So Array is the same as Array{T, N} where {T,N} as you can see here:

julia> (Array{T, N} where {T,N}) === Array
true

So the difference is that a can store arrays of any dimension but they must have Float64 element type, while b can store arrays of any dimension and any element type. The distinction, in this case, has no performance impact as you have noted, but will have an impact on what can be stored in a and b. Here are some examples.

In this case they work the same, as you try to store an Int in them, but they allow only arrays:

julia> a[1] = 1
ERROR: MethodError: Cannot `convert` an object of type

julia> b[1] = 1
ERROR: MethodError: Cannot `convert` an object of type

but here they differ:

julia> a[1] = ["a"]
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Float64

julia> b[1] = ["a"]
1-element Array{String,1}:
 "a"

julia> a
3-element Array{Array{Float64,N} where N,1}:
 [1.0, 2.0]
 [3.0, 4.0]
 [1.0 2.0; 3.0 4.0]

julia> b
3-element Array{Array,1}:
 ["a"]
 [3.0, 4.0]
 [1.0 2.0; 3.0 4.0]

As you can see you can store an array of String in b, but this is not allowed to be stored in a.

Two additional comments (both topics are a bit complex so I leave out the details, but just give you hints what is going on):

  1. You are allowed not to specify element type of an array when defining it. In this case Julia will automatically pick its element type:
julia> [[1.0, 2.0], [3.0,4.0], [1.0 2.0; 3.0 4.0]]
3-element Array{Array{Float64,N} where N,1}:
 [1.0, 2.0]
 [3.0, 4.0]
 [1.0 2.0; 3.0 4.0]
  1. The performance impact of the choice of element type of an array depends on the fact:

    1. if the type is abstract (that can be checked by isabstracttype; this impact type inference made by the compiler),
    2. if it is bits type (that can be checked by isbitstype; this impacts the storage layout),
    3. if the element type is Union (small unions can are handled more efficiently).

In your case both element types are abstract and non-bits so the performance will be the same.

like image 50
Bogumił Kamiński Avatar answered Jan 31 '23 18:01

Bogumił Kamiński