use wide::f32x8;
use crate::GpuMultivector;
use cliffy_core::GA3;
#[must_use]
pub fn geometric_product_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
let c0 = a.coeffs[0] * b.coeffs[0] + a.coeffs[1] * b.coeffs[1] + a.coeffs[2] * b.coeffs[2] - a.coeffs[3] * b.coeffs[3] + a.coeffs[4] * b.coeffs[4] - a.coeffs[5] * b.coeffs[5] - a.coeffs[6] * b.coeffs[6] - a.coeffs[7] * b.coeffs[7];
let c1 = a.coeffs[0] * b.coeffs[1] + a.coeffs[1] * b.coeffs[0] - a.coeffs[2] * b.coeffs[3] + a.coeffs[3] * b.coeffs[2] - a.coeffs[4] * b.coeffs[5] + a.coeffs[5] * b.coeffs[4] + a.coeffs[6] * b.coeffs[7] - a.coeffs[7] * b.coeffs[6];
let c2 = a.coeffs[0] * b.coeffs[2] + a.coeffs[1] * b.coeffs[3] + a.coeffs[2] * b.coeffs[0] - a.coeffs[3] * b.coeffs[1] - a.coeffs[4] * b.coeffs[6] - a.coeffs[5] * b.coeffs[7] + a.coeffs[6] * b.coeffs[4] + a.coeffs[7] * b.coeffs[5];
let c3 = a.coeffs[0] * b.coeffs[3] + a.coeffs[1] * b.coeffs[2] - a.coeffs[2] * b.coeffs[1] + a.coeffs[3] * b.coeffs[0] + a.coeffs[4] * b.coeffs[7] + a.coeffs[5] * b.coeffs[6] - a.coeffs[6] * b.coeffs[5] + a.coeffs[7] * b.coeffs[4];
let c4 = a.coeffs[0] * b.coeffs[4] + a.coeffs[1] * b.coeffs[5] + a.coeffs[2] * b.coeffs[6] + a.coeffs[3] * b.coeffs[7] + a.coeffs[4] * b.coeffs[0] - a.coeffs[5] * b.coeffs[1] - a.coeffs[6] * b.coeffs[2] - a.coeffs[7] * b.coeffs[3];
let c5 = a.coeffs[0] * b.coeffs[5] + a.coeffs[1] * b.coeffs[4] - a.coeffs[2] * b.coeffs[7] - a.coeffs[3] * b.coeffs[6] - a.coeffs[4] * b.coeffs[1] + a.coeffs[5] * b.coeffs[0] + a.coeffs[6] * b.coeffs[3] + a.coeffs[7] * b.coeffs[2];
let c6 = a.coeffs[0] * b.coeffs[6] + a.coeffs[1] * b.coeffs[7] + a.coeffs[2] * b.coeffs[4] + a.coeffs[3] * b.coeffs[5] - a.coeffs[4] * b.coeffs[2] - a.coeffs[5] * b.coeffs[3] + a.coeffs[6] * b.coeffs[0] - a.coeffs[7] * b.coeffs[1];
let c7 = a.coeffs[0] * b.coeffs[7] + a.coeffs[1] * b.coeffs[6] - a.coeffs[2] * b.coeffs[5] + a.coeffs[3] * b.coeffs[4] + a.coeffs[4] * b.coeffs[3] - a.coeffs[5] * b.coeffs[2] + a.coeffs[6] * b.coeffs[1] + a.coeffs[7] * b.coeffs[0];
GpuMultivector {
coeffs: [c0, c1, c2, c3, c4, c5, c6, c7],
}
}
#[inline]
#[must_use]
pub fn addition_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
let a_vec = f32x8::from(a.coeffs);
let b_vec = f32x8::from(b.coeffs);
let result = a_vec + b_vec;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[inline]
#[must_use]
pub fn subtraction_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
let a_vec = f32x8::from(a.coeffs);
let b_vec = f32x8::from(b.coeffs);
let result = a_vec - b_vec;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[inline]
#[must_use]
pub fn scalar_mul_simd(a: &GpuMultivector, scalar: f32) -> GpuMultivector {
let a_vec = f32x8::from(a.coeffs);
let s_vec = f32x8::splat(scalar);
let result = a_vec * s_vec;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[inline]
#[must_use]
pub fn reverse_simd(a: &GpuMultivector) -> GpuMultivector {
let signs = f32x8::from([1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0]);
let a_vec = f32x8::from(a.coeffs);
let result = a_vec * signs;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[inline]
#[must_use]
pub fn grade_involution_simd(a: &GpuMultivector) -> GpuMultivector {
let signs = f32x8::from([1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0]);
let a_vec = f32x8::from(a.coeffs);
let result = a_vec * signs;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[inline]
#[must_use]
pub fn conjugate_simd(a: &GpuMultivector) -> GpuMultivector {
let signs = f32x8::from([1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0]);
let a_vec = f32x8::from(a.coeffs);
let result = a_vec * signs;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[inline]
#[must_use]
pub fn norm_squared_simd(a: &GpuMultivector) -> f32 {
let rev = reverse_simd(a);
let prod = geometric_product_simd(a, &rev);
prod.coeffs[0]
}
#[inline]
#[must_use]
pub fn norm_simd(a: &GpuMultivector) -> f32 {
norm_squared_simd(a).sqrt()
}
#[inline]
#[must_use]
pub fn normalize_simd(a: &GpuMultivector) -> GpuMultivector {
let n = norm_simd(a);
if n > 1e-10 {
scalar_mul_simd(a, 1.0 / n)
} else {
GpuMultivector::zero()
}
}
#[inline]
#[must_use]
pub fn sandwich_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
let b_rev = reverse_simd(b);
let temp = geometric_product_simd(b, a);
geometric_product_simd(&temp, &b_rev)
}
#[inline]
#[must_use]
pub fn dot_simd(a: &GpuMultivector, b: &GpuMultivector) -> f32 {
let a_vec = f32x8::from(a.coeffs);
let b_vec = f32x8::from(b.coeffs);
let prod = a_vec * b_vec;
prod.reduce_add()
}
#[must_use]
pub fn exp_bivector_simd(b: &GpuMultivector) -> GpuMultivector {
let bx = b.coeffs[3]; let by = b.coeffs[5]; let bz = b.coeffs[6];
let mag_sq = bx * bx + by * by + bz * bz;
let mag = mag_sq.sqrt();
if mag < 1e-10 {
let mut result = GpuMultivector::scalar(1.0);
result.coeffs[3] = bx;
result.coeffs[5] = by;
result.coeffs[6] = bz;
return result;
}
let cos_mag = mag.cos();
let sin_mag_over_mag = mag.sin() / mag;
GpuMultivector {
coeffs: [
cos_mag,
0.0,
0.0,
bx * sin_mag_over_mag,
0.0,
by * sin_mag_over_mag,
bz * sin_mag_over_mag,
0.0,
],
}
}
#[inline]
#[must_use]
pub fn lerp_simd(a: &GpuMultivector, b: &GpuMultivector, t: f32) -> GpuMultivector {
let a_vec = f32x8::from(a.coeffs);
let b_vec = f32x8::from(b.coeffs);
let t_vec = f32x8::splat(t);
let one_minus_t = f32x8::splat(1.0 - t);
let result = a_vec * one_minus_t + b_vec * t_vec;
GpuMultivector {
coeffs: result.to_array(),
}
}
#[must_use]
pub fn rotor_slerp_simd(a: &GpuMultivector, b: &GpuMultivector, t: f32) -> GpuMultivector {
let dot = dot_simd(a, b);
let dot = dot.clamp(-1.0, 1.0);
if dot.abs() > 0.9995 {
let result = lerp_simd(a, b, t);
return normalize_simd(&result);
}
let a_rev = reverse_simd(a);
let ratio = geometric_product_simd(&a_rev, b);
let scalar = ratio.coeffs[0];
let theta = scalar.clamp(-1.0, 1.0).acos();
if theta.abs() < 1e-10 {
return *a;
}
let scale = t * theta / theta.sin();
let mut log_ratio = GpuMultivector::zero();
log_ratio.coeffs[3] = ratio.coeffs[3] * scale;
log_ratio.coeffs[5] = ratio.coeffs[5] * scale;
log_ratio.coeffs[6] = ratio.coeffs[6] * scale;
let exp_log = exp_bivector_simd(&log_ratio);
geometric_product_simd(a, &exp_log)
}
pub struct SimdBatch;
impl SimdBatch {
#[must_use]
pub fn geometric_product(a: &[GpuMultivector], b: &[GpuMultivector]) -> Vec<GpuMultivector> {
a.iter()
.zip(b.iter())
.map(|(a, b)| geometric_product_simd(a, b))
.collect()
}
#[must_use]
pub fn addition(a: &[GpuMultivector], b: &[GpuMultivector]) -> Vec<GpuMultivector> {
a.iter()
.zip(b.iter())
.map(|(a, b)| addition_simd(a, b))
.collect()
}
#[must_use]
pub fn sandwich(rotors: &[GpuMultivector], vectors: &[GpuMultivector]) -> Vec<GpuMultivector> {
rotors
.iter()
.zip(vectors.iter())
.map(|(r, v)| sandwich_simd(v, r))
.collect()
}
pub fn exp(bivectors: &[GpuMultivector]) -> Vec<GpuMultivector> {
bivectors.iter().map(exp_bivector_simd).collect()
}
#[must_use]
pub fn rotor_slerp(a: &[GpuMultivector], b: &[GpuMultivector], t: f32) -> Vec<GpuMultivector> {
a.iter()
.zip(b.iter())
.map(|(a, b)| rotor_slerp_simd(a, b, t))
.collect()
}
pub fn normalize(mvs: &[GpuMultivector]) -> Vec<GpuMultivector> {
mvs.iter().map(normalize_simd).collect()
}
#[must_use]
pub fn from_ga3(mvs: &[GA3]) -> Vec<GpuMultivector> {
mvs.iter().map(std::convert::Into::into).collect()
}
#[must_use]
pub fn to_ga3(mvs: &[GpuMultivector]) -> Vec<GA3> {
mvs.iter().map(|mv| (*mv).into()).collect()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::float_cmp)]
use super::*;
#[test]
fn test_addition_simd() {
let a = GpuMultivector::vector(1.0, 2.0, 3.0);
let b = GpuMultivector::vector(4.0, 5.0, 6.0);
let result = addition_simd(&a, &b);
assert_eq!(result.get_vector(), (5.0, 7.0, 9.0));
}
#[test]
fn test_subtraction_simd() {
let a = GpuMultivector::vector(5.0, 7.0, 9.0);
let b = GpuMultivector::vector(1.0, 2.0, 3.0);
let result = subtraction_simd(&a, &b);
assert_eq!(result.get_vector(), (4.0, 5.0, 6.0));
}
#[test]
fn test_scalar_mul_simd() {
let a = GpuMultivector::vector(1.0, 2.0, 3.0);
let result = scalar_mul_simd(&a, 2.0);
assert_eq!(result.get_vector(), (2.0, 4.0, 6.0));
}
#[test]
fn test_geometric_product_scalars() {
let a = GpuMultivector::scalar(3.0);
let b = GpuMultivector::scalar(4.0);
let result = geometric_product_simd(&a, &b);
assert!((result.get_scalar() - 12.0).abs() < 1e-6);
}
#[test]
fn test_geometric_product_vectors() {
let e1 = GpuMultivector {
coeffs: [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
};
let result = geometric_product_simd(&e1, &e1);
assert!((result.get_scalar() - 1.0).abs() < 1e-6);
}
#[test]
fn test_geometric_product_bivector() {
let e1 = GpuMultivector {
coeffs: [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
};
let e2 = GpuMultivector {
coeffs: [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0],
};
let result = geometric_product_simd(&e1, &e2);
assert!((result.coeffs[3] - 1.0).abs() < 1e-6); }
#[test]
fn test_reverse_simd() {
let mut a = GpuMultivector::zero();
a.coeffs[0] = 1.0; a.coeffs[1] = 2.0; a.coeffs[3] = 3.0; a.coeffs[7] = 4.0;
let rev = reverse_simd(&a);
assert_eq!(rev.coeffs[0], 1.0); assert_eq!(rev.coeffs[1], 2.0); assert_eq!(rev.coeffs[3], -3.0); assert_eq!(rev.coeffs[7], -4.0); }
#[test]
fn test_norm_simd() {
let v = GpuMultivector::vector(3.0, 4.0, 0.0);
let n = norm_simd(&v);
assert!((n - 5.0).abs() < 1e-6);
}
#[test]
fn test_normalize_simd() {
let v = GpuMultivector::vector(3.0, 4.0, 0.0);
let normalized = normalize_simd(&v);
let (x, y, z) = normalized.get_vector();
assert!((x - 0.6).abs() < 1e-6);
assert!((y - 0.8).abs() < 1e-6);
assert!(z.abs() < 1e-6);
}
#[test]
fn test_lerp_simd() {
let a = GpuMultivector::scalar(0.0);
let b = GpuMultivector::scalar(10.0);
let mid = lerp_simd(&a, &b, 0.5);
assert!((mid.get_scalar() - 5.0).abs() < 1e-6);
}
#[test]
fn test_exp_bivector_small() {
let mut b = GpuMultivector::zero();
b.coeffs[3] = 0.001; let result = exp_bivector_simd(&b);
assert!((result.coeffs[0] - 1.0).abs() < 0.01);
}
#[test]
fn test_sandwich_identity() {
let rotor = GpuMultivector::scalar(1.0);
let v = GpuMultivector::vector(1.0, 2.0, 3.0);
let result = sandwich_simd(&v, &rotor);
let (x, y, z) = result.get_vector();
assert!((x - 1.0).abs() < 1e-6);
assert!((y - 2.0).abs() < 1e-6);
assert!((z - 3.0).abs() < 1e-6);
}
#[test]
fn test_batch_operations() {
let a = vec![GpuMultivector::scalar(1.0), GpuMultivector::scalar(2.0)];
let b = vec![GpuMultivector::scalar(3.0), GpuMultivector::scalar(4.0)];
let results = SimdBatch::addition(&a, &b);
assert!((results[0].get_scalar() - 4.0).abs() < 1e-6);
assert!((results[1].get_scalar() - 6.0).abs() < 1e-6);
}
}