#geometric-algebra #algebra #clifford #proc-macro #hyper-complex

macro no-std algebraic-gen

Procedural Macro for Geometric Products of Any Dimension

1 unstable release

0.1.0 Nov 29, 2022

#960 in Math

Custom license

21KB
243 lines

algebraic-gen

Generates performant geometric products for any dimension of space.

The crate provides a macro generate_geometric_product!. Given a function identifier and a nonnegative integer literal, the macro generates a function that computes the geometric product of two coefficient representations of multivectors.

For example, generate_geometric_product!(my_product, 3) generates the following function:

pub fn my_product<A, B, T>(a: &A, b: &B) -> [T; 8]
where
    A: ::core::ops::Index<usize, Output = T>,
    B: ::core::ops::Index<usize, Output = T>,
    T: Copy +
        ::core::ops::Mul<Output = T> +
        ::core::ops::Add<Output = T> +
        ::core::ops::Sub<Output = T>,
{
    [
        a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3] - a[4] * b[4] - a[5] * b[5] - a[6] * b[6] + a[7] * b[7],
        a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2] + a[4] * b[5] - a[5] * b[4] - a[6] * b[7] - a[7] * b[6],
        a[0] * b[2] - a[1] * b[3] + a[2] * b[0] + a[3] * b[1] + a[4] * b[6] + a[5] * b[7] - a[6] * b[4] + a[7] * b[5],
        a[0] * b[3] + a[1] * b[2] - a[2] * b[1] + a[3] * b[0] - a[4] * b[7] + a[5] * b[6] - a[6] * b[5] - a[7] * b[4],
        a[0] * b[4] - a[1] * b[5] - a[2] * b[6] - a[3] * b[7] + a[4] * b[0] + a[5] * b[1] + a[6] * b[2] - a[7] * b[3],
        a[0] * b[5] + a[1] * b[4] + a[2] * b[7] - a[3] * b[6] - a[4] * b[1] + a[5] * b[0] + a[6] * b[3] + a[7] * b[2],
        a[0] * b[6] - a[1] * b[7] + a[2] * b[4] + a[3] * b[5] - a[4] * b[2] - a[5] * b[3] + a[6] * b[0] - a[7] * b[1],
        a[0] * b[7] + a[1] * b[6] - a[2] * b[5] + a[3] * b[4] + a[4] * b[3] - a[5] * b[2] + a[6] * b[1] + a[7] * b[0],
    ]
}

In this example, the implementation of the function will access a and b at indices 0..8. In the general case, it will access its arguments at indices 0..1 << D and return a value of type [T; 1 << D].

Example

The functions are straightforward to generate and use. Pass a (small) nonnegative integer to the macro and it generates the product function. (Check it out with cargo expand.)

use algebraic_gen::generate_geometric_product;

generate_geometric_product!(my_product, 3);

fn main() {
  let a: [f64; 8] = [1., 2., 3., 4., 5., 6., 7., 8.];
  let b: [f64; 8] = [8., 7., 6., 5., 4., 3., 2., 1.];

  let c = my_product(&a, &b);

  println!("The geometric product of {:?} and {:?} is {:?}", a, b, c);

  assert_eq!(c, [-88.0, -18.0, 60.0, -18.0, 72.0, 102.0, -36.0, 114.0]);
}

Geometric Algebra

A geometric algebra is defined with respect to a given dimension D of space. In such an algebra, there exist objects of different degrees from zero to D (inclusive). Objects of degree zero are scalars, objects of degree one are vectors, and objects of degree two, three, etc. are bivectors, trivectors, et cetera. These higher degree objects are created by wedge product of vectors. Combining all vectors via wedge product results a pseudoscalar of degree D. Objects of any degree can be scaled and added to form multivectors.

Importantly for this crate, any multivector can be represented uniquely as a linear combination of elements of a chosen base with 2^D different objects, meaning that we can represent multivectors as arrays of floating point numbers of that size. The return types [T; 1], [T; 2], [T; 4], [T; 8], etc., of the functions generated by this crate are exactly those representations of multivectors of geometric algebras of spaces with 0, 1, 2, 3, etc. dimensions, respectively.

The macro attaches documentation of the choice of base to the generated function. Sticking to 3D for an example, the choice of base in this crate is: one scalar (S), three vectors (X, Y, Z), three bivectors (X∧Y, X∧Z, Y∧X), and one pseudoscalar (X∧Y∧Z). The generated product interprets the 8-dimensional coefficient arrays as [S, X, Y, X∧Y, Z, X∧Z, Y∧Z, X∧Y∧Z]. In the documentation attached to the respective generated product functions, this is represented as [[], [0], [1], [0,1], [2], [0,2], [1,2], [0,1,2]].

On the Generation

Maybe there is better way to get the products that doesn't use the 'sledgehammer' proc macro approach. The generation is not performant, but we're talking build time.

The generation logic is inspired by All Hail Geometric Algebra!.

Brief description of the how the macro works:

  • Generates elements (base)
    • Product is simple concatenation here
    • Canonization with Bubblesort & a kind of duplication elimination
  • Generates product sums
    • Expand terms
    • More products & canonization
    • Adding/Subtracting based on sign returned by canonization
  • A bit of formatting

More details in the source code: algebra_generation.rs.

Dependencies

~1.5MB
~33K SLoC