Geometric Algebra Basics

Usually introductions to GA begin by defining various rules and going over derivations before doing anything useful with them. I will also define some rules but try to get to the interesting stuff more quickly.

Vectors

Like for the standard 2D vector algebra in GA we have two basis vectorsex,eyusing which arbitrary vectorsv=xex+yeycan be formed. Below is some runnable and editable code that forms such vectors and then displays them as points. The basis vectorsex,eyare labeled e0 and e1 in the code. We specify the non-zero coefficients for each basis vector when creating a new vector.
// Render point at x=10, y=-60
renderPointGA({ e0: 10, e1: -60 })
// Render point at x=-50, y=80
renderPointGA({ e0: -50, e1: 80 }, "red")
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-100-80-60-40-2020406080100-100-80-60-40-20020406080100

Geometric Product

The product which defines Geometric Algebra and is its most important aspect is called the Geometric Product. There are two useful rules for using the geometric product which will now be introduced.
Rule 1: Basis vectors square to +1
Multiplying two same basis vectors together with the geometric product will result in+1(for now...) if they are the same.exex=1,eyey=1This is similar to how the dot product in standard vector algebra works. Let's verify these results with the code again, this time just logging some text instead of visualizing.
var exSquared = ga.geometricProduct({ e0: 1 }, { e0: 1 })
var eySquared = ga.geometricProduct({ e1: 1 }, { e1: 1 })
log("e0^2:", exSquared)
log("e1^2:", eySquared)
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Press run
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Rule 2: Different basis vectors anti-commute
What is new is that we can also multiply two different basis vectors and the result will not be zero, but instead can't be simplified further.exey=exyexyhere is just shorthand for the two basis vectors multiplied together. Such elements made up of two basis vectors are called bivectors.
Importantly the order of the product matters. A rule is that when you swap the factors of a product of basis vectors you pick up a minus sign. We say that the basis vectors anti-commute.exy=exey=eyex=eyx
var exEy = ga.geometricProduct({ e0: 1 }, { e1: 1 })
var eyEx = ga.geometricProduct({ e1: 1 }, { e0: 1 })
log("e0 e1:", exEy)
log("e1 e0:", eyEx)
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Press run
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Practice
Let's now use these two basic rules we just learnt and see what some results are when we use them:exeyex=(Rewrite as shorthand)exyx=(Swap neighbours on the right, pick up minus sign)exxy=(Multiplying same basis vectors results in 1, e_xx = e_x e_x = 1)eyWe can verify these results with the code:
var exEy = ga.geometricProduct({ e0: 1 }, { e1: 1 })
var exEyEx = ga.geometricProduct(exEy, { e0: 1 })
log("e0 e1 e0:", exEyEx)
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Press run
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Terminology
Here's a list of some more terminology that is often used in GA
  • Multivector: any element of the algebra (eg.1+2ex+5exy)
  • Basis blade: basis vectors and any combination of them (eg. in 2D we have four in total:1,ex,ey,exy)
  • Grade: the degree of a multivector (eg.1is grade0,exis grade1,ex+5eyit also grade1,exyis grade2)

Rotors

Squaring bivectors

Now for something more interesting, let's see what happens if we square the bivectorexy, that is, multiplying it with itself:
var exEy = { e01: 1 }
var exEySquared = ga.geometricProduct(exEy, exEy)
log("e01^2", exEySquared)
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Press run
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
exy2=exyexy=exyxy=exyyx=exx=1We can see the square of the bivectorexyis1. This shows that such a bivector is very similar to the imaginary unitiof complex numbers which was specifically introduced to square to1. Here we didn't have to introduce anything new and we automatically had such an element in our algebra.

There is still one caveat. Whileexysquares to1, so doeseyx. So which one do we use? Let's try to visualize what multiplying a vector does for both of them usingv=exyvandv=eyxv.
var eXy = { e01: 1 }
var eYx = { e01: -1 } // e_yx = -e_xy
var p = { e0: 70, e1: 0 }
var a = ga.geometricProduct(eXy, p)
var b = ga.geometricProduct(eYx, p)
renderPointGA(p)
renderPointGA(a, "red")
renderPointGA(b, "blue")
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-100-80-60-40-2020406080100-100-80-60-40-20020406080100
We can see thatexyproduces a clockwise (CW) rotation by 90° andeyxproduces a counter-clockwise (CCW) rotation by 90°. We will stick with the CCW version usingeyx. Instead of that to make a CCW rotation we could have also swapped the order of the product (v=vexy) but usingeyxinstead will allow us to follow the usual conventions later.

Rotating 2D vectors using rotors

As mentioned beforeeyxcan be identified as the imaginary unitiof complex numbers hence we can represent complex numbers asa+beyxand a CCW rotation in the XY plane by an arbitrary angleϕcan be performed just like with complex numbers using Euler's formulaR(ϕ)=eϕeyx=cos(ϕ)+eyxsin(ϕ)The objectRyou get after exponentiating is called a rotor (because it rotates when you multiply with it). Unlike with complex numbers now however, we can multiply a vector by a rotor directly instead of having to treat vectors as if they were complex numbers.
var phi = Math.PI * 3 / 4 // 135°
// e^(phi e_{yx}) = e^(-phi e_{xy})
var r = ga.exponential({ e01: -phi })
var p = { e0: 70, e1: 0 }
// p rotated by 135° counter-clockwise
var rotatedP = ga.geometricProduct(r, p)
renderPointGA(p)
renderPointGA(rotatedP, "red")
renderInfo(ga.repr(rotatedP), "red")
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-100-80-60-40-2020406080100-100-80-60-40-20020406080100
R(ϕ)v=(cos(ϕ)+eyxsin(ϕ))(xex+yey)=ex(xcos(ϕ)ysin(ϕ))+ey(xsin(ϕ)+ycos(ϕ))

Higher dimensions

It turns out that the two dimensional rotor application formulav=Rvwas slightly special. In the general case it is necessary to use a two sided productv=RvR~which is also called the sandwich product.R~here stands for reversion ofRwhich just means reversing the order of all basis vectors. For exampleeyxbecomesexy. As we already know from the second rule of the geometric product, such a change of order just produces a minus sign for the bivectors, soeyx~=exy.
Another thing that changes with the sandwich product is that we multiply with the rotor twice, so our rotor will only need to contain half of the rotation angle.R(ϕ)=eeyxϕ2
We can now verify that this will indeed give the same results in 2D as the simple one-sided product
var phi = Math.PI * 3 / 4 // 135°
// e^(phi/2 e_{yx}) = e^(-phi/2 e_{xy})
// Only half the angle required with sandwich product
var r = ga.exponential({ e01: -phi / 2 })
var p = { e0: 70, e1: 0 }
// R p ~R (sandwich product)
// p rotated by 135° counter-clockwise
var rotatedP = ga.geometricProduct(
r,
ga.geometricProduct(p, ga.reversion(r))
)
renderPointGA(p)
renderPointGA(rotatedP, "red")
renderInfo(ga.repr(rotatedP), "red")
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-100-80-60-40-2020406080100-100-80-60-40-20020406080100
In the three dimensional case, if we wanted to create a rotor that rotates in the XZ plane byϕCCW our rotor would look like this:R(ϕ)=eezxϕ2We can then also combine rotations in different planes into a single rotor by multiplying them. A rotor that rotates byϕCCW in the XZ plane followed by a rotation ofθCCW in the XY plane looks like thisR(ϕ,θ)=eeyxθ2eezxϕ2
var phi = Math.PI * 3 / 4 // 135°
var theta = Math.PI / 2 // 90°
// CCW XZ rotation by phi
var r1 = ga3d.exponential({ e02: -phi / 2 })
// CCW XY rotation by theta
var r2 = ga3d.exponential({ e01: -theta / 2 })
// Compose XY and XZ rotation
var r = ga3d.geometricProduct(r2, r1)
var p = { e0: 70, e1: 0, e2: 0 }
// p first rotated by phi in XZ, then by theta in XY
var rotatedP = ga3d.geometricProduct(
r,
ga3d.geometricProduct(p, ga3d.reversion(r))
)
log("Rotated P:", rotatedP)
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Press run
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
These elements that are like the 3D version of complex numbers are called quaternions.

More on reversion

Applying the reversion operation on a rotor reverses its effect, for example applying the reversion operation to a rotor that rotates by 90° CCW will make it rotate by 90° CW (ie. -90° CCW). A result of this is that a rotor multiplied by its reversal produces the identityRR~=1which does nothing when applied as demonstrated in the code below. Here we also make use of the sandwichProduct() function instead of writing the sandwich product using geometricProduct() and reversion().
var phi = Math.PI * 3 / 4 // 135°
var theta = Math.PI / 2 // 90°
var r1 = ga3d.exponential({ e02: -phi / 2 })
var r2 = ga3d.exponential({ e01: -theta / 2 })
// Compose XY and XZ rotation
var r = ga3d.geometricProduct(r2, r1)
var p = { e0: 70, e1: 0, e2: 0 }
// (R ~R) p (R ~P)
var q = ga3d.sandwichProduct(
p,
ga3d.geometricProduct(r, ga3d.reversion(r))
)
log("q:", q)
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Press run
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Summary

  • Geometric product rule 1: basis vectors square to +1 (exex=exx=1,eyey=eyy=1)
  • Geometric product rule 2: different basis vectors anti-commute (exey=exy=eyx=eyex)
  • Rotor in XY plane rotating byϕcounter-clockwise:R(ϕ)=eϕeyx=cos(ϕ)+eyxsin(ϕ)
  • Reversion: reverse order of basis vectors (eg.exyz=ezyx=exyz), inverts rotors
  • Apply RotorRtoxusing sandwich product:x=RxR~

Conclusion

In this section we introduced Geometric Algebra. We learnt the basic rules of the Geometric Product and how to work with them. We also learnt about rotors which generalize the rotations created by complex numbers to any dimension and also puts them directly in the context of vectors.

In the next section we will learn how we can introduce translation in the rotors so we can do both translation and rotation using the same rotor and just one sandwich product. This will prove to have many advantages allow even further generalization.

Next: Projective Geometric Algebra