Multivectors

Summary: Multivectors are a linear combination of basis blades.
from dataclasses import dataclass, field, InitVar
from geomalgebra.basisblades import ga2d, BasisBlade
# For convenience, we unpack the basis blades into the global scope
scalar, e1, e2, e12 = ga2d

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
    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.prettyprint

Multivector

 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)])
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 called args.

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 do list_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.