This repository collects small, self-contained Python implementations of several geometric algebras and the visualization tools used to compare their elements and transformations side by side. Each subpackage is kept independent so that the underlying representations (multivector layout, basis ordering, sign conventions) can be studied in isolation and then contrasted against the others.
The collection currently contains three implementations:
| Section | Module | Algebra | Underlying representation |
|---|---|---|---|
| PGA3D | pga3d/ | 3-D Projective Geometric Algebra R(3, 0, 1) |
16-component multivector |
| SimpleGA | simplega/ | GA(2,0), GA(3,0), GA(3,1), quaternions |
grade-split (Even / Odd) blocks |
| Ganja | ganja/ | generic Algebra(p, q, r) |
dense 2^n component multivector |
All figures in this README are generated from code in doc/; see the "Regenerating the figures" subsection of each section to reproduce them.
pga3d is a Python library for doing geometry in 3-D using 3-D
Projective Geometric Algebra (R(3, 0, 1)). It exposes high-level
objects (Point, Line, Plane, Translator, Rotor) so that
intersections, joins, and rigid-body transformations are one-liners,
while hiding the algebra from callers who do not want to learn it.
Benefits over linear-algebra-based libraries
- Small core: once the algebra is set up, conversions, intersections, and joins are one-liners.
- Translations and rotations compose with minimal loss of precision (compared with multiplying 4×4 matrices).
- Transformations are linear in the algebra, so multiplying them yields smooth interpolation.
- Edge cases are first-class: intersecting two parallel lines yields a point at infinity (a direction) rather than an exception.
- No implicit choice of right-handed vs left-handed conventions.
This package is derived from Almar Klein's pga3d project, which served
as the starting point for the implementation and API. See:
- Almar Klein, pga3d — https://github.com/almarklein/pga3d
Background reading on (projective) geometric algebra:
- Introductory talk: https://www.youtube.com/watch?v=tX4H_ctggYo
- General resources: https://bivector.net
- 3D PGA cheat sheet: https://bivector.net/3DPGA.pdf
- PGA explained for devs: https://observablehq.com/@enkimute/understanding-pga-1
- Geometric algebra in JS (interactive): https://github.com/enkimute/ganja.js
- C++ implementation of 3D PGA (Klein): https://github.com/jeremyong/Klein
All geometric objects and transformations live in the top-level
pga3d package. Points, lines, and planes are
built from incidence (e.g. a line is the join of two points), and
Translator / Rotor act on any object via .project(...).
import math
from pga3d import Point, Line, Plane, Translator, Rotor
# Build geometric objects from points
p1 = Point(2, 3, 4)
p2 = Point(20, 3, 7)
p3 = Point(9, 12, 17)
line = Line.from_points(p1, p2) # join of two points
plane = Plane.from_points(p1, p2, p3) # join of three points
# Compose a rigid-body motion: rotate 90° around z, then translate by +3x
t = Translator.from_xyz(3, 0, 0)
r = Rotor.from_angle_and_line(math.pi / 2, Line.from_xyz(0, 0, 1))
m = t * r # composition is just multiplication
p1_moved = m.project(p1) # apply the motion to a point
# Projection / intersection are one-liners
p_on_line = p1.project_onto(Line.from_points(p3, p1))
plane_through_origin = plane.project_onto(Point(0, 0, 0))A runnable version of this snippet lives in pga3d/pga3d_examples.py.
The figures below are produced by doc/generate_figures.py, which drives the plotting helpers in pga3d_display.py.
In R(3, 0, 1) incidence is expressed by two products: the join
(&, regressive product) builds higher-grade objects from lower-grade
ones, and the meet (^, outer product) intersects them. The
figures below are also generated by
doc/generate_figures.py.
python doc/generate_figures.py
Experimental and a work in progress.
simplega is a Python port of the Julia package
SimpleGA.jl by Chris
Doran. It provides compact, grade-split implementations of several
geometric algebras with a uniform API:
| Algebra | Module | Underlying representation |
|---|---|---|
GA(2,0) |
simplega/ga20.py | complex numbers |
GA(3,0) |
simplega/ga30.py | quaternion-like (w, x, y, z) |
GA(3,1) |
simplega/ga31.py | 2×2 complex matrices |
| Quaternions | simplega/quaternions.py | (w, x, y, z) |
Each algebra splits its multivectors into an Even part (grade 0 +
grade 2) and an Odd part (grade 1 + grade 3) so the geometric
product reduces to four small fixed-shape multiplications. The
top-level simplega/init.py module exposes
shared helpers: project, bivector_exp, inject, dot, tr,
norm, adjoint, isapprox.
- Chris Doran, SimpleGA.jl — https://github.com/MonumoLtd/SimpleGA.jl
- Upstream documentation — https://monumoltd.github.io/SimpleGA.jl/dev/
- Doran & Lasenby, Geometric Algebra for Physicists, Cambridge University Press, 2003.
A Python-side documentation set adapted from the upstream Julia docs lives under doc/simplega/:
- Overview — installation, first example, the Even / Odd trick.
- API reference — bases, arithmetic, projection, exponentiation, helpers.
- Algebras — per-algebra notes for
GA(2,0),GA(3,0),GA(3,1), and quaternions.
Each algebra exposes the same Even / Odd pair plus a set of basis
elements. Multiplying an Odd by an Odd yields an Even (scalar +
bivector); multiplying an Even by an Odd yields an Odd again.
Rotations are written as the sandwich product R v R† where R is
the exponential of a bivector.
import math
from simplega import ga30 as GA30
from simplega import project, bivector_exp, dot, norm
e1, e2, e3, I3 = GA30.e1, GA30.e2, GA30.e3, GA30.I3
Even = GA30.Even
# Build vectors (grade 1) and decompose the geometric product
v1 = 0.9 * e1 + 0.4 * e2
v2 = 0.4 * e1 + 0.8 * e3
g = v1 * v2 # Even = scalar + bivector
inner = project(g, 0) # <v1, v2>
outer = project(g, 2) # v1 ^ v2
# Build a rotor from a bivector and rotate a vector via sandwich product
B = Even(0.0, 0.0, 0.0, math.pi / 4) # −(π/4)·e1e2 (90° around z)
R = bivector_exp(B)
v = 0.8 * e1 + 0.2 * e2 + 0.5 * e3
v_rot = R * v * R.adjoint # R v R†
print("|v| =", norm(v))
print("|R v R†| =", norm(v_rot)) # rotations preserve the normQuaternion-style rotations use the dedicated module:
import math
from simplega.quaternions import Quaternion
# 90° rotation around z as a unit quaternion
q = Quaternion(math.cos(math.pi / 4), 0.0, 0.0, math.sin(math.pi / 4))
p = Quaternion(0.0, 1.0, 0.0, 0.0) # pure-imaginary = vector (1, 0, 0)
p_rot = q * p * q.conj() # → (0, 1, 0)GA(3,0) is isomorphic to the quaternions: its Even subalgebra
(scalar + bivectors) reproduces the quaternion algebra, and the
sandwich product R v R† rotates a grade-1 vector v exactly like
quaternion rotation. The figures below are produced by
doc/generate_simplega_figures.py,
which uses the GA30Visualizer class and the
plot_grade_decomposition helper from
simplega/ga30_visualization.py.
python doc/generate_simplega_figures.py
Standalone interactive demos for each algebra:
python simplega/ga30_visualization.py
python simplega/quaternion_visualization.py
python simplega/bivector_visualization.py
ganja is an original Python reimplementation inspired by the API of
ganja.js by Steven De Keninck.
It provides a generic Algebra(p, q, r) factory that produces a full
Clifford / geometric algebra of any signature, with a single dense
Multivector type covering every grade.
The implementation is intentionally small (a few hundred lines) and is not a line-by-line port of the JavaScript source. It is written from first principles around a precomputed basis-blade sign table.
- Steven De Keninck, ganja.js — https://github.com/enkimute/ganja.js
- Interactive PGA tutorial — https://observablehq.com/@enkimute/understanding-pga-1
- Bivector community / cheat sheets — https://bivector.net
import math
from ganja import Algebra, graph
# 3-D projective geometric algebra: signature (3, 0, 1)
PGA3 = Algebra(3, 0, 1)
e1, e2, e3, e0 = PGA3.basis_vectors() # e0 squares to 0
# Build a rotor as exp of a bivector and apply it via the sandwich product
B = (math.pi / 4) * (e1 ^ e2) # 90° generator in the e1-e2 plane
R = B.exp() # rotor (unit norm)
rotated = R @ e1 # equivalent to R * e1 * ~R
# A PGA point at (1, 2, 3): a trivector mixing e1e2e3 with the e0 blades
P = (e1 ^ e2 ^ e3) + 1*(e0 ^ e2 ^ e3) - 2*(e0 ^ e1 ^ e3) + 3*(e0 ^ e1 ^ e2)
# A PGA plane: x + y + z = 1
plane = e1 + e2 + e3 - e0
# Render points / lines / planes side by side
ax = graph([P, "P(1,2,3)", "tab:red", plane, "plane", "tab:green",
e2 ^ e3, "x-axis"],
title="PGA(3,0,1) demo", lim=2.5, show=True)Operator cheat sheet:
| Operator | Meaning |
|---|---|
a * b |
geometric product |
a ^ b |
outer (wedge) product |
| `a | b` |
~a |
reverse |
R @ a |
sandwich product R a ~R |
a.dual() / a.undual() |
duality (PGA-aware) |
a.exp() |
exponential (closed form for pure bivectors) |
a.grade(k) |
projection onto grade k |
The figures below are produced by doc/generate_ganja_figures.py.
![]() |
![]() |
| A point, a plane, and a line in 3-D PGA. | Two points and a line in 2-D PGA. |
python doc/generate_ganja_figures.py
The core algebra (arithmetic, projection, reverse, dual, exp, sandwich)
is complete and validated against PGA identities. The graph()
function currently supports 2-D and 3-D PGA; other signatures are
accepted by the algebra layer but cannot yet be drawn.
A broader showcase of the graph() renderer, exercising every supported
object kind in both 2-D and 3-D PGA. The scenes are produced by
ganja/visualization_test.py, which can be
run interactively or in headless mode:
python ganja/visualization_test.py # opens matplotlib windows
python ganja/visualization_test.py --save # writes PNGs to doc/
The script is adapted from the conventions of
ganja.js by Steven De Keninck —
in particular the Algebra.graph([...]) mixed-item rendering style
(multivectors interleaved with labels, colours, polylines, and
callables). The underlying algebra and renderer in this repository are
original Python code; only the API style of the test scenes follows
ganja.js.
- Test scenes inspired by the
Algebra.graph([...])examples in enkimute/ganja.js (MIT, © Steven De Keninck). - PGA modelling conventions follow the bivector.net cheat sheets and the PGA tutorial notebook.

























