t-matrix

A small type-array based matrix lib. A core matrix type plus just the other functions you need.

Usage no npm install needed!

<script type="module">
  import tMatrix from 'https://cdn.skypack.dev/t-matrix';
</script>

README

npm version Build Status Coverage Status

t-matrix

Installation

npm install t-matrix -S

Throughout the examples given I will use ES6 module syntax. If you prefer CommonJS then just use const Matrix = require('t-matrix'); in place of the import statement. If you do want to use ES6 modules then you may find it useful to try reify.

Example usage

import * as Matrix from 't-matrix';

//create a 5x5 magic square matrix
const M=Matrix.magic(5);

//and a target vector (column matrix)
const v=Matrix.from([65,65,65,65,65])
//the expected sum of any row of a magic square is (n^3 + n)/2, which for n=5 is 65.

//then solve v = M * a by rearranging to M \ v = a
const a=Matrix.ldiv(M,v);

//the answer should be [1,1,1,1,1] (plus some roundoff);
console.log([...a]);

Aims

  • a small library with no dependencies
  • Be a familiar as possible to those used to other tools, in particular Matlab/Octave
  • based on linear double (float64) arrays for precision and speed
  • use row and column offset arrays so common operations, such as transpose or row/column swaps, are 'free' (no copying of data required)
  • an ES6 module - only use the methods you need. If building with a tree-shaking bundler (e.g. rollup) then you only include what you use.
  • provide what you need for most linear algebra work
  • matrix construction (zeros, ones, eye, rand, diagonal, from arrays)
  • operations (add, multiple, map elements)
  • submatrices and transpose without copying data
  • a good balance between speed and ease of use (if you need super-fast operations on large matrices you may want to look elsewhere).
  • LU decomposition-based operations - inv, div, ldiv, det, solve
  • flexible, expressive, extendable.

Status

V1.1 complete with binary matrices as the main new feature. Started work on 1.2 which is mostly numerical methods such as interpolation, integration.

Release Notes

  • v1.1.2
    • cumsum added.
  • v1.1.1
    • gridInterp1 added - just linear interpolation so far
  • v1.1.0
    • kron and shift added
    • Test and docs for kron and shift
    • roadmap for v1.1. complete
  • v1.0.7
    • Binary matrices added
    • Binary matrix addressing
    • Testing and docs for binary matrices
  • v1.0.6
    • Added testing and coverage badges
    • Added a dot product operation and testing
  • v1.0.5
    • More doc typo/inconsistency corrections.
    • Added a cross product operation and testing.
    • Updated devDependencies to remove vulnerabilities.
  • v1.0.1 - v1.0.4
    • Entirely documentation corrections and improvements.
  • v1.0.0
    • Refactored to the new API, fully testing implemented and passing, documentation now derived from jsdoc.

Roadmap

The current plan for future versions. Obviously the version numbers further out are more speculative.

  • v1.2
    • conv, grad, trapz, cumsum, interp1, interp2
  • v1.3
    • LU and QR decomposition
  • after v1.3
    • eigen, SVD
    • fft and supporting methods
    • sort, unique

Guide

Creating matrices

There is no way to create a Matrix class instance directly (using a new operator), instead there are a few standard functions which can be used to create matrices.

zeros, ones and rand create matrices of arbitrary dimension initialised in different ways and all have the same general form:

import {zeros,ones,rand} from 't-matrix';
const m1=zeros(3); //a 3x3 square matrix filled with zeros
const m2=ones(4,5); //a matrix with 4 rows and 5 columns filled with ones
const m3=rand([6,5]); //a matrix with 6 rows and 5 columns filled with random values in the range [0,1)
const m4=zeros(m3.size); //a matrix the same size as m3 filled with zeros.

eye and magic both take just one number which is the matrix size as both produce only square matrices:

import {eye,magic} from 't-matrix';
const m5=eye(3); //a 3x3 identity matrix
console.log(JSON.stringify(m5));
//'[[1,0,0],[0,1,0],[0,0,1]]'
const m6=magic(4); //a 4x4 magic square
console.log(JSON.stringify(m6));
//'[[16,2,3,13],[5,11,10,8],[9,7,6,12],[4,14,15,1]]'

from is the more general purpose function and will try and convert arrays into a matrix:

import * as Matrix from 't-matrix';
const m7=Matrix.from([1,2,3]); //An array of numbers becomes a column matrix
const m8=Matrix.from([[1,2,3]]); //An array of arrays assumes row-major order, so this becomes a row matrix
const m9=Matrix.from([[1,2],[3,4]]); //and this is a 2x2 matrix.

diag can be used to create a diagonal matrix if provided with a single parameter which is an array or a column or row matrix:

import {diag} from 't-matrix';
const m10=Matrix.diag([1,2,3,4]);//a 4x4 matrix with 1,2,3,4 on the diagonal.

In addition diag can als be used to get or set the diagonal elements of a matrix. See the API help for more details.

grid creates a pair of matrices filled with row and column indices:

import {grid} from 't-matrix';
const [m11,m12]=Matrix.grid(2,3);
//m11 = [[0,0,0],[1,1,1]], m12 = [[0,1,2],[0,1,2]]

grid the grid parameters can also be Ranges and in general works in a similar way to the 2D case of ndgrid in matlab or octave.

Matrix methods and properties

Core methods

The Matrix class has a few core methods and properties to provide information regarding a matrix and examine and manipulate its values:

You can use the .get and .set methods to retrieve and assign single values, for example m.get(0,1) would get the value in row 0, column 1, and m.set(0,1,5) would set that same value to the number 5. However .get and .set become much more useful when used with a Range to set the row and column indices.

The way to define a range should be (at least somewhat) familiar to those used to Matlab/Octave. For example m.set(0,':',1) will set all the values in row 0 to 1, or m.get([1,2],[':',4]) will return a matrix which contains all columns up to (and including) column 4 of rows 1 and 2 (a 2x5 matrix).

An important point to note is that .get, when it returns a matrix, returns one which maps onto the same underlying data array as the original matrix - any changes to either matrix will be reflected in the other. There are many more examples in the documentation for the Range data type and the get and set methods, however a couple of basic examples:

