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.

References

  1. Julia Docs

Leave a Reply

Your email address will not be published.