Contact: zegraph  @  yahoo.com      Last update: June 2020

User's Guide to Matrix

A matrix contains a pointer to a continuous memory block. Since ZeScript uses the term "array" for its hash array. "Matrix" is used here to mean a 2D numerical array of C-equivalent types of char, unsigned char, short, unsigned short, int, unsigned int, float, or double.

Data in a matrix are in row major layout. Its rows and columns are counted from 0, as shown in the following figure.

Although matrix operations are intended for 1-D or 2D data, it does not mean that the matrix library cannot be used for a higher dimensional array. For example, assuming you have data of 3x3x3 stored in a hard disk, you can create a 3x9 matrix, read the data, and then treat each row as a 3x3 matrix.

Note that to test script examples in this guide, user must include

load("matrix.dll");

Matrix Creation

A matrix is created by a function with a name related to a C-type:

B = float(10);           // create float matrix of 10 x 1
C = double(10,10);       // create double matrix of 10 x 10
D = uchar(10,10);        // create unsigned char matrix of 10 x 10

When the input items to those functions are more than 2, they are not mean to be matrix dimensions, but numbers for filling an one row matrix:

E = int(1,2,3,4,5);        // create 1x5 matrix and fill it with 1,2,3,4,5.
E.csv();

Matrix Duplication

When the input to a matrix creation function is a matrix, it means to duplicate the input matrix:

A = double(3,3);         // create a 3x3 double matrix
A.fill(1,1);
B = int(A);              // create a 3x3 int and fill it with A's data

Another simple way to duplicate a matrix is to use "+" operators:

A = int(10,10);         // create in integer matrix of 10 x 10
A.fill(0,1);            // a is filled with number from 0 to 99
B = A+0;                // duplicate a

When adding 0 to a matrix, nothing more than matrix duplication happens. The same is true for subtracting 0 from a matrix and multiplying or dividing a matrix by 1.

The clone() function let you duplicate matrixes of different types:

A = uint(10,10);                 // create in unsigned integer matrix of 10 x 10
A.fill(0,1);                     // a is filled with number from 0 to 99
B = double();
B.clone(A);                      // resize B and transfer data from A to B
C = double(A)                    // alternative way to clone a matrix

Pointer

The ptr() function may be used to obtain the pointer of a matrix and pass it to functions of other libraries:

A = double(10,10);
p = A.ptr();
ptr = p[0];              // the pointer
n = p[1];                // the number of matrix element
e = p[2];                // the number of bytes of an element
[ptr,n,e] = A.ptr();     // A shortcut to get ptr, n, and e

Indexing

In manipulating a matrix, the row and column index may be an integer, a matrix of char-type, or an array produced by a range expression. An integer index specifies a row or column. When the column index is not given, a matrix is treated as 1 dimensional. For example,

A = int(2,3);               // 2-rows, 3-columns int-type matrix
A.fill(0,1);                // A contains
                            //     [ 0 1 2 ]
                            //     [ 3 4 5 ]
a = A[0,2];                 // a is 2
b = A[5];                   // b is 4
A[2] = 0;                   // A becomes
                            //     [ 0 1 0 ]
                            //     [ 3 4 5 ]
A[1,1] = 0;                 // A becomes
                            //     [ 0 1 0 ]
                            //     [ 3 0 5 ]

The operator * may be used in a row/column index to indicate all element in the row/column:

A[*,1] = 9;                 // A becomes
                            //     [ 0 9 0 ]
                            //     [ 3 9 5 ]
B = A[0,*]                  // B is
                            //     [ 0 9 0 ]

A char-type matrix is usually resulted from a comparison of matrix and may be used to get and set matrix elements. For example,

A = int(10,10);             // 10-rows, 10-columns int-type matrix
A.fill(0,1);                // A contains
                            //     [ 0   1 ...  9 ]
                            //     [ 10 11 ... 19 ]
                            //     ...
                            //     [ 90 91 ... 99 ]
I = A[1,*] > 10;            // I is
                            //     [ 0   1 ...  1 ]