import * as Matrix from 't-matrix';
const m=Matrix.zeros(4); //a 4x4 matrix filled with zeros
m.set([1,2],':',5)  //fill the middle two rows with fives.
 .set([0,1],[1,2],eye(2)); //set the top three elements of column 2 to ones.
console.log(JSON.stringify(m.get(':',[1,2])));
//'[[1,0],[0,1],[5,5],[0,0]]'

m.set([1,2],':',m.get([2,1],':'));//swap rows 1 and 2
console.log(JSON.stringify(m));
//'[[0,1,0,0],[5,5,5,5],[5,0,1,5],[0,0,0,0]]'

Note on that last example that the library is aware when setting a range to a get derived from the same data, so it is safe to use this to (for example) swap or rotate data within a matrix.

To get or set the diagonal of a matrix see diag.

Iterables

A matrix is itself an iterable, iterating in row-major order over all values:

import * as Matrix from 't-matrix';
let t=0;
for(let v of Matrix.magic(3)) t+=v;
console.log('Total of a 3x3 magic square = '+t);
//Total of a 3x3 magic square = 45

There are also helper functions, Matrix.rows and Matrix.cols, to iterate over rows and columns:

import * as Matrix from 't-matrix';
const tots=[];
for(let r of Matrix.rows(Matrix.magic(3))) tots.push(Matrix.sum(r));
console.log('Row sums = '+tots);
//Row sums = 15,15,15

These functions can be mixed-in to become methods

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix.cols);
const tots=[];
for(let r of Matrix.magic(3).cols()) tots.push(Matrix.sum(r));
console.log('Column sums = '+tots);
//Column sums = 15,15,15

Operations on matrices

So far there are only a small set of the basic matrix arithmetic operations. More will follow in time.

  • Matrix.sum, Matrix.max, Matrix.min, Matrix.product are all element-wise operations. They can operate:
    • Over an entire matrix, returning a single value.
    • Along the rows of a matrix, returning a column matrix with a result per row.
    • Down the columns of a matrix, returning a row matrix with a result per column.
    • Over a set of matrices and/or scalars (see the API reference for more details).
  • matrix.t is the matrix transpose.
  • Matrix.mult, Matrix.div, Matrix.ldiv and Matrix.inv.
    • inv is a unary operation.
    • div and ldiv are binary operations.
      • div(a,b) is equivalent to a/b.
      • ldiv(a,b) is equivalent to a\b which is equivalent to (b'/a')' where ' is the transpose.
    • mult can take an arbitrary number of matrices and scalars and will multiply them together. There must be at least one matrix, and the matrix dimensions must agree with standard matrix multiplication rules.
  • Matrix.det returns the determinant of a matrix.
  • Matrix.trace returns the trace (the sum of the diagonal) of a matrix.
  • Matrix.kron returns the Kronecker product of two matrices.
  • Matrix.mapMany is an extension of the matrix.map which takes an arbitrary list of matrices and a mapping lambda function to create a new matrix. This can be a much more concise method to use if you end up with many nested or chained arithmetic operations.

Binary matrices

There is just one function required for binary matrices, Matrix.bin, which acts as a creation function, a conversion function or to map the contents of several matrices to a binary matrix. the standard matrix.get and matrix.set both can be used with binary matrices for indexing into and modifying matrix contents.

Matrix manipulation

Again just the most essential matrix manipulation methods have been implemented so far. More will follow, however the flexibility of the matrix get and set methods should allow most others to be composed.

  • Matrix.hcat, Matrix.vcat and Matrix.mcat concatenate matrices in various ways
    • hcat and vcat are the expected horizontal and vertical concatentation methods
    • mcat is more flexible and allows composition through both horizontal and vertical concatenation. See the function reference for more details.
    • For all of these functions the matrix dimensions must appropriately agree.
  • Matrix.reshape can change the dimensions of a matrix. Row-major element order is assumed.
  • Matrix.repmat can repeat a matrix horizontally and vertically.
  • Matrix.minor returns a matrix with a specified row and column removed.
  • Matrix.swapRows and Matrix.swapCols does pretty much what you might expect.

Helpers and Mixins

There are two helper generator functions, Matrix.rows and Matrix.cols which are described with the matrix iterables. There is also an isMatrix function to test if an object is a valid matrix. The potentially most useful helper function is, however, Matrix.mixin.

The t-matrix library has intentionally been designed to be tree-shaking friendly. You can get the banana without the gorilla holding it. However this means that the matrix object is quite light on methods and instead most of the functionality is wrapped up in many functions, which can make some expressions quite cumbersome. e.g. you want to calculate (xA + yB)/C and you have to express that as Matrix.div(Matrix.sum(Matrix.mult(A,x),Matrix.mult(B,y)),C). Ugh. Wouldn't it be nicer if you could do A.mult(x).sum(B.mult(y)).div(C) instead? This is where Matrix.mixin comes in.

Matrix.mixin(fn) adds functions to the Matrix prototype so that fn(matrix, ...params) becomes matrix.fn(...params). mixin can take any number of functions as arguments. Each function is added as a method using the name the function was originally declared with (using fn.name). Alternatively a string can be included before a function in the list of parameters and that string will be used as the name. As well as adding in-built methods this also allows the addition of custom methods.

For example, to add in the arithmetic operations above, have a file which configures t-matrix how you want it:

import {mixin, sum, mult, div, ldiv} from 't-matrix';
mixin(sum,mult,div,ldiv);

then from elsewhere in your code:

import * as Matrix from 't-matrix';
//Solve A*x = B
const A = Matrix.magic(3);
const B = Matrix.from([15,15,15]);
const x = A.ldiv(B);
console.log(x.toJSON);
//[ [ 1 ], [ 1 ], [ 1 ] ]

Alternatively, to add a custom method:

import * as Matrix from 't-matrix';
Matrix.mixin('sqrt',m=>m.map(Math.sqrt));
console.log(Matrix.from([[1,4,9]]).sqrt().toJSON());
//[ [ 1, 2, 3 ] ]

However, if all of this is too much pain, and you really don't care about tree-shaking, or are only every going to run your code in nodejs, then:

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix);

will just add every standard function which can be a method as a method.

A final word of caution however. If what you are building 'owns' the global namespace, the mixin as much as you like. However if what is being built is a library, then it is recommended not to use the mixin function as it will modify the returned Matrix class for all libraries which use it.

