Snapshot / Computational Thinking Repo ↗

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
end

A 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 = 1.0b = 0.0
c = 0.0d = 1.0
draw_map (generic function with 1 method)
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
end
draw_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) = 30

let
    θ = deg * 3.141592653589793 / 180.0
    co = cos(θ)
    si = sin(θ)
    draw_map(co, -si, si, co, 0.0)
end

Nonlinearity: 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 = 0.3

draw_map(1.0, 0.0, 0.0, 1.0, nl)
Dict{Symbol, Any}(:diagnostics => Dict{Symbol, Any}[Dict(:line => 14, :from => 653, :message => "unterminated string literal", :severity => "error", :to => 653, :source => "JuliaSyntax.jl")], :source => "md\"\"\"\n# Summary\n\n- A **linear transformation** of the plane is exactly a **2×2 matrix**; it acts by\n \$(x,y) \\mapsto (a x + b y,\\; c x + d y)\$.\n- The **columns** of the matrix are where the basis vectors \$e_1, e_2\$ land — that's\n all the information the map contains.\n- **Linear** maps send straight lines to straight lines and fix the origin: rotation,\n scaling, shear, reflection. They *compose* by multiplying matrices.\n- **Nonlinear** maps can bend lines — strictly more expressive, and the reason image\n warps, neural nets and curved geometry are interesting.\n\nEvery grid above is a live WebAssembly island recomputed as you drag the sliders.\n\"\"")