The @matrix module contains a single class @matrix, for doing matrix math. A matrix is a 2D collection of numbers with a variety of practical applications: representing systems of linear equations, representing the parameters of a neural network, and projecting 3D coordinates onto 2D space for computer graphics. Note that in matrix notation, rows come before columns. So a RxC matrix has R rows and C columns, and the position (r,c) is row r and column c.

Using the @matrix class has several advantages over using arrays of numbers. First, common matrix operations are implemented. Second, the matrix class is more efficient for several reasons: The matrix class operates on 32-bit floating point numbers instead of 64-bit numbers/arbitrary variants. The matrix class is copy-on-modify, meaning that copying or slicing a matrix does not copy the underlying data until it is modified. Lastly, the matrix class will use the GPU to accelerate large matrix multiplications. This requires OpenGL with shaders, which is supported by default in Windows and MacOS, and is supported in Linux if X is installed.

Methods

:new() #
:new(array)
:new(string)
:new(rows,cols,fill)

Create a new matrix.

Parameters

Return

Notes

If array is a 1D array, a matrix of size Rx1 is created, where R is the length of the array. If array is a 2D array, a matrix of size RxC is created, where R is the length of the array, and C is the length of each sub-array. It is assumed that all sub-arrays have the same length as the first sub-array.

The maximum matrix size is is 32,767 by 32,767.

If no parameters are specified, an invalid matrix is created.

Example

#create the same matrix two different ways m = matrix('[ 1 2 3, 4 5 6, 7 8 9]') m = matrix([[1,2,3],[4,5,6],[7,8,9]]) :valid() #

Determine whether or not this matrix is valid.

Return

Notes

Within this module, an invalid matrix is a 0x0 matrix used to pass errors.

Example

#create an invalid matrix m = matrix() print 'valid =',m.valid() #false :rows() #

Get the number of rows in this matrix.

Return

:cols() #

Get the number of columns in this matrix.

Return

:size() #

Get the number of rows and columns in this matrix.

Return

:string(newlines) #

Convert this matrix into a string.

Parameters

Return

Notes

This is the same format taken by constructor.

:array() #

Convert this matrix into an array.

Return

Notes

If this is an RxC matrix, a 2D array is returned with length R, where each sub-array has length C. If this is an Rx1 matrix, a 1D array is returned with length R. This is the same format taken by constructor.

:slice(i,rows,j,cols) #

Slice this matrix.

Parameters

Return

Notes

Either rows or cols can be 0 to include all rows after i or j, respectively.

Slicing a matrix is efficient because the underlying data is not copied until it is modified.

Example

m = matrix('[ 1 2 3, 4 5 6, 7 8 9]') print m.slice(1,2,1,2).string() #[ 5 6, 8 9 ] :row(i) #

Get the ith row.

Parameters

Return

Notes

Uses :slice() under the hood.

Example

m = matrix('[ 1 2 3, 4 5 6, 7 8 9]') print m.row(1).string() #[ 4 5 6 ] :col(j) #

Get the jth column.

Parameters

Return

Notes

Uses :slice() under the hood.

Example

m = matrix('[ 1 2 3, 4 5 6, 7 8 9]') print m.col(1).string() #[ 2, 5, 8 ] :get(i,j) #

Get the value at row i column j.

Parameters

Return

:set(i,j,f) #

Set the value at row i column j to value f.

Parameters

:add(other) #

Add this matrix to other.

Parameters

Return

Notes

Matrices of different sizes are processed in the following way:

:sub(other) #

Subtract other from this matrix.

Parameters

Return

Notes

Matrices of different sizes are processed in the following way:

:dot(other) #

Dot product this with other.

Parameters

Return

Notes

Matrices of different sizes are processed in the following way:

To multiply by a scalar, use :mul().

:div(other) #

Divide the elements of this with the elements of other.

Parameters

Return

Notes

Matrices of different sizes are processed in the following way:

:fill(f) #

Fill this matrix with f.

Parameters

Return

:transpose() #

Transpose this matrix.

Return

:mul(other) #

Multiply this matrix with other.

Parameters

Return

Notes

It is an error to multiply two matrices where this.cols() != other.rows(). See Matrix multiplication on Wikipedia for details.

This will call :mul_cpu() or :mul_gpu() under the hood depending on the size of the matrices. In general, it is more efficient to multiply large matrices on the GPU and small matrices on the CPU.

The exception is that :mul_cpu() is always used on Raspberry Pi. On the Pi 0 and 3 systems, matrix multiplication is actually slower on the GPU than on the CPU for all sizes. However, :mul_gpu() remains enabled for benchmarking purposes.

:mul_cpu(other) #

Explicitly multiply this matrix with other on the CPU.

Parameters

Return

Notes

Can be used for benchmarks against :mul_gpu().

:mul_gpu(other) #

Explicitly multiply this matrix with other on the GPU.

Parameters

Return

Notes

Can be used for benchmarks against :mul_cpu().

Example