Tutorial - making magic squares

Introduction

This will follow some of the code already in the library, however it is a useful example in how to express a matrix algorithm using this library. The algorithms used are the same as those used in Matlab as described here.

Odd-order magic squares

A vectorised version of de La Loubere's method is expressed in Matlab/Octave code as:

[I,J] = ndgrid(1:n);
A = mod(I+J+(n-3)/2,n);
B = mod(I+2*J-2,n);
M = n*A + B + 1;

We will need three methods here, grid, sum and product (NOTE: mult could be used instead as we are only multiplying by a scalar).

import {grid, sum, product} from 't-matrix';
export function magic(n){
  //code to go here...
}

Calculating I and J looks very similar to the matlab:

const [I,J] = grid([1,':',n]);

To get A we are summing a number of terms:

const A = sum(I, J, (n-3)/2);

however that is incomplete as we need to mod all the values of A. This can be done using a .map:

const A = sum(I, J, (n-3)/2).map(v => v%n);

We can similarly calculate B:

const B = sum(I, product(2,J), -2).map(v => v%n);

And finally return the magic square

return sum(product(n,A), B, 1);

So, now we have some code which deals with odd-order magic squares:

import {grid, sum, product} from 't-matrix';
export function magic(n){
  if (n%2){
    const [I,J] = grid([1,':',n]);
    const A = sum(I, J, (n-3)/2).map(v => v%n);
    const B = sum(I, product(2,J), -2).map(v => v%n);
    return sum(product(n,A), B, 1);
  }
}

Doubly-even-order magic squares

The matlab code for the doubly-even algorithm is:

[I,J] = ndgrid(1:n);
M = reshape(1:n^2,n,n)';
K = floor(mod(I,4)/2) == floor(mod(J,4)/2);
M(K) = n^2+1 - M(K);

The I and J matrices are the same. The calculation of the initial matrix M is slightly different as t-matrix uses row-major order whereas Matlab assumes column-major order. All this means, however, is that we don't need the transpose:

const [I,J] = grid([1,':',n]);
const M = reshape(from([1,':',n*n]),n,n);

The final step requires the use of binary (logical in Matlab/Octave terminology) matrices and binary addressing. The binary matrix function Matrix.bin is used to create K and a mapping function is used in set to then modify the matrix values.

import {bin} from 't-matrix'
const K = bin(I, J, (i,j) => Math.floor((i%4)/2) === Math.floor((j%4)/2));
M.set(K, m => n*n+1-m);
return M;

The expanded code now looks like this:

import {grid, sum, product, from, bin} from 't-matrix';
export function magic(n){
  if (n%2){
    const [I,J] = grid([1,':',n]);
    const A = sum(I, J, (n-3)/2).map(v => v%n);
    const B = sum(I, product(2,J), -2).map(v => v%n);
    return sum(product(n,A), B, 1);
  } else if (n%4===0) {
    const [I,J] = grid([1,':',n]);
    const M = reshape(from([1,':',n*n]),n,n);
    const K = bin(I, J, (i,j) => Math.floor((i%4)/2) === Math.floor((j%4)/2));
    M.set(K, m => n*n+1-m);
    return M;
  }
}

Singly-even-order magic squares

This is the trickiest case of the three. In essence the method is to repeat odd-order magic squares in the four quarters of the matrix with the addition of [0,2;3,1]*n²/4. This gets the rows adding up correctly. To get the columns working some swapping of values is required.

  p = n/2;   %p is odd.
  M = oddOrderMagicSquare(p);
  M = [M M+2*p^2; M+3*p^2 M+p^2];
  i = (1:p)';
  k = (n-2)/4;
  j = [1:k (n-k+2):n];
  M([i; i+p],j) = M([i+p; i],j);
  i = k+1;
  j = [1 i];
  M([i; i+p],j) = M([i+p; i],j);

Forming the M matrix on the third line needs the introduction of another new t-matrix method, Matrix.mcat. This could be done using separate horizontal and vertical concatenation (hcat and vcat), however mcat makes this job a lot simpler:

const p = n>>1;
let M = magic(p);
M = mcat([[      M     , sum(M,2*p*p)],
          [sum(M,3*p*p),  sum(M,p*p) ]]);

We need to be careful in the matrix addressing with the next two steps as matlab uses a 1-based index, whereas t-matrix (like JavaScript) is 0-based. Note also that the [i; i+p] is just addressing the whole range. Similarly [i+p; i] is the whole range with a half-way circular shift. Here the flexibility of Range indexing really helps:

let k = (n-2)>>2, j = [':',k-1,n+1-k,':'];
M.set(':', j, M.get([p,':',':',p-1], j));
j = [0,k];
M.set([k,k+p], j, M.get([k+p,k], j));
return M;

The range expressions benefit from a little explanation. [p,':'] provides a range from p to the end of the dimension concerned (so if there were 10 rows, [5,':'] would expand to [5,6,7,8,9]). Similarly [':',p-1] will range from 0 up to and including p-1. When taken back-to-back the two ranges will therefore extend to the end of the dimension and then again back from the start, so [p,':',':',p-1] for p=5 and a size of 10 would expand to [5,6,7,8,9,0,1,2,3,4] swapping the left and right halves of a full range.

So, the final code for generating magic squares:

import {grid, sum, product, from, bin, mcat, reshape} from 't-matrix';
//Valid for any integer n of 3 or greater
export function magic(n){
  if (n%2){
    const [I,J] = grid([1,':',n]);
    const A = sum(I, J, (n-3)/2).map(v => v%n);
    const B = sum(I, product(2,J), -2).map(v => v%n);
    return sum(product(n,A), B, 1);
  }
  if (n%4===0) {
    const [I,J] = grid([1,':',n]);
    const M = reshape(from([1,':',n*n]),n,n);
    const K = bin(I, J, (i,j) => Math.floor((i%4)/2) === Math.floor((j%4)/2));
    M.set(K, m => n*n+1-m);
    return M;
  }
  const p = n>>1;
  let M = magic(p);
  M = mcat([[      M     , sum(M,2*p*p)],
            [sum(M,3*p*p),  sum(M,p*p) ]]);
  let k = (n-2)>>2, j = [':',k-1,n+1-k,':',n-1];
  M.set(':', j, M.get([p,':',':',p-1], j));
  j = [0,k];
  M.set([k,k+p], j, M.get([k+p,k], j));
  return M;
}

