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
true

PGA: 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)
true

PGA: 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
true

PGA: 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)
true

PGA: 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
true

Cl3: 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
true

STA: 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)
true

CGA: 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
true

CGA: 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
true

Tips

  • Use matrix(mv) and vector(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
true

Cl3: 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
true

STA: 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
true

Advanced 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
true

CGA: 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
true

PGA: 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
true

Cl3/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
true

STA: 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