diff --git a/.github/workflows/actions.yaml b/.github/workflows/actions.yaml index 59607fe..37ca833 100644 --- a/.github/workflows/actions.yaml +++ b/.github/workflows/actions.yaml @@ -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: diff --git a/README.md b/README.md index f5a43fd..c6d1518 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/codegen/src/algebra.rs b/codegen/src/algebra.rs index 4c4b84d..cda9507 100644 --- a/codegen/src/algebra.rs +++ b/codegen/src/algebra.rs @@ -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::()); - formatter.pad_integral(self.scalar >= 0, "", string.as_str()) - } + let name = format!("e{}", self.component_bits().map(|index| format!("{:X}", index)).collect::()); + 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)), ] } diff --git a/codegen/src/compile.rs b/codegen/src/compile.rs index 10c560a..9b8af07 100644 --- a/codegen/src/compile.rs +++ b/codegen/src/compile.rs @@ -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![], + ), }, )], ), diff --git a/codegen/src/main.rs b/codegen/src/main.rs index 3b133bb..571be4b 100644 --- a/codegen/src/main.rs +++ b/codegen/src/main.rs @@ -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, ¶meter_a); + MultiVectorClass::derive_squared_magnitude("SquaredMagnitude", &scalar_product, &reversal, ¶meter_a); emitter.emit(&squared_magnitude).unwrap(); let magnitude = MultiVectorClass::derive_magnitude("Magnitude", &squared_magnitude, ¶meter_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, ¶meter_a); + MultiVectorClass::derive_inverse("Inverse", &geometric_product, &squared_magnitude, &reversal, ¶meter_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(¶meter_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(¶meter_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"), - ¶meter_a, - ¶meter_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"), + ¶meter_a, + ¶meter_b, + ); + emitter.emit(&transformation).unwrap(); } } } diff --git a/src/lib.rs b/src/lib.rs index 4ddc13f..f84d724 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { fn scalar_product(self, other: T) -> Self::Output; } -/// `self * other * self` -/// -/// Basically a sandwich product without an involution -pub trait Reflection { - type Output; - fn reflection(self, other: T) -> Self::Output; -} - -/// `self * other * self.transpose()` +/// `self * other * self.reversion()` /// /// Also called sandwich product pub trait Transformation { @@ -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 {