if matrix.gpu(true) p = matrix.random(1000,1000) q = matrix.random(1000,1000) t = os.ticks() s = p.mul_gpu(q) print 'mul_gpu():',(os.ticks()-t),'ms' t = os.ticks() r = p.mul_cpu(q) print 'mul_cpu():',(os.ticks()-t),'ms' print 'verification:',r.eq(s) else print 'GPU not supported!' ## on Intel Macbook: GL_VENDOR = Intel Inc. GL_RENDERER = Intel(R) UHD Graphics 617 GL_VERSION = 2.1 INTEL-12.10.22 GLSL_VERSION = 1.20 mul_gpu(): 437 ms mul_cpu(): 1308 ms verification: true on Raspberry Pi 3: GL_VENDOR = VMware, Inc. GL_RENDERER = llvmpipe (LLVM 9.0.1, 128 bits) GL_VERSION = 3.1 Mesa 19.3.2 GLSL_VERSION = 1.40 mul_gpu(): 33910 ms mul_cpu(): 9008 ms verification: true ## :eq(other,tolerance) #

description

Parameters

Return

Notes

Floating-point mathematics is not perfectly precise, and so you may need to adjust tolerance to suit your needs.

:solve(tolerance) #

Solve the system of linear equations represented by this matrix by using Gauss-Jordan elimination to turn this matrix into reduced row-echelon form.

Parameters

Return

Notes

Floating-point mathematics is not perfectly precise, and so you may need to adjust tolerance to suit your needs.

:inverse(tolerance) #

Find the inverse of this matrix, if any.

Parameters

Return

Notes

Floating-point mathematics is not perfectly precise, and so you may need to adjust tolerance to suit your needs.

:append(other) #

Append other to the bottom of this matrix.

Parameters

Return

:adjoin(other) #

Adjoin other to the right side of this matrix.

Parameters

Return

:standardize() #

Standardize the elements of this matrix such that for each column, the mean is 0 and the standard deviation is 1.

Return

:normalize() #

Normalize the elements of this matrix such that all values are within range [0,1). This is accomplished by finding the min/max values for each column, and scaling each value f to become (f-min)/(max-min).

Return

:log_scale() #

Logarithmically scale all elements of this matrix.

Return

Notes

This applies the following function to all elements of the array:

Compared to simply doing a logarithm of each element, this technique can handle values less than 1.

:apply(fn) #

Apply the function fn to all elements of this matrix.

Parameters

Return

Example

:abs(f) return f.abs() m = matrix('[ 1, -1, 2, -2]') print m.apply(abs).string() #[ 1, 1, 2, 2 ] :sum() #

Sum the values in each column.

Return

:stats(col) #

Get statistics for a certain column.

Parameters

Return

Notes

The returned object has the following fields:

Example

m = matrix('[ 1 2 3, 4 5 6, 7 8 9]') print m.stats(0) ## { "avg" : 4, "std" : 2.44949, "min" : 1, "max" : 7 } ## :bias() #

Adjoin a column of 1's to the left side of this matrix.

Return

Notes

This is useful for certain mathematical calculations.

:fit(labels,tolerance) #

Fit this matrix to labels, assuming this matrix a matrix of samples, one per row. Specifically, solve the linear equation Xb*W = Y, where Xb is this.bias(), Y is labels, and W is an unknown matrix of weights.

Parameters

Return

Notes

This performs linear regression by linear least squares using the ordinary least squares method. For underdetermined systems, the pseudo-inverse formula is used.

:run(weights) #

Run this matrix using the weights from :fit(), assuming this matrix a matrix of samples, one per row. Specifically, run the linear equation Xb*W = P, where Xb is this.bias(), W is weights, and P is a matrix of predictions.

Parameters

Return

Notes

If this matrix is the same matrix called with :fit(), then the matrix P is a prediction of the labels Y, and Y-P is the prediction error.

Example

#simple linear regression x = matrix([ 1, 2, 3, 4]) y = matrix([ 3.1, 4.9, 7.1, 8.9]) #y = 2x+1 with some deviation w = x.fit(y) print w.string() #[ 1.1, 1.96 ] (close to [1, 2]) print x.run(w).string() #[ 3.06, 5.02, 6.98, 8.94 ] (close to y) :save(path) #

Save this matrix to path in a binary format.

Parameters

Return

Notes

The binary format is:

The binary format is designed to be portable and easy to load in other languages, like C++. The reason the row/column sizes appear at the end of the file is to discourage using them for buffer allocation; doing so would be a security risk (buffer underflow/overflow) when parsing a malformed file. Instead, parsers should first read the elements and then verify that the element count maches the given row/column sizes.

Functions

:load(path) #

Load a matrix from a binary file generated by :save().

Parameters

Return

:gpu(show_info) #

Whether or not a GPU is available.

Parameters

Return

Notes

If show_info is true, information about the GPU is printed to stdout, which is useful for debugging GPU support. See example for details.

Example

print matrix.gpu(true) ## GL_VENDOR = Intel Inc. GL_RENDERER = Intel(R) UHD Graphics 617 GL_VERSION = 2.1 INTEL-12.10.22 GLSL_VERSION = 1.20 true ## :vstack(a) #

Vertically stack matrices.

Parameters

Return

Notes

Every matrix in a should have the same number of columns, otherwise it is an error.

:hstack(a) #

Horizontally stack matrices.

Parameters

Return

Notes

Every matrix in a should have the same number of rows, otherwise it is an error.

:identity(size) #

Create an identity matrix.

Parameters

Return

Notes

An identity matrix identity matrix is just a square matrix where the diagonal is 1's and the rest of the elements are 0's.

:random(rows,cols) #

Create a matrix of random values in range [0,1).

Parameters

Return