A[1,I] = 0;                 // A becomes
                            //     [ 0   1 ...  9 ]
                            //     [ 10  0 ...  0 ]
                            //     ...
                            //     [ 90 91 ... 99 ]
I = A > 10;                 // I contains
                            //     [ 0   0 ...  0 ]
                            //     [ 0   0 ...  0 ]
                            //     ...
                            //     [ 1   1 ...  1 ]
A[I] = 0;                   //
                            //     [ 0   1 ...  9 ]
                            //     [ 10  0 ...  0 ]
                            //     ...
                            //     [ 0   0 ...  0 ]

Also you can use matrix indexing like

A[A>10] = 0;

You can see that a matrix comparison produces a char-type matrix with 1 marking the true condition and 0 the false condition. When such a matrix is used as index, it means to set or get those elements where their corresponding index element is 1.

A range expression may be used to indicate the begin, the end, and the step of a row or column:

A = int(2,10);
A.fill(0,1);                // A is
                            //     [  0  1  2  3  4  5  6  7  8  9 ]
                            //     [ 10 11 12 13 14 15 16 17 18 19 ]
A[*,5:*] = 0;               // A becomes
                            //     [  0  1  2  3  4  0  0  0  0  0 ]
                            //     [ 10 11 12 13 14  0  0  0  0  0 ]
A.fill(0,1);                // recover A 
A[1,0:6:2] = 0;             // A becomes
                            //     [  0  1  2  3  4  5  6  7  8   9 ]
                            //     [  0 11  0 13  0  15 0  17 18 19 ]

Again the operator * may be used in a range expression to indicate to-the-end; and when the step is not given, it is assumed as 1.

When both row and column indices are specified by arrays, The set and get operations are mean to act on block data:

B = A[1:3,0:2];                   // get a block of data from A starting
                                  // from row index 1 to 3 and column index 0 to 2
A[0:6:2,0] = 0;                   // set 0 to rows of 0,2,4,6 of the 2st column

Operators for Matrix Manipulation

The matrix reference shows that nearly all ZeScript operators have been implemented in the matrix library for element-by-element operations. Especially, the operators of <<, >>, ^, |, &, ~ for integer variable of ZeScript have been redefined for left column shifting, right column shifting, upward row shifting, vertical concatenation, horizontal concatenation, and transpose. For example,

A = int(3,3);
A.fill(1,1);
A.print();            /* show:  1  2  3
                                4  5  6
                                7  8  9 */
B = A << 1;           
B.print();            /* show   2  3  1
                                5  6  4
                                8  9  7 */

(A^-1).print();       /* show:  7  8  9
                                1  2  3
                                4  5  6 */

(A~).print();         /* show:  1  4  7
                                2  5  6
                                3  6  9 */
B = A | A*10;
C = B & B;
C.print();            /* show:  1 2 3 10 20 30
                                4 5 6 40 50 60
                                7 8 9 70 80 90
                                1 2 3 10 20 30
                                4 5 6 40 50 60
                                7 8 9 70 80 90 */

 

Applying Functions to Matrix

You can treat a matrix just like a regular numerical variable in script programs. This feature greatly simplifies the use of user object by script functions:
// convert longitude and latitude to point on sphere surface
function convert(r, lon, lat)
{

    lon *= 0.017453292519943;    // degree to radius, i.e., 2pi/360
    lat *= 0.017453292519943;
    z = r * sin(lat);
    d = r * cos(lat);
    x = d * cos(lon);
    y = d * sin(lon);
    return [x,y,z];
}

a = convert(100,180,30);
x = a[0];                  // x is a number
y = a[1];                  // so is y
z = a[2];                  // and z too

X = double(10);
X.fill(0,10);
Y = double(10);
Y.fill(0,5);
a = convert(100,X,Y);
x = a[0];                  // now x is a matrix
y = a[1];                  // so is y
z = a[2];                  // and z too

Set Operators

Using the set-add operator is more efficient then using the add operator because the later involves creation of a temporal matrix:

a = int(10,10);
a = a + 10;

// more efficient
b = int(10,10);
b += 10;

The same is true for -=, *=, /=, and etc.