Linear Transformations
Concept
A matrix is a machine that moves vectors. Feed it a vector, and it hands back another one β stretched, spun, flipped, or skewed. Two rules make this a linear transformation: it keeps the origin fixed, and it sends straight lines to straight lines. The punchline of the chapter: every matrix is such a machine, and every such machine is a matrix β and the matrix itself tells you exactly what the machine does, just by reading its columns.
Here is the whole chapter in one concrete example. The matrix
doubles the $x$-coordinate and triples the $y$-coordinate of any vector it touches: the point $(1, 2)$ becomes $(2, 6)$, the point $(4, 5)$ becomes $(8, 15)$. By the end of this chapter you'll be able to look at any $2 \times 2$ matrix and picture what it does to the whole plane β no arithmetic required.
Why This Matters
A matrix doesn't just sit there β it does something. Multiplying a matrix by a vector transforms that vector into a new one: it can stretch it, shrink it, rotate it, flip it, or skew it. Understanding what a matrix does geometrically β not just how to multiply it β is the bridge from algebra to intuition. Later this will let you understand why certain weight matrices in neural networks work the way they do, but for now the goal is simpler: when you see a matrix, you should be able to picture the transformation it represents. And the picture is built from just two vectors: the basis vectors.
Mathematical Notation
Standard basis vectors: the axes, given names
In $\mathbb{R}^2$ the standard basis vectors are the two unit steps along the axes:
$\mathbf{e}_1$ means "one unit to the right", $\mathbf{e}_2$ means "one unit up". (In $\mathbb{R}^3$ there is a third one, $\mathbf{e}_3 = (0, 0, 1)$ β one unit out of the page.) They are the simplest arrows the plane has, and every other vector is built from them:
This sum of scaled vectors is a linear combination: "scale each basis vector, then add the results." The numbers $3$ and $4$ are the coordinates of the point β they say "walk 3 steps right, then 4 steps up."
Why do we care? Because a linear transformation is completely determined by what it does to these two vectors. If you know where $\mathbf{e}_1$ and $\mathbf{e}_2$ land, you know where every vector lands:
The transformation is a machine; the basis vectors are the test inputs that reveal how the machine works.
Linear Transformation Definition
A function $T: \mathbb{R}^n \to \mathbb{R}^m$ is a linear transformation if it satisfies two properties:
- Additivity: $T(\mathbf{u} + \mathbf{v}) = T(\mathbf{u}) + T(\mathbf{v})$
- Homogeneity: $T(\alpha \mathbf{v}) = \alpha T(\mathbf{v})$
Reading the notation $T: \mathbb{R}^n \to \mathbb{R}^m$ β yes: $\mathbb{R}^n$ is the domain (where inputs come from), $\mathbb{R}^m$ the codomain (where outputs land). The order is the arrow's β "from β to", the same convention as every function, $f: X \to Y$: source first, target second. The letters are just names: $n$ = input dimension, $m$ = output dimension, which is why $T$'s matrix is $m \times n$ (one row per output, one column per input). Nothing forces the letter $n$ before $m$ β if the inputs were $m$-dimensional you'd write $T: \mathbb{R}^m \to \mathbb{R}^n$ and the matrix would be $n \times m$. The arrow's direction decides; the letters just name the sizes.
Concretely, take $T(x, y) = (x,\, y,\, x + y)$, mapping $\mathbb{R}^2$ to $\mathbb{R}^3$: the 2D point $(1, 2)$ goes in and the 3D point $(1, 2, 3)$ comes out, through the $3 \times 2$ matrix $\begin{pmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \end{pmatrix}$.
New to multiplying a matrix by a vector? It's the $p = 1$ case of matrix multiplication β see ch03, Multiplying a Matrix by a Vector. Each entry of the result is the dot product of one row of the matrix with the vector: row $[0\ 1]$ of the matrix above, dotted with $(1, 2)$, gives $0\cdot1 + 1\cdot2 = 2$ β the middle entry of $(1, 2, 3)$.
Together these give linearity:
These rules are not abstract β they say "you may add first and transform later, or transform first and add later; the answer is the same." Check with the scaling machine $\mathbf{S} = \begin{pmatrix} 2 & 0 \\ 0 & 3 \end{pmatrix}$ using $\mathbf{u} = (1, 2)$ and $\mathbf{v} = (3, 4)$:
Matrix of a Transformation
Every linear transformation $T: \mathbb{R}^n \to \mathbb{R}^m$ is represented by an $m \times n$ matrix $\mathbf{A}$ whose columns are the images of the standard basis vectors:
Concretely: our scaling machine sends $\mathbf{e}_1 = (1, 0)$ to $(2, 0)$ and $\mathbf{e}_2 = (0, 1)$ to $(0, 3)$, so its matrix is
Read the columns and you've read the transformation.
Applying the transformation to a vector $\mathbf{x}$ is matrix-vector multiplication β a linear combination of the columns with weights $x_1, \dots, x_n$:
Concretely:
which is just "scale the coordinates": $(3, 4) \to (6, 12)$, as promised.
Composition
If $S: \mathbb{R}^n \to \mathbb{R}^m$ has matrix $\mathbf{A}$ and $T: \mathbb{R}^m \to \mathbb{R}^p$ has matrix $\mathbf{B}$, then the composition $T \circ S: \mathbb{R}^n \to \mathbb{R}^p$ (apply $S$ first, then $T$) has matrix $\mathbf{B}\mathbf{A}$:
Composition is matrix multiplication β this is why matrix multiplication is defined the way it is.
Concretely, rotate by $90Β°$ and then reflect across the $x$-axis. The point $(1, 0)$ rotates to $(0, 1)$, then reflects to $(0, -1)$. The composed matrix is
and indeed $\begin{pmatrix} 0 & -1 \\ -1 & 0 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 0 \\ -1 \end{pmatrix}$, matching the two-step trip.
Intuition
The unit square as a test object
The unit square β the $1 \times 1$ square with corners $(0,0)$, $(1,0)$, $(1,1)$, $(0,1)$ β is the best way to see a transformation. Watch where its four corners go and you've watched the whole plane move, because the square is just the basis vectors stretched into a shape:
y
|
(0,1) ββββββββββ (1,1)
| |
| |
(0,0) ββββββββββ (1,0) β x
The bottom-left corner is the origin (which never moves), the bottom-right corner is $\mathbf{e}_1 = (1, 0)$, and the top-left corner is $\mathbf{e}_2 = (0, 1)$. Every transformation below is shown by what it does to this square.
Scaling β stretch the grid
The matrix $\mathbf{S} = \begin{pmatrix} 2 & 0 \\ 0 & 3 \end{pmatrix}$ multiplies $x$ by 2 and $y$ by 3. The basis vectors become $\mathbf{e}_1 \to (2, 0)$ and $\mathbf{e}_2 \to (0, 3)$ β the square stretches into a $2 \times 3$ rectangle:
y y
| |
(0,3) ββββββββββ (2,3) |
| | |
| | |
(0,0) ββββββββββ (2,0) β x |
Every point follows the same rule: $(4, 5) \to (8, 15)$. Scale by a negative number and the grid flips as it stretches: $\begin{pmatrix} -1 & 0 \\ 0 & 1 \end{pmatrix}$ sends $(x, y) \to (-x, y)$ β a mirror flip through the $y$-axis.
Rotation β spin the grid
Rotating counter-clockwise by $90Β°$ is the matrix $\mathbf{R} = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix}$. Watch what happens to the basis vectors: $\mathbf{e}_1 = (1, 0)$ becomes $(0, 1)$ β it is $\mathbf{e}_2$ now β and $\mathbf{e}_2 = (0, 1)$ becomes $(-1, 0)$:
before after (90Β° CCW)
y y
| |
(0,1)ββββββ(1,1) (-1,1)ββββββ(0,1)
| | | |
| | | |
(0,0)ββββββ(1,0) (-1,0)ββββββ(0,0) β x
The square is still a square β rotation is a rigid motion, it changes no distances or angles β it's just spun. A point like $(3, 4)$ lands at $(-4, 3)$. Rotating by $45Β°$ instead gives $\begin{pmatrix} \sqrt{2}/2 & -\sqrt{2}/2 \\ \sqrt{2}/2 & \sqrt{2}/2 \end{pmatrix}$, and $\mathbf{e}_1 \to (\sqrt{2}/2, \sqrt{2}/2) \approx (0.7071, 0.7071)$ β one unit along the diagonal.
Reflection β flip the grid
Reflecting across the $x$-axis is $\begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}$: the $x$-axis is the mirror line, so $\mathbf{e}_1$ stays put while $\mathbf{e}_2 \to (0, -1)$:
y
(0,1)ββββββ(1,1)
| |
| | β mirror line: the x-axis
(0,0)ββββββ(1,0)
| |
| |
(0,-1)ββββββ(1,-1)
Every point's height flips sign: $(3, 4) \to (3, -4)$. Reflecting twice returns the original square β flipping a pancake twice puts it back.
Shear β skew the grid
A horizontal shear, $\begin{pmatrix} 1 & 2 \\ 0 & 1 \end{pmatrix}$, slides each row to the right by an amount proportional to its height. The bottom edge stays glued to the $x$-axis ($\mathbf{e}_1 \to (1, 0)$), while the top edge slides: $\mathbf{e}_2 \to (2, 1)$:
before after
y y
(0,1)ββββββ(1,1) (2,1)ββββββ(3,1)
\ \ / /
\ \ / /
(0,0)ββββββ(1,0) (0,0)ββββββ(1,0)
It's the motion of pushing the top of a deck of cards sideways: the square leans into a parallelogram with the same area (nothing was stretched, just slid). A point $(3, 4)$ becomes $(3 + 2 \cdot 4, 4) = (11, 4)$.
Visual summary
Each transformation is fully described by where it sends the basis vectors β those two arrows are the columns of the matrix:
e1 = (1,0) -> (2, 0) (0, 1) (1, 0) (1, 0)
e2 = (0,1) -> (0, 3) (-1, 0) (0, -1) (2, 1)
Scale(2,3) Rotate(90Β°) Reflect x Shear x(2)
Key insight: reading down each column tells you the entire transformation. No need to visualize the whole grid β just look at where the two basis vectors land. This is exactly what transform(&[1.0, 0.0]) and transform(&[0.0, 1.0]) compute in code.
Worked Examples
Example 1: Scale(2,3) β the unit square becomes a rectangle
Take $\mathbf{S} = \begin{pmatrix} 2 & 0 \\ 0 & 3 \end{pmatrix}$ and trace all four corners of the unit square:
| Corner | Image | What happened |
|---|---|---|
| $(0, 0)$ | $(0, 0)$ | origin stays put |
| $(1, 0)$ | $(2, 0)$ | $\mathbf{e}_1$ doubled |
| $(1, 1)$ | $(2, 3)$ | both coordinates scaled |
| $(0, 1)$ | $(0, 3)$ | $\mathbf{e}_2$ tripled |
The square became a $2 \times 3$ rectangle, so its area went from $1$ to $2 \cdot 3 = 6$. That factor β "by how much does the transformation stretch area?" β is the determinant of the matrix, the subject of ch07.
Example 2: Rotate 90Β° counter-clockwise β e1 becomes e2
$\mathbf{R} = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix}$. Follow the point $(3, 4)$ step by step, column by column:
So $(3, 4)$ β "3 right, 4 up" β spins to $(-4, 3)$ β "4 left, 3 up". A quarter-turn of the whole plane. The same number appears in the code test test_rotation_90_degrees: $\mathbf{e}_1 = (1, 0)$ becomes $(0, 1)$.
Example 3: Reflect across the x-axis β a triangle flips
$\mathbf{F} = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}$ flips each point over the $x$-axis: $(x, y) \to (x, -y)$. Take the triangle with vertices $(1, 1)$, $(3, 2)$, $(2, 4)$:
| Vertex | Image |
|---|---|
| $(1, 1)$ | $(1, -1)$ |
| $(3, 2)$ | $(3, -2)$ |
| $(2, 4)$ | $(2, -4)$ |
The triangle appears upside-down below the $x$-axis β the mirror image. The code test test_reflect_x checks the same rule on $(3, 4) \to (3, -4)$.
Example 4: Compose rotate-then-reflect β order matters
Apply rotation by $90Β°$ first, then reflection across the $x$-axis, to the point $(1, 0)$:
- Rotate: $(1, 0) \to (0, 1)$
- Reflect: $(0, 1) \to (0, -1)$
The composed matrix is $\mathbf{F}\mathbf{R} = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix} = \begin{pmatrix} 0 & -1 \\ -1 & 0 \end{pmatrix}$, and indeed $\begin{pmatrix} 0 & -1 \\ -1 & 0 \end{pmatrix}(1, 0) = (0, -1)$ β the two-step trip in one multiplication.
Now reverse the order β reflect first, then rotate. Reflect $(1, 0)$ leaves it alone, and rotating gives $(0, 1)$. Different answer! The matrices don't commute: $\mathbf{R}\mathbf{F} = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} \neq \mathbf{F}\mathbf{R}$. This is why composition order matters everywhere in linear algebra β rightmost matrix is applied first. The code test test_composition_rotate_then_reflect verifies both the composed matrix and the two-step application agree.
Rust Implementation
Add a new crate to your workspace:
cd code && cargo new --lib --edition 2024 ch05-linear-transformations
This crate builds on the Matrix struct from ch03. Open code/ch05-linear-transformations/src/lib.rs and start by copying the full Matrix impl from ch03-linear-algebra-matrices/src/lib.rs. Then add the following methods and free functions:
/// ββ Linear transformations ββ
impl Matrix {
/// Apply this transformation to a vector (as a column matrix).
///
/// If this matrix is mΓn, the input vector must have n components,
/// and the result has m components.
///
/// Mathematically: T(v) = A * v where A is this matrix.
pub fn transform(&self, v: &[f64]) -> Vec<f64> {
assert_eq!(
self.cols,
v.len(),
"Matrix cols {} doesn't match vector length {}",
self.cols,
v.len()
);
let mut result = vec![0.0; self.rows];
for i in 0..self.rows {
let mut sum = 0.0;
for j in 0..self.cols {
sum += self.get(i, j) * v[j];
}
result[i] = sum;
}
result
}
}
/// ββ 2D transformation factory functions ββ
/// Create a 2D scaling matrix: Scale(sx, sy).
///
/// [ sx 0 ]
/// [ 0 sy ]
pub fn scale_2d(sx: f64, sy: f64) -> Matrix {
Matrix::new(vec![sx, 0.0, 0.0, sy], 2, 2)
}
/// Create a 2D rotation matrix (counter-clockwise by `angle` radians).
///
/// [ cosΞΈ -sinΞΈ ]
/// [ sinΞΈ cosΞΈ ]
pub fn rotate_2d(angle: f64) -> Matrix {
let c = angle.cos();
let s = angle.sin();
Matrix::new(vec![c, -s, s, c], 2, 2)
}
/// Create a 2D reflection matrix across the x-axis.
///
/// [ 1 0 ]
/// [ 0 -1 ]
pub fn reflect_x_2d() -> Matrix {
Matrix::new(vec![1.0, 0.0, 0.0, -1.0], 2, 2)
}
/// Create a 2D reflection matrix across the y-axis.
///
/// [ -1 0 ]
/// [ 0 1 ]
pub fn reflect_y_2d() -> Matrix {
Matrix::new(vec![-1.0, 0.0, 0.0, 1.0], 2, 2)
}
/// Create a 2D shear matrix (horizontal shear).
///
/// [ 1 shx ]
/// [ 0 1 ]
pub fn shear_x_2d(shx: f64) -> Matrix {
Matrix::new(vec![1.0, shx, 0.0, 1.0], 2, 2)
}
/// Create a 2D shear matrix (vertical shear).
///
/// [ 1 0 ]
/// [ shy 1 ]
pub fn shear_y_2d(shy: f64) -> Matrix {
Matrix::new(vec![1.0, 0.0, shy, 1.0], 2, 2)
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < 1e-10
}
#[test]
fn test_scale_basis_vectors() {
let s = scale_2d(2.0, 3.0);
// e1 = (1, 0) β (2, 0)
let r1 = s.transform(&[1.0, 0.0]);
assert!(approx_eq(r1[0], 2.0) && approx_eq(r1[1], 0.0));
// e2 = (0, 1) β (0, 3)
let r2 = s.transform(&[0.0, 1.0]);
assert!(approx_eq(r2[0], 0.0) && approx_eq(r2[1], 3.0));
}
#[test]
fn test_scale_point() {
let s = scale_2d(2.0, 3.0);
let r = s.transform(&[4.0, 5.0]);
// (4, 5) β (8, 15)
assert!(approx_eq(r[0], 8.0) && approx_eq(r[1], 15.0));
}
#[test]
fn test_rotation_90_degrees() {
// Rotating (1, 0) by 90Β° CCW should give (0, 1)
let r = rotate_2d(std::f64::consts::FRAC_PI_2);
let v = r.transform(&[1.0, 0.0]);
assert!(approx_eq(v[0], 0.0) && approx_eq(v[1], 1.0));
}
#[test]
fn test_rotation_180_degrees() {
// Rotating (1, 0) by 180Β° should give (-1, 0)
let r = rotate_2d(std::f64::consts::PI);
let v = r.transform(&[1.0, 0.0]);
assert!(approx_eq(v[0], -1.0) && approx_eq(v[1], 0.0));
}
#[test]
fn test_reflect_x() {
let r = reflect_x_2d();
let v = r.transform(&[3.0, 4.0]);
// (3, 4) β (3, -4)
assert!(approx_eq(v[0], 3.0) && approx_eq(v[1], -4.0));
}
#[test]
fn test_reflect_y() {
let r = reflect_y_2d();
let v = r.transform(&[3.0, 4.0]);
// (3, 4) β (-3, 4)
assert!(approx_eq(v[0], -3.0) && approx_eq(v[1], 4.0));
}
#[test]
fn test_composition_rotate_then_reflect() {
let rotate = rotate_2d(std::f64::consts::FRAC_PI_2); // 90Β° CCW
let reflect = reflect_x_2d();
// Compose: reflect β rotate (rotate first, then reflect)
// Composition matrix = reflect * rotate
let composed = reflect.multiply(&rotate);
// Apply to (1, 0):
// rotate(1, 0) = (0, 1)
// reflect(0, 1) = (0, -1)
let v = composed.transform(&[1.0, 0.0]);
assert!(approx_eq(v[0], 0.0) && approx_eq(v[1], -1.0));
// Apply sequentially to verify
let step1 = rotate.transform(&[1.0, 0.0]);
let step2 = reflect.transform(&step1);
assert!(approx_eq(step2[0], 0.0) && approx_eq(step2[1], -1.0));
}
#[test]
fn test_composition_scale_then_rotate() {
// Scale by (2, 1) then rotate 45Β°
let s = scale_2d(2.0, 1.0);
let r = rotate_2d(std::f64::consts::FRAC_PI_4);
let composed = r.multiply(&s);
// Apply to (1, 0):
// scale(1, 0) = (2, 0)
// rotate(2, 0) = (2cos45Β°, 2sin45Β°) = (β2, β2) β (1.4142, 1.4142)
let v = composed.transform(&[1.0, 0.0]);
let sqrt2 = std::f64::consts::FRAC_1_SQRT_2 * 2.0; // = 2/β2 = β2
assert!(approx_eq(v[0], sqrt2) && approx_eq(v[1], sqrt2));
}
#[test]
fn test_shear_x() {
let sh = shear_x_2d(2.0);
let v = sh.transform(&[3.0, 4.0]);
// (3, 4) β (3 + 2*4, 4) = (11, 4)
assert!(approx_eq(v[0], 11.0) && approx_eq(v[1], 4.0));
}
#[test]
fn test_identity_transformation() {
let i = Matrix::identity(3);
let v = i.transform(&[5.0, 6.0, 7.0]);
assert!(approx_eq(v[0], 5.0) && approx_eq(v[1], 6.0) && approx_eq(v[2], 7.0));
}
#[test]
fn test_double_reflection_is_identity() {
// Reflect across x-axis twice β back to original
let r = reflect_x_2d();
let double = r.multiply(&r); // (reflect β reflect) = identity
let v = double.transform(&[123.0, -456.0]);
assert!(approx_eq(v[0], 123.0) && approx_eq(v[1], -456.0));
}
#[test]
fn test_rotation_then_inverse() {
// Rotating by ΞΈ then by -ΞΈ gives identity
let theta = 0.7;
let r = rotate_2d(theta);
let r_inv = rotate_2d(-theta);
// Compose: r_inv β r = identity
// (since rotation matrices satisfy R(-ΞΈ) = R(ΞΈ)^T = R(ΞΈ)^{-1})
let composed = r_inv.multiply(&r);
let v = composed.transform(&[3.0, 4.0]);
assert!(approx_eq(v[0], 3.0) && approx_eq(v[1], 4.0));
}
}
Run the tests:
cargo test -p ch05-linear-transformations
You should see:
running 12 tests
test tests::test_composition_rotate_then_reflect ... ok
test tests::test_composition_scale_then_rotate ... ok
test tests::test_double_reflection_is_identity ... ok
test tests::test_identity_transformation ... ok
test tests::test_reflect_x ... ok
test tests::test_reflect_y ... ok
test tests::test_rotation_180_degrees ... ok
test tests::test_rotation_90_degrees ... ok
test tests::test_rotation_then_inverse ... ok
test tests::test_scale_basis_vectors ... ok
test tests::test_scale_point ... ok
test tests::test_shear_x ... ok
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered
Walkthrough
-
transform(&self, v: &[f64])β Matrix-vector multiplication. This is the core operation of a linear transformation: for each row of the matrix, compute the dot product with the input vector. The result is a new vector. -
Factory functions β
scale_2d,rotate_2d,reflect_x_2d,reflect_y_2d,shear_x_2d,shear_y_2dare free functions (not methods) that construct 2Γ2 matrices. They're defined outsideimpl Matrixbecause they don't operate on an existing matrix. -
Composition is multiplication β Applying transformation $\mathbf{B}$ after $\mathbf{A}$ means computing $\mathbf{B}(\mathbf{A}\mathbf{x})$, which is $(\mathbf{B}\mathbf{A})\mathbf{x}$. The matrix for the composed transformation is $\mathbf{B}\mathbf{A}$ β matrix multiplication in action. This is tested in
test_composition_rotate_then_reflectandtest_composition_scale_then_rotate. -
Column interpretation β The first column of a transformation matrix is where $\mathbf{e}_1 = (1, 0)$ goes. For
scale_2d(2, 3), the first column is $(2, 0)$ β $\mathbf{e}_1$ is scaled by 2 along x. The second column is $(0, 3)$ β $\mathbf{e}_2$ is scaled by 3 along y. This is verified intest_scale_basis_vectors. -
Inverse transformations β Some transformations can be undone. Rotating by $-\theta$ undoes a rotation by $\theta$. Reflecting twice gives back the original. These are tested in
test_double_reflection_is_identityandtest_rotation_then_inverse.
Verification
| Test | What it checks | Invariant |
|---|---|---|
test_scale_basis_vectors | Columns of scale matrix = scaled basis vectors | $A\mathbf{e}_i = i\text{th column}$ |
test_scale_point | Scale transforms a point correctly | $(4, 5) \to (8, 15)$ |
test_rotation_90_degrees | 90Β° rotation of (1, 0) β (0, 1) | $\mathbf{e}_1 \to \mathbf{e}_2$ |
test_rotation_180_degrees | 180Β° rotation of (1, 0) β (-1, 0) | $\mathbf{e}_1 \to -\mathbf{e}_1$ |
test_reflect_x | Reflection across x-axis flips y | $(3, 4) \to (3, -4)$ |
test_reflect_y | Reflection across y-axis flips x | $(3, 4) \to (-3, 4)$ |
test_shear_x | Horizontal shear shifts by height | $(3, 4) \to (11, 4)$ |
test_composition_rotate_then_reflect | Composed matrix = sequential application | $\mathbf{BAx} = \mathbf{B}(\mathbf{Ax})$ |
test_composition_scale_then_rotate | Two-step composition with mixed types | $\mathbf{BAx} = \mathbf{B}(\mathbf{Ax})$ |
test_double_reflection_is_identity | Reflect twice = do nothing | $R_x \circ R_x = I$ |
test_rotation_then_inverse | Rotate by ΞΈ then -ΞΈ = identity | $R_{-\theta} R_{\theta} = I$ |
Key Takeaways
- A linear transformation is any function $T$ satisfying $T(\alpha\mathbf{u} + \beta\mathbf{v}) = \alpha T(\mathbf{u}) + \beta T(\mathbf{v})$ β it preserves lines and the origin.
- The standard basis vectors $\mathbf{e}_1 = (1, 0)$, $\mathbf{e}_2 = (0, 1)$ are the axes' unit steps, and every vector is a linear combination of them: $(3, 4) = 3\mathbf{e}_1 + 4\mathbf{e}_2$.
- Every linear transformation is represented by a matrix whose columns are where the basis vectors land β read the columns, and you've read the transformation (Scale(2,3) has columns $(2,0)$ and $(0,3)$).
- Applying a transformation is matrix-vector multiplication β a linear combination of the columns: $\mathbf{S}(3,4) = 3(2,0) + 4(0,3) = (6,12)$.
- Composing transformations is matrix multiplication β the order matters: rotate-then-reflect sends $(1, 0)$ to $(0, -1)$, while reflect-then-rotate sends it to $(0, 1)$.
- Common 2D transformations are simple 2Γ2 matrices with clear geometry: scale (stretch the grid), rotate (spin it, distances unchanged), reflect (flip it over an axis), shear (skew it, area unchanged).
- The identity matrix does nothing; an inverse transformation undoes a transformation.