Lesson 48 of 180, in Analytic Geometry
Orthogonal Complements Capture What Is Left
A model may explain an observation with a few learned directions and still leave something over. How can it tell whether that remainder is a genuinely new direction, rather than a hidden copy of what the model already explained?
The orthogonal complement gives a precise answer. It collects every direction that has zero inner product with an entire subspace, not merely with one chosen vector. For a feature plane in three dimensions, the complement is the line of normal directions. That line gives a clean home for a residual component: it has no linear overlap with the plane, while it can still have nonzero size and useful information.
By the end of this lesson, you will be able to describe an orthogonal complement, check its dimension and basis, interpret a plane’s normal vector, and use an orthonormal basis in NumPy to separate an explained component from a residual.
Learning outcome: Describe the directions orthogonal to a subspace.
This lesson builds on Lesson 045: Orthogonality Means No Linear Overlap, which made zero inner product a geometric test, and Lesson 036: Kernel and Image Explain Loss and Reach, which connected subspaces to information a linear map can lose or reach. Section 3.6 of the official Mathematics for Machine Learning book is the curricular reference. The definitions, numbers, visual, implementation, AI connection, and assessments here are original.
A subspace has a perpendicular remainder
Let be a -dimensional Euclidean vector space. Let be an -dimensional subspace of . A subspace is a collection of vectors that is closed under addition and scalar multiplication; it contains the zero vector and all linear combinations of its basis directions.
Three facts make the complement useful:
-
If has dimension inside a -dimensional space, then . Each independent direction in contributes one orthogonality constraint.
-
and meet only at the zero vector: . A nonzero vector cannot be both inside a subspace and perpendicular to every vector in that same subspace, because it would have a nonzero inner product with itself.
-
In the standard Euclidean geometry, every has one decomposition
The first term is the part described by the subspace. The second is what the subspace cannot explain through its own directions.
The dimension rule is a counting guide, not a shortcut around the definition. The complement of a plane in is one-dimensional, but the complement of a line in is a two-dimensional plane. “The orthogonal complement” is not always a single arrow.
A plane and its normal line
Consider the plane
An arbitrary vector in this plane is
So the plane consists of vectors satisfying . To find a normal, look for that is orthogonal to both basis vectors:
Choosing gives , so one normal is
Every scalar multiple of is also orthogonal to both and . Because has dimension and the ambient space has dimension , the complement has dimension ; therefore
The diagram uses a plane for and an arrow for its normal direction. Its table is a complete numerical alternative to the drawing: it records both orthogonality tests and the dimension conclusion without relying on color, perspective, or JavaScript.
Static geometry visual
A two-dimensional subspace and its one-dimensional complement
The dark arrows span U. The orange arrow is n = (−1, −1, 1), a normal direction whose dot product with both basis vectors is zero.
| Vector | Dot-product test | Interpretation |
|---|---|---|
| b₁ = (1, 0, 1) | nᵀb₁ = −1 + 1 = 0 | A basis direction in U |
| b₂ = (0, 1, 1) | nᵀb₂ = −1 + 1 = 0 | A basis direction in U |
| n = (−1, −1, 1) | nᵀn = 1 + 1 + 1 = 3 | n spans U⊥ |
| x = (2, 1, 4) | nᵀx = −2 − 1 + 4 = 1 | x has a nonzero normal component |
The plane contains every linear combination of b₁ and b₂. The complement is the whole normal line, including both directions and the zero vector—not just the single arrow drawn here.
The visual uses an oblique drawing of three-dimensional space, so the screen angle is only a representation of the geometry. The algebra carries the meaning: the normal is perpendicular to every vector in , because every vector in is a linear combination of and .
Worked example: split an observation into explained signal and residual
The normal is a useful direction, but it is not unit length. To read coordinates cleanly, first use the orthonormal directions from the previous lessons. Gram–Schmidt applied to gives the following orthonormal basis for :
The unit normal is
These three directions are pairwise orthonormal, and span while spans . Take the observation
Because the basis is orthonormal, each coefficient is one inner product.
This calculation turns “explained versus left over” into a geometric statement. The word explained means “reconstructed from the selected subspace,” not “all useful information.” The residual can contain another feature, noise, a nonlinear pattern, or a signal that a later model should inspect.
Why the decomposition is unique
The uniqueness follows from the intersection fact rather than from a lucky choice of coordinates. Suppose the same vector had two decompositions:
Rearrange:
The left side lies in because a subspace is closed under subtraction. The right side lies in for the same reason. Therefore this common vector lies in , which contains only . So and .
The same proof explains why the two pieces do not double-count a direction. A nonzero vector cannot be both an explained direction in and a residual direction in .
NumPy keeps the dimensions visible
Store the orthonormal basis vectors as the columns of a matrix . The observation has shape , the coordinate vector has shape , and the reconstructed explained vector returns to shape .
The code below uses numpy.asarray
to make the inputs arrays, the matrix-multiplication operator documented by
numpy.matmul
for the matrix products, numpy.linalg.norm
for the Euclidean residual size, and numpy.allclose
for tolerance-aware checks. The explicit atol and
rtol=0 make the intended zero comparisons visible; NumPy warns that
a default absolute tolerance may be inappropriate near zero.
import numpy as np
def split_against_orthonormal_basis(x, Q, *, atol=1e-12):
x = np.asarray(x, dtype=float)
Q = np.asarray(Q, dtype=float)
if x.ndim != 1 or Q.ndim != 2 or Q.shape[0] != x.shape[0]:
raise ValueError('x must be (D,) and Q must be (D, M)')
gram = Q.T @ Q
if not np.allclose(
gram, np.eye(Q.shape[1]), atol=atol, rtol=0.0
):
raise ValueError('Q columns must be orthonormal')
coordinates = Q.T @ x
explained = Q @ coordinates
residual = x - explained
return coordinates, explained, residual
Q = np.array([
[1 / np.sqrt(2), -1 / np.sqrt(6)],
[0.0, 2 / np.sqrt(6)],
[1 / np.sqrt(2), 1 / np.sqrt(6)],
])
x = np.array([2.0, 1.0, 4.0])
coordinates, explained, residual = split_against_orthonormal_basis(x, Q)
normal = np.array([-1.0, -1.0, 1.0]) / np.sqrt(3)
print(np.round(Q.T @ Q, 6))
print(np.round(coordinates, 6))
print(np.round(explained, 6))
print(np.round(residual, 6))
print(np.round(np.linalg.norm(residual), 6))
print(np.allclose(Q.T @ residual, 0.0, atol=1e-12, rtol=0.0))
print(np.allclose(residual, (1 / np.sqrt(3)) * normal, atol=1e-12, rtol=0.0))
The verified output is:
[[1. 0.]
[0. 1.]]
[4.242641 1.632993]
[2.333333 1.333333 3.666667]
[-0.333333 -0.333333 0.333333]
0.57735
True
True
The first matrix is the Gram matrix of the columns. Its identity shape confirms unit length and pairwise orthogonality. The final two checks say that the residual is orthogonal to both columns of and lies on the unit normal direction. The function does not search for a nearest point or prove a least-squares fit; it uses an already verified orthonormal basis to expose the two coordinate blocks. That keeps this lesson focused on the complement. A later projection lesson can explain the nearest-point interpretation.
A residual is separated, not erased
In an AI representation, let be a subspace of feature directions selected by a linear encoder, a principal subspace, or a hand-designed feature family. For an input , the decomposition gives:
- : the component expressible by the selected directions;
- : a component invisible to every linear score that only looks along ;
- : the size of what remains under this geometry.
This can improve interpretability. If a linear readout is built only from the columns of , its score cannot obtain a contribution from through an inner product with those columns, because . That is a claim about this subspace, this inner product, and this representation—not a claim that the residual is useless. A nonlinear layer, a different metric, or a different dataset can reveal relationships that the current orthogonal split does not capture.
There are two boundary cases worth keeping close:
- If , then and . Everything is explained by the full space, so the only residual is zero.
- If , then and . Nothing is explained by the zero subspace, so every vector is residual.
The dimension rule also assumes the standard dot product. If a system defines a weighted inner product, “orthogonal” changes with that metric; the ordinary dot-product normal above is not automatically the correct residual direction.
The next lesson extends the same split into the geometry of the nearest point on a subspace. For now, the durable test is simpler: name the ambient space, write a basis for , check zero inner products against that basis, and treat the complement as a subspace of all allowed residual directions.
Check your understanding
Question 1
Let U be an M-dimensional subspace of R^D. What is U⊥, and what dimension should it have?
Show the step-by-step solution
By definition, contains every vector whose inner product with every is zero. It is enough to impose the test on an -vector basis of , because inner products distribute across linear combinations. Those independent orthogonality conditions leave independent directions, so under the usual Euclidean geometry.
Question 2
For U = span{(1, 0, 1), (0, 1, 1)}, test n = (−1, −1, 1) and state U⊥.
Show the step-by-step solution
Test the first basis vector: . Test the second: . Because every vector in is a linear combination of those two basis vectors, is orthogonal to all of . The two basis vectors are independent, so in and . Therefore .
Question 3
Using the same plane, decide whether y = (2, 1, 3) has a residual component in U⊥. Show the test.
Show the step-by-step solution
The plane satisfies . For , , so it already lies in . The normal test says the same thing: . Since is the normal line and the normal coordinate is zero, the residual component is the zero vector.
Question 4
For x = (2, 1, 4), use the worked decomposition to verify that the residual is orthogonal to U but is not zero.
Show the step-by-step solution
The decomposition gives . It is not zero because its Euclidean norm is . Check both basis directions: and . Therefore the residual is a genuine nonzero direction in , not an absence of information.
Question 5
Why is checking a candidate vector against only one vector in U insufficient when U is a plane?
Show the step-by-step solution
Orthogonality to a single vector removes only one linear overlap. A vector can be perpendicular to while still having a nonzero dot product with , so it would not be perpendicular to every combination in the plane. For the candidate to belong to , it must pass the zero-dot-product test for every basis direction of . Passing those basis tests then covers every linear combination by distributivity.
Question 6
In the NumPy code, Q has shape (D, M). What are the shapes of Q.T @ x, Q @ (Q.T @ x), and x − Q @ (Q.T @ x)?
Show the step-by-step solution
has shape and has shape . Its transpose has shape , so has shape : one coefficient per basis direction. Multiplying by that coefficient vector returns shape , which is the explained component. Subtracting two vectors of shape leaves a residual in the original ambient space.
Question 7
What does the allclose check on Q.T @ Q verify, and why is an exact == comparison a poor floating-point test?
Show the step-by-step solution
For column-stored basis vectors, is their Gram matrix. An
identity result means each column has unit norm and distinct columns have
zero inner product, so the columns are orthonormal. Floating-point square
roots and products can leave tiny values such as where
exact arithmetic gives zero. np.allclose compares within
explicit tolerances; the tolerance still needs to be chosen for the scale
and precision of the data rather than accepted blindly.
Question 8
A model labels every residual in U⊥ as useless. Which part of that conclusion follows from the mathematics, and which part overreaches?
Show the step-by-step solution
The valid part is narrow: a residual has zero inner product with every direction in , so a linear readout that only scores those directions cannot recover a contribution from by that same inner product. The claim that is useless overreaches. It can have nonzero norm, encode another linear direction, carry nonlinear structure, or become useful to a later layer or a different metric.