from dataclasses import dataclass, field, InitVar
from geomalgebra.basisblades import ga2d, BasisBladeMultivectors
# For convenience, we unpack the basis blades into the global scope
scalar, e1, e2, e12 = ga2dJust 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
basis_blades: InitVar[list[BasisBlade]] = None
blades: dict[int, BasisBlade] = field(init=False, default_factory=dict)
prettyprint: str = field(init=False, repr=False)
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
existing_bb = self.blades[bb.basis]
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.prettyprintMultivector
Multivector (basis_blades:dataclasses.InitVar[list[geomalgebra.basisblade s.BasisBlade]]=None)
A Multivector is a linear combination of basis blades.
m = Multivector([BasisBlade(3.0, e2), BasisBlade(-5.0, e12), BasisBlade(12.2, scalar)])
mMultivector(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
*argssyntax. 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"""
basis_blades: list[BasisBlade] = list()
for m in args:
if isinstance(m, Multivector):
# Convert multivectors/basisblades to a list of BasisBlades
basis_blades = [*basis_blades, *list(m.blades.values())]
if isinstance(m, BasisBlade):
basis_blades = [*basis_blades, m]
return Multivector(basis_blades)add
add (*args:list[__main__.Multivector|geomalgebra.basisblades.BasisBlade])
Add Multivectors and BasisBlades
# Add BasisBlades
b1 = BasisBlade(2.0, e1)
b2 = BasisBlade(4.0, e12)
b3 = BasisBlade(-1.0, e1)
print(f'({b1}) + ({b2}) + ({b3}) = {add(b1, b2, b3)}')(2.0e1) + (4.0e12) + (-1.0e1) = e1 + 4.0e12
# Add Multivectors
m1 = Multivector([b1, b2, b3])
m2 = Multivector([b2, b3])
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.