API

Matrix Creation

Matrix.from(data)Matrix

Create a matrix from the supplied data.

Matrix.zeros(rows, [cols])Matrix

creates a new matrix filled with zeros

Matrix.ones(rows, [cols])Matrix

creates a new matrix filled with ones

Matrix.eye(n)Matrix

creates a new identity matrix of size n

Matrix.rand(rows, [cols])Matrix

creates a new matrix filled with random values between 0 inclusive and 1 exclusive

Matrix.magic(size)Matrix

Creates a magic square of the specified size

Matrix.diag(matrix, [set])Matrix

gets, sets or creates diagonal matrices

Matrix Manipulation

Matrix.mcat(array)Matrix

Concatenates a nested array of matrices - horizontally and vertically as required.

Matrix.reshape(matrix, rows, cols)Matrix

Reshape the matrix to the dimensions specified treating the matrix data in row-major order

Matrix.swapRows(matrix, rowsA, rowsB)Matrix

Swap the rows of a matrix.

Matrix.swapCols(matrix, colsA, colsB)Matrix

Swap the columns of a matrix.

Matrix.minor(matrix, row, col)Matrix

Return a matrix with the given row and column removed.

Matrix.repmat(matrix, vRepeat, hRepeat)Matrix

Repeat the supplied matrix the specified number of times horizontally and vertically.

Matrix.vcat(matrices)Matrix

Vertically concatenate matrices together

Matrix.hcat(matrices)Matrix

Horizontally concatenate matrices together

Matrix.shift(matrix, [rowShift], [colShift])Matrix

Circularly shift the matrix by the specified steps. The shifts are MOD the respective matrix dimension.

Matrix.diag(matrix, [set])Matrix

gets, sets or creates diagonal matrices

Matrix Operations

Matrix.sum(...matrices)Matrix | Number

Return the sum of the matrix in the direction specified or the element-wise sum of the set of matrices.

Matrix.max(...matrices)Matrix | Number

Return the maximum of the matrix in the direction specified or the element-wise maximum of the set of matrices.

Matrix.min(...matrices)Matrix | Number

Return the minimum of the matrix in the direction specified or the element-wise minimum of the set of matrices.

Matrix.product(...matrices)Matrix | Number

Return the product of the matrix values in the direction specified or the element-wise product of the set of matrices.

Matrix.trace(matrix)Number

Returns the trace of a matrix (the sum of the diagonal elements)

Matrix.mapMany(...matrices, fn)Matrix

Creates a new matrix with the results of calling a provided function on every element in the supplied set of matrices.

Matrix.bin(...matrices, [fn])Matrix

Creates a new binary matrix with the results of calling a provided function on every element in the supplied set of one or more matrices.

Matrix.mult(...matrices)Matrix

Performs matrix multiplication on a list of matrices and/or scalars

Matrix.det(matrix)number

Calculate the determinant of a matrix.

Matrix.ldiv(A, B)Matrix

Left-division. Solve Ax = B for x.

Matrix.div(A, B)Matrix

Right-division. Solve xB = A for x.

Matrix.inv(matrix)Matrix

Calculate the inverse of a matrix.

Matrix.abs(matrix)Matrix

Return a new matrix containing the element-wise absolute values of the source matrix.

Matrix.grid(rows, [cols])Array.<Matrix>

Generate a regular grid in 2D space

Matrix.cross(A, B, [dim])Matrix

Calculate the cross product(s) of two vectors or sets of vectors.

Matrix.dot(A, B, [dim])Matrix

Calculate the scalar dot product(s) of two vectors or sets of vectors.

Matrix.kron(A, B)Matrix

Calculate the Kronecker tensor product of two matrices

Matrix Calculations

Matrix.gridInterp1(v, q)Matrix

1D interpolation of uniformly spaced data.

Matrix.cumsum(m, dim)Matrix

Calculate the cumulative sum along the rows or down the columns of a matrix.

Other Matrix Functions

Matrix.rows(matrix)IterableIterator.<Array.Number>

Iterate over the rows.

Matrix.cols(matrix)IterableIterator.<Array.Number>

Iterate over the columns.

Matrix.isMatrix(val)boolean

Tests if a value is an instance of a Matrix

Matrix.isBinary(val)boolean

Tests if a value is an instance of a binary Matrix

Matrix.mixin(...methods)

Add static functions of the form fn(matrix,...args) to the Matrix prototype as matrix.fn(args)

Classes

Matrix

The core matrix class

Typedefs

Range : Array.<(Number|String)> | Number | String

A Specification of indices of the row or column of a matrix, or a range of array values.

Matrix

The core matrix class


new Matrix()

This class is not intended to be directly created by a user of this library, rather it is returned by the various creation functions (such as zeros, eye or from) and as a returned result from various operation and manipulation methods and functions.


matrix.toJSON ⇒ Array.Array.Number

Convert the matrix to an array of number arrays.

Example

const m=Matrix.from([0,':',5]); //will create a column vector
console.log(m.toJSON()); //[0,1,2,3,4,5]
console.log(m.t.toJSON()); //[[0,1,2,3,4,5]]
console.log(Matrix.reshape(m,2,3).toJSON()); //[[0,1,2],[3,4,5]]
//enables a matrix instance to be serialised by JSON.stringify
console.log(JSON.stringify(m)); //"[0,1,2,3,4,5]"

matrix.size ⇒ Array.<Number>

The matrix height and width in an array.

Example

const m=Matrix.from([1,2,3]);
console.log(m.size);
//[3,1]

matrix.t ⇒ Matrix

The transpose of the matrix

Example

const m=Matrix.from([[1,2],[3,4]]);
console.log(m.t.toJSON()); // [[1,3],[2,4]]

matrix[Symbol.iterator]() ⇒ IterableIterator.<Number>

Iterates through the matrix data in row-major order

Example (Iterating the matrix values in a for..of loop)

//Calculate the L²-norm of a matrix
function norm(matrix){
  let tot=0;
  for(let v of matrix) tot+=v*v;
  return Math.sqrt(tot);
}

Example (Using the ES6 spread operator with a matrix)

const m=Matrix.from([[1,2,3],[4,5,6]]);
console.log([...m]); //=> [1,2,3,4,5,6];

