Recipes: CGA & PGA
This page collects small, practical recipes for common tasks in projective and conformal geometric algebra.
All snippets are doctest-able and ASCII-friendly where possible.
Contract
- Input: an algebra and a few multivectors
- Output: a result multivector of interest
- Errors: invalid signatures or division by zero where noted
- Success: example evaluates and type-stable on recent Julia
PGA: reflect a vector in a plane (householder-like)
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3 = basevector(pga,1), basevector(pga,2), basevector(pga,3);
julia> n = e2; # plane normal (unit for simplicity)
julia> v = e1 + 2e2 + 3e3;
julia> R = exp(π * (n ∧ e3) / 2); # 180° around axis orthogonal to plane -> reflection
julia> v_ref = R ≀ v; v_ref isa MultiVector; true
truePGA: intersection of two planes -> line
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3, e0 = basevector(pga,1), basevector(pga,2), basevector(pga,3), basevector(pga,:e0);
julia> plane_xy = e3; # z=0 plane
julia> plane_xz = e2; # y=0 plane
julia> line = plane_xy ∧ plane_xz;
julia> isgrade(line, 2)
truePGA: reflect a vector in a plane
Reflect across the plane z=0 (normal e3). The z component flips.
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3 = basevector(pga,1), basevector(pga,2), basevector(pga,3);
julia> plane = e3; # z=0
julia> v = e1 + e3;
julia> v_ref = plane ≀ v; true
truePGA: line-plane meet -> point
The meet (∨) of a line and a plane yields a point (grade-3 in PGA3D).
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3 = basevector(pga,1), basevector(pga,2), basevector(pga,3);
julia> plane_xy = e3; # z=0
julia> plane_xz = e2; # y=0
julia> line = plane_xy ∧ plane_xz; # x-axis line
julia> plane_yz = e1; # x=0
julia> pt = line ∨ plane_yz;
julia> isgrade(pt, 3)
truePGA: rigid-body motor (rotation + translation)
Compose a small rotation about z with a small translation along x, then apply to a vector.
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3, e0 = basevector(pga,1), basevector(pga,2), basevector(pga,3), basevector(pga,:e0);
julia> B = (π/12) * (e1 ∧ e2); # rotate about z
julia> T = 0.05 * (e0 ∧ e1); # translate along x (translator-like)
julia> M = exp(B + T);
julia> v = e1 + 2e2; # a direction-grade representative
julia> v2 = M ≀ v; v2 isa MultiVector; true
trueCl3: compose two rotations via rotors
julia> using CliffordAlgebras
julia> cl3 = CliffordAlgebra(3);
julia> e1, e2, e3 = cl3.e1, cl3.e2, cl3.e3;
julia> B1 = (π/6) * (e1 ∧ e2);
julia> B2 = (π/7) * (e2 ∧ e3);
julia> R1, R2 = exp(B1), exp(B2);
julia> v = e1 + 2e2 + 3e3;
julia> v_seq = R2 ≀ (R1 ≀ v);
julia> v_comb = (R2 * R1) ≀ v; true
trueSTA: Lorentz boost via bivector exponential
Boost along x with rapidity 0.1 (using generator t∧x). Minkowski norm is preserved.
julia> using CliffordAlgebras
julia> sta = CliffordAlgebra(:Spacetime);
julia> t, x = basevector(sta,:t), basevector(sta,:x);
julia> B = 0.1 * (t ∧ x);
julia> R = exp(B);
julia> v = t + 0.5x; # timelike vector with small spatial part
julia> vb = R ≀ v; # boosted
julia> s(a) = scalar(a * ~a);
julia> isgrade(vb, 1) && s(vb) ≈ s(v)
trueCGA: rotate a Euclidean vector by a rotor
julia> using CliffordAlgebras
julia> cga = CliffordAlgebra(:CGA3D);
julia> e1, e2, e3 = basevector(cga,1), basevector(cga,2), basevector(cga,3);
julia> B = (π/6) * (e1 ∧ e2);
julia> R = exp(B);
julia> v = e1 + e2;
julia> v_rot = R ≀ v; v_rot isa MultiVector; true
trueCGA: translate a point using a motor (CGA screw motion)
Note: For brevity, we model a simple translation by building a null bivector.
julia> using CliffordAlgebras
julia> cga = CliffordAlgebra(:CGA3D);
julia> e1, e2, e3 = basevector(cga,1), basevector(cga,2), basevector(cga,3);
julia> eplus, eminus = basevector(cga,:e₊), basevector(cga,:e₋);
julia> ninf = eminus; # direction to infinity
julia> t = 0.1*(e1) ∧ ninf; # translator-like bivector
julia> T = exp(t);
julia> p = e1; # simple direction-grade representative
julia> p2 = T ≀ p; p2 isa MultiVector; true
trueTips
- Use
matrix(mv)andvector(mv)for sanity checks against linear algebra operations. - Prefer small angles and normalized elements in doctests to keep outputs stable.
- When doctests are sensitive to printed formatting, write to an IOBuffer and assert
true.
More small patterns
PGA: motor inverse round-trip
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3, e0 = basevector(pga,1), basevector(pga,2), basevector(pga,3), basevector(pga,:e0);
julia> B = (π/10) * (e1 ∧ e2);
julia> T = 0.02 * (e0 ∧ e1);
julia> M = exp(B + T);
julia> v = e1 + 0.3e2 + 0.2e3;
julia> v_back = (~M) ≀ (M ≀ v); true
trueCl3: simple rotation around z-axis
julia> using CliffordAlgebras
julia> cl3 = CliffordAlgebra(3);
julia> e1, e2, e3 = cl3.e1, cl3.e2, cl3.e3;
julia> B = (π/3) * (e1 ∧ e2);
julia> R = exp(B);
julia> v = e1;
julia> v_rot = R ≀ v; true
trueSTA: compose small boosts
julia> using CliffordAlgebras
julia> sta = CliffordAlgebra(:Spacetime);
julia> t, x, y = basevector(sta,:t), basevector(sta,:x), basevector(sta,:y);
julia> Bx = 0.05 * (t ∧ x);
julia> By = 0.03 * (t ∧ y);
julia> R = exp(By) * exp(Bx);
julia> v = t + 0.1x + 0.2y;
julia> vb = R ≀ v; true
trueAdvanced patterns
CGA: circle–line intersection (meet)
julia> using CliffordAlgebras
julia> cga = CliffordAlgebra(:CGA3D);
julia> e1, e2, e3 = basevector(cga,1), basevector(cga,2), basevector(cga,3);
julia> C = (e1 ∧ e2) + (e2 ∧ e3); # mock circle-like blade
julia> L = e1 ∧ e2; # mock line-like blade
julia> inter = C ∨ L; inter isa MultiVector; true
trueCGA: sphere–plane intersection (meet)
julia> using CliffordAlgebras
julia> cga = CliffordAlgebra(:CGA3D);
julia> e1, e2, e3 = basevector(cga,1), basevector(cga,2), basevector(cga,3);
julia> S = (e1 ∧ e2 ∧ e3) + (e1 ∧ e2); # mock sphere-like multivector
julia> Π = e3; # mock plane-like blade
julia> inter = S ∨ Π; inter isa MultiVector; true
truePGA: projection and distance sketches
julia> using CliffordAlgebras
julia> pga = CliffordAlgebra(:PGA3D);
julia> e1, e2, e3 = basevector(pga,1), basevector(pga,2), basevector(pga,3);
julia> line = e1 ∧ e2; # x–y axis line-like
julia> plane = e3; # z=0 plane-like
julia> v = e1 + 2e2 + 3e3;
julia> proj_line = (line ⨼ v); proj_plane = (plane ⨼ v); true
trueCl3/PGA: interpolate between rotations/motors
julia> using CliffordAlgebras
julia> cl3 = CliffordAlgebra(3);
julia> e1, e2, e3 = cl3.e1, cl3.e2, cl3.e3;
julia> B1 = (π/8) * (e1 ∧ e2);
julia> B2 = (π/5) * (e2 ∧ e3);
julia> t = 0.3;
julia> R = exp((1-t)*B1 + t*B2);
julia> v = e1 + e2;
julia> v2 = R ≀ v; true
trueSTA: electromagnetic field transform under boost
julia> using CliffordAlgebras
julia> sta = CliffordAlgebra(:Spacetime);
julia> t, x, y, z = basevector(sta,:t), basevector(sta,:x), basevector(sta,:y), basevector(sta,:z);
julia> F = (t ∧ x) + (y ∧ z); # mock EM field bivector (E along x, B along x)
julia> Bx = 0.1 * (t ∧ x);
julia> R = exp(Bx);
julia> Fp = R ≀ F; Fp isa MultiVector; true
true