The Algebra Class¶
The Algebra class is the central configuration object in pytanga. It
defines a geometric algebra \(G(d, s)\) with dimension \(d\), signature \(s\),
and a value type (float64, int64, etc.). On first construction for a new
combination of parameters, the C++ binding is compiled and cached (~5–20 s).
Subsequent constructions load the cached binary (~ms).
Construction¶
An Algebra is created by specifying the vector-space dimension, the
signature, and the data type. For convenience, dedicated Basis subclasses expose named blades and
factory methods for common geometries.
# G(3,0) — 3D Euclidean
alg = Algebra(3, 0, "float64")
# G(4,1) — 4D with negative-signature basis vector
alg = Algebra(4, 0b10000, "float64")
# bit 4 set → e5 squares to -1
# PGA3 — 5D with null-vector embedding
alg = Algebra(5, 0b10000, "float64")
# From a Basis subclass (named blades + factory methods)
from pytanga.basis import BasisE3, BasisP3, BasisPGA3
alg = BasisE3("float64")
alg = BasisP3("float64")
alg = BasisPGA3("float64")
The signature bitmask encodes which basis vectors square to \(-1\): bit k
set means \(e_{k+1}^2 = -1\). A signature can also be passed as a tuple of
1-based indices, e.g. (1, 4, 5) has the same effect as 0b11001.
Available Basis subclasses: BasisE2, BasisE3, BasisP2, BasisP3,
BasisN2, BasisN3, BasisPGA2, BasisPGA3. These expose named blades
as attributes and provide convenience factory methods.
Properties¶
These properties give read-only access to the algebra's configuration:
| Property | Type | Description |
|---|---|---|
dim |
int |
Vector-space dimension |
sig |
int |
Signature bitmask |
dtype |
str |
"float64", "float32", "int64", or "int32" |
modulus |
int \| None |
Stored modulus for integer algebras |
algebra_dim |
int |
2**dim — total number of basis blades |
pseudoscalar_id |
int |
Blade bitmask of the pseudoscalar \(I\) |
rng |
random.Random |
Random number generator seeded from construction argument |
print_fmt |
str |
Python format spec for coefficient display (default '.4g') |
Creating Multivectors¶
Calling an Algebra instance (or equivalently alg.multivector(...)) creates
a multivector. Five input forms are accepted:
# 1. Zero multivector
a = alg()
# 2. Dict with integer keys — raw blade bitmasks
a = alg({1: 1.0, 3: -0.5}) # 1.0·e1 − 0.5·e12
# 3. Dict with string keys — blade names
a = alg({"e1": 1.0, "e12": -0.5})
# 4. Dict with tuple keys — 1-based vector index tuples
a = alg({(2,): 1, (1, 3): 2}) # 1·e2 + 2·e13
# 5. String expression — parsed sum of signed terms
a = alg("2.3 + 4 e2 - 5 e1,2") # comma-separated indices for dim > 9
String blade names use e1, e2, … for single-index blades, e12 or
e1,2 for multi-index blades (comma form required for dim > 9), s for
the scalar, and I for the pseudoscalar.
GA Operations¶
All primitive GA operations are methods on Algebra that take two MV
arguments. Most have corresponding operator overloads on MV for
convenience:
c = alg.gp(a, b) # geometric product a * b (also a * b)
c = alg.op(a, b) # outer product a ^ b (also a ^ b)
c = alg.ip(a, b) # inner product a | b (also a | b)
c = alg.add(a, b) # addition a + b (also a + b)
c = alg.sub(a, b) # subtraction a - b (also a - b)
c = alg.neg(a) # negation (also -a)
c = alg.scale(a, s) # scalar scaling (also s * a)
Involutions¶
The rev and conj methods apply blade-level involutions:
r = alg.rev(a) # reverse: rev(blade) = (-1)^(k(k-1)/2) · blade
c = alg.conj(a) # Clifford conjugate: conj(blade) = rev(blade) · (-1)^r
The reverse negates blades of grades 2 and 3 mod 4. The conjugate adds an extra sign factor \((-1)^r\) where \(r\) is the count of negative-metric basis vectors in the blade.
For convenience, MV also exposes a.rev() and a.conj() which delegate
to the parent algebra.
Grade‑based involutions (galgebra‑compatible):
gi = alg.grade_involution(a) # negate odd-grade parts: (−1)^k · ⟨A⟩_k
gc = alg.grade_conj(a) # (−1)^(k(k+1)/2) · ⟨A⟩_k = grade_involution(a).rev()
grade_involution negates odd-grade parts. grade_conj is the
metric‑independent grade‑based Clifford conjugate (galgebra's ccon), unlike
the metric‑aware conj.
Grade extraction:
Miscellaneous operations¶
scalar = alg.scalar(a) # scalar coefficient
grade_k = alg.grade_proj(a, k) # ⟨A⟩_k — grade-k part (also list[int])
mag_sq = alg.magnitude_sq(a) # sum of squared coefficients
magnitude = alg.magnitude(a) # sqrt of magnitude_sq
is_zero = alg.is_zero(a) # True if all coefficients zero
is_scalar = alg.is_scalar(a) # True if only scalar blade non-zero
comp_a = alg.complement(a) # unsigned complement — see [Duals](duals.md)
dual_a = alg.dual(a) # signed dual ★A = A · I⁺ — see [Duals](duals.md)
ldual_a = alg.ldual(a) # left dual I · A — see [Duals](duals.md)
sp = alg.sp(a, b) # scalar product: scalar part of a * b
inv_a = alg.inv(a) # multiplicative inverse
inv_a = alg.inv(a, 97) # modular inverse (integer algebras only)
Norms, quadratic form, and exponential¶
q = alg.qform(a) # scalar_part(rev(A) * A)
n2 = alg.norm2(a) # |scalar_part(rev(A) * A)|
n = alg.norm(a) # sqrt(norm2(A))
e = alg.exp(a) # exponential (requires A² ∈ ℝ)
norm2/norm are the quadratic‑form‑based norms (equal to mag/mag2 in
Euclidean algebras, distinct in non‑Euclidean). exp raises ValueError
if A² is not a scalar.
Commutator, contractions, and grade products¶
c = alg.cp(a, b) # commutator (A·B − B·A)/2
ac = alg.acp(a, b) # anti-commutator (A·B + B·A)/2
rc = alg.rc(a, b) # right contraction A ⌊ B
mn = alg.gp_min(a, b) # Hestenes inner product ⟨AB⟩_{|k−j|} (pure blades)
mx = alg.gp_max(a, b) # outermost grade product ⟨AB⟩_{k+j} (pure blades)
rc vanishes when grade(A) < grade(B); gp_min/gp_max require pure‑blade
operands and raise ValueError otherwise.
Dual inverse¶
undual is algebra‑specific: A · I in E3/P3/N3. In PGA the pseudoscalar is
not invertible, so undual inverts the complement dual (J‑map / Hodge star):
in PGA2 the J‑map is involutive (undual == dual), while in PGA3 it is
grade_involution(dual(a)) because the double Hodge dual is the grade
involution. See Duals.
Products with reverse/conjugate flags¶
alg.gp_rev(a, b, rev_a=True) # GP with reverse on first operand
alg.gp_conj(a, b, conj_b=True) # GP with conjugate on second operand
alg.ip_rev(a, b, rev_a=True) # IP with reverse on first operand
alg.ip_conj(a, b, conj_b=True) # IP with conjugate on second operand
alg.op_rev(a, b, rev_a=True) # OP with reverse on first operand
alg.op_conj(a, b, conj_b=True) # OP with conjugate on second operand
Type checks and coefficients¶
alg.is_vector(a) # only grade-1 blades
alg.is_base(a) # exactly one basis blade, coefficient 1
alg.is_blade(a) # simple r-vector
alg.is_versor(a) # versor (product of invertible vectors)
alg.is_grade(a, 2) # pure grade-2 element
alg.blade_coefs(a, blades) # coefficients per blade
alg.components(a) # list of single-blade MVs
alg.get_coefs(a, k) # grade-k coefficients
alg.normalized(a) # a / |a|
Versor product¶
The versor product \(R \cdot v \cdot \tilde{R}\) applies a versor (rotor / reflector) to a multivector:
rotated = alg.vp(R, v) # R * v * rev(R)
rotated = alg.nvp(R, v) # R * v * inv(R) — magnitude-independent
vp uses the reverse (standard for unit versors). nvp uses the algebraic
inverse, making the result independent of the versor's magnitude.
Blade operations¶
These operate on pure blades (homogeneous multivectors of a single grade):
alg.blade_inverse(blade) # inverse of a blade
alg.blade_pseudo_inverse(blade) # inner-product inverse; reciprocal of a null blade
alg.blade_factorize(blade) # → list of k grade-1 vectors
alg.join(a, b) # join of two blades
alg.meet(a, b) # meet of two blades
alg.blade_project(a, blade) # project a onto a non-degenerate blade
alg.blade_reject(a, blade) # reject a from a non-degenerate blade
For the plane‑based PGA models (BasisPGA2/BasisPGA3) the join/meet
names follow Gunn/Dorst and are swapped relative to the other algebras: meet
is the intersection (outer product) and join is the union (regressive
product). See basis_pga3.md and
basis_pga2.md.
Integer modular operations¶
For integer-dtype algebras, explicit _mod variants apply modular reduction
per operation. Full details in Modulus:
alg.gp_mod(a, b, 97) # geometric product modulo 97
alg.op_mod(a, b, 97) # outer product with reduction
alg.ip_mod(a, b, 97) # inner product with reduction
alg.reduce(a, 97) # apply hmod coefficient-wise
Display¶
If the algebra is a Basis subclass, show() uses a named display basis
(e.g. einf, eo for conformal algebras).