Matrix

Go To: Linear Indexing, Link Vectors, Mathematical Operations, Member Functions

 

Similar to Vector data structure that is specialized to manage 1D numeric data, Matrix is also very flexible but to work with 2D numeric data. The following are the possible cases to access (read/write) the elements of a matrix:

  1. Accessing single values
  2. Accessing multiple values

 

Accessing to single values

>>m=std.tomatrix{ {1, 2, 3} , {3, 4, 5} , {6 , 7, 8} }
>>m
1    2    3
3    4    5
6    7   8

>>m[1][1]=10
>>m[{2}]=20

>>m
10    20   3
3      4      5
6      7      8

Command (m[1][1]=10): We access the matrix using m[row][column] access style. Here we changed the first row and first column from 1 to 10.

Command (m[{2}]=20): We accessed the matrix using the linear indexing concept. The linear indexing for the (3x3) matrix, m, ranges from 1 to 9, where index 1 is equal to m[1][1] and index 9 is equal to m[3][3].

 

>>m[2][3] or >>m(2,3)
5

>> m[{7}]
6

One can quickly notice that the call m[2][3] is equivalent to m(2,3). Similar to Vector, Matrix also supports reading both with index, [][], and function call, (), styles.

 

Accessing to multiple values

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }
>>m
1    2    3
4    5    6
7    8    9

>>m[{1, 3, 5}]
1    3    5    COL

>>m({1, 3, 5})
1    3    5    COL

 

It is seen that if the return value is a single number then the return type is number; however, if it is more than a single value and 1D, then the return type is Vector.

>>m[ {from=3, to=5} ] = {30, 40, 50, 60}
>>m
1       2    30
40    50    6
7        8    9

Here using linear indexing we request that elements 3, 4 and 5 be equal to 30, 40 and 50. Since we request only 3 elements to be changed, the 4th element on the right-hand side, number 60, was ignored. Following is another case.

 

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m[ {from=3, to=5} ] = {30, 40}

>>m
1     2     30
40
   5     6
7     8     9

This time we have requested 3 elements on the left-hand side to be changed but only provided 2 numbers on the right-hand side. Therefore, only 2 elements were changed.

 

In the following example, notice the use of keyword by:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m[ {from=3, to=9, by=4} ] = {30, 70, 90}
>>m
1     2    30
4     5    6
70  
8    9

 

The following is doable as well:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m[ {} ]={10, 20, 30, 40}
>>m
10    20    30
40   
5       6
7      8       9

 

On the right-hand side we can also use a Vector. The following example demonstrates this:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }
>>v=std.tovector{10, 20, 30}

>>m[{}] = v
>>m
10    20    30
4      5       6
7      8       9

 

To change all the elements of a matrix to a single value:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m[{}] = 10
>>m
10 10 10
10 10 10
10 10 10

 

To read a single-column or a row, the following syntax can be used:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m({}, 1)
-- read the first column
1    4    7    COL

>>m(1, {})
--read the first row
1    2    3    ROW

 


Link Vectors:

Internally a matrix is composed of vectors. Therefore, it is highly efficient to read a row in the following way:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m[1]
1    2    3    LINK

However, although it can be noticed that the return type is a Vector, it is neither row nor a column vector, but a link vector. A link vector shares the same memory space with the row of a matrix and as mentioned above this is due to the fact that Matrix is made up of Vectors.

 

The following example is illustrative what could happen if caution is not exercised when working with link vectors.

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>v=m[1]
>>v[1]=10

>>m
10    2     3
4      5     6
7      8     9

In the above example:

  1. Initially a link vector, namely v, is created using v=m[1]
  2. The first element of the link Vector is set to 10 by v[1]=10.

We notice that the first element of the first row of the matrix was also changed.

 

Question arises, what happens if v is garbage collected by Lua (such as v=nil or when it is out of scope):

>>v=nil
>>m
10    2    3
4      5    6
7      8    9

It is seen that the matrix remains intact. Internally, when link vectors are destroyed, the memory shared by the vector is not deleted, therefore the matrix is not affected.

 

Mathematical Operations

Matrix structure supports essential arithmetic operations: Let m1, m2 and m3 denote matrix of same size, and a an arbitrary number.

  1.  Addition: m1+m2=m3 m1+ a=a+m1=m2

  2.  Subtraction: m1-m2=m3 m1-a=m2 a-m1=m3

  3.  Multiplication: m1*m2=m3 a*m1=m1*a=m2

Besides the arithmetic operators, Matrix also supports __pairs and next methods, therefore is an iteratable container.

 

 

Member Functions


append (elem, align): Appends a vector or a Lua table to the matrix (please see insert). The arguments:

