Matrices #

Math.js supports multi dimensional matrices and arrays. Matrices can be created, manipulated, and used in calculations. Both regular JavaScript arrays as well as the matrix type implemented by math.js can be used interchangeably in all relevant math.js functions. math.js supports both dense and sparse matrices.

Arrays and matrices #

Math.js supports two types of matrices:

In most cases, the type of matrix output from functions is determined by the function input: An Array as input will return an Array, a Matrix as input will return a Matrix. In case of mixed input, a Matrix is returned. For functions where the type of output cannot be determined from the input, the output is determined by the configuration option matrix, which can be a string 'Matrix' (default) or 'Array'.

// create an array and a matrix
const array = [[2, 0], [-1, 3]]               // Array
const matrix = math.matrix([[7, 1], [-2, 3]]) // Matrix

// perform a calculation on an array and matrix
math.map(array, math.square)                  // Array,  [[4, 0], [1, 9]]
math.map(matrix, math.square)                 // Matrix, [[49, 1], [4, 9]]

// perform calculations with mixed array and matrix input
math.add(array, matrix)                       // Matrix, [[9, 1], [-3, 6]]
math.multiply(array, matrix)                  // Matrix, [[14, 2], [-13, 8]]

// create a matrix. Type of output of function ones is determined by the
// configuration option `matrix`
math.ones(2, 3)                               // Matrix, [[1, 1, 1], [1, 1, 1]]

Creation #

A matrix can be created from an array using the function math.matrix. The provided array can contain nested arrays in order to create a multi-dimensional matrix. When called without arguments, an empty matrix will be created.

// create matrices
math.matrix()                           // Matrix, size [0]
math.matrix([0, 1, 2])                  // Matrix, size [3]
math.matrix([[0, 1], [2, 3], [4, 5]])   // Matrix, size [3, 2]

Math.js supports regular Arrays. Multiple dimensions can be created by nesting Arrays in each other.

// create arrays
[]                                      // Array, size [0]
[0, 1, 2]                               // Array, size [3]
[[0, 1], [2, 3], [4, 5]]                // Array, size [3, 2]

Matrices can contain different types of values: numbers, complex numbers, units, or strings. Different types can be mixed together in a single matrix.

// create a matrix with mixed types
const a = math.matrix([2.3, 'hello', math.complex(3, -4), math.unit('5.2 mm')]) 
a.subset(math.index(1))  // 'hello'

There are a number of functions to create a matrix with a specific size and content: ones, zeros, identity.

// zeros creates a matrix filled with zeros
math.zeros(3)           // Matrix, size [3],    [0, 0, 0]
math.zeros(3, 2)        // Matrix, size [3, 2], [[0, 0], [0, 0], [0, 0]]
math.zeros(2, 2, 2)     // Matrix, size [2, 2, 2],
                        //   [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]

// ones creates a matrix filled with ones
math.ones(3)                        // Matrix, size [3],    [1, 1, 1]
math.multiply(math.ones(2, 2), 5)   // Matrix, size [2, 2], [[5, 5], [5, 5]]

// identity creates an identity matrix
math.identity(3)      // Matrix, size [3, 3], [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
math.identity(2, 3)   // Matrix, size [2, 3], [[1, 0, 0], [0, 1, 0]]

The functions ones, zeros, and identity also accept a single array or matrix containing the dimensions for the matrix. When the input is an Array, the functions will output an Array. When the input is a Matrix, the output will be a Matrix. Note that in case of numbers as arguments, the output is determined by the option matrix as discussed in section Arrays and matrices.

// Array as input gives Array as output
math.ones([2, 3])               // Array,  size [3, 2], [[1, 1, 1], [1, 1, 1]]
math.ones(math.matrix([2, 3]))  // Matrix, size [3, 2], [[1, 1, 1], [1, 1, 1]]

Ranges can be created using the function range. The function range is called with parameters start and end, and optionally a parameter step. The start of the range is included, the end of the range is excluded.

math.range(0, 4)        // [0, 1, 2, 3]
math.range(0, 8, 2)     // [0, 2, 4, 6]
math.range(3, -1, -1)   // [3, 2, 1, 0]

Calculations #

Most functions of math.js support matrices and arrays. Unary functions can be applied element-wise using via math.map(matrix, function).

// perform an element-wise operation on a matrix using math.map
const a = math.matrix([1, 4, 9, 16, 25])  // Matrix, [1, 4, 9, 16, 25]
math.map(a, math.sqrt)                    // Matrix, [1, 2, 3, 4, 5]

// use a function that has built-in matrix and array support
const b = [1, 2, 3, 4, 5] 
math.factorial(b)                         // Array,  [1, 2, 6, 24, 120]

// multiply an array with a matrix
const c = [[2, 0], [-1, 3]]               // Array
const d = math.matrix([[7, 1], [-2, 3]])  // Matrix
math.multiply(c, d)                       // Matrix, [[14, 2], [-13, 8]]

// add a number to a matrix (see broadcasting)
math.add(c, 2)                            // Array, [[4, 2], [1, 5]]

// calculate the determinant of a matrix
math.det(c)                               // 6
math.det(d)                               // 23

Broadcasting #

Functions that require two or more matrix like arguments that operate elementwise automatically operate as if the arguments were the same size.

A = math.matrix([1, 2])       // Matrix, [1, 2]
math.add(A, 3)                // Matrix, [3, 4]

B = math.matrix([[3], [4]])   // Matrix, [[3], [4]]
math.add(A, B)                // Matrix, [[4, 5], [5, 6]]

Any index that is in one of the arguments, can be found as if it existed on the others when the size on that dimension is one or not existing. This is valid in N dimensions.

It’s not possible to broadcast in cases where the size in that dimension is higher than one.

math.add([1, 2], [3, 4, 5])
// Error: shape missmatch: missmatch is found in arg with shape (2) not possible to broadcast dimension 0 with size 2 to size 3

math.add([[1], [2], [3]], [[4], [5]])
// Error: shape missmatch: missmatch is found in arg with shape (2,1) not possible to broadcast dimension 0 with size 2 to size 3

Size and Dimensions #

Math.js uses geometric dimensions:

The size of a matrix can be calculated with the function size. Function size returns a Matrix or Array, depending on the configuration option matrix. Furthermore, matrices have a function size as well, which always returns an Array.

// get the size of a scalar
math.size(2.4)                                // Matrix, []
math.size(math.complex(3, 2))                 // Matrix, []
math.size(math.unit('5.3 mm'))                // Matrix, []

// get the size of a one-dimensional matrix (a vector) and a string
math.size([0, 1, 2, 3])                       // Array, [4]
math.size('hello world')                      // Matrix, [11]

// get the size of a two-dimensional matrix
const a = [[0, 1, 2, 3]]                      // Array
const b = math.matrix([[0, 1, 2], [3, 4, 5]]) // Matrix
math.size(a)                                  // Array, [1, 4]
math.size(b)                                  // Matrix, [2, 3]

// matrices have a function size (always returns an Array)
b.size()                                      // Array, [2, 3]

// get the size of a multi-dimensional matrix
const c = [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]
math.size(c)                                  // Array, [2, 2, 3]

Note that the dimensions themselves do not have a meaning attached. When creating and printing a two dimensional matrix, the first dimension is normally rendered as the column, and the second dimension is rendered as the row. For example:

console.table(math.zeros([2, 4]))
// 0 0 0 0
// 0 0 0 0

If you have a matrix where the first dimension means x and the second means y, this will look confusing since x is printed as column (vertically) and y as row (horizontally).

Resizing #

Matrices can be resized using their resize function. This function is called with an Array with the new size as the first argument, and accepts an optional default value. By default, new entries will be set to 0, but it is possible to pass a different default value like null to clearly indicate that the entries haven’t been explicitly set.

const a = math.matrix() // Matrix, size [0],       []
a.resize([2, 3])        // Matrix, size [2, 3],    [[0, 0, 0], [0, 0, 0]]
a.resize([2, 2, 2])     // Matrix, size [2, 2, 2],
                        //   [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]

const b = math.matrix()
b.resize([3], 7)        // Matrix, size [3],    [7, 7, 7]
b.resize([5], 9)        // Matrix, size [5],    [7, 7, 7, 9, 9]
b.resize([2])           // Matrix, size [2],    [7, 7]

Outer dimensions of a matrix can be squeezed using the function squeeze. When getting or setting a single value in a matrix using subset, the value is automatically squeezed or unsqueezed too.

// squeeze a matrix
const a = [[[0, 1, 2]]]
math.squeeze(a)             // [0, 1, 2]
math.squeeze([[3]])         // 3

// when getting/setting a single value in a matrix using subset, 
// it automatically squeeze/unsqueeze the value
const b = math.matrix([[0, 1], [2, 3]])
b.subset(math.index(1, 0))  // 2 and not [[2]]

Getting or replacing subsets #

Subsets of a matrix can be retrieved or replaced using the function subset. Matrices have a subset function, which is applied to the matrix itself: Matrix.subset(index [, replacement]). For both matrices and arrays, the static function subset(matrix, index [, replacement]) can be used. When parameter replacement is provided, the function will replace a subset in the matrix, and if not, a subset of the matrix will be returned.

A subset can be defined using an Index. An Index contains a single value or a set of values for each dimension of a matrix. An Index can be created using the function index. When getting a single value from a matrix, subset will return the value itself instead of a matrix containing just this value.

The function subset normally returns a subset, but when getting or setting a single value in a matrix, the value itself is returned.

Matrix indexes in math.js are zero-based, like most programming languages including JavaScript itself. Note that mathematical applications like Matlab and Octave work differently, as they use one-based indexes.

// create some matrices
const a = [0, 1, 2, 3]
const b = [[0, 1], [2, 3]]
const c = math.zeros(2, 2)
const d = math.matrix([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
const e = math.matrix()

// get a subset
math.subset(a, math.index(1))                 // 1
math.subset(a, math.index([2, 3]))            // Array, [2, 3]
math.subset(a, math.index(math.range(0,4)))   // Array, [0, 1, 2, 3]
math.subset(b, math.index(1, 0))              // 2
math.subset(b, math.index(1, [0, 1]))         // Array, [2, 3]
math.subset(b, math.index([0, 1], 0))         // Matrix, [[0], [2]]

// get a subset
d.subset(math.index([1, 2], [0, 1]))          // Matrix, [[3, 4], [6, 7]]
d.subset(math.index(1, 2))                    // 5

// replace a subset. The subset will be applied to a clone of the matrix
math.subset(b, math.index(1, 0), 9)           // Array, [[0, 1], [9, 3]]
math.subset(b, math.index(2, [0, 1]), [4, 5]) // Array, [[0, 1], [2, 3], [4, 5]]

// replace a subset. The subset will be applied to the matrix itself
c.subset(math.index(0, 1),1)                  // Matrix, [[0, 1], [0, 0]]
c.subset(math.index(1, [0, 1]), [2, 3])       // Matrix, [[0, 1], [2, 3]]
e.resize([2, 3], 0)                           // Matrix, [[0, 0, 0], [0, 0, 0]]
e.subset(math.index(1, 2), 5)                 // Matrix, [[0, 0, 0], [0, 0, 5]]

Getting and setting a value in a matrix #

There are two methods available on matrices that allow to get or set a single value inside a matrix. It is important to note that the set method will mutate the matrix.

const p = math.matrix([[1, 2], [3, 4]])
p.set([0, 1], 5)
// p is now [[1, 5], [3, 4]]
p.get([1, 0]) // 3

When setting a value at a location outside of the current matrix size using the method .set(), the matrix will be resized. By default, new items will be initialized with zero, but it is possible to specify an alternative value using the optional third argument defaultValue.

Advanced Indexing #

Boolean array indexing is a technique that allows you to filter, replace, and set values in an array based on logical conditions. This can be done by creating a boolean array that represents the desired conditions, and then using that array as an index to select the elements of the original array that meet those conditions.

For example, a boolean array can be created to represent all the even numbers in an array, and then used to filter the original array to only include the even numbers. Alternatively, a boolean array can be created to represent all the elements of an array that are greater than a certain value, and then used to replace all the elements of the original array that are greater than that value with a new value.

const q = [1, 2, 3, 4]
math.subset(q, math.index([true, false, true, false]))      // Array [1, 3]

// filtering
math.subset(q, math.index(math.larger(q, 2)))               // Array [3, 4]

// filtering with no matches
math.subset(q, math.index(math.larger(q, 5)))               // Array []

// setting specific values, please note that the replacement value is broadcasted
q = math.subset(q, math.index(math.smaller(q, 3)), 0)       // q = [0, 0, 3, 4]

// replacing specific values
math.subset(q, math.index(math.equal(q, 0)), [1, 2])        // q = [1, 2, 3, 4]

The same can be accomplished in the parser in a much more compact manner. Please note that everything after # are comments.

math.evaluate(`
q = [1, 2, 3, 4]
q[[true, false, true, false]] # Matrix [1, 3]
q[q>2]                        # Matrix [3, 4]
q[q>5]                        # Matrix []
q[q <3] = 0                   # q = [0, 0, 3, 4]
q[q==0] = [1, 2]              # q = [1, 2, 3, 4]
`)

The expression inside the index can be as complex as needed as long it evaluates to an array of booleans of the same size.

math.evaluate(`
q = [1, 2, 3, 4]
r = [6, 5, 4, 3]
q[q > 3 and r < 4]     # [4]
`)

Iterating #

Matrices contain functions map and forEach to iterate over all elements of the (multidimensional) matrix. The callback function of map and forEach has three parameters: value (the value of the currently iterated element), index (an array with the index value for each dimension), and matrix (the matrix being iterated). This syntax is similar to the map and forEach functions of native JavaScript Arrays, except that the index is no number but an Array with numbers for each dimension.

const a = math.matrix([[0, 1], [2, 3], [4, 5]])

// The iteration below will output the following in the console:
//    value: 0 index: [0, 0]
//    value: 1 index: [0, 1]
//    value: 2 index: [1, 0]
//    value: 3 index: [1, 1]
//    value: 4 index: [2, 0]
//    value: 5 index: [2, 1]
a.forEach(function (value, index, matrix) {
  console.log('value:', value, 'index:', index) 
}) 

// Apply a transformation on the matrix
const b = a.map(function (value, index, matrix) {
  return math.multiply(math.sin(value), math.exp(math.abs(value))) 
}) 
console.log(b.format(5))  // [[0, 2.2874], [6.7188, 2.8345], [-41.32, -142.32]]

// Create a matrix with the cumulative of all elements
let count = 0
const cum = a.map(function (value, index, matrix) {
  count += value 
  return count 
}) 
console.log(cum.toString())  // [[0, 1], [3, 6], [10, 15]]

Storage types #

Math.js supports both dense matrices as well as sparse matrices. Sparse matrices are efficient for matrices largely containing zeros. In that case they save a lot of memory, and calculations can be much faster than for dense matrices.

Math.js supports two type of matrices:

The type of matrix can be selected when creating a matrix using the construction functions matrix, diag, identity, ones, and zeros.

// create sparse matrices
const m1 = math.matrix([[0, 1], [0, 0]], 'sparse')
const m2 = math.identity(1000, 1000, 'sparse')

You can also coerce an array or matrix into sparse storage format with the sparse function.

const md = math.matrix([[0, 1], [0,0]])  // dense
const ms = math.sparse(md)               // sparse

Caution: sparse called on a JavaScript array of n plain numbers produces a matrix with one column and n rows – in contrast to matrix, which produces a 1-dimensional matrix object with n entries, i.e., a vector (not a 1 by n “row vector” nor an n by 1 “column vector”, but just a plain vector of length n).

const mv = math.matrix([0, 0, 1])  // Has size [3]
const mc = math.sparse([0, 0, 1])  // A "column vector," has size [3, 1]

API #

All relevant functions in math.js support Matrices and Arrays. Functions like math.add and math.subtract, math.sqrt handle matrices element wise. There is a set of functions specifically for creating or manipulating matrices, such as:

A full list of matrix functions is available on the functions reference page.

Two types of matrix classes are available in math.js, for storage of dense and sparse matrices. Although they contain public functions documented as follows, using the following API directly is not recommended. Prefer using the functions in the “math” namespace wherever possible.

Fork me on GitHub