matrix.get(rows, [cols]) ⇒ Matrix | Number

Return a value or subset of a matrix. The matrix subset is a view into the current matrix. This means that any change to the returned matrix subset will also change the original matrix. If a copy of the matrix data is desired then clone should be used.

Param Type Description
rows Matrix | Range | Number Zero-based row or linear index or indices or a binary matrix
[cols] Matrix | Range | Number Zero-based column index or indices or a binary matrix

Example

const m=Matrix.from([[1,2],[3,4]]);
// Specify single indices to return a value
m.get(0,0) //1

// The same indices in an array will return a matrix
m.get([0],[0]) //Matrix [1]

// A general [Range](#Range) can be specified.
m.get(':',0) // Matrix [1;3]
m.get(':',':') // The original matrix.
m.get(['::',-1],':') // Return a matrix flipped vertically

// Any sub-matrix returned is a view into the source matrix.
const a=zeros(4), b=a.get([1,2],[1,2]);
b.set(2);
console.log(a.toJSON())  // [[0,0,0,0], [0,2,2,0], [0,2,2,0], [0,0,0,0]]

// Binary 1D matrices can also be used to select rows or columns
const b = Matrix.bin([1,0,1,0]);
const m = Matrix.magic(4);
console.log(m.get(b,b).toJSON()); // [ [ 16, 3 ], [ 9, 6 ] ]

// Linear indices can also be used.  The index is in **row major order**.
// A single index returns a single value.
const m = Matrix.magic(4);
console.log(m.get(3)); // 13,

// Ranges or matrices can be used.  A column vector will always be returned
console.log(Matrix.magic(4).get([4,':',7]).toJSON()); // [ 5, 11, 10, 8 ]

// A binary matrix can also be used.  This is often derived from the matrix itself
const m = Matrix.magic(4);
const b = Matrix.bin(m,v=>v>12);
console.log(m.get(b).toJSON()); // [ 16, 13, 14, 15 ]

matrix.set([rows], [cols], val) ⇒ Matrix

Set a value or range of values of the matrix

Param Type Description
[rows] Range | Number Row index or indices. zero-based
[cols] Range | Number Column index or indices. zero-based
val Number | Matrix | Array | function | Boolean Values to assign to the specified range or a function to modify the values

Example

const m=Matrix.zeros(3);
//Set a single value
m.set(1,1,5); //[0,0,0; 0,5,0; 0,0,0]

//Set a range to a single value
m.set(0,':',3); //[3,3,3; 0,5,0; 0,0,0]

//The value can also be a matrix of the matching size, or an array which resolves to such.
m.set(2,':',[[7,8,6]]); //[3,3,3; 0,5,0; 7,8,6]
//If val is an array, [from](#from) will be used to convert it to a matrix.

//If no row and column indices are provided, the value will apply to the whole matrix
m.set(1); //[1,1,1; 1,1,1; 1,1,1]

//Linear indices can be used for single values
m.set(4,2); //[1,1,1; 1,2,1; 1,1,1]

//Or for vectors of values.  Note that the addressing is **row major order** although data must be provided in a column matrix
m.set([2,':',6],Matrix.zeros(5,1)); //[1,1,0; 0,0,0; 0,1,1]

//A binary matrix can also be used.
Matrix.mixin(Matrix.bin);
m.set(m.bin(v=>v===0), 2); //[1,1,2; 2,2,2; 2,1,1]

matrix.clone([rows], [cols]) ⇒ Matrix

Clone the current matrix, or a subset of the current matrix if rows and columns are specified.

Param Type Description
[rows] Range | Number If specified, the rows to clone
[cols] Range | Number If specified, the columns to clone

matrix.map(fn) ⇒ Matrix

Creates a new matrix with the results of calling a provided function on every element in the supplied matrix.

Param Type
fn function

Example

const m=Matrix.from([0,':',5]).map(v=>Math.pow(2,v));
console.log([...m]); //[1,2,4,8,16,32]

Matrix.rows(matrix) ⇒ IterableIterator.<Array.Number>

Iterate over the rows.

Param Type
matrix Matrix

Example

//Log each matrix row
for(let row of Matrix.rows(matrix)){
  console.log(row);
}

Matrix.cols(matrix) ⇒ IterableIterator.<Array.Number>

Iterate over the columns.

Param Type
matrix Matrix

Example

//Log the range of each column
for(let col of Matrix.cols(matrix)){
  console.log(`Range [${Math.min(...col)}|${Math.max(...col)}]`);
}

Matrix.isMatrix(val) ⇒ boolean

Tests if a value is an instance of a Matrix

Returns: boolean - 'true' if val is an instance of Matrix, 'false' otherwise.

Param Type Description
val * The value to test.

Matrix.isBinary(val) ⇒ boolean

Tests if a value is an instance of a binary Matrix

Returns: boolean - 'true' if val is an instance of a binary Matrix, 'false' otherwise.

Param Type Description
val * The value to test.

Matrix.mixin(...methods)

Add static functions of the form fn(matrix,...args) to the Matrix prototype as matrix.fn(args)

Param Type Description
...methods function | Object | Array.<function()> The method(s) to add

Example (Adding standard functions)

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix.max, Matrix.min);
const m=Matrix.from([[1,2,3],[4,5,6]]);
console.log(m.min() + ', ' + m.max()); //=> 1, 6

Example (Adding a custom function)

import * as Matrix from 't-matrix';
const sqrt = matrix => matrix.map(Math.sqrt);
Matrix.mixin('sqrt',sqrt);
const m=Matrix.from([1,4,9]);
console.log([...m.sqrt()]); //=> [1,2,3]

Example (Using a config file for the Matrix class)

// inside 'matrix-setup.js'
import {mixin, reshape} from 't-matrix';
const neg = matrix => matrix.map(v=>-v);
mixin(reshape,'neg',neg);

// inside other modules
import * as Matrix from 't-matrix';
console.log(Matrix.from([1,':',9]).reshape(3,3).neg().toJSON());//[[-1,-2,-3],[-4,-5,-6],[-7,-8,-9]]

Example (Just include everything which can be included)

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix);
console.log(Matrix.from([1,':',9]).reshape(3,3).mult(2).toJSON());//[[2,4,6],[8,10,12],[14,16,18]]

