Skip to content

Affine Spaces

Everything so far insisted on passing through the origin. Subspaces must contain 0\mathbf{0}; linear mappings must send 0\mathbf{0} to 0\mathbf{0}. That insistence bought a great deal of structure, and it also excluded most of the objects you actually care about: a regression line with a nonzero intercept, a decision boundary that is not through the origin, a neural network layer with a bias.

This section relaxes it, in the smallest possible way: take a subspace and slide it.

  • What an affine subspace is — support point plus direction space — and why it is not a subspace.
  • The parametric equation, and why the representation is unique once you fix an ordered basis.
  • Lines, planes and hyperplanes, and the dimension counting behind each.
  • Why the solution set of an inhomogeneous system is exactly an affine subspace.
  • Affine mappings, and the fact that every one is a linear mapping followed by a translation.

Fit a straight line to data and you get y=wx+by = wx + b. If b0b \neq 0 the line does not pass through the origin, so the set of points on it is not a subspace. It fails the very first test: the origin is not on it.

But it is only barely not a subspace. Take the line y=wxy = wx through the origin — that is a subspace, a one-dimensional one — and slide it up by bb. Same direction, shifted position. That is the entire construction, and the terminology follows it:

  • the direction space is the subspace you started with,
  • the support point is how far you slid it.
diagram Diagram mermaid

Let VV be a vector space, x0V\mathbf{x}_0 \in V, and UVU \subseteq V a subspace. The subset

L=x0+U:={x0+u:uU}={vV:uU,  v=x0+u}VL = \mathbf{x}_0 + U := \{\mathbf{x}_0 + \mathbf{u} : \mathbf{u} \in U\} = \{\mathbf{v} \in V : \exists\mathbf{u} \in U,\; \mathbf{v} = \mathbf{x}_0 + \mathbf{u}\} \subseteq V

is an affine subspace of VV, also called a linear manifold. UU is the direction or direction space, and x0\mathbf{x}_0 is the support point.

The definition excludes 0\mathbf{0} whenever x0U\mathbf{x}_0 \notin U. So an affine subspace is not a linear subspace in that case — the book says so explicitly, and it is the whole distinction.

If L=x0+UL = \mathbf{x}_0 + U is kk-dimensional and (b1,,bk)(\mathbf{b}_1,\dots,\mathbf{b}_k) is an ordered basis of UU, then every xL\mathbf{x} \in L can be uniquely described as

x=x0+λ1b1++λkbk\mathbf{x} = \mathbf{x}_0 + \lambda_1\mathbf{b}_1 + \cdots + \lambda_k\mathbf{b}_k

with λ1,,λkR\lambda_1,\dots,\lambda_k \in \mathbb{R}. This is the parametric equation of LL with directional vectors b1,,bk\mathbf{b}_1,\dots,\mathbf{b}_k and parameters λ1,,λk\lambda_1,\dots,\lambda_k.

Uniqueness here is §2.6’s uniqueness-of-coordinates, inherited: once x0\mathbf{x}_0 and the ordered basis are fixed, the parameters are determined. Change the support point and every λ\lambda shifts, but for a given choice there is exactly one answer.

kknameparametric equationin Rn\mathbb{R}^n
1liney=x0+λx1\mathbf{y} = \mathbf{x}_0 + \lambda\mathbf{x}_1needs a support point and 1 direction
2planey=x0+λ1x1+λ2x2\mathbf{y} = \mathbf{x}_0 + \lambda_1\mathbf{x}_1 + \lambda_2\mathbf{x}_22 linearly independent directions
n1n-1hyperplaney=x0+i=1n1λixi\mathbf{y} = \mathbf{x}_0 + \sum_{i=1}^{n-1}\lambda_i\mathbf{x}_in1n-1 independent directions

The hyperplane row is the one worth internalising, because “hyperplane” is used constantly in machine learning and its dimension is relative:

  • In R2\mathbb{R}^2, a hyperplane is a line.
  • In R3\mathbb{R}^3, a hyperplane is a plane.
  • In R1000\mathbb{R}^{1000}, a hyperplane is a 999-dimensional object nobody can picture.

