Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Galaga — Geometric Algebra for Python

Galaga 2 is a numeric geometric-algebra library built on a native Gram-matrix core. Its public API composes eager multivector values with optional immutable expression provenance, configurable blade conventions, and shared ASCII, Unicode, and LaTeX rendering.

  • General symmetric Gram matrices, not only diagonal signatures.
  • Long, unambiguous operation names as the primary mathematical API.
  • Optional concise aliases such as gp, op, rev, and sw.
  • Explicit inner products and contractions rather than one overloaded ip.
  • Immutable names and expression provenance over eager numeric values.
  • Presets that configure the metric, blade convention, notation, and model metadata together while still permitting component-level overrides.
  • Thread- and async-safe scoped presentation changes.
  • NumPy array conversion and checked scalar conversion.

Install

pip install galaga

Galaga requires Python 3.11 or newer and NumPy. The optional galaga_marimo package requires Python 3.14 because it uses t-strings.

Quick start

from galaga import Algebra, DisplayPolicy, norm, outer_product

alg = Algebra((1, 1, 1), display=DisplayPolicy("full"))
e1, e2, e3 = alg.basis_vectors(expr=True)

u = (2 * e1 + e2).named("u")
v = (0.5 * e1 + 1.8 * e2).named("v")
area = outer_product(u, v)

print(area)
print(area.latex())
print(norm(area))

The operators are conveniences over named operations:

u * v  # geometric_product(u, v)
u ^ v  # outer_product(u, v)
u | v  # doran_lasenby_inner(u, v)
~u     # reverse(u)

Constructing an algebra

Diagonal algebras retain the familiar call shapes:

from galaga import Algebra

cl3 = Algebra(3)                       # Cl(3, 0)
sta = Algebra(1, 3)                    # Cl(1, 3)
pga = Algebra(3, 0, 1)                 # Cl(3, 0, 1)
ordered = Algebra((1, -1, -1, -1))    # explicit basis order

A full symmetric Gram matrix defines a non-orthogonal or native-null basis:

import numpy as np
from galaga import Algebra

null_plane = Algebra(
    gram=np.array(
        [
            [0.0, -1.0],
            [-1.0, 0.0],
        ]
    )
)

Complete presets configure numeric and presentation choices together:

from galaga import Algebra, p_cga, p_lengyel_cga, p_pga, p_rga, p_sta

sta = Algebra(config=p_sta("mostly-minus"))
pga = Algebra(config=p_pga(spatial_dim=3))
cga = Algebra(config=p_cga(spatial_dim=3, frame="null"))
lengyel_cga = Algebra(config=p_lengyel_cga())
rga = Algebra(config=p_rga(spatial_dim=3))

config= owns the whole algebra definition. The lower-level presentation=, blades=, notation=, local_names=, display_order=, and display= parameters permit deliberate overrides when constructing an algebra directly.

Native-null conformal model

The conformal preset stores eo and einf as actual null basis vectors in a non-diagonal Gram matrix. Attach the model-specific semantics explicitly:

from galaga import Algebra, outer_product, p_cga
from galaga.cga import ConformalModel

algebra = Algebra(config=p_cga(spatial_dim=3))
cga = ConformalModel(algebra, expr=True)
expanded_cga = cga.with_expression_form("expanded")

a = cga.up((0, 0, 0))
b = cga.up((1, 0, 0))
line = outer_product(a, b, cga.infinity)

assert cga.carrier(cga.round_point((1, 2, 3))).homogeneous_grade() == 2

round_point = cga.round_point((3, 4, 0), radius_squared=4)
assert float(cga.center_norm(round_point)) == 5
assert float(cga.radius_norm(round_point)) == 2

# The same value can explain itself with compact CGA vocabulary or its formula.
compact = cga.carrier(round_point)
expanded = expanded_cga.carrier(round_point)
assert compact == expanded

The native-null CGA guide covers round and flat objects, operator/expanded expression forms, att/car/ccr/cen/con/par, dual conventions, projection, Eric Lengyel's ●/○/■/□ components and weighted norms, and transformation recipes.

Point-based rigid model

p_rga() selects Eric Lengyel's point-based interpretation of Cl(3, 0, 1). Attach RigidModel when coordinates, projective measurement, projection, support, or validity constraints are needed:

from galaga import Algebra, p_rga
from galaga.rga import RigidModel

algebra = Algebra(config=p_rga())
rga = RigidModel(algebra, expr=True)

p = rga.point((3, 4, 0)).named("P")
q = rga.point((1, 0, 0)).named("Q")
line = p ^ q

assert float(rga.bulk_norm(p)) == 5
assert rga.is_valid_line(line)

The RGA guide covers the algebraic convention layer, paired norms, homogeneous distance and angle, projections, support, line/motor/flector constraints, transwedge correction, and the dual relationship with plane-based PGA.

Values, names, and expressions

Multivectors are eager and immutable. Naming and expression tracking return a new wrapper without changing the coefficients or the original value:

from galaga import Algebra, exp

alg = Algebra(3)
e1, e2, _ = alg.basis_vectors(expr=True)

theta = alg.scalar(0.6).named(r"\theta")
B = (e1 ^ e2).named("B")
R = exp(-theta * B / 2).named("R")

R.name       # immutable semantic Name
R.expr       # immutable expression provenance
R.numeric    # presentation-independent galaga.core.Multivector
R.data       # read-only coefficient array

Useful state transformations include:

R.named("Q")       # replace the semantic name
R.without_name()   # retain expression provenance
R.with_expr()      # attach literal provenance if absent
R.without_expr()   # retain the eager value and name

Factories accept expr=True when the construction itself should participate in a later expression tree:

e1, e2, e3 = alg.basis_vectors(expr=True)
scalar = alg.scalar(2, expr=True)
vector = alg.vector([1, 2, 3], expr=True)

Blades

blade() accepts configured labels, bitmasks, signed blade references, or an existing signed unit blade. blades() is the ordered plural form:

rga = Algebra(config=p_rga())
e1, e2, e3, e4 = rga.basis_vectors(expr=True)
e23, e31, e41, e42 = rga.blades(
    e2 ^ e3,
    e3 ^ e1,
    e4 ^ e1,
    e4 ^ e2,
    expr=True,
)

alg.locals() returns a read-only mapping for environments where bulk name injection is appropriate. In reactive notebooks, explicit blade() or blades() calls preserve dependency tracking more clearly.

Product and contraction family

The long names are canonical:

Family Canonical operation
Clifford product geometric_product
Exterior product outer_product
Left and right contractions left_contraction, right_contraction
Doran–Lasenby inner doran_lasenby_inner
Hestenes inner hestenes_inner
Metric inner metric_inner_product
Scalar product scalar_product
Lengyel antidot antidot_product
Regressive product regressive_product
Transwedge family transwedge, transwedge_antiproduct

There is deliberately no top-level ip or inner_product: these operations disagree for mixed grades and scalar inputs. Choose the intended definition explicitly, or define a local notation:

from galaga import doran_lasenby_inner as ip

Variadic geometric and outer products lower left-to-right through their binary catalog operation:

from galaga import geometric_product, outer_product

geometric_product(a, b, c)
outer_product(e1, e2, e3)

Commutators and anticommutators are unscaled:

commutator(a, b)            # ab - ba
anticommutator(a, b)        # ab + ba
half_commutator(a, b)       # (ab - ba) / 2
half_anticommutator(a, b)   # (ab + ba) / 2
lie_bracket(a, b)           # unscaled commutator
jordan_product(a, b)        # unscaled anticommutator

Grades and scalar conversion

from galaga import grade, grades, scalar_part

scalar_component = grade(value, 0)      # scalar multivector
selected = grades(value, [0, 2, 4])
coefficient = float(grade(value, 0))
same_coefficient = scalar_part(value)   # optional helper

float(value) succeeds only when the entire multivector is scalar. It never silently discards non-scalar grades. NumPy conversion exposes the coefficient array:

coefficients = np.asarray(value)

Rendering and presentation

DisplayPolicy chooses content independently from the target format:

from galaga import Algebra, DisplayPolicy

alg = Algebra(3, display=DisplayPolicy(content="full"))
e1, e2, _ = alg.basis_vectors(expr=True)
x = (2 * e1 + e2).named("x")

x.display("full/latex")
x.display("expr/unicode")
x.display("value/ascii")
x.latex()

Persistent presentation changes return cheap algebra views sharing the same numeric algebra. Scoped overrides use ContextVar, so they are isolated by thread and asynchronous task:

teaching_alg = alg.with_presentation(teaching_presentation)

with alg.use_presentation(teaching_presentation):
    print(x.latex())

Numeric core and explicit facade namespace

The public package is an exact re-export of the facade objects:

import galaga
import galaga.facade

assert galaga.Algebra is galaga.facade.Algebra
assert galaga.Multivector is galaga.facade.Multivector

Use galaga.core when presentation, names, and expression provenance are not needed:

from galaga.core import Algebra, geometric_product

numeric = Algebra(gram=[[1.0, 0.2], [0.2, 1.0]])
e1, e2 = numeric.basis_vectors()
result = geometric_product(e1, e2)

Phase 8 legacy access

Galaga 1 remains deliberately available as a temporary migration and test oracle during Phase 8:

from galaga import legacy

old_alg = legacy.Algebra(3)
old_e1, old_e2, _ = old_alg.basis_vectors()
old_value = legacy.gp(old_e1, old_e2)

Legacy and Galaga 2 values are separate domains and must not be mixed. The galaga.legacy namespace, including galaga.legacy.render and galaga.legacy.simplify, is scheduled for removal with the old table engine in Phase 9.

More documentation

Executable examples live under examples/galaga_v2, examples/algebra, and the model-specific example directories.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

galaga-2.0.0a1.tar.gz (324.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

galaga-2.0.0a1-py3-none-any.whl (167.1 kB view details)

Uploaded Python 3

File details

Details for the file galaga-2.0.0a1.tar.gz.

File metadata

  • Download URL: galaga-2.0.0a1.tar.gz
  • Upload date:
  • Size: 324.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for galaga-2.0.0a1.tar.gz
Algorithm Hash digest
SHA256 bd022f54f459690cfc4063929ee520458076e9227dae857ef22fd0669a401dbf
MD5 dbcc743e495d2179b9799a4662f4decf
BLAKE2b-256 7b78de2ce70d8e19cfb774d1e5766c8948cc0a18fccd8078bc99417b29a012d5

See more details on using hashes here.

File details

Details for the file galaga-2.0.0a1-py3-none-any.whl.

File metadata

  • Download URL: galaga-2.0.0a1-py3-none-any.whl
  • Upload date:
  • Size: 167.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for galaga-2.0.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 d5cf36414c5cd09fc550bd5a4e00c09fd97d21146bdf3255712a47e668df7646
MD5 3e129fa2103214d7b8892ee39e3ebd63
BLAKE2b-256 b822ce1d31c6ee0e36b8de614a419cd29f0e76c36b2f97d56f5ea7f1a99d9c5a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.0.0a1 This release

2 files

1.8.1

2 files

1.8.0

2 files

1.7.6

2 files

1.7.5

2 files

1.7.4

2 files

1.7.3

2 files

1.7.2

2 files

1.7.1

2 files

1.7.0

2 files

1.6.5

2 files

1.6.4

2 files

1.6.3

2 files

1.6.1

2 files

1.6.0

2 files

1.5.0

2 files

1.4.3

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.1

2 files

1.3.0

2 files

1.2.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.12

2 files

0.3.11

2 files

0.3.10

2 files

0.3.9

2 files

0.3.8

2 files

0.3.7

2 files

0.3.6

2 files

0.3.5

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page