Range : Array.<(Number|String)> | Number | String

A Specification of indices of the row or column of a matrix, or a range of array values.

Example

//An arbitrary sequence of indices or numbers can be expressed
[1,2,3] //=> expands to the same list of indices: 1,2,3
[-1,-2,-3] //=> -1,-2,-3

//If specifying indices, negative numbers index from the end of an array.
[-1,-2,-3] //for an array of length 10, => 9,8,7

//Ranges can be expressed with the special character ':'
[1,':',5] //=> 1,2,3,4,5

//Therefore to express the full range
[0,':',-1] // for length 10, => 0,1,2,3,4,5,6,7,8,9

//When used at the start of a range definition, the range start is assumed
[':',-1] // equivalent to [0,':',-1]

//When used at the end of a range definition, the range end is assumed
[':'] // equivalent to [0,':'] and [':',-1] and [0,':',-1]

//Ranges with a larger step can be expressed using '::'
[1,'::',2,5] //=> 1,3,5

//Similar to ':' start and end limits can be implied
['::',2] // equivalent to [0,'::',2,-1]

//Negative steps can also be used
[5,'::',-2,1] //=> 5,3,1

//Similarly end limits can be implied
['::',-1] //=> [-1,'::',-1,0] which for length 10 => 9,8,7,6,5,4,3,2,1,0

//However if the step size is missing, an error will be thrown
['::'] //will throw an error when used

//Many ranges can be used in one definition
[5,':',-1,0,':',4] //for length 10=> 5,6,7,8,9,0,1,2,3,4

//Wherever a range definition is truncated by a second definition, end points are implied
[5,':',':',4] //equivalent to [5,':',-1,0,':',4]

//The same is true of the '::' operator
[4,'::',-1,'::',-1,5] // for length 10=>4,3,2,1,0,9,8,7,6,5

//Where there is only one entry, this can be expressed outside of an array
4 //equivalent to [4]
':' //specifies the full range

Matrix.gridInterp1(v, q) ⇒ Matrix

1D interpolation of uniformly spaced data.

The first parameter, v, is the data to be interpolated and the second parameter, q, indicates 0-based fractional index positions into v. If v is a column vector then the returned matrix will be the size of q. Alternatively v can contain multiple columns of data in which case q must be a column vector and the output matrix will have the row count of q and the column count of v.

Category: calculation

Param Type
v Matrix
q Matrix

Matrix.cumsum(m, dim) ⇒ Matrix

Calculate the cumulative sum along the rows or down the columns of a matrix.

Category: calculation

Param Type
m Matrix
dim Number

Matrix.from(data) ⇒ Matrix

Create a matrix from the supplied data.

Category: creation

Param Type Description
data Matrix | Array.Number | Array.Array.Number If data is a matrix then it is just returned. An array of numbers becomes a column matrix. An array of an array of numbers becomes a row matrix. An array of arrays of numbers becomes a general matrix. The inner arrays must all have the same length.

Example (Creating a column matrix)

Matrix.from([1,2,3,4])
//[1; 2; 3; 4]

Example (Creating a row matrix)

Matrix.from([[1,2,3,4]])
//[1,2,3,4]

Example (Creating an arbitrary matrix)

Matrix.from([[1,2],[3,4],[5,6]]);
//a 3x2 matrix [1,2; 3,4; 5,6]

Example (A matrix is just passed through)

const m = Matrix.from([[1,2],[3,4]]);
check = Matrix.from(m) === m; //true

Matrix.zeros(rows, [cols]) ⇒ Matrix

creates a new matrix filled with zeros

Category: creation

Param Type Description
rows number number of rows
[cols] number number of columns

Matrix.ones(rows, [cols]) ⇒ Matrix

creates a new matrix filled with ones

Category: creation

Param Type Description
rows number number of rows
[cols] number number of columns

Matrix.eye(n) ⇒ Matrix

creates a new identity matrix of size n

Category: creation

Param Type Description
n number number of rows and columns

Matrix.rand(rows, [cols]) ⇒ Matrix

creates a new matrix filled with random values between 0 inclusive and 1 exclusive

Category: creation

Param Type Description
rows number number of rows
[cols] number number of columns

Matrix.magic(size) ⇒ Matrix

Creates a magic square of the specified size

Category: creation

Param Type Description
size Number The size of the magic square. Must be 1 or an integer 3 or greater.

Matrix.diag(matrix, [set]) ⇒ Matrix

gets, sets or creates diagonal matrices

Category: creationAndManipulation

Param Type
matrix Matrix | Array
[set] Matrix | Array | function | Number

Example (Extract the diagonal elements from a matrix)

import * as Matrix from 't-matrix';
//Create a magic square
const mag = Matrix.magic(3);
//Get the sum of the diagonal elements - should add up to 15 for a 3x3 magic square
console.log(Matrix.sum(Matrix.diag(mag))); //15

Example (Set the diagonal elements of a matrix)

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix); //just add everything in for ease
//Create a new matrix with a diagonal 1,2,3,4
const mDiag = Matrix.zeros(4).diag([1,2,3,4]);
console.log(mDiag.toJSON());//[[1,0,0,0],[0,2,0,0],[0,0,3,0],[0,0,0,4]]
//Create it using the diag call directly
console.log(Matrix.diag([1,2,3,4]).toJSON());//returns the same as above

Matrix.mcat(array) ⇒ Matrix

Concatenates a nested array of matrices - horizontally and vertically as required.

The matrices to be concatenated must be supplied as an array of arrays of matrices. The inner arrays are concatenated horizontally and the outer arrays are concatenated vertically.

Category: manipulation

Param Type
array Array.<Array.<Matrix>>

Example

const m = Matrix.mcat([[Matrix.ones(2),Matrix.zeros(2)],[Matrix.zeros(2),Matrix.ones(2)]]);
console.log(m.toJSON()); //[[1,1,0,0],[1,1,0,0],[0,0,1,1],[0,0,1,1]]

Matrix.reshape(matrix, rows, cols) ⇒ Matrix

Reshape the matrix to the dimensions specified treating the matrix data in row-major order

Category: manipulation

Param Type Description
matrix Matrix The matrix to reshape.
rows Number The row count for the new matrix.
cols Number The column count for the new matrix.