A hyperplane is always one dimension short of the ambient space, which is exactly why it divides it into two halves. That is what makes it a decision boundary, and Chapter 12 builds support vector machines on precisely this — the book even notes in this section that Chapter 12 will refer to such a subspace as a hyperplane.

Inhomogeneous systems are affine subspaces

Section titled “Inhomogeneous systems are affine subspaces”

The connection that makes this section retroactively explain §2.3. For ARm×n\mathbf{A} \in \mathbb{R}^{m\times n} and bRm\mathbf{b} \in \mathbb{R}^m, the solution set of Ax=b\mathbf{A}\mathbf{x} = \mathbf{b} is either empty or an affine subspace of Rn\mathbb{R}^n of dimension nrk(A)n - \operatorname{rk}(\mathbf{A}).

That is §2.3’s particular-plus-general decomposition, renamed:

xpsupport point+ker(A)direction space\underbrace{\mathbf{x}_p}_{\text{support point}} + \underbrace{\ker(\mathbf{A})}_{\text{direction space}}

And the converse holds too: in Rn\mathbb{R}^n, every kk-dimensional affine subspace is the solution of an inhomogeneous system Ax=b\mathbf{A}\mathbf{x} = \mathbf{b} with rk(A)=nk\operatorname{rk}(\mathbf{A}) = n - k. Affine subspaces and inhomogeneous systems are two descriptions of one thing, exactly as subspaces and homogeneous systems were in §2.4.

Two special cases worth naming:

  • The solution of a single equation λ1x1++λnxn=b\lambda_1x_1 + \cdots + \lambda_nx_n = b with not all λi\lambda_i zero is a hyperplane in Rn\mathbb{R}^n. One equation removes one dimension.
  • A homogeneous system’s solution set is a subspace, which the book notes can be thought of as “a special affine space with support point x0=0\mathbf{x}_0 = \mathbf{0}”.

So subspaces are affine subspaces that happen to be centred. The general notion is the affine one.

For vector spaces V,WV, W, a linear mapping Φ:VW\Phi : V \to W and a vector aW\mathbf{a} \in W, the mapping

ϕ:VW,xa+Φ(x)\phi : V \to W, \qquad \mathbf{x} \mapsto \mathbf{a} + \Phi(\mathbf{x})

is an affine mapping from VV to WW, and a\mathbf{a} is the translation vector.

Three properties:

  • Every affine mapping is the composition of a linear mapping and a translation: ϕ=τΦ\phi = \tau \circ \Phi, and the two are uniquely determined.
  • The composition ϕϕ\phi' \circ \phi of affine mappings is affine.
  • Affine mappings keep the geometric structure invariant, and preserve dimension and parallelism.

A line in R2\mathbb{R}^2. Take x0=(1.5,1.4)\mathbf{x}_0 = (-1.5, 1.4)^\top and direction u=(1.6,0.7)\mathbf{u} = (1.6, 0.7)^\top. The parametric equation is y=x0+λu\mathbf{y} = \mathbf{x}_0 + \lambda\mathbf{u}, so

λ\lambday\mathbf{y}
2-2(4.7,0.0)(-4.7, 0.0)
1-1(3.1,0.7)(-3.1, 0.7)
00(1.5,1.4)(-1.5, 1.4) — the support point
11(0.1,2.1)(0.1, 2.1)
22(1.7,2.8)(1.7, 2.8)

Is 0\mathbf{0} on this line? We would need λ\lambda with 1.5+1.6λ=0-1.5 + 1.6\lambda = 0 and 1.4+0.7λ=01.4 + 0.7\lambda = 0, giving λ=0.9375\lambda = 0.9375 and λ=2\lambda = -2 respectively. Different values, so no. The line misses the origin and is therefore not a subspace.

