@atell_soundtheory This looks really nice. There’s a few things that you’ve done here that seem really nice from a data presentation and ergonomic standpoint.
However, if I were to make some performance suggestions, it seems like you’re encoding a bit too much data in the type of the objects. For instance,
julia> cl2 = CliffordAlgebra(2)
Cl(2,0,0)
┌───────┬──────────────┬───────┐
│ +1 │ +e1 +e2 │ +e1e2 │
├───────┼──────────────┼───────┤
│ +e1 │ +1 +e1e2 │ +e2 │
│ +e2 │ -e1e2 +1 │ -e1 │
├───────┼──────────────┼───────┤
│ +e1e2 │ -e2 +e1 │ -1 │
└───────┴──────────────┴───────┘
julia> t1 = typeof(cl2.e1)
MultiVector{CliffordAlgebra{2, 0, 0, (:e1, :e2), ((), (1,), (2,), (1, 2)), (((1, 1), (2, 1), (3, 1), (4, 1)), ((2, 1), (1, 1), (4, 1), (3, 1)), ((3, 1), (4, -1), (1, 1), (2, -1)), ((4, 1), (3, -1), (2, 1), (1, -1)))}, Int64, (2,), 1}
julia> t2 = typeof(cl2.e2)
MultiVector{CliffordAlgebra{2, 0, 0, (:e1, :e2), ((), (1,), (2,), (1, 2)), (((1, 1), (2, 1), (3, 1), (4, 1)), ((2, 1), (1, 1), (4, 1), (3, 1)), ((3, 1), (4, -1), (1, 1), (2, -1)), ((4, 1), (3, -1), (2, 1), (1, -1)))}, Int64, (3,), 1}
julia> t1 == t2
false
which means that you’re going to be causing a lot of dynamic dispatch in general. I think ideally, all elements of a specific Clifford algebra should have the same type in order to efficiently do (runtime) operations with them
Of course, your approach does make it easier to do compile time operations, but generally I find it easier to lift things from runtime to compile time than vice versa.