Example

const m=Matrix.from([1,':',9]);
const m2=Matrix.reshape(m,3,3);
console.log(m2.toJSON()); //[[1,2,3],[4,5,6],[7,8,9]]
//If reshape is used a lot to form new matrices, consider adding it to the matrix prototype with mixin
Matrix.mixin(Matrix.reshape);
console.log(Matrix.from([1,':',4]).reshape(2,2).toJSON()); // [[1,2],[3,4]]

Matrix.swapRows(matrix, rowsA, rowsB) ⇒ Matrix

Swap the rows of a matrix.

No data is actually copied here, so this is a very efficient operation. Two lists of indices are supplied, and these can both be Range types. The pairs of rows from rowsA and rowsB are then swapped in order from the start of each list. If more indices are specified in one list than the other then these additional indices are ignored.

This function can be added to the Matrix prototype as a method using Matrix.mixin, it returns the matrix object for chaining.

Category: manipulation

Param Type Description
matrix Matrix
rowsA Range | Number The first list of rows to swap
rowsB Range | Number The second list of rows to swap, must be the same length as rowsA

Matrix.swapCols(matrix, colsA, colsB) ⇒ Matrix

Swap the columns of a matrix.

No data is actually copied here, so this is a very efficient operation. Two lists of indices are supplied, and these can both be Range types. The pairs of columns from colsA and colsB are then swapped in order from the start of each list. If more indices are specified in one list than the other then these additional indices are ignored.

This function can be added to the Matrix prototype as a method using Matrix.mixin, it returns the matrix object for chaining.

Category: manipulation

Param Type Description
matrix Matrix
colsA Range | Number The first list of columns to swap
colsB Range | Number The second list of columns to swap, must be the same length as rowsA

Matrix.minor(matrix, row, col) ⇒ Matrix

Return a matrix with the given row and column removed.

The minor of a matrix is the matrix with the specified row and column removed. The matrix returned by this function is a new matrix, but references the same data. No data is copied so this is a fast operation.

Category: manipulation

Param Type
matrix Matrix
row Number
col Number

Matrix.repmat(matrix, vRepeat, hRepeat) ⇒ Matrix

Repeat the supplied matrix the specified number of times horizontally and vertically.

Category: manipulation

Param Type
matrix Matrix
vRepeat Number
hRepeat Number

Matrix.vcat(matrices) ⇒ Matrix

Vertically concatenate matrices together

Category: manipulation

Param Type
matrices Matrix

Matrix.hcat(matrices) ⇒ Matrix

Horizontally concatenate matrices together

Category: manipulation

Param Type
matrices Matrix

Matrix.shift(matrix, [rowShift], [colShift]) ⇒ Matrix

Circularly shift the matrix by the specified steps. The shifts are MOD the respective matrix dimension.

Category: manipulation

Param Type Description
matrix Matrix
[rowShift] Number Number of rows to shift down. A negative number shifts up. If matrix is a vector and only one shift parameter is specified then this applies to the first dimension of length greater than 1.
[colShift] Number Number of columns to shift right. A negative number shifts left.

Example

const m = Matrix.from([1,2,3],[4,5,6],[7,8,9]);
Matrix.shift(m,1,1) //  shift the contents one down and one to the right
Matrix.shift(m,-1,3) // shift the contents one up.  The matrix has three columns so a shift of 3 does nothing.

Matrix.sum(...matrices) ⇒ Matrix | Number

Return the sum of the matrix in the direction specified or the element-wise sum of the set of matrices.

Matrix.sum(m) or m.sum() will sum all the values of a matrix, returning a number.

Matrix.sum(m,null,1) or m.sum(null,1) will sum the matrix columns, returning a row matrix.

Matrix.sum(m,null,2) or m.sum(null,2) will sum the matrix rows, returning a column matrix.

Matrix.sum(m1,m2,m3,...) or m1.sum(m2,m3,...) will calculate an element-wise sum over all the matrices.

For the last case, the supplied list of matrices must either have the same row count or a row count of 1, and the same column count or a column count of 1. This includes scalar values which implicitly are treated as 1x1 matrices. Arrays can also be provided and these will be converted to matrices using Matrix.from. Row matrices will be added to every row, column matrices to every column and scalar values to every matrix element.

Category: operation

Param Type
...matrices Matrix | Number

Example

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix);
console.log(Matrix.magic(3).sum(null,1).toJSON());//[[15,15,15]];
console.log(Matrix.magic(3).sum());//45
console.log(Matrix.sum([[0,1,2]], [6,3,0], 1).toJSON());//[[7,8,9],[4,5,6],[1,2,3]];

Matrix.max(...matrices) ⇒ Matrix | Number

Return the maximum of the matrix in the direction specified or the element-wise maximum of the set of matrices.

Matrix.max(m) or m.max() will return the max of all the values of a matrix.

Matrix.max(m,null,1) or m.max(null,1) will return a row matrix containing max of each matrix column.

Matrix.max(m,null,2) or m.max(null,2) will return a column matrix containing max of each matrix row.

Matrix.max(m1,m2,m3,...) or m1.max(m2,m3,...) will calculate an element-wise max over all the matrices.

For the last case, the supplied list of matrices must either have the same row count or a row count of 1, and the same column count or a column count of 1. This includes scalar values which implicitly are treated as 1x1 matrices. Arrays can also be provided and these will be converted to matrices using Matrix.from. An element of the returned matrix of a given row and column will be the max of that row and column of all regular matrices, of that row of all column matrices, of that column of all row matrices and of all scalar values.

Category: operation

Param Type
...matrices Matrix | Number

Example

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix);
console.log(Matrix.magic(3).max(null,1).toJSON());//[[8,9,7]];
console.log(Matrix.magic(3).max());//9
console.log(Matrix.max([[0,1,2]], [6,3,0], 1).toJSON());//[[6,6,6],[3,3,3],[1,1,2];

Matrix.min(...matrices) ⇒ Matrix | Number

Return the minimum of the matrix in the direction specified or the element-wise minimum of the set of matrices.

Works the same way as other similar operations. See Matrix.max for more details.

Category: operation

Param Type
...matrices Matrix | Number