Closure fails, concretely. Take the two points at λ=0\lambda = 0 and λ=1\lambda = 1: (1.5,1.4)(-1.5, 1.4) and (0.1,2.1)(0.1, 2.1). Their sum is (1.4,3.5)(-1.4, 3.5). Is that on the line? Solve 1.5+1.6λ=1.4-1.5 + 1.6\lambda = -1.4, giving λ=0.0625\lambda = 0.0625; then the second coordinate would be 1.4+0.7(0.0625)=1.443753.51.4 + 0.7(0.0625) = 1.44375 \neq 3.5. Not on the line. Affine subspaces are not closed under addition, which is the second reason they are not subspaces.

An inhomogeneous system, as an affine subspace. Solve x1+x2+x3=3x_1 + x_2 + x_3 = 3 in R3\mathbb{R}^3. One particular solution is (3,0,0)(3, 0, 0). The direction space is the null space of [1,1,1][1, 1, 1], which is two-dimensional, spanned by (1,1,0)(1,-1,0) and (1,0,1)(1,0,-1). So

L=[300]+λ1[110]+λ2[101]L = \begin{bmatrix}3\\0\\0\end{bmatrix} + \lambda_1\begin{bmatrix}1\\-1\\0\end{bmatrix} + \lambda_2\begin{bmatrix}1\\0\\-1\end{bmatrix}

Dimension nrk=31=2n - \operatorname{rk} = 3 - 1 = 2: a plane in R3\mathbb{R}^3, which is also a hyperplane because 2=312 = 3 - 1. Checking λ1=2,λ2=1\lambda_1 = 2, \lambda_2 = -1: the point is (3+21,2,1)=(4,2,1)(3 + 2 - 1, -2, 1) = (4, -2, 1), and 42+1=34 - 2 + 1 = 3 ✓.

And the affine-to-linear trick. The affine map ϕ(x)=2x+3\phi(x) = 2x + 3 on R\mathbb{R} becomes the linear map

[23][x1]=2x+3\begin{bmatrix}2 & 3\end{bmatrix}\begin{bmatrix}x\\ 1\end{bmatrix} = 2x + 3

on R2\mathbb{R}^2. Check the zero test in the new space: the input (0,1)(0, 1)^\top is not the zero vector of R2\mathbb{R}^2, so nothing is violated — the trick works precisely because the augmented input never is zero.

The violet arrow is the support point; the amber arrow is the direction. The white point slides along the affine line as λ\lambda changes. The dashed grey line is the direction space itself — parallel, and passing through the origin.

sketch A line as support point plus direction p5.js
The violet arrow is the support point x0. The amber arrow is the direction u. The point y = x0 + lambda*u (white) slides along the affine line (solid green) as lambda changes. The dashed grey line is the direction space U through the origin — parallel, but passing through 0.

The two lines are parallel and never meet. The grey one is a subspace — the origin sits on it. The green one is its translate, and no value of λ\lambda ever brings the white point to the origin. That gap is the support point, and it is the whole difference between the two objects.

affine_spaces.py
import numpy as np
 
# ---- a line as support point plus direction --------------------------
x0 = np.array([-1.5, 1.4])
u  = np.array([ 1.6, 0.7])
 
def on_line(y, tol=1e-9):
    """Is y on the affine line x0 + lambda*u? Solve both coordinates and compare."""
    lams = (y - x0) / u
    return np.allclose(lams, lams[0], atol=tol), lams
 
for lam in (-2.0, -1.0, 0.0, 1.0, 2.0):
    print(f"lambda={lam:5}  y = {x0 + lam * u}")
 
print("\nis the origin on the line?", on_line(np.zeros(2))[0],
      " -> the two lambdas disagree:", np.round(on_line(np.zeros(2))[1], 4))
 
# ---- affine subspaces are not closed --------------------------------
p, q = x0 + 0.0 * u, x0 + 1.0 * u
print("\ntwo points on the line:", p, q)
print("their sum:", p + q, " on the line?", on_line(p + q)[0])
print("2*p      :", 2 * p, "        on the line?", on_line(2 * p)[0])
print("-> not closed under addition OR scaling, so not a subspace")
 
