Rand Stats



Geometric Algebra in Raku

Build Status

This module is an attempt to implement basic Geometric Algebra in Raku.

With this module you can create euclidean and anti-euclidean vectors of arbitrary size. You can then add and substract them, as well as multiplying them by real scalars as you would do with any vectors, but you can also multiply and divide as made possible by the geometric algebra.

Euclidean space

The module exports an array constant @e which serves as an orthonormal basis for an Euclidean space of infinite, countable dimension.

use Clifford;

say @e[0]**2;         # +1
say @e[0] + 2*@e[1];  # e0+2*e1
say @e[0]*@e[1];      # e0∧e1

Anti-Euclidean space

The module also exports an array constant which serves as an orthonormal basis for an anti-Euclidean space of infinite, countable dimension. This space is orthogonal to the Euclidean space.

use Clifford;

say @ē[0]**2;        # -1
say @e[0] + @ē[0];   # e0 + ē0
say @e[0]*@ē[0];     # e0∧ē0

The ē character is the voyel e with a macron. It is available as a default digraph on Vim as e-.

Minkowski plane

The module exports two constants no and ni which form a null basis of a Minkowski plane. This plane is orthogonal to both the Euclidean space and the anti-Euclidean space.

use Clifford;
say no;                 # 𝑜
say ni;                 # ∞
say no**2;              # 0
say ni**2;              # 0
say no*@e[0];           # 𝑜∧e0
say ni*@ē[0];           # ∞∧ē0
say no*ni;              # -1+𝑜∧∞
say (no*ni + ni*no)/2   # -1


Grade projection

The AT-POS method returns the grade projection:

say (no + @e[1] + @e[0]*@e[1])[1];   # 𝑜+e1

Derived products

There are several multiplicative operators derived from the geometric product. They are extensively discussed by Leo Dorst in his 2002 paper the inner products of Geometric Algebra.

This module uses unicode symbols as infix operators, but a ASCII method form is also available.

namedigraphinfix notationmethod notation
outer productAN$a ∧ $b$a.op($b)
inner product.M$a · $b$a.ip($b)
scalar product*-$a ∗ $b$a.sp($b)
commutator*X$a × $b$a.co($b)
left contraction7>$a ⌋ $b$a.lc($b)
right contraction7<$a ⌊ $b$a.rc($b)
dot productSb$a ∙ $b$a.dp($b)

All those infix operators are tighter than &infix:<*>.

All symbols are available as Vim digraphs by default.

Beware of the symbol used for the scalar product. It is the asterisk operator (digraph *-), not the usual multiplication sign (*). Here they are besides one an other: ∗*. Also the symbols for inner product ("centered dot") and dot product ("bullet operator", or "fat dot") look very similar in certain fonts, apparently.


The module also implements the three involutions:

given 1 + @e[0] + @e[0]∧@e[1] + @e[0]∧@e[1]∧@e[2] {
    say .reversion;    # 1+e0-e0∧e1-e0∧e1∧e2
    say .involution;   # 1-e0+e0∧e1-e0∧e1∧e2
    say .conjugation;  # 1-e0-e0∧e1+e0∧e1∧e2

Here the involution called 'involution' is the so-called main involution.

The module exports two postfix operators &postfix:<~> and &postfix:<^> respectively for the reversion and the main involution.


This module attempts to optimize computations by generating code during runtime, as inspired by Pablo Colapinto's work (see next section). In order to understand how the method works, consider for instance the geometric product of two vectors X = x1*e1 + x2*e2 + x3*e3 and Y = y1*e1 + y2*e2 + y3*e3:

X*Y = (x1*y1+x2*y2+x3*y3) +
      (x1*y2-x2*y1)*e1∧e2 +
      (x2*y3-x3*y2)*e2∧e3 +

This multivector is a linear combination of the unit scalar and the basis bivectors e1∧e2, e2∧e3 and e3∧e1. It would have been possible to know that beforehand by taking all possible products from e1, e2 and e3 and classify them, keeping track of the sign changes.

Assuming an array of coefficients can be associated with a list of basis unit multivectors in order to form a generic multivector, the coefficients of a product of two vectors could have been calculated by a function such as:

sub (@x, @y) {

This would be very efficient but we would have to use a different function for all possible kinds of products, and there are infinitely many of them. Since it's not possible to precompute an infinity of things, the module starts from nothing and generates a function at runtime, every time it encounters a product type it has never seen before.

To generate a function, early versions of this module were simply generating the literal string that defines it in Perl 6 and use EVAL on it. Current version uses a closure.