Example

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix);
console.log(Matrix.magic(3).max(null,1).toJSON());//[[3,1,2]];
console.log(Matrix.magic(3).max());//1
console.log(Matrix.max([[0,1,2]], [6,3,0], 1).toJSON());//[[0,1,1],[0,1,1],[0,0,0];

Matrix.product(...matrices) ⇒ Matrix | Number

Return the product of the matrix values in the direction specified or the element-wise product of the set of matrices.

Works the same way as other similar operations. See Matrix.sum for more details.

Category: operation

Param Type
...matrices Matrix | Number

Example

import * as Matrix from 't-matrix';
Matrix.mixin(Matrix);
console.log(Matrix.magic(3).product(null,1).toJSON());//[[96,45,84]];
console.log(Matrix.magic(3).product());//362880
console.log(Matrix.product([[0,1,2]], [6,3,0], 1).toJSON());//[[0,6,12],[0,3,6],[0,0,0]];

Matrix.trace(matrix) ⇒ Number

Returns the trace of a matrix (the sum of the diagonal elements)

Category: operation

Param
matrix

Matrix.mapMany(...matrices, fn) ⇒ Matrix

Creates a new matrix with the results of calling a provided function on every element in the supplied set of matrices.

Category: operation

Param Type
...matrices Matrix | Number
fn function

Example

//Calculate a gaussian function in 2D for a range -3:0.1:3 in x and y.
import * as Matrix from 't-matrix';
const [Y,X]=Matrix.grid([-3,'::',0.1,3]);
const gauss=Matrix.mapMany(Y,X,(y,x)=>Math.exp(-Math.pow(x+y,2)));

Matrix.bin(...matrices, [fn]) ⇒ Matrix

Creates a new binary matrix with the results of calling a provided function on every element in the supplied set of one or more matrices.

Category: operation

Param Type Description
...matrices Matrix | Number
[fn] function Optional for the special case of a single parameter, mandatory otherwise

Example

//Sum only the values of a matrix above a threshold
import * as Matrix from 't-matrix';
const m = Matrix.magic(10);
const selection = Matrix.bin(m, v=>v>0.5);
const sum = Matrix.sum(m.get(selection));

//If using bin a lot, consider mixing it in
Matrix.mixin(Matrix.bin);
console.log('count of non-zero values of m:',Matrix.sum(m.bin()));

Matrix.mult(...matrices) ⇒ Matrix

Performs matrix multiplication on a list of matrices and/or scalars

Category: operation

Param Type Description
...matrices Matrix | Number At least one parameter must be a matrix or convertible to a matrix through Matrix.from

Example

import * as Matrix from 't-matrix';
const mag = Matrix.magic(3);
console.log(Matrix.mult(mag,Matrix.inv(mag)).toJSON());//a 3x3 identity matrix (plus some round-off error)

Matrix.det(matrix) ⇒ number

Calculate the determinant of a matrix.

The determinant is calculated by the standard naive algorithm which scales really really badly (the algorithm is O(n!)). Once LU decomposition has been added to the library then that will provide an O(n^3) method which is much faster.

Category: operation

Param Type
matrix Matrix

Matrix.ldiv(A, B) ⇒ Matrix

Left-division. Solve Ax = B for x.

Solve the system of linear equations Ax = B for x. In Matlab/Octave this can be expressed as A\B. Equivalent to using Matrix.div where Matrix.ldiv(A,B) gives the same answer as Matrix.div(B.t,A.t).t.

Category: operation

Param Type
A Matrix
B Matrix

Matrix.div(A, B) ⇒ Matrix

Right-division. Solve xB = A for x.

Solve the system of linear equations xB = A for x. In Matlab/Octave this can be expressed as A/B. Equivalent to using Matrix.div where Matrix.div(A,B) gives the same answer as Matrix.ldiv(B.t,A.t).t.

Category: operation

Param Type
A Matrix
B Matrix

Matrix.inv(matrix) ⇒ Matrix

Calculate the inverse of a matrix.

Uses the ldiv operation to calculate the inverse. NOTE: it is really not good practice to use a matrix inverse, instead consider using div or ldiv directly. For a more thorough exposition on this see, for example, "Don't invert that matrix"

Category: operation

Param Type
matrix Matrix

Matrix.abs(matrix) ⇒ Matrix

Return a new matrix containing the element-wise absolute values of the source matrix.

Category: operation

Param Type
matrix Matrix

Matrix.grid(rows, [cols]) ⇒ Array.<Matrix>

Generate a regular grid in 2D space

This is equivalent to the Matlab/Octave function ndgrid for the 2d case. Once the rows and cols parameters are expanded to arrays, the first returned matrix contains the rows array as a column matrix repeated to match the size of the cols array. Similarly the second returned matrix is the cols array as a row matrix repeated to match the size of the rows array.

Category: operation

Param Type Description
rows Range | Number If a number n this is converted to a range 0:n-1, otherwise a range is expected.
[cols] Range | Number If a number n this is converted to a range 0:n-1, otherwise a range is expected.

Matrix.cross(A, B, [dim]) ⇒ Matrix

Calculate the cross product(s) of two vectors or sets of vectors.

Both matrices must contain either 1 or N 3-element row vectors or column vectors. The orientation of the vectors must be consistent between the two matrices, and the returned matrix will use the same orientation. If both contain a single vector, the cross product of those vectors will be returned. If both contain N vectors, then the returned matrix will contain the N cross products of each vector pair. If one matrix has 1 vector and the other N then the returned matrix will be the N cross products of the single vector with each of N vectors from the other matrix.

Category: operation

Param Type
A Matrix
B Matrix
[dim] Number

Example

import * as Matrix from 't-matrix';
console.log([...Matrix.cross([1,0,0],[0,1,0])]); // should be [0,0,1]

Matrix.dot(A, B, [dim]) ⇒ Matrix

Calculate the scalar dot product(s) of two vectors or sets of vectors.

Both matrices must contain either 1 or N row vectors or column vectors of equal length. The orientation of the vectors must be consistent between the two matrices, and the returned matrix will use the same orientation. If both contain a single vector, the dot product of those vectors will be returned as a scalar value. If both contain N vectors, then the returned matrix will contain the N dot products of each vector pair. If one matrix has 1 vector and the other N then the returned matrix will be the N dot products of the single vector with each of N vectors from the other matrix.

Category: operation

Param Type
A Matrix
B [Matri