Geometric Algebra
Project description
gacalc
A small, readable Geometric (Clifford) Algebra library in Python, built as a
companion to Hestenes & Sobczyk, Clifford Algebra to Geometric Calculus. It
runs both numerically and fully symbolically (coefficients may be plain
numbers or sympy expressions).
The algebra of n-dimensional Euclidean space is written 𝒢ₙ (Hestenes' notation). This package gives you:
Gn— the general, dimension-agnostic representation (any n), andG1/G2/G3— specialized, much faster representations of 𝒢₁ / 𝒢₂ / 𝒢₃ whose geometric product is a closed form generated fromGnso it is provably consistent with the reference.
Terminology: 𝒢ₙ denotes the algebra; an instance of a class is an element of that algebra (a multivector). The classes are named after their algebra.
Layout
src/gacalc/
base.py AbstractMultiVector (the abstract base) + type aliases
gn.py Gn (general 𝒢ₙ) + e_1.. constants + transforms + `MultiVector` alias
g1.py g2.py g3.py one specialized class each (generated, not in git -- run `make generate`)
Installing from a git checkout (not from PyPI)? Run
make generateonce first — the specializedg*.pymodules aren't committed; they're generated fromGn(and baked into the published wheel, sopip install gacalcneeds no generator).
Import just the algebra you need:
from gacalc.g2 import G2, e_1, e_2
a = 3 * e_1 + 4 * e_2
a.magnitude_squared() # 25 (a vector squared is its magnitude squared)
a * a == G2.from_scalar(25) # True
e_1 * e_2 # the unit bivector e_12
a.dual() # the dual; n defaults to this algebra's dimension (2)
a.component(e_1) # 3 (the scalar coefficient along a unit blade; works
# for any grade, e.g. B.component(e_1 ^ e_2))
Each g* module exports its own basis constants (zero, one, e_1, …, and the
pseudoscalar e_12 / e_123), each of that module's type — so g2.e_1 * g2.e_2
is a G2, and 2D vs 3D e_1 are simply in different modules.
Graded subtypes (Vector, Bivector, Rotor, …)
Besides the full multivector classes, each algebra has graded subtypes that hold only one grade's components — the way mathematicians usually work:
| dimension | graded types |
|---|---|
| shared | Scalar (grade 0) |
| 𝒢₁ | Vector1 |
| 𝒢₂ | Vector2, Bivector2, Rotor2 (the even subalgebra, ≅ ℂ) |
| 𝒢₃ | Vector3, Bivector3, Trivector3, Rotor3 (≅ the quaternions ℍ) |
The product decides the return type — resolved when the classes are generated, so it never depends on (float-fuzzy) coefficient values:
from gacalc.g2 import Vector2
a, b = 3 * Vector2.e_1 + 4 * Vector2.e_2, 1 * Vector2.e_1 + 2 * Vector2.e_2
type(a * b) # Rotor2 (a·b scalar + a∧b bivector)
type(a ^ b) # Bivector2 (the wedge — ask for a blade with ^)
type(a.inner_product(b)) # Scalar
Each class exposes its basis blades as class constants of its own type — Vector2.e_1 /
Vector2.e_2 (vectors), Bivector2.e_12, G3.e_123, etc. — equivalent to cls.basis_vector(n) but
named. They live on the class (Vector2.e_1); because the stored coefficient fields are named
coeff_e_1 … (not e_1), an instance v.e_1 resolves to the same basis constant, while
v.coeff_e_1 is that component's value. Read a component back out with v.component(Vector2.e_1).
(Gn, being dimension-agnostic, has no fixed class constants — use the module-level gn.e_1 … or
Gn.basis_vector(n).)
Iterating a value yields its coefficient values in blade order — so list(v) / tuple(v) /
np.array([list(v), …]) give the components (a vector reads as its coordinate tuple). To decompose
into one single-blade multivector per term instead, iterate v.to_blade_dict().
Return-type table for the geometric product * (𝒢₂ shown):
* |
Scalar | Vector2 | Bivector2 | Rotor2 |
|---|---|---|---|---|
| Scalar | Scalar | Vector2 | Bivector2 | Rotor2 |
| Vector2 | Vector2 | Rotor2 | Vector2 | Vector2 |
| Bivector2 | Bivector2 | Vector2 | Scalar | Rotor2 |
| Rotor2 | Rotor2 | Vector2 | Rotor2 | Rotor2 |
A result that spans grades no single type covers widens to the full G_n
(e.g. Vector3 * Bivector3 -> G3). Build values by linear combination of the basis
(3*e_1 + 4*e_2; a bivector via e_1 ^ e_2; a rotor via scalar + bivector — +/-
also narrow to the tightest type). Rotors carry plane_of_rotation(), and
rotor_from_vectors(from, to) builds the rotor whose sandwich R v R.inverse() equals
rotate(from, to)(v). A full walkthrough is in notebooks/displaygraded.py.
Adding a new algebra (worked example: G4 for 𝒢₄)
The specialized classes are generated from Gn, so adding a dimension is a
one-line edit — no new math by hand.
-
Open
tools/gen_specialized.pyand add one entry to theALGEBRASlist:ALGEBRAS = [ (1, "G1", "g1.py"), (2, "G2", "g2.py"), (3, "G3", "g3.py"), (4, "G4", "g4.py"), # <-- (dimension, class name, output file) ]
-
Regenerate. This writes
src/gacalc/g4.py(and rewrites the others identically); it auto-formats its own output:make generate # = python tools/gen_specialized.py
That's it — from gacalc.g4 import G4, e_1, e_2 now works. The docstring, the
DIMENSION, the basis constants, and all the dimension-fixed methods (dual(),
unit_pseudoscalar(), …) are generated automatically; you do not touch
base.py or gn.py. (Optional: add it to the SPECIALIZED map in
tests/test_conformance.py to include it in the conformance suite.)
Heads-up — generation cost grows fast. The generator derives the closed forms by running the general symbolic geometric, inner, and outer products in
Gn, which has 2ⁿ basis blades, 4ⁿ term pairs, and eagerly simplifies. 𝒢₁/𝒢₂ generate in well under a second; 𝒢₃ takes tens of seconds; 𝒢₄ takes a few minutes; higher dimensions longer still. This cost is paid once, at generation time — the generated code itself is fast.
Benchmarks
python tools/bench.py compares Gn against the specialized classes. The
specialized geometric product is ~15–34× faster numerically and thousands of
times faster symbolically (the general Gn eagerly sympy.simplifys every
intermediate; the closed form does a single simplify-free pass).
License
GPL v2 or later. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gacalc-0.0.3.tar.gz.
File metadata
- Download URL: gacalc-0.0.3.tar.gz
- Upload date:
- Size: 67.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a7ac2bd8b7ba93fa6d0bb13f06df032f62cd4d544afd2f03fc47dbc09f9bda8
|
|
| MD5 |
463f2446e2ec634130f8d749d974061a
|
|
| BLAKE2b-256 |
b42587f81fd8f006bdcead4ae3cf4b16c9a3c319dec98fa39cf23d67af630559
|
File details
Details for the file gacalc-0.0.3-py3-none-any.whl.
File metadata
- Download URL: gacalc-0.0.3-py3-none-any.whl
- Upload date:
- Size: 59.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
981579a615c685981045137c88de732affef0684b822a7627294450a676865a8
|
|
| MD5 |
1d971b1d3bd945ddd2348131fe62c1ab
|
|
| BLAKE2b-256 |
7bb066c2ac0f5538b452553af90fe561f713fd04b933bf33aa0382347b34c51b
|