# ---- but the DIRECTION space is a subspace --------------------------
d1, d2 = 1.3 * u, -0.4 * u
print("\ndirections", np.round(d1, 3), "and", np.round(d2, 3))
print("their sum is still a multiple of u:",
      np.allclose(np.cross(np.append(d1 + d2, 0), np.append(u, 0)), 0))
 
# ---- an inhomogeneous system IS an affine subspace -----------------
A = np.array([[1.0, 1.0, 1.0]])
b = np.array([3.0])
xp = np.array([3.0, 0.0, 0.0])                  # a particular solution
n1 = np.array([1.0, -1.0, 0.0])                 # null-space basis
n2 = np.array([1.0,  0.0, -1.0])
n, r = A.shape[1], np.linalg.matrix_rank(A)
 
print("\nsolution set of x1+x2+x3 = 3")
print("  particular solution works:", np.allclose(A @ xp, b))
print("  n1, n2 in the null space :", np.allclose(A @ n1, 0), np.allclose(A @ n2, 0))
print("  dimension n - rank       :", n - r, "-> a plane in R^3")
print("  which is a hyperplane    :", (n - r) == n - 1)
pt = xp + 2 * n1 - 1 * n2
print("  a sample point           :", pt, " satisfies the equation:", np.allclose(A @ pt, b))
print("  contains the origin?     :", np.allclose(A @ np.zeros(3), b))
 
# ---- the containment test needs BOTH conditions -------------------
# L = x0 + span(u)  inside  Ltilde = 0 + span(u)?  Directions nest, offset does not.
offset_in_direction = np.allclose(np.cross(np.append(x0 - np.zeros(2), 0),
                                           np.append(u, 0)), 0)
print("\ndirections nest:", True, " offset is a direction:", offset_in_direction)
print("so L is a subset of the direction space?", offset_in_direction)
 
# ---- affine mapping = linear mapping + translation ----------------
W = np.array([[2.0, -1.0], [0.0, 3.0]])
a = np.array([5.0, -2.0])
phi = lambda v: a + W @ v
 
print("\nphi(0) =", phi(np.zeros(2)), " == the translation vector:", np.allclose(phi(np.zeros(2)), a))
x, y = np.array([1.0, 2.0]), np.array([-3.0, 0.5])
print("phi(x+y) == phi(x) + phi(y)?", np.allclose(phi(x + y), phi(x) + phi(y)),
      "  <- fails, so not linear")
print("but phi(x+y) - a == (phi(x)-a) + (phi(y)-a)?",
      np.allclose(phi(x + y) - a, (phi(x) - a) + (phi(y) - a)),
      "  <- the linear part IS linear")
 
# ---- the augmentation trick: affine in R^n = linear in R^(n+1) ---
W_aug = np.c_[W, a]                              # absorb the translation
for v in (x, y, np.zeros(2)):
    v_aug = np.append(v, 1.0)
    print(f"  phi({v}) = {phi(v)}   augmented: {W_aug @ v_aug}")
print("identical:", all(np.allclose(phi(v), W_aug @ np.append(v, 1.0))
                        for v in (x, y, np.zeros(2))))
 
# ---- affine mappings preserve parallelism ------------------------
line_a = [x0 + t * u for t in (-1.0, 0.0, 1.0, 2.0)]
line_b = [x0 + np.array([0.0, 2.0]) + t * u for t in (-1.0, 0.0, 1.0, 2.0)]
img_a = np.array([phi(v) for v in line_a])
img_b = np.array([phi(v) for v in line_b])
dir_a = img_a[1] - img_a[0]
dir_b = img_b[1] - img_b[0]
print("\ntwo parallel lines stay parallel:",
      np.allclose(np.cross(np.append(dir_a, 0), np.append(dir_b, 0)), 0))
text
lambda= -2.0  y = [-4.7  0. ]
lambda= -1.0  y = [-3.1  0.7]
lambda=  0.0  y = [-1.5  1.4]
lambda=  1.0  y = [0.1 2.1]
lambda=  2.0  y = [1.7 2.8]
 
is the origin on the line? False  -> the two lambdas disagree: [ 0.9375 -2.    ]
 
two points on the line: [-1.5  1.4] [0.1 2.1]
their sum: [-1.4  3.5]  on the line? False
2*p      : [-3.   2.8]         on the line? False
-> not closed under addition OR scaling, so not a subspace
 
directions [2.08 0.91] and [-0.64 -0.28]
their sum is still a multiple of u: True
 
solution set of x1+x2+x3 = 3
  particular solution works: True
  n1, n2 in the null space : True True
  dimension n - rank       : 2 -> a plane in R^3
  which is a hyperplane    : True
  a sample point           : [ 4. -2.  1.]  satisfies the equation: True
  contains the origin?     : False
 
directions nest: True  offset is a direction: False
so L is a subset of the direction space? False
 
phi(0) = [ 5. -2.]  == the translation vector: True
phi(x+y) == phi(x) + phi(y)? False   <- fails, so not linear
but phi(x+y) - a == (phi(x)-a) + (phi(y)-a)? True   <- the linear part IS linear
 
  phi([1. 2.]) = [5. 4.]   augmented: [5. 4.]
  phi([-3.   0.5]) = [-1.5 -0.5]   augmented: [-1.5 -0.5]
  phi([0. 0.]) = [ 5. -2.]   augmented: [ 5. -2.]
identical: True
 
two parallel lines stay parallel: True

Four readings.

The origin test gives two different λ\lambdas0.93750.9375 and 2-2 — which is exactly what “not on the line” looks like when you compute it. One coordinate can always be matched; both is the question.

Closure fails on both operations. The sum of two points on the line is off it, and so is twice a point. Two independent reasons this is not a subspace.

The inhomogeneous solution set is a hyperplane, dimension 31=23 - 1 = 2, containing (4,2,1)(4,-2,1) and not containing the origin. §2.3’s general solution, renamed.

The augmentation trick is exact, including at v=0\mathbf{v} = \mathbf{0} where the affine map returns the translation vector and the augmented linear map returns the same thing — because the augmented input (0,0,1)(0,0,1) is not the zero vector of R3\mathbb{R}^3.

figure One translation, two failures matplotlib
Two panels. Left: a line through the origin with two points and their sum all lying on it. Right: the same line translated upwards, with two points on it whose sum lies clearly off it, and the origin marked as not belonging. Two panels. Left: a line through the origin with two points and their sum all lying on it. Right: the same line translated upwards, with two points on it whose sum lies clearly off it, and the origin marked as not belonging.
Sliding a subspace breaks both closure and the zero test at once. The direction is untouched, which is why the two lines stay parallel.
figure A hyperplane is one dimension short, which is why it separates matplotlib
Scatter of two labelled classes in the plane with a separating line drawn, its normal vector marked, and the two half-spaces shaded. An annotation notes that in n dimensions the same object has n minus one dimensions. Scatter of two labelled classes in the plane with a separating line drawn, its normal vector marked, and the two half-spaces shaded. An annotation notes that in n dimensions the same object has n minus one dimensions.
One equation removes one dimension, leaving an object that cuts the space in two. That is the whole geometric basis of the support vector machine in Chapter 12.

The second figure is why this section exists at all in a machine learning book.

A hyperplane in Rn\mathbb{R}^n is defined by one linear equation, and one equation removes exactly one dimension — hence n1n-1. That deficiency is not a technicality: it is precisely what allows the object to have two sides. Evaluate wx+b\mathbf{w}^\top\mathbf{x} + b at a point and the sign tells you which half-space you are in. A lower-dimensional object, like a line in R3\mathbb{R}^3, does not separate anything — you can walk around it.

So classification by a linear model is: place a hyperplane, and read off signs. The offset bb is what lets the hyperplane sit anywhere rather than being forced through the origin — and forcing it through the origin would make a whole class of problems unsolvable, which is the practical reason the bias term exists.

Chapter 12 then asks the natural follow-up: of all the hyperplanes that separate the data, which is best? Its answer is the one furthest from the nearest points, and §3.8’s projections supply the distance.

subspaceaffine subspace
contains 0\mathbf{0}yes, requiredonly if x0U\mathbf{x}_0 \in U
closed under additionyesno
closed under scalingyesno
descriptionspan[b1,,bk]\operatorname{span}[\mathbf{b}_1,\dots,\mathbf{b}_k]x0+span[b1,,bk]\mathbf{x}_0 + \operatorname{span}[\mathbf{b}_1,\dots,\mathbf{b}_k]
as a solution sethomogeneous Ax=0\mathbf{A}\mathbf{x} = \mathbf{0}inhomogeneous Ax=b\mathbf{A}\mathbf{x} = \mathbf{b}
the associated mappinglinear, Φ(x)\Phi(\mathbf{x})affine, a+Φ(x)\mathbf{a} + \Phi(\mathbf{x})
in deep learningWx\mathbf{W}\mathbf{x}Wx+b\mathbf{W}\mathbf{x} + \mathbf{b} — what layers actually compute
pch.quizTag Check yourself
  1. An affine subspace with support point outside its direction space fails how many of the three subspace conditions?

    pch.quizShowAnswer

    B — All three — no zero vector, and closed under neither addition nor scaling — All three. The numerical check on this page shows the sum of two points on the line is off it, and twice a point is off it, in addition to the origin not being on it.

  2. What is a hyperplane in R to the 1000?

    pch.quizShowAnswer

    B — A 999-dimensional affine subspace — always one dimension less than the ambient space — Hyperplane means codimension one. One linear equation removes exactly one dimension, and that deficiency is what gives the object two sides — which is what makes it a decision boundary.

  3. A neural network layer computes W x plus b. Is it linear?

    pch.quizShowAnswer

    B — No — it is affine, because it sends the zero vector to b rather than to zero — The bias is a translation vector, so the map is affine. The book explicitly notes the literature blurs this. It matters because a stack of affine maps with nothing between them collapses to one affine map.

  4. Why does appending a constant one to the input turn an affine map into a linear one?

    pch.quizShowAnswer

    B — Because the translation can be absorbed as an extra column, and the augmented input is never the zero vector so the zero test is not violated — The bias becomes an extra matrix column acting on the constant one. The augmented map is genuinely linear in the larger space, and the reason no contradiction arises is that the augmented input can never be zero. This is why design matrices carry a column of ones.

Exercise 3 – An inhomogeneous system is a hyperplane

Section titled “Exercise 3 – An inhomogeneous system is a hyperplane”

Exercise 4 – An affine map is not linear

Section titled “Exercise 4 – An affine map is not linear”
  • An affine subspace is a subspace slid off the origin — support point plus direction space — and it is not a subspace whenever the support point lies outside the direction space.
  • It fails all three subspace conditions: no zero vector, and closure under neither addition nor scaling.
  • The support point is not canonical. Any point of the set works, so two different descriptions can name the same set.
  • The containment test needs two conditions — the direction spaces nest, and the offset between support points is itself a direction.
  • The parametric equation gives a unique representation once the support point and an ordered basis of the direction space are fixed.
  • A hyperplane is always one dimension short of the ambient space — a line in the plane, a plane in three dimensions — and that codimension of one is what gives it two sides.
  • The solution set of an inhomogeneous system is exactly an affine subspace of dimension nn minus the rank, and conversely every affine subspace is such a solution set.
  • A single linear equation defines a hyperplane, because one equation removes one dimension.
  • A subspace is an affine subspace with support point at the origin — the affine notion is the more general one.
  • Every affine mapping is a linear mapping followed by a translation, uniquely determined, and affine mappings preserve dimension and parallelism.
  • A “linear layer” computing Wx + b is affine, not linear — the bias is the translation vector, and the book notes the literature blurs this.
  • Appending a constant one absorbs the translation into the matrix, turning an affine map in nn dimensions into a linear map in n+1n+1. That is why design matrices have a column of ones.

Next: Chapter 2 gave you vectors, matrices and the maps between them, all without a notion of length or angle. Chapter 3 adds the geometry — Analytic Geometry.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading