Arrays in Julia
In general, unlike many other technical computing languages, Julia does not expect programs to be written in a vectorized style for performance. Julia’s compiler uses type inference and generates optimized code for scalar array indexing, allowing programs to be written in a style that is convenient and readable, without sacrificing performance, and using less memory at times.[1]
The above quote, taken directly from Julia documentation is the feature that got me into Julia in the first place. It might seem trivial at first, but this is the same level of paradigm-shift as PyTorch caused after Tensorflow. When the compiler isn’t responsible for vectorization, everyone else is! Let’s see how many people that is. The user must keep track of the best-practices, which must be maintained by the community. Moreover, packages need to be written to allow idiomatic vectorization. All this takes a lot of time and effort by way too many people. When the compiler is responsible instead, vectorization is a concern only for core developers.
Definition
Manually defining an array in Julia takes much less effort and keystrokes than most other languages.
Defining vectors is particularly pleasant, you can specify ranges (or elements) separated by a semicolon to concatenate all of them into a column vector:
julia> [1:3;4:6]
6-element Array{Int64,1}:
1
2
3
4
5
6
If instead of the semicolon, you use spaces, the concatenation is horizontal:
julia> [1:3 4:6]
3×2 Array{Int64,2}:
1 4
2 5
3 6
You can then combine the two types of concatenation to define matrices:
julia> A=[[1,2,3] [4,5,6] [7,8,9]]
3×3 Array{Int64,2}:
1 4 7
2 5 8
3 6 9
julia> A=[[1 2 3];[4 5 6];[7 8 9]]
3×3 Array{Int64,2}:
1 2 3
4 5 6
7 8 9
You can force the ‘type’ of an array by prefixing with it:
julia> Real[[1 2 3];[4 5 6];[7 8 9]]
3×3 Array{Real,2}:
1 2 3
4 5 6
7 8 9
As must be familiar to you, arrays have many helper methods:
julia> eltype(A) #Element type of A
Int64
julia> ndims(A) #Number of dimensions of A
2
julia> size(A) #Shape of A
(3, 3)
Comprehension
Syntax for comprehension is as natural as could be, let’s see how you’d populate a matrix with the values of a polynomial in two variables at various points of the Argand plane:
julia> poly=[42x^3+3y^2+9x*y+1 for x=-3:3,y=3:9] #That's it! One line only!!
7×7 Array{Int64,2}:
-1187 -1193 -1193 -1187 -1175 -1157 -1133
-362 -359 -350 -335 -314 -287 -254
-41 -29 -11 13 43 79 121
28 49 76 109 148 193 244
97 127 163 205 253 307 367
418 457 502 553 610 673 742
1243 1291 1345 1405 1471 1543 1621
In general, the syntax is [ expression for var1=rangeVar1,var2=rangeVar2,...]
. Now, we showcase reshape
to define a higher dimensional matrix:
julia> A=reshape(1:64,(4,4,4))
4×4×4 reshape(::UnitRange{Int64}, 4, 4, 4) with eltype Int64:
[:, :, 1] =
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
[:, :, 2] =
17 21 25 29
18 22 26 30
19 23 27 31
20 24 28 32
[:, :, 3] =
33 37 41 45
34 38 42 46
35 39 43 47
36 40 44 48
[:, :, 4] =
49 53 57 61
50 54 58 62
51 55 59 63
52 56 60 64
Then, you’d index the matrix as per usual:
julia> poly[3,5]
43
julia> A[3,4,4]
63
You can also use this indexing to change a matrix:
julia> poly[end,end]=345678909876543 #change the bottom-right value
345678909876543
julia> poly
7×7 Array{Int64,2}:
-1187 -1193 -1193 -1187 -1175 -1157 -1133
-362 -359 -350 -335 -314 -287 -254
-41 -29 -11 13 43 79 121
28 49 76 109 148 193 244
97 127 163 205 253 307 367
418 457 502 553 610 673 742
1243 1291 1345 1405 1471 1543 345678909876543
You can also use ranges as indices:
julia> poly[1:2,:]
2×7 Array{Int64,2}:
-1187 -1193 -1193 -1187 -1175 -1157 -1133
-362 -359 -350 -335 -314 -287 -254
Remember that in Julia, ranges are inclusive, and indices start at 1, always.
Broadcasting
Now, broadcasting, in Julia can be done explicitly:
julia> poly[1:2,:]=broadcast(+,1000000,poly[1:2,:])
2×7 Array{Int64,2}:
998813 998807 998807 998813 998825 998843 998867
999638 999641 999650 999665 999686 999713 999746
julia> poly
7×7 Array{Int64,2}:
998813 998807 998807 998813 998825 998843 998867
999638 999641 999650 999665 999686 999713 999746
-41 -29 -11 13 43 79 121
28 49 76 109 148 193 244
97 127 163 205 253 307 367
418 457 502 553 610 673 742
1243 1291 1345 1405 1471 1543 345678909876543
Or it can be done implicitly:
julia> poly[3:4,:] .+ -10000 #This doesn't change the matrix poly
2×7 Array{Int64,2}:
-10041 -10029 -10011 -9987 -9957 -9921 -9879
-9972 -9951 -9924 -9891 -9852 -9807 -9756
As homework, I suggest you play with the .*
operator on matrices with at least 4 dimensions. In the next article, we’ll be looking at Tuples in Julia.