elem: Either a Vector or a Lua table

align:Alignment of the inserted element, either "row" or "col".

>>m=std.tomatrix{ {1,2,3} , {4,5,6} , {7,8,9} }
>>m
1    2    3
4    5    6
7    8    9

>>m:append( {10, 20, 30}, "row")
-- append by row
>>m:append( {11, 12, 13, 14}, "col")
-- append by column

>>m
1     2     3      11
4     5     6      12
7     8     9      13
10
 20   30    14

 

Appending a vector is as same as appending a Lua table:

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }
>>v=std.tovector{10, 20, 30}

>>m:append(v, "row")
>>m
1       2     3
4       5     6
7       8     9
10    20
  30

 

 

clone(): Makes an exact copy of the existing matrix. Notice the difference in the following commands.

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m1=m[{}]
-- array access via linear indexing (return type is vector)
>>m1
1    2    3    4    5    6    7    8    9    COL

>>m2=m({},{})
-- function call access via two empty Lua tables (return type is matrix)
>>m2
1    2    3
4    5    6
7    8    9

>>m3=m:clone()
-- direct call of the clone member function equivalent to m({},{})
>>m3
1    2    3
4    5    6
7    8    9

 

 

delc(pos): Deletes a column at the specified position, if the matrix will have at least 2 columns after the deletion. Otherwise an error message is returned.

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m:delc(2)
>>m
1    3
4    6
7    9

 

 

delr(pos): Deletes a row at the specified position, if the matrix will have at least 2 rows after the deletion. Otherwise an error message is returned.

>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m:delr(2)
>>m
1     2     3
7     8     9

Alternatively, m[2]=nil would have given the same result.

 

 

equal(arg, tol=1E-5): Tests whether the elements of the Matrix is equal to a number (mij==a) or elements of the Matrix is equal to the elements of another Matrix (m1ij==m2ij). The equality test is done using a predefined tolerance, which is 1E-5. The tolerance level can be specified by the user.

>>m=std.tomatrix{ {1, 2, 3} , {1.1, 2.1, 3.1} , {7, 8, 9} }
>>m
1         2         3
1.1     2.1     3.1
7         8         9

>>m:equal(3)--default tolerance level
0    0    1
0    0    0
0    0    0

>>m:equal(3, 0.11)--tolerance level is set to 0.11
0     0     1
0     0     1
0     0     0


>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }
>>m2=std.tomatrix{ {1, 2, 3} , {1.1, 2.1, 3.1} , {7, 8, 9} }

>>m:equal(m2)
1    1    1
0    0    0
1    1    1

 

 

greater(arg): Works the same way as member function equal, except there is no tolerance defined. Tests for the > operator.

 


greater_equal(arg, tol=1E-5): The function first tests for equality of the element with the given tolerance as detailed in the equal member function, if this test fails then tests whether it is greater. Tests for the >= operator.


 

insert (elem, pos, align): Inserts a vector or a Lua table to the matrix (please see append). The arguments:

elem: Either a Vector or a Lua table

pos: A valid position starting from 1.

align: Alignment of the inserted element, either "row" or "col".


>>m=std.tomatrix{ {1, 2, 3} , {4, 5, 6} , {7, 8, 9} }

>>m:insert( {10, 20, 30}, 2, "col")
>>m
1   10    2    3
4   20    5    6
7   30    8    9

>>m:insert({11, 12, 13, 14}, 1, "row")
>>m
11    12    13    14
1      10    2      3
4      20    5      6
7      30    8      9

 

The syntax of inserting a Vector to the matrix is same as inserting a Lua table.

>>m=std.tomatrix{ {1, 2, 3}, {4, 5, 6} , {7, 8, 9 } }
>>v=std.tovector{10, 20, 30}

>>m:insert(v, 3, "col")
>>m
1    2   10    3
4    5   20    6
7    8   30    9

 

less(arg): Works the same way as member function equal, except there is no tolerance defined. Tests for the < operator.

 

less_equal(arg, tol=1E-5): The function first tests for equality of the element with the given tolerance as described in the equal member function, if this test fails then tests whether it is smaller. Tests for the <= operator.

 

ncols(): Returns the number of columns. The returned value is of type integer.

 

nrows(): Returns the number of rows. The returned value is of type integer.

 

sort(pos, order): Sorts the matrix by given column position.

pos: Column number according to which the matrix will be sorted.

order: Sort order, either "A" or "D" for ascending and descending sorts.


>>m=std.floor( std.rand(3, 3)*10 )
>>m
6     7     2
5     8     9
6     9     6

>>m:sort(1, "D")
>>m
6     7     2
6     9     6
5     8     9