Skip to content
Varlig Guide
Varlig home

Vectors and matrices

A vector is a flat list of numbers, such as a direction in space or a set of quantities. A matrix is a list of rows. This page covers dot and cross products, multiplying matrices, determinants and inverses, solving several linear equations at once, and the numerical tools of linear algebra: eigenvalues and the QR, LU and singular value decompositions.

Reach for it for linear algebra coursework, geometry and transformations, or any problem with several unknowns tied together by straight-line equations. Vectors and matrices are ordinary lists, so everything in Lists and statistics, such as indexing, also applies.

Vectors

calc
dot([1, 2, 3], [4, 5, 6]) gives 32
dot([3, 4], [4, -3]) gives 0
cross([1, 0, 0], [0, 1, 0]) gives [0, 0, 1]
cross([1, 2, 3], [4, 5, 6]) gives [-3, 6, -3]
v = [3, 4]
sqrt(dot(v, v)) gives 5

dot multiplies matching elements and adds the results. A dot product of zero means the two vectors are at right angles, as [3, 4] and [4, -3] are. cross gives a vector at right angles to both of its inputs: the x direction crossed with the y direction is the z direction. The length of a vector is the square root of its dot product with itself.

dot needs two vectors of the same length, and cross needs two vectors of three elements. To add vectors or scale them, use ordinary list arithmetic: [1, 2] + [3, 4] is [4, 6] and 2 * [3, 4] is [6, 8].

Writing and multiplying matrices

Write a matrix as a list of rows, each row in its own square brackets:

calc
shape([[1, 2, 3], [4, 5, 6]]) gives [2, 3]
transpose([[1, 2, 3], [4, 5, 6]]) gives [[1, 4], [2, 5], [3, 6]]
identity(3) gives [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
[[1, 2], [3, 4]] * [[5, 6], [7, 8]] gives [[19, 22], [43, 50]]

shape gives [rows, columns]. transpose swaps rows and columns. identity(n) is the n by n matrix that leaves another matrix unchanged when you multiply by it.

* between two matrices is matrix multiplication, which needs the left matrix to have as many columns as the right matrix has rows. To multiply a matrix by a vector, write the vector as a column, one element per row:

calc
# Rotate the point (3, 1) by 90° anticlockwise
rotate = [[0, -1], [1, 0]]
rotate * [[3], [1]] gives [[-1], [3]]
rotate * rotate gives [[-1, 0], [0, -1]]

Rotating twice by 90° gives a half turn, which is why rotate * rotate flips both signs. A flat list such as [3, 1] isn’t accepted on the right of a matrix. If you have a flat list in a name, transpose([v]) turns it into a column.

Matrices of the same shape can be added and subtracted, and multiplied or divided by a number, like other lists.

A few rules apply to every matrix operation:

  • Every row must have the same number of elements.
  • Elements must be plain numbers. Units and money aren’t accepted.
  • A matrix can have at most 32 rows and 32 columns.
  • There is no semicolon shorthand. Write [[1, 2], [3, 4]], not [1, 2; 3, 4].

Determinant, inverse, trace and rank

calc
M = [[2, 1], [5, 3]]
det(M) gives 1
inverse(M) gives [[3, -1], [-5, 2]]
M * inverse(M) gives [[1, 0], [0, 1]]
trace(M) gives 5
inverse([[2, 1], [1, 3]]) gives [[0.6, -0.2], [-0.2, 0.4]]
det([[1, 2], [2, 4]]) gives 0
rank([[1, 2, 3], [2, 4, 6]]) gives 1

det, inverse and trace (the sum of the diagonal) need a square matrix. A determinant of zero means the matrix is singular: its rows aren’t independent, so it has no inverse, and inverse([[1, 2], [2, 4]]) is an error. rank counts how many rows are independent, and also accepts matrices that aren’t square. In the last line the second row is twice the first, so the rank is 1.

These four use exact fractions rather than decimals, which is why M * inverse(M) comes back as exactly the identity matrix.

Solve a linear system

Suppose three coffees and two teas cost £13.50, and one coffee and four teas cost £11.50. Put the number of each drink in a row per equation, with the columns in a fixed order (coffee, tea), and the totals in a matching list:

calc
solve_system([[3, 2], [1, 4]], [13.5, 11.5]) gives [3.1, 2.1]

The answer is in the same order as the columns: a coffee costs £3.10 and a tea £2.10. Leave the currency symbols off, because matrix elements must be plain numbers.

solve_system(A, b) solves A * x = b for x. It needs a square matrix, one row per unknown, and a flat list of right-hand sides of the same length. It uses exact fractions, so the answer has no rounding error.

If the equations don’t pin down a single answer, you get an error saying the system has no unique solution. For example, solve_system([[1, 2], [2, 4]], [3, 6]) fails because the second equation is the first one doubled. solve_system handles straight-line (linear) equations only; for an equation such as x^2 = 2, use solve from Summation, equation solving and calculus.

Eigenvalues

calc
eigenvalues([[2, 1], [1, 2]]) gives [1, 3]
eigenvalues([[4, 1], [2, 3]]) gives [5, 2]
eigenvalues([[0, -1], [1, 0]]) gives [i, -i]

eigenvalues takes a square matrix and returns a flat list. A 90° rotation leaves no direction unchanged, so its eigenvalues are the complex pair i and -i; see Complex numbers. Don’t rely on the order of the list.

Decompositions: QR, LU and SVD

Each decomposition returns a list of matrices. Use indexes to pull out the parts:

calc
A = [[3, 0], [4, 5]]
parts = qr(A)
q = parts[0] gives [[0.6, -0.8], [0.8, 0.6]]
r = parts[1] gives [[5, 4], [0, 3]]
q * r gives [[3, 0], [4, 5]]

Multiplying the factors back together gives A again, so you can check a decomposition at a glance. Working in binary floating point leaves tiny rounding leftovers, around 10⁻¹⁵, and Varlig shows those as 0 in lists and matrices. The stored values keep every digit, so compare decomposition results by eye or by looking at the difference, not with ==.

calc
lu([[2, 1], [4, 3]]) gives [[[0, 1], [1, 0]], [[1, 0], [0.5, 1]], [[4, 3], [0, -0.5]]]

lu returns [P, L, U], where P records any row swaps and P * A equals L * U. Here the rows were swapped so that the larger number, 4, is used first.

calc
B = [[3, 1], [4, 7]]
f = svd(B)
u = f[0]
s = f[1]
vt = f[2]
u * [[s[0], 0], [0, s[1]]] * vt gives [[3, 1], [4, 7]]
svd([[3, 4]]) gives [[[1]], [5], [[0.6, 0.8]]]

svd returns [U, s, Vt], where s is a flat list of singular values, largest first. In textbooks B equals U * diag(s) * Vt. There is no diag function, so build the diagonal matrix yourself, as in [[s[0], 0], [0, s[1]]].

Function What it needs and returns
identity(n) The n by n identity matrix
shape(A) [rows, columns]
transpose(A) Rows and columns swapped
det(A) Determinant of a square matrix
inverse(A) Inverse of a square matrix with a non-zero determinant
trace(A) Sum of the diagonal of a square matrix
rank(A) Number of independent rows; any rectangular matrix
solve_system(A,b) Solution of A * x = b for a square matrix with a single solution
eigenvalues(A) Eigenvalues of a square matrix, possibly complex, in no particular order
qr(A) [Q, R] with Q * R approximately A; any rectangular matrix
lu(A) [P, L, U] with P * A approximately L * U; square matrix
svd(A) [U, s, Vt], where s is a flat list; any rectangular matrix

For a matrix that isn’t square, qr and svd return thin factors: for a 3 by 2 matrix, Q is also 3 by 2 rather than 3 by 3.

Algorithms and limits

det, inverse, rank and solve_system work with exact fractions, within the calculator’s limits on the size of a number. eigenvalues, qr, lu and svd work in binary floating point, so their answers are close approximations. Their iterations stop after 10,000 steps.

Matrices whose rows are nearly dependent, such as [[1, 1], [1, 1.0000001]], are called ill-conditioned. Small rounding errors grow large when you work with them, so decompositions and eigenvalues of such matrices can lose accuracy, and very extreme cases can exceed the numeric limits and give an error.

The full rules are in the advanced mathematics reference.

Putting it together

Three receipts from a market stall list how many apples, bananas and oranges were bought and the total paid, but not the individual prices:

calc
# Each row: apples, bananas, oranges
baskets = [[2, 3, 1], [1, 2, 3], [4, 1, 2]]
paid = [2.15, 2.70, 3.05]
det(baskets) gives 25
prices = solve_system(baskets, paid) gives [0.4, 0.25, 0.6]
# Check the prices against every receipt
baskets * transpose([prices]) gives [[2.15], [2.7], [3.05]]
# What would 5 apples, 2 bananas and 4 oranges cost?
dot([5, 2, 4], prices) gives 4.9

The non-zero determinant confirms the receipts are enough to find one set of prices: apples at 40p, bananas at 25p and oranges at 60p. Multiplying the baskets by the prices as a column reproduces the totals, and dot prices a new basket at £4.90.