from dataclasses import dataclass, field, InitVar
from geomalgebra.basisblades import ga2d, BasisBlade
Multivectors
# For convenience, we unpack the basis blades into the global scope
= ga2d scalar, e1, e2, e12
Just like how complex numbers are a linear combination of real and imaginary numbers, a multivector is just a linear combination of weighted basis blades.
For example, let \(m\) be a multivector where \(m = 3.0e_1 -2.3e_2 + 4.02e_{23}\)
Implementation
Again, we use a dataclass (they are incredibly useful!), but this time we need to use the InitVar
type from the dataclasses
module. It tells the dataclass decorator that a particular field should be passed only to the __post_init__
method and not attached to the class.
We need this for our implementation because we want to be able to construct a Multivector
using a list of BasisBlades
, but we don’t actually want to store the basis blades as a list internally. A better data structure is a dict
.
The keys of the Multivector dictionary will be the basis blades.
@dataclass(frozen=True)
class Multivector():
"""A Multivector is a linear combination of basis blades."""
# InitVar variable is not set as class field, just sent to post_init method
list[BasisBlade]] = None
basis_blades: InitVar[dict[int, BasisBlade] = field(init=False, default_factory=dict)
blades: str = field(init=False, repr=False)
prettyprint:
def __post_init__(self, basis_blades: list[BasisBlade]):
for bb in basis_blades:
if self.blades.get(bb.basis, False):
# If basis blade already exists, sum the weights of the existing and new basis blade
= self.blades[bb.basis]
existing_bb self.blades[bb.basis] = BasisBlade(existing_bb.weight + bb.weight, existing_bb.basis)
else:
# If basis blade doesn't exist, create a new key-value pair for that basis blade
self.blades[bb.basis] = bb
object.__setattr__(self, 'prettyprint', " + ".join(str(b) for b in self.blades.values()))
def __str__(self) -> str:
return self.prettyprint
Multivector
Multivector (basis_blades:dataclasses.InitVar[list[geomalgebra.basisblade s.BasisBlade]]=None)
A Multivector is a linear combination of basis blades.
= Multivector([BasisBlade(3.0, e2), BasisBlade(-5.0, e12), BasisBlade(12.2, scalar)])
m m
Multivector(blades={2: BasisBlade(weight=3.0, basis=2, basis_name='e2'), 3: BasisBlade(weight=-5.0, basis=3, basis_name='e12'), 0: BasisBlade(weight=12.2, basis=0, basis_name='s')})
# Pretty printing like math notation
print(m)
str(m)
3.0e2 + -5.0e12 + 12.2s
'3.0e2 + -5.0e12 + 12.2s'
Addition
Given that multivectors are a sum of weighted basis blades, we should implement an add
function that accepts an arbitrary number of multivectors and/or basis blades and concatenates them into one multivector. Just like with real numbers, addition of multivectors is associative so we don’t need to worry about the order of arguments.
Since we designed our multivector class to accept a list of basis blades, we can easily implement add()
to take advantage of that.
See Arbitrary Arguments List for details on the
*args
syntax. In a nutshell, it combines a list of comma separated function arugments into a tuple calledargs
.When
*
is prepended to specific iterables (like tuples or lists), it does the opposite, unpacking the elements of the iterable. So, to combine two lists, we can dolist_combined = [*list_a, *list_b]
.
def add(*args: list[Multivector|BasisBlade]) -> Multivector:
"""Add Multivectors and BasisBlades"""
list[BasisBlade] = list()
basis_blades: for m in args:
if isinstance(m, Multivector):
# Convert multivectors/basisblades to a list of BasisBlades
= [*basis_blades, *list(m.blades.values())]
basis_blades if isinstance(m, BasisBlade):
= [*basis_blades, m]
basis_blades return Multivector(basis_blades)
add
add (*args:list[__main__.Multivector|geomalgebra.basisblades.BasisBlade])
Add Multivectors and BasisBlades
# Add BasisBlades
= BasisBlade(2.0, e1)
b1 = BasisBlade(4.0, e12)
b2 = BasisBlade(-1.0, e1)
b3 print(f'({b1}) + ({b2}) + ({b3}) = {add(b1, b2, b3)}')
(2.0e1) + (4.0e12) + (-1.0e1) = e1 + 4.0e12
# Add Multivectors
= Multivector([b1, b2, b3])
m1 = Multivector([b2, b3])
m2 print(f'({m1}) + ({m2}) = {add(m1, m2)}')
(e1 + 4.0e12) + (4.0e12 + -1.0e1) = 0.0e1 + 8.0e12
# Add Multivectors and BasisBlades
print(f'({m1}) + ({b2}) = {add(m1, b2)}')
(e1 + 4.0e12) + (4.0e12) = e1 + 8.0e12
Readings
Geometric Algebra Primer (Suter, 2003)
- Chapter 3 Introduction
- Chapter 3.2 Multivectors
What’s Next?
We can add multivectors now, but how do we multiply them? The next notebook covers the most fundamental operation in Geometric Algebra: the geometric product.