Matrix css

From emergent
Jump to: navigation, search

This page documents how to operate on Matrix objects using css expressions.

The basic syntax is very similar to numpy for python, which is reviewed here: (extensive) or (brief)

See also css list functions for more general information about accessing information in container objects such as lists, groups, and matrices.

cssMath shows the special math functions defined in css -- you can call any function there (and in inherited taMath_double) on a matrix, such as vec_mean(matrix) etc.

Comparison between css, NumPy, and MATLAB

THE MOST IMPORTANT DIFFERENCE: Matrix objects in emergent are always accessed natively in a "natural" inner-to-outer dimensional order, e.g., x,y,z for a 3D matrix. For 2D images, units within a layer, and many other cases, this is natural in that you index x,y. However, for mathematical matrices, the conventional index order is y,x or row, column (and if there are higher dimensions, typically y,x,z.. -- such that only the first two are reversed, while the remaining higher dimensions come later). Given the many uses of matrices in emergent, and that there needs to be a single default dimension order, we use the natural order, which is consistent with C++ code and all other Matrix access in the software. However, if you wish to convert code from Python or MATLAB, you can just use the rc(row, col) function to convert indexes. The actual Matrix data in css is stored in the same order of the indexes: horizontal (inner-most dimension) is contiguous in memory, followed by stacks of the next dimension, and so-on.

CSS and Python use 0-based indexing, MATLAB uses 1-based: the first element in a given dimension is indexed by 0, not 1, in css and Python -- this is the natural C/C++ indexing semantics, and is consistent with all other uses of Matrix objects in emergent. Also, you'll see that MATLAB typically uses () parentheses for many things, whereas Python and css tend to use [ ] square brackets for dealing with Matrix objects. In css, any expression surrounded by [ ] brackets creates a Matrix object, which then can serve as indexes to other Matrix objects (as in a[2,3]), or as arguments to functions that take Matrix objects, etc. Whereas Python has a "tuple" defined by a list of values in () parentheses, css only has the [ ] Matrix objects.

You must use a single [ ] expression to access the elements of a matrix, with each dimension separated by a comma, not multiple [ ][ ] expressions: e.g., to access a 2 dimensional matrix, you use: mat[x,y], not mat[x][y]. However, to access data in a DataTable, you do use two sequential [ ] expressions, the first [ ] expression access the specific column of data, and then once you're in a column, you have a second [ ] expression for accessing the data within the col -- that must be the same dimensionality as the col (e.g., 1 for rows of scalar data, 3 for a 2d matrix cell, with the 3rd dim being the rows, etc -- the last dimension is always the rows).

Results of operations are shown with -> notation. In css, all access of subsets of matrix elements (including fully specific access of a single element) returns a "view" onto the matrix data, and the resulting Matrix object return value points back to the original data that it copied from. Thus, modifying one of these view Matrix objects will modify the original data. Other basic arithmetic and function evaluation routines use this view to determine which elements to process. Once you do an assignment to another non-temporary Matrix object, the data is copied. This is similar to how it works in Python, and is semantically intuitive in practice -- it just works out right most of the time.

There are two kinds of views into a Matrix -- either an explicit matrix of coordinates (stored as an int_Matrix), or a boolean mask of 0's and 1's (stored as a byte_Matrix). A boolean logical operation on matrix elements (e.g., a>5) returns a mask, and if you do a[a>5] you are using the mask view to access the original elements of a. If you do a[3,5] or a[:,2] you are using the index view to access elements of a. The find or nonzero function converts a boolean mask to an explicit list of indices -- there are some operations such as iteration where it can be much faster to have the explicit list of indices, so it is a good idea to try inserting a find function call to see if it speeds up your code (only relevant if dealing with large matrixes).

Slices in MATLAB are start:step:end whereas css and Python are start:end:step -- end is exclusive (goes to end-1) as in a standard C for loop -- when using for specifying a general range expression (e.g., foreach(int i in [0:10:1])) it behaves just like the corresponding for loop: for(int i=0; i<10; i++) -- when using as indexes for a matrix, then you can use negative numbers to refer to the "from the end" of the size of the matrix, e.g., matrix[-10:0:1] gives you the last 10 items in the matrix (use 0 instead of -1 for the end because of this implicit end-1 logic). Outside of this context, negative numbers are just negative numbers, and can be used where sensible, e.g., [-10:0:1] or [0:-10:-1] etc to specify ranges of integers.

To run a css script with all of these test cases, see css_lib/tests/css_matrix_test.css in the emergent share directory. Better yet, pull up the program_examples.proj demo project and look at the Matrix_css example code under Data group.

core Matrix functionality in css vs. NumPy and MATLAB
MATLAB NumPy css Notes
Basic Matrix Properties and Access
a = [1,2,3; 4,5,6] a = array([[1.,2.,3.], [4.,5.,6.]]) a = [1.,2.,3.; 4.,5.,6.]; assign variable a to matrix with 3 columns, 2 rows -- a will be properly created as a new variable in css with appropriate type, as long as the rhs contains a [ ] matrix expression -- best way to do this generically is a = [0.]; to create a double precision matrix that can then be assigned to any other function return value. this value of a is assumed for examples below
ndims(a) ndim(a) or a.ndim ndims(a) or ndim(a) or a.dims() -> 2 number of dimensions of matrix (tensor rank)
size(a) shape[a] or a.shape shape(a) or a.Shape() -> [3,2] size / shape / geometry of the matrix -- how many elements along each dimension -- note difference in dimension order in css
 ?  ? a.SetShape([2,3]) or a.SetGeom(2,2,3) (re-)shape the geometry of the matrix to given geometry -- this preserves existing values to the extent possible
a(1,2) a[0,1] a[1,0] or a[rc(0,1)] -> 2 access element at row 1, column 2 -- note css native index order is horizontal, vertical or x,y or column, row, not row, column. use rc to reverse
a(1,:) a[0,:] a[:,0] -> [1,2,3] extract first row of the matrix. note css index order: column, row.
a(1,:)(1) a[0,:][1] a[:,0][1] -> 2 multiple [ ] in a row cause an implicit flatten of the first access -- flattening (see below) always collapses to 1d, so second is always one-dimensional unless you re-shape the resulting matrix
a(:, 1:3:1) a[:, 0:2:1] a[0:2:1, :] -> [1,2] [4,5] first two columns, all rows
a(:) = 3 a[:] = 3 a = 3 set all elements in matrix to given scalar value
 ?  ? a = [10,20,30]; copy matrix to a -- assignment to a matrix resets the geometry to that of the source (right-hand-side of =) and copies all the values over (copy not reference)
[a a; a a] vstack([hstack([a, a]), hstack([a,a])]) [a, a; a, a] construct matrices from other matrices -- , = horizontal stacking (rows) and ; = vertical stacking (columns)
 ?  ? i = m["str*"] using a string for the index into a matrix returns one or more indexes where that string is found -- simple * and ? wildcards can be used to match string values -- numerical matrices convert the value to a string representation for matching, so your an do things like ["1*"] to match all the numbers that start with 1. The resulting index(es) can be used as an index into the same or corresponding matrices (e.g., for DataTable css columns) to find actual values.
Math Operators and Functions
a + a a + a a + a -> [[2,4,6]; [8,10,12]] element-wise addition -- same holds for - / * operators, all of which operate element-wise.
a * b dot(a,b) mat_mult(a,b) or dot(a,b) matrix multiplication -- not element-wise
 ?  ? vec_sum(a) compute sum over elements -- many vec_ and mat_ functions are avail as documented in taMath_double
transpose(a) a.transpose() or a.T transpose(a) or a.Transpose() transpose the row and column values of the matrix
det(a) linalg.det(a) det(a) determinant of square matrix (note: would not work on non-square a matrix from this example)
max(max(a)) a.max() max(a) or a.Max() maximum element of a -- same applies for min
max(a,b) maximum(a,b) max(a,b) element-wise max of two matrices or b can also be a scalar -- same applies for min
cos(a) cos(a) cos(a) apply function to each element of matrix, returning new matrix of results -- applies to all manner of mathematical functions taking a floating point number arg, e.g., sin, exp, log, etc.
a.^3 a**3 or power(a,3) a ^ 3 or pow(a,3) raise each element in matrix to given exponential power -- can even do element wise on two matrices, a ^ b where b is the exponents to apply to values in a
Selecting Elements using Condtional and Logical Expressions
(a>3) (a>3) (a>3) -> [[0,0,0][1,1,1]] evaluate > operator on each element, returning matrix of bool values as a byte_Matrix, which can then be used as a mask -- same applies for <, >=, <=, ==, !=
a & b logcial_and(a,b) a && b element-wise logical AND -- same applies for NOT ! and OR
a(a>3) a[a>3] a[a>3] -> [[-, -, -][4,5,6]] use a>3 expression as a mask to view into values of a matrix -- zero or false values are filtered out -- same applies for <, >=, <=, ==, !=
find(a>3) nonzero(a>3) find(a>3) or nonzero(a>3) or (a>3).Find() transform the bool mask into a list of indexes of the nonzero (true) elements in the mask -- can be used as more efficient view into matrix, as in a[find(a>3)]
b=(a>3) b=a[a>3].flatten(1) b=flatten(a[a>3]) or a[a>3].Flatten() copy the values from a matrix (typically with a view or selection of sub-elements) into a new 1D matrix that is just a list of the values -- this can then be re-shaped into a different geometry if it makes sense to do so, by calling something like b.SetShape([2,2]) -- reshaping an existing matrix preserves values. this is how you extract submatricies
Constructing Standard Matricies
zeros(3,4) -> [[0,0,0,0][0,0,0,0][0,0,0,0]] zeros((3,4)) zeros([4,3]) or zeros(rc(3,4)) create a 3-row, 4-column matrix of zeros
eye(3) eye(3) eye(3) create a 3x3 identity matrix (all 0 with 1's along the diagonal)
diag(a) diag(a) diag(a,1) vector of diagonal elements of a -- any non-zero value for 2nd argument will work, to distinguish from diag(a,0)
diag(a,0) diag(a,0) diag(a,0) square diagonal matrix with diagonal elements being the elements of a, in order
rand(3,4) random.rand(3,4) rand([4,3]) 3 rows, 4 columns matrix with 0-1 uniform random numbers
linspace(1,3,3) linspace(1,3,3) linspace(1,3,3) -> [1, 2, 3] n (last arg) linearly spaced values between start and end (first 2 args)
0:2:20 range(start, stop, step) [0:20:2].Expand() or foreach(int i in [0:20:2])... expand the slice range notation into the literal vector of indexes implied by the start:end:step slice range -- foreach implicitly does this when encountering slice ranges -- the defaults for start, end and step are 0:100:1 when not otherwise specified (e.g., :: = 0:100:1)
meshgrid(1:4, 1:3) mgrid(0:3., 0:2) meshgrid([0:3, 0:2]) grid-like mesh of coordinates from n slice args -- result has one extra dim that is the coordinates (x, y, etc) outer dimension
Complex Numbers and FFT
complex(a,b) complex(a,b) complex(a,b) create a complex matrix where the real components come from real matrix a an the imaginary ones come from real matrix b (both of which must be the same size) -- internally complex numbers are stored as inner dimension of size 2 with real, imag numbers in sequence -- see complex_Matrix NOTE: css does not understand sqrt(-1) or i or j as ways of creating imaginary numbers
z = exp(ia) z = exp(ia) z = expi(a) create a complex matrix from angles given in real matrix a, using Euler's formula exp(ia) = cos(a) + i sin(a) -- the complex elements are the cartesian coordinates for the given angles.
a = angle(z)  ? a = angle(z) or z.Angle() extract the angles from complex matrix z, where each element is atan2(imag, real) -- inverse of expi
a = abs(z) a = abs(z) a = abs(z) or z.Abs() returns the magnitudes of the complex elements of matrix z, where each element is sqrt(real^2 + imag^2) (the hypotenuse formed by the two legs of the triangle)
a = real(z) a = real(z) a = real(z) or z.Real() returns the real elements from complex matrix z
a = imag(z) a = imag(z) a = imag(z) or z.Imag() returns the imaginary elements from complex matrix z
c = conj(z) c = conj(z) c = conj(z) or z.Conj() returns the complex conjugate of elements in complex matrix z (real - image)
 ?  ? z.SetReal(a) set the real components of matrix z from real matrix a
 ?  ? z.SetImag(a) set the imaginary components of matrix z from real matrix a
z = fft(a) z = fft(a) z = fft(a) or fft2(a) returns a fast fourier transform of input matrix a (which can be either real or complex) -- operation is much faster if size of a is factorable by small number factors, and terrible if it is a prime number. fft2 form does a 2D fft (fft on the rows, then an fft on the output of that over the columns)
a = ffti(z) a = ffti(z) a = ffti(z) or ffti2(z) returns an inverse fast fourier transform of complex input matrix z. fft2 form does an inverse 2D fft (ffti on the columns, then an ffti on the output of that over the rows)
 ?  ? b = z * a or b = z / a multiply or divide a complex matrix by a real matrix -- does an element-wise multiply or divide of both the real and imaginary components of the complex matrix by the corresponding value in the real matrix -- this only works if you put the complex matrix first, then the real matrix (which is sensible for division but may not be as obvious for multiplication).

todo: eigen values, repmat/tile, inv, pinv, etc.

Copying and Replicating Sub-Matricies

The above syntax allows for some very powerful ways of copying sub-matricies, which can also be used in C++ as well -- there are some fairly awkward methods in the taMatrix and DataTable for doing this as well, but the new "slicing" way of specifying subsets of matrix elements is extremely powerful.

For example, imagine you have an input data DataTable that has an "Input" column that has a cell type with a 4D geometry (for unit groups in a layer), with the unit group (inner 2 dimensions) being 2x3, and the outer 2 dimensions (arrangement of unit groups in the layer) are 4x5. You can set the values of one unit group in one row with the following css commands (at the console -- similar code can be used in Programs):

emergent> print .StdInputData["Input"][:,:,0,0,0]
[ [0,0,0,0,0]: 0, [1,0,0,0,0]: 0, [0,1,0,0,0]: 0, [1,1,0,0,0]: 0, [0,2,0,0,0]: 0, [1,2,0,0,0]: 0 ]
emergent> .StdInputData["Input"][:,:,0,0,0] = [.1, .2, .3];
emergent> print .StdInputData["Input"][:,:,0,0,0]
[ [0,0,0,0,0]: 0.1, [1,0,0,0,0]: 0.2, [0,1,0,0,0]: 0.3, [1,1,0,0,0]: 0.1, [0,2,0,0,0]: 0.2, [1,2,0,0,0]: 0.3 ]

So we're selecting the Input column from the data table, and the :,: specifies all of the first two dimensions (2x3) and only the first (0) of the remainder (last one is the row number).

Note that we only specified 3 specific values to assign to in the right-hand-side expression -- the copy process wraps-around, so that it repeats the sequence twice. Taking further advantage of this wrap-around process, we can now replicate this one unit-group pattern to all other unit groups:

emergent> .StdInputData["Input"][:,:,:,:,0] = .StdInputData["Input"][:,:,0,0,0];

All the other unit groups will now copy the values from that first unit group. As you can see, the possibilities are endless -- you can replicate one unit group across rows, etc.

Furthermore, you can use the conditional filtering functionality to selectively assign values based on their current value:

emergent> .StdInputData["Input"].ar[(.StdInputData["Input"][:,:,:,:,0]>.2)] = [.7, .8, .9];

this assigns all values from the first row that are greater than .2 to the values of .7, .8, .9 as a repeating sequence..