We know that the notation [1, 2, 3] is used to create an array. In fact, this notation denotes a special type of array, called a (column) vector in Julia, as shown in the following screenshot:
To create this as a row vector (1 2 3), use the notation [1 2 3] with spaces instead of commas. This array is of type 1 x 3 Array{Int64,2}, so it has two dimensions. (The spaces used in [1, 2, 3] are for readability only, we could have written this as [1,2,3]).
A matrix is a two- or multidimensional array (in fact, a matrix is an alias for the two-dimensional case). We can write this as follows:
So, the column vector from the beginning can also be written as [1; 2; 3]. However, you cannot use commas and semicolons together.
To get the value from a specific element in the matrix, you need to index it by row and then by column, for example, matrix[2, 1] returns the value 3 (second row, first column).
Using the same notation, one can calculate products of matrices such as [1 2] * [3 ; 4]; this is calculated as [1 2] * [3 4], which returns the value 11 (which is equal to 1*3 + 2*4). In contrast to this, conventional matrix multiplication is defined with the operator .*:
[1 2] .* [3 ; 4]
# 2 Array{Int64,2}:
# 3 6
# 4 8
To create a matrix from random numbers between 0 and 1, with three rows and five columns, use ma1 = rand(3, 5), which shows the following results:
The ndims function can be used to obtain the number of dimensions of a matrix. Consider the following example:
julia> ndims(ma1) #> 2
julia> size(ma1) #> a tuple with the dimensions (3, 5)
To get the number of rows (3), run the following command:
julia> size(ma1,1) #> 3
The number of columns (5) is given by:
julia> size(ma1,2) #> 5
julia> length(ma1) #> 15, the number of elements
That's why you will often see this: nrows, ncols = size(ma), where ma is a matrix, nrows is the number of rows, and ncols is the number of columns.
If you need an identity matrix, where all the elements are zero, except for the elements on the diagonal that are 1.0, use the I function (from the LinearAlgebra package) with the argument 3 for a 3 x 3 matrix:
If ma is a matrix, say [1 2; 3 4], then ma' is the transpose matrix, that is [1 3; 2 4]:
ma: 1 2 ma' 1 3 3 4 2 4
ma' is an operator notation for the transpose(ma) function.
Multiplication is defined between matrices, as in mathematics, so ma * ma' returns the 2 x 2 matrix or type Array{Int64,2} as follows:
5 1111 25
If you need element-wise multiplication, use ma .* ma', which returns 2 x 2 Array{Int64,2}:
1 66 16
The inverse of a matrix ma (if it exists) is given by the inv(ma) function. The inv(ma) function returns 2 x 2 Array{Float64,2}:
-2.0 1.01.5 -0.5
The inverse means that ma * inv(ma) produces the identity matrix:
1.0 0.00.0 1.0
Trying to take the inverse of a singular matrix (a matrix that does not have a well-defined inverse) will result in LAPACKException or SingularException, depending on the matrix type. Suppose you want to solve the ma1 * X = ma2 equation, where ma1, X, and ma2 are matrices. The obvious solution is X = inv(ma1) * ma2. However, this is actually not that good. It is better to use the built-in solver, where X = ma1 \ ma2. If you have to solve the X * ma1 = ma2 equation, use the solution X = ma2 / ma1. Solutions that use / and \ are much more numerically stable, and also much faster.
If v = [1.,2.,3.] and w = [2.,4.,6.], and you want to form a 3 x 2 matrix with these two column vectors, then use hcat(v, w) (for horizontal concatenation) to produce the following output:
1.0 2.02.0 4.03.0 6.0
vcat(v,w) (for vertical concatenation) results in a one-dimensional array with all the six elements with the same result as append!(v, w).
Thus, hcat concatenates vectors or matrices along the second dimension (columns), while vcat concatenates along the first dimension (rows). The more general cat can be used to concatenate multidimensional arrays along arbitrary dimensions.
There is an even simpler literal notation: to concatenate two matrices a and b with the same number of rows to a matrix c, just execute c = [a b]. now b is appended to the right of a. To put b beneath c, use c = [a; b]. The following is a concrete example, a = [1 2; 3 4]and b = [5 6; 7 8]:
The reshape function changes the dimensions of a matrix to new values if this is possible, for example:
reshape(1:12, 3, 4) #> returns a 3x4 array with the values 1 to 123x4 Array{Int64,2}: 1 4 7 10 2 5 8 11 3 6 9 12a = rand(3, 3) #> produces a 3x3 Array{Float64,2}3x3 Array{Float64,2}: 0.332401 0.499608 0.355623 0.0933291 0.132798 0.967591 0.722452 0.932347 0.809577reshape(a, (9,1)) #> produces a 9x1 Array{Float64,2}:9x1 Array{Float64,2}: 0.332401 0.0933291 0.722452 0.499608 0.132798 0.932347 0.355623 0.967591 0.809577reshape(a, (2,2)) #> does not succeed:ERROR: DimensionMismatch("new dimensions (2,2) must be consistent with array size 9")
When working with arrays that contain arrays, it is important to realize that such an array contains references to the contained arrays, not their values. If you want to make a copy of an array, you can use the copy() function, but this produces only a shallow copy with references to the contained arrays. In order to make a complete copy of the values, we need to use the deepcopy() function.
The following example makes this clear:
x = Array{Any}(undef, 2) #> 2-element Array{Any,1}: #undef #undef
x[1] = ones(2) #> 2-element Array{Float64} 1.0 1.0
x[2] = trues(3) #> 3-element BitArray{1}: true true true
x #> 2-element Array{Any,1}: [1.0,1.0] Bool[true,true,true]
a = x
b = copy(x) c = deepcopy(x)
# Now if we change x:
x[1] = "Julia"
x[2][1] = false
x #> 2-element Array{Any,1}: "Julia" Bool[false,true,true]
a #> 2-element Array{Any,1}: "Julia" Bool[false,true,true]
isequal(a, x) #> true, a is identical to x
b #> 2-element Array{Any,1}: [1.0,1.0] Bool[false,true,true]
isequal(b, x) #> false, b is a shallow copy of x
c #> 2-element Array{Any,1}: [1.0,1.0] Bool[true,true,true]
isequal(c, x) #> false
The value of a remains identical to x when this changes, because it points to the same object in the memory. The deep copy c function remains identical to the original x. The b value retains the changes in a contained array of x, but not if one of the contained arrays becomes another array.
To further increase performance, consider using the statically-sized and immutable vectors and matrices from the ImmutableArrays package, which is a lot faster, certainly for small matrices, and particularly for vectors.