TensorSpace Notesnumerical computing, written down

Shapes and broadcasting

Updated October 2026

Broadcasting lets an operation combine arrays of different shapes without writing a loop. The rule is short, and most surprises come from forgetting that it applies silently.

The rule

Write the two shapes one above the other, aligned on the right. Working from the last dimension backwards, each pair of sizes must either match, or one of them must be 1. A missing dimension counts as 1. The result takes the larger size in each position.

LeftRightResult
(3, 1)(4,)(3, 4)
(5, 3, 2)(3, 1)(5, 3, 2)
(2, 3)(2,)error: 3 and 2 differ
import numpy as np

a = np.arange(3).reshape(3, 1)   # shape (3, 1)
b = np.arange(4)                 # shape (4,)
(a + b).shape                    # (3, 4)

No data is copied to do this. Conceptually the size-1 dimension is stretched, but in memory the same values are reused with a stride of zero.

A common bug

The third row in the table fails loudly, which is the good case. The bad case is one that succeeds:

y = np.zeros(100)          # shape (100,)
pred = np.zeros((100, 1))  # shape (100, 1)
(y - pred).shape           # (100, 100)

A mean squared error computed on that difference still returns one number, so nothing looks wrong until the model stops learning. Two habits catch this early: assert shapes at function boundaries, and flatten or add dimensions explicitly with pred.squeeze(-1) or y[:, None].

Adding dimensions on purpose

Pairwise distances are the classic deliberate use. With x of shape (n, d), the expression x[:, None, :] - x[None, :, :] has shape (n, n, d), holding every difference. Summing the squares over the last axis gives the distance matrix without an explicit loop. The memory cost is real, though: the intermediate array holds n² rows, so large inputs need chunking.

Memory layout →