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
dot([1, 2, 3], [4, 5, 6]) gives 32dot([3, 4], [4, -3]) gives 0cross([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 5dot 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:
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:
# Rotate the point (3, 1) by 90° anticlockwiserotate = [[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
M = [[2, 1], [5, 3]]det(M) gives 1inverse(M) gives [[3, -1], [-5, 2]]M * inverse(M) gives [[1, 0], [0, 1]]trace(M) gives 5inverse([[2, 1], [1, 3]]) gives [[0.6, -0.2], [-0.2, 0.4]]det([[1, 2], [2, 4]]) gives 0rank([[1, 2, 3], [2, 4, 6]]) gives 1det, 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:
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
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:
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 ==.
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.
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:
# Each row: apples, bananas, orangesbaskets = [[2, 3, 1], [1, 2, 3], [4, 1, 2]]paid = [2.15, 2.70, 3.05]det(baskets) gives 25prices = solve_system(baskets, paid) gives [0.4, 0.25, 0.6]# Check the prices against every receiptbaskets * 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.9The 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.