Adds more prebuilt projective geometric algebras.

Renames Automorph => Automorphism.
Renames Transpose => Reversal.
Renames Conjugate => Conjugation.
Removes Reflection (as that is covered by Transformation).
Fixes std::fmt::Display for BasisElement again (not only -1 but also 0).
This commit is contained in:
Alexander Meißner 2021-04-21 19:06:05 +02:00
parent 8c025b5dc2
commit 53e514d861
6 changed files with 78 additions and 68 deletions

View file

@ -21,12 +21,24 @@ jobs:
strategy:
matrix:
algebra:
- name: complex
descriptor: "complex:-1;Scalar:1;MultiVector:1,e0"
- name: epga1d
descriptor: "epga1d:1,1;Scalar:1;ComplexNumber:1,e01"
- name: ppga1d
descriptor: "ppga1d:0,1;Scalar:1;DualNumber:1,e01"
- name: hpga1d
descriptor: "hpga1d:-1,1;Scalar:1;SplitComplexNumber:1,e01"
- name: epga2d
descriptor: "epga2d:1,1,1;Scalar:1;MultiVector:1,e12,e1,e2|e0,e012,e01,-e02;Rotor:1,e12;Point:e12,e01,-e02;Plane:e0,e2,e1;Translator:1,e01,-e02;Motor:1,e12,e01,-e02;MotorDual:e012,e0,e2,e1"
- name: ppga2d
descriptor: "ppga2d:0,1,1;Scalar:1;MultiVector:1,e12,e1,e2|e0,e012,e01,-e02;Rotor:1,e12;Point:e12,e01,-e02;Plane:e0,e2,e1;Translator:1,e01,-e02;Motor:1,e12,e01,-e02;MotorDual:e012,e0,e2,e1"
- name: hpga2d
descriptor: "hpga2d:-1,1,1;Scalar:1;MultiVector:1,e12,e1,e2|e0,e012,e01,-e02;Rotor:1,e12;Point:e12,e01,-e02;Plane:e0,e2,e1;Translator:1,e01,-e02;Motor:1,e12,e01,-e02;MotorDual:e012,e0,e2,e1"
- name: epga3d
descriptor: "epga3d:1,1,1,1;Scalar:1;MultiVector:1,e23,-e13,e12|e0,-e023,e013,-e012|e123,e1,e2,e3|e0123,e01,e02,e03;Rotor:1,e23,-e13,e12;Point:e123,-e023,e013,-e012;Plane:e0,e1,e2,e3;Line:e01,e02,e03|e23,-e13,e12;Translator:1,e01,e02,e03;Motor:1,e23,-e13,e12|e0123,e01,e02,e03;PointAndPlane:e123,-e023,e013,-e012|e0,e1,e2,e3"
- name: ppga3d
descriptor: "ppga3d:0,1,1,1;Scalar:1;MultiVector:1,e23,-e13,e12|e0,-e023,e013,-e012|e123,e1,e2,e3|e0123,e01,e02,e03;Rotor:1,e23,-e13,e12;Point:e123,-e023,e013,-e012;Plane:e0,e1,e2,e3;Line:e01,e02,e03|e23,-e13,e12;Translator:1,e01,e02,e03;Motor:1,e23,-e13,e12|e0123,e01,e02,e03;PointAndPlane:e123,-e023,e013,-e012|e0,e1,e2,e3"
- name: hpga3d
descriptor: "hpga3d:-1,1,1,1;Scalar:1;MultiVector:1,e23,-e13,e12|e0,-e023,e013,-e012|e123,e1,e2,e3|e0123,e01,e02,e03;Rotor:1,e23,-e13,e12;Point:e123,-e023,e013,-e012;Plane:e0,e1,e2,e3;Line:e01,e02,e03|e23,-e13,e12;Translator:1,e01,e02,e03;Motor:1,e23,-e13,e12|e0123,e01,e02,e03;PointAndPlane:e123,-e023,e013,-e012|e0,e1,e2,e3"
steps:
- uses: actions/download-artifact@v2
with:

View file

@ -3,7 +3,7 @@
[![crates.io](https://img.shields.io/crates/v/geometric_algebra.svg)](https://crates.io/crates/geometric_algebra)
## About
This repository allows you to describe [geometric algebras](https://en.wikipedia.org/wiki/Geometric_algebra) with 1 to 16 generator elements and generate SIMD-ready, dependency-less libraries for them.
This repository allows you to describe [geometric algebras](https://en.wikipedia.org/wiki/Geometric_algebra) with 1 to 16 generator elements and generate SIMD-ready, dependency-less libraries for them. It also comes with a set of prebuilt projective geometric algebras in 1D, 2D and 3D which are elliptic, parabolic (euclidian) or hyperbolic.
## Architecture
- [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) Parser: See [examples](.github/workflows/actions.yaml)

View file

@ -50,8 +50,9 @@ impl BasisElement {
let mut generator_indices = name.chars();
assert_eq!(generator_indices.next().unwrap(), 'e');
for generator_index in generator_indices {
let generator = Self::from_index(1 << (generator_index.to_digit(16).unwrap()));
result = BasisElement::product(&result, &generator, algebra);
let generator_index = generator_index.to_digit(16).unwrap();
assert!((generator_index as usize) < algebra.generator_squares.len());
result = BasisElement::product(&result, &Self::from_index(1 << generator_index), algebra);
}
result
}
@ -95,12 +96,18 @@ impl BasisElement {
impl std::fmt::Display for BasisElement {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.index == 0 {
formatter.pad_integral(self.scalar >= 0, "", &self.scalar.abs().to_string())
} else {
let string = format!("e{}", self.component_bits().map(|index| format!("{:X}", index)).collect::<String>());
formatter.pad_integral(self.scalar >= 0, "", string.as_str())
}
let name = format!("e{}", self.component_bits().map(|index| format!("{:X}", index)).collect::<String>());
formatter.pad_integral(
self.scalar >= 0,
"",
if self.scalar == 0 {
"0"
} else if self.index == 0 {
"1"
} else {
name.as_str()
},
)
}
}
@ -171,9 +178,9 @@ impl Involution {
let involution = Self::identity(algebra);
vec![
("Neg", involution.negated(|_grade| true)),
("Automorph", involution.negated(|grade| grade % 2 == 1)),
("Transpose", involution.negated(|grade| grade % 4 >= 2)),
("Conjugate", involution.negated(|grade| (grade + 3) % 4 < 2)),
("Automorphism", involution.negated(|grade| grade % 2 == 1)),
("Reversal", involution.negated(|grade| grade % 4 >= 2)),
("Conjugation", involution.negated(|grade| (grade + 3) % 4 < 2)),
("Dual", involution.dual(algebra)),
]
}

View file

@ -1179,19 +1179,15 @@ impl MultiVectorClass {
DataType::MultiVector(involution_result.multi_vector_class()),
Expression {
size: 1,
content: match name {
"Reflection" => ExpressionContent::Variable(parameter_a.name),
"Transformation" => ExpressionContent::InvokeInstanceMethod(
parameter_a.data_type.clone(),
Box::new(Expression {
size: 1,
content: ExpressionContent::Variable(parameter_a.name),
}),
involution_result.name,
vec![],
),
_ => unreachable!(),
},
content: ExpressionContent::InvokeInstanceMethod(
parameter_a.data_type.clone(),
Box::new(Expression {
size: 1,
content: ExpressionContent::Variable(parameter_a.name),
}),
involution_result.name,
vec![],
),
},
)],
),

View file

@ -60,7 +60,6 @@ fn main() {
for class in registry.classes.iter() {
emitter.emit(&AstNode::ClassDefinition { class }).unwrap();
}
let involution_name = if generator_squares == vec![-1] { "Conjugate" } else { "Transpose" };
let mut trait_implementations = std::collections::BTreeMap::new();
for class_a in registry.classes.iter() {
let parameter_a = Parameter {
@ -118,10 +117,10 @@ fn main() {
}
for (parameter_b, pair_trait_implementations) in pair_trait_implementations.values() {
if let Some(scalar_product) = pair_trait_implementations.get("ScalarProduct") {
if let Some(involution) = single_trait_implementations.get(involution_name) {
if let Some(reversal) = single_trait_implementations.get("Reversal") {
if parameter_a.multi_vector_class() == parameter_b.multi_vector_class() {
let squared_magnitude =
MultiVectorClass::derive_squared_magnitude("SquaredMagnitude", &scalar_product, &involution, &parameter_a);
MultiVectorClass::derive_squared_magnitude("SquaredMagnitude", &scalar_product, &reversal, &parameter_a);
emitter.emit(&squared_magnitude).unwrap();
let magnitude = MultiVectorClass::derive_magnitude("Magnitude", &squared_magnitude, &parameter_a);
emitter.emit(&magnitude).unwrap();
@ -140,9 +139,9 @@ fn main() {
single_trait_implementations.insert(result_of_trait!(signum).name.to_string(), signum);
}
if let Some(squared_magnitude) = single_trait_implementations.get("SquaredMagnitude") {
if let Some(involution) = single_trait_implementations.get(involution_name) {
if let Some(reversal) = single_trait_implementations.get("Reversal") {
let inverse =
MultiVectorClass::derive_inverse("Inverse", &geometric_product, &squared_magnitude, &involution, &parameter_a);
MultiVectorClass::derive_inverse("Inverse", &geometric_product, &squared_magnitude, &reversal, &parameter_a);
emitter.emit(&inverse).unwrap();
single_trait_implementations.insert(result_of_trait!(inverse).name.to_string(), inverse);
}
@ -187,7 +186,7 @@ fn main() {
emitter.emit(&division).unwrap();
}
}
if let Some(involution) = single_trait_implementations.get(involution_name) {
if let Some(reversal) = single_trait_implementations.get("Reversal") {
if let Some(b_trait_implementations) = trait_implementations.get(&geometric_product_result.multi_vector_class().class_name) {
if let Some(b_pair_trait_implementations) = b_trait_implementations.2.get(&parameter_a.multi_vector_class().class_name) {
if let Some(geometric_product_2) = b_pair_trait_implementations.1.get("GeometricProduct") {
@ -198,18 +197,16 @@ fn main() {
if let Some(c_pair_trait_implementations) =
c_trait_implementations.2.get(&parameter_b.multi_vector_class().class_name)
{
for name in &["Reflection", "Transformation"] {
let transformation = MultiVectorClass::derive_sandwich_product(
name,
&geometric_product,
&geometric_product_2,
&involution,
c_pair_trait_implementations.1.get("Into"),
&parameter_a,
&parameter_b,
);
emitter.emit(&transformation).unwrap();
}
let transformation = MultiVectorClass::derive_sandwich_product(
"Transformation",
&geometric_product,
&geometric_product_2,
&reversal,
c_pair_trait_implementations.1.get("Into"),
&parameter_a,
&parameter_b,
);
emitter.emit(&transformation).unwrap();
}
}
}

View file

@ -1,12 +1,18 @@
#![cfg_attr(all(target_arch = "wasm32", target_feature = "simd128"), feature(wasm_simd))]
#![cfg_attr(all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "neon"), feature(stdsimd))]
pub mod complex;
pub mod epga1d;
pub mod ppga1d;
pub mod hpga1d;
pub mod epga2d;
pub mod ppga2d;
pub mod hpga2d;
pub mod epga3d;
pub mod ppga3d;
pub mod hpga3d;
pub mod simd;
impl complex::Scalar {
impl epga1d::Scalar {
pub const fn new(real: f32) -> Self {
Self { g0: real }
}
@ -15,16 +21,16 @@ impl complex::Scalar {
self.g0
}
pub fn sqrt(self) -> complex::MultiVector {
pub fn sqrt(self) -> epga1d::ComplexNumber {
if self.g0 < 0.0 {
complex::MultiVector::new(0.0, (-self.g0).sqrt())
epga1d::ComplexNumber::new(0.0, (-self.g0).sqrt())
} else {
complex::MultiVector::new(self.g0.sqrt(), 0.0)
epga1d::ComplexNumber::new(self.g0.sqrt(), 0.0)
}
}
}
impl complex::MultiVector {
impl epga1d::ComplexNumber {
pub const fn new(real: f32, imaginary: f32) -> Self {
Self {
g0: simd::Simd32x2 {
@ -73,23 +79,23 @@ pub trait Dual {
/// Negates elements with `grade % 2 == 1`
///
/// Also called main involution
pub trait Automorph {
pub trait Automorphism {
type Output;
fn automorph(self) -> Self::Output;
fn automorphism(self) -> Self::Output;
}
/// Negates elements with `grade % 4 >= 2`
///
/// Also called reversion
pub trait Transpose {
/// Also called transpose
pub trait Reversal {
type Output;
fn transpose(self) -> Self::Output;
fn reversal(self) -> Self::Output;
}
/// Negates elements with `(grade + 3) % 4 < 2`
pub trait Conjugate {
pub trait Conjugation {
type Output;
fn conjugate(self) -> Self::Output;
fn conjugation(self) -> Self::Output;
}
/// General multi vector multiplication
@ -140,15 +146,7 @@ pub trait ScalarProduct<T> {
fn scalar_product(self, other: T) -> Self::Output;
}
/// `self * other * self`
///
/// Basically a sandwich product without an involution
pub trait Reflection<T> {
type Output;
fn reflection(self, other: T) -> Self::Output;
}
/// `self * other * self.transpose()`
/// `self * other * self.reversion()`
///
/// Also called sandwich product
pub trait Transformation<T> {
@ -170,7 +168,7 @@ pub trait Magnitude {
fn magnitude(self) -> Self::Output;
}
/// Direction without magnitude (set to scalar `1.0`)
/// Direction without magnitude (set to scalar `-1.0` or `1.0`)
///
/// Also called sign or normalize
pub trait Signum {