Transformations II: Composability, Linearity and Nonlinearity
In the original lecture you grab a "scrubbable" 2×2 matrix and watch it warp a photo of Philip the corgi. Loading a photo isn't possible inside a WebAssembly island, so here we warp the next best thing — a grid — which actually makes the mathematics clearer: you can see exactly where every point goes.
Everything below runs in your browser. Drag the sliders and the grid bends live.
begin
using PlutoUI, WasmMakie
endA linear map is a 2×2 matrix
A linear transformation sends the point $(x, y)$ to
$$\begin{pmatrix} a & b \\ c & d \end{pmatrix}\begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} a\,x + b\,y \\ c\,x + d\,y \end{pmatrix}.$$
Drag the four numbers of the matrix. The light blue lines are a grid being mapped; the two bold lines are where the basis vectors $e_1 = (1,0)$ and $e_2 = (0,1)$ land — they are exactly the columns of the matrix.
| a = | b = |
| c = | d = |
begin
# Draw a square grid of lines after the point map
# (x, y) ↦ (a·x + b·y + nl·sin(3y), c·x + d·y + nl·sin(3x)).
# nl = 0 gives a pure LINEAR map (straight lines stay straight); nl > 0 adds a
# nonlinear wobble so straight lines bend. Pure integer loops + lines!, the
# wasm-stable WasmMakie path (same as the Newton notebook).
function draw_map(a, b, c, d, nl)
span = 1.5
nlines = 7
samples = 40
fig = Figure(size = (430, 430))
ax = Axis(fig[1, 1])
# vertical grid lines (x = const), sampled along y
for gi in 0:nlines
xv = -span + 2.0 * span * gi / nlines
xs = Float64[]
ys = Float64[]
for k in 0:samples
yv = -span + 2.0 * span * k / samples
push!(xs, a * xv + b * yv + nl * sin(3.0 * yv))
push!(ys, c * xv + d * yv + nl * sin(3.0 * xv))
end
lines!(ax, xs, ys)
end
# horizontal grid lines (y = const), sampled along x
for gi in 0:nlines
yv = -span + 2.0 * span * gi / nlines
xs = Float64[]
ys = Float64[]
for k in 0:samples
xv = -span + 2.0 * span * k / samples
push!(xs, a * xv + b * yv + nl * sin(3.0 * yv))
push!(ys, c * xv + d * yv + nl * sin(3.0 * xv))
end
lines!(ax, xs, ys)
end
# images of the basis vectors = the columns of the matrix
lines!(ax, [0.0, a], [0.0, c]) # e₁ = (1,0) ↦ (a, c)
lines!(ax, [0.0, b], [0.0, d]) # e₂ = (0,1) ↦ (b, d)
fig
end
enddraw_map(a, b, c, d, 0.0)Lines stay lines — that's what "linear" means
No matter how you set $a, b, c, d$, every straight grid line maps to another straight line, and the origin stays put. That is the defining property of a linear map. Try to make the grid fold or curve with the sliders above — you can't. A linear map can rotate, scale, shear and flip, but it can never bend a straight line.
A familiar one: rotation
Some matrices have names. A rotation by angle $\theta$ is
$$\begin{pmatrix}\cos\theta & -\sin\theta \\ \sin\theta & \cos\theta\end{pmatrix}.$$
Spin the angle and watch the grid turn rigidly — distances and angles are preserved.
angle θ (degrees) =
let
θ = deg * 3.141592653589793 / 180.0
co = cos(θ)
si = sin(θ)
draw_map(co, -si, si, co, 0.0)
endNonlinearity: when lines bend
Drop the linearity requirement and the world gets wavy. Below we keep the identity linear part but add a nonlinear term $\text{nl}\cdot\sin(3y)$ to $x$ (and symmetrically). At strength $0$ it's the plain grid; turn it up and the straight lines curve — no matrix can do that.
nonlinear strength =
draw_map(1.0, 0.0, 0.0, 1.0, nl)