Multiplying Matrices

Matrix-times-vector transforms exactly one vector at a time — feed it in, get the transformed vector out. But real problems rarely stop at one vector. A game engine needs to transform every vertex of a mesh with thousands of points. A rendering pipeline needs to rotate, then scale, then move an object every single frame — three separate transformations chained together, over and over. Recomputing the whole chain vector by vector, frame by frame, would be painfully slow.

Matrix-times-matrix multiplication solves both problems in one stroke: it can transform a whole batch of vectors at once, and it can also fuse two chained transformations into a single combined matrix, computed just once. Best of all, it's built from nothing more exotic than repeating matrix-times-vector, one column at a time.

It helps to think of a batch of vectors — say, every corner of a triangle — stacked side by side as the columns of one matrix. Transforming the whole shape at once is then just matrix-times-matrix: one multiplication moves every corner in a single step, instead of looping over each vector one at a time by hand.

The recipe: column by column

Think of the second matrix B as a row of vectors stacked side by side — its columns. To build AB, transform each column of B by A, the exact matrix-times-vector operation you already know, and place the results side by side as the columns of the answer:

AB = A\begin{bmatrix} | & & | \\ \vec{b}_1 & \cdots & \vec{b}_p \\ | & & | \end{bmatrix} = \begin{bmatrix} | & & | \\ A\vec{b}_1 & \cdots & A\vec{b}_p \\ | & & | \end{bmatrix}.

There's a shortcut for reading off any single entry without writing out a whole column: take each row of the left matrix and dot it with each column of the right matrix. The entry in row i, column j of the product is

(AB)_{ij} = (\text{row } i \text{ of } A) \cdot (\text{column } j \text{ of } B).

For this to work the rows of A and columns of B must be the same length — so the columns of A must match the rows of B. An (m\times n) times an (n\times p) gives an (m\times p). The shared n is consumed.

Row dot column, cell by cell

Step through the four output cells. Each one lights up a row of A and a column of B, and their dot product fills the answer. Four little dot products build the whole product.

Notice that the highlighted row never touches any entry of B outside its highlighted column, and vice versa — each output cell only ever depends on one row and one column, never the whole matrices at once. That's exactly why the calculation splits so cleanly into four independent little dot products instead of one tangled computation.

Worked example: two 2×2 matrices, entry by entry

Let A = \begin{bmatrix} 2 & 1 \\ 0 & 3 \end{bmatrix} and B = \begin{bmatrix} 1 & 4 \\ 2 & 0 \end{bmatrix}. Every entry is a dot product of a row of A with a column of B:

(AB)_{11} = (2)(1) + (1)(2) = 4, \qquad (AB)_{12} = (2)(4) + (1)(0) = 8, (AB)_{21} = (0)(1) + (3)(2) = 6, \qquad (AB)_{22} = (0)(4) + (3)(0) = 0.

Four dot products, four entries:

AB = \begin{bmatrix} 4 & 8 \\ 6 & 0 \end{bmatrix}.

Worked example: checking one entry two ways

The column method and the row-dot-column shortcut are the exact same calculation seen two ways — let's prove it on the same matrices. The column method transforms column 1 of B, which is \begin{bmatrix}1\\2\end{bmatrix}, by A:

A\begin{bmatrix}1\\2\end{bmatrix} = \begin{bmatrix} (2)(1)+(1)(2) \\ (0)(1)+(3)(2) \end{bmatrix} = \begin{bmatrix}4\\6\end{bmatrix}.

That gives the whole first column of AB in one go: (4, 6). Now check just its top entry with the row-dot-column shortcut — row 1 of A dotted with column 1 of B:

(AB)_{11} = \begin{bmatrix}2 & 1\end{bmatrix}\cdot\begin{bmatrix}1\\2\end{bmatrix} = (2)(1)+(1)(2) = 4.

Both routes land on 4. That's not a coincidence — the row-dot-column rule for a single entry is exactly what's hiding inside the column-by-column recipe; it just lets you compute one number without writing out the whole column first.

Worked example: a 2×2 times a 2×3

Multiplication isn't limited to square matrices. Let A = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} (a 2\times 2) and B = \begin{bmatrix} 1 & 0 & 2 \\ 3 & 1 & 0 \end{bmatrix} (a 2\times 3). The inner dimensions both equal 2, so the product is defined and has shape 2\times 3 — three dot products per row, one per column of B:

AB = \begin{bmatrix} (1)(1)+(2)(3) & (1)(0)+(2)(1) & (1)(2)+(2)(0) \\ (3)(1)+(4)(3) & (3)(0)+(4)(1) & (3)(2)+(4)(0) \end{bmatrix} = \begin{bmatrix} 7 & 2 & 2 \\ 15 & 4 & 6 \end{bmatrix}.

Notice the shape rule in action: the 2's in the middle (the columns of A, the rows of B) matched and vanished, leaving a result built from A's 2 rows and B's 3 columns.

Worked example: fusing two transformations into one

Here's the payoff promised in the hook. Let S = \begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix} stretch x by 2 and y by 3, and let R = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} rotate 90°. Applying S first and then R to a vector \vec{v} means computing R(S\vec{v}) — but matrix multiplication lets us fuse the two steps into a single matrix M = RS up front:

M = RS = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix}\begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix} = \begin{bmatrix} 0 & -3 \\ 2 & 0 \end{bmatrix}.

Now try it on \vec{v} = (1, 1) two different ways. Step by step: S\vec{v} = (2, 3), then R(2,3) = (-3, 2). In one shot with the fused matrix: M\vec{v} = (0(1)+(-3)(1),\ 2(1)+0(1)) = (-3, 2). Same answer, but the fused version only needed one matrix-times-vector instead of two — and if you had a thousand vectors to transform, you'd only pay for computing M once, no matter how many vectors follow.

Why "row dot column"?

It looks arbitrary until you remember what a matrix does. Multiplying by B then by A means transforming a vector twice: A(B\vec{x}). The product matrix AB is the single matrix that does both steps at once — which is exactly why composing transformations is matrix multiplication, and why a deep neural network is just a chain of matrix products.

Turn it around and the row-dot-column rule stops looking arbitrary at all: entry (AB)_{ij} has to be "what happens to component j of the input, once it lands in output slot i, after passing through both transformations" — and that is precisely a dot product between a row of A and a column of B. The rule isn't a memorised recipe; it falls straight out of asking what "do this transformation, then that one" has to mean.

Two habits from ordinary number multiplication will trip you up here:

Every time a 3D game renders a frame, each object on screen typically gets rotated, scaled, and moved — three separate transformation matrices chained together. Multiplying those three matrices together once, at the start, fuses them into a single combined matrix. Then every one of an object's thousands of vertices only needs one matrix-times-vector each frame, instead of three — a huge performance win repeated dozens of times a second, for every object on screen.

Scaled up, matrix multiplication is also the single most expensive computation inside training a modern neural network — layer after layer is nothing but matrices multiplying matrices of numbers, over and over, millions or billions of entries at a time. That's the whole reason specialised chips (GPUs and TPUs) exist: they're built almost entirely to do enormous matrix multiplications as fast as physically possible.