1use wide::f32x8;
13
14use crate::GpuMultivector;
15use cliffy_core::GA3;
16
17#[must_use]
23pub fn geometric_product_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
24 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 {
117 coeffs: [c0, c1, c2, c3, c4, c5, c6, c7],
118 }
119}
120
121#[inline]
123#[must_use]
124pub fn addition_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
125 let a_vec = f32x8::from(a.coeffs);
126 let b_vec = f32x8::from(b.coeffs);
127 let result = a_vec + b_vec;
128 GpuMultivector {
129 coeffs: result.to_array(),
130 }
131}
132
133#[inline]
135#[must_use]
136pub fn subtraction_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
137 let a_vec = f32x8::from(a.coeffs);
138 let b_vec = f32x8::from(b.coeffs);
139 let result = a_vec - b_vec;
140 GpuMultivector {
141 coeffs: result.to_array(),
142 }
143}
144
145#[inline]
147#[must_use]
148pub fn scalar_mul_simd(a: &GpuMultivector, scalar: f32) -> GpuMultivector {
149 let a_vec = f32x8::from(a.coeffs);
150 let s_vec = f32x8::splat(scalar);
151 let result = a_vec * s_vec;
152 GpuMultivector {
153 coeffs: result.to_array(),
154 }
155}
156
157#[inline]
162#[must_use]
163pub fn reverse_simd(a: &GpuMultivector) -> GpuMultivector {
164 let signs = f32x8::from([1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0]);
170 let a_vec = f32x8::from(a.coeffs);
171 let result = a_vec * signs;
172 GpuMultivector {
173 coeffs: result.to_array(),
174 }
175}
176
177#[inline]
181#[must_use]
182pub fn grade_involution_simd(a: &GpuMultivector) -> GpuMultivector {
183 let signs = f32x8::from([1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0]);
186 let a_vec = f32x8::from(a.coeffs);
187 let result = a_vec * signs;
188 GpuMultivector {
189 coeffs: result.to_array(),
190 }
191}
192
193#[inline]
197#[must_use]
198pub fn conjugate_simd(a: &GpuMultivector) -> GpuMultivector {
199 let signs = f32x8::from([1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0]);
202 let a_vec = f32x8::from(a.coeffs);
203 let result = a_vec * signs;
204 GpuMultivector {
205 coeffs: result.to_array(),
206 }
207}
208
209#[inline]
211#[must_use]
212pub fn norm_squared_simd(a: &GpuMultivector) -> f32 {
213 let rev = reverse_simd(a);
215 let prod = geometric_product_simd(a, &rev);
216 prod.coeffs[0]
217}
218
219#[inline]
221#[must_use]
222pub fn norm_simd(a: &GpuMultivector) -> f32 {
223 norm_squared_simd(a).sqrt()
224}
225
226#[inline]
228#[must_use]
229pub fn normalize_simd(a: &GpuMultivector) -> GpuMultivector {
230 let n = norm_simd(a);
231 if n > 1e-10 {
232 scalar_mul_simd(a, 1.0 / n)
233 } else {
234 GpuMultivector::zero()
235 }
236}
237
238#[inline]
242#[must_use]
243pub fn sandwich_simd(a: &GpuMultivector, b: &GpuMultivector) -> GpuMultivector {
244 let b_rev = reverse_simd(b);
245 let temp = geometric_product_simd(b, a);
246 geometric_product_simd(&temp, &b_rev)
247}
248
249#[inline]
251#[must_use]
252pub fn dot_simd(a: &GpuMultivector, b: &GpuMultivector) -> f32 {
253 let a_vec = f32x8::from(a.coeffs);
254 let b_vec = f32x8::from(b.coeffs);
255 let prod = a_vec * b_vec;
256 prod.reduce_add()
258}
259
260#[must_use]
264pub fn exp_bivector_simd(b: &GpuMultivector) -> GpuMultivector {
265 let bx = b.coeffs[3]; let by = b.coeffs[5]; let bz = b.coeffs[6]; let mag_sq = bx * bx + by * by + bz * bz;
272 let mag = mag_sq.sqrt();
273
274 if mag < 1e-10 {
275 let mut result = GpuMultivector::scalar(1.0);
277 result.coeffs[3] = bx;
278 result.coeffs[5] = by;
279 result.coeffs[6] = bz;
280 return result;
281 }
282
283 let cos_mag = mag.cos();
284 let sin_mag_over_mag = mag.sin() / mag;
285
286 GpuMultivector {
287 coeffs: [
288 cos_mag,
289 0.0,
290 0.0,
291 bx * sin_mag_over_mag,
292 0.0,
293 by * sin_mag_over_mag,
294 bz * sin_mag_over_mag,
295 0.0,
296 ],
297 }
298}
299
300#[inline]
302#[must_use]
303pub fn lerp_simd(a: &GpuMultivector, b: &GpuMultivector, t: f32) -> GpuMultivector {
304 let a_vec = f32x8::from(a.coeffs);
305 let b_vec = f32x8::from(b.coeffs);
306 let t_vec = f32x8::splat(t);
307 let one_minus_t = f32x8::splat(1.0 - t);
308 let result = a_vec * one_minus_t + b_vec * t_vec;
309 GpuMultivector {
310 coeffs: result.to_array(),
311 }
312}
313
314#[must_use]
318pub fn rotor_slerp_simd(a: &GpuMultivector, b: &GpuMultivector, t: f32) -> GpuMultivector {
319 let dot = dot_simd(a, b);
321
322 let dot = dot.clamp(-1.0, 1.0);
324
325 if dot.abs() > 0.9995 {
327 let result = lerp_simd(a, b, t);
328 return normalize_simd(&result);
329 }
330
331 let a_rev = reverse_simd(a);
334 let ratio = geometric_product_simd(&a_rev, b);
335
336 let scalar = ratio.coeffs[0];
339 let theta = scalar.clamp(-1.0, 1.0).acos();
340
341 if theta.abs() < 1e-10 {
342 return *a;
343 }
344
345 let scale = t * theta / theta.sin();
347 let mut log_ratio = GpuMultivector::zero();
348 log_ratio.coeffs[3] = ratio.coeffs[3] * scale;
349 log_ratio.coeffs[5] = ratio.coeffs[5] * scale;
350 log_ratio.coeffs[6] = ratio.coeffs[6] * scale;
351
352 let exp_log = exp_bivector_simd(&log_ratio);
353 geometric_product_simd(a, &exp_log)
354}
355
356pub struct SimdBatch;
361
362impl SimdBatch {
363 #[must_use]
365 pub fn geometric_product(a: &[GpuMultivector], b: &[GpuMultivector]) -> Vec<GpuMultivector> {
366 a.iter()
367 .zip(b.iter())
368 .map(|(a, b)| geometric_product_simd(a, b))
369 .collect()
370 }
371
372 #[must_use]
374 pub fn addition(a: &[GpuMultivector], b: &[GpuMultivector]) -> Vec<GpuMultivector> {
375 a.iter()
376 .zip(b.iter())
377 .map(|(a, b)| addition_simd(a, b))
378 .collect()
379 }
380
381 #[must_use]
383 pub fn sandwich(rotors: &[GpuMultivector], vectors: &[GpuMultivector]) -> Vec<GpuMultivector> {
384 rotors
385 .iter()
386 .zip(vectors.iter())
387 .map(|(r, v)| sandwich_simd(v, r))
388 .collect()
389 }
390
391 pub fn exp(bivectors: &[GpuMultivector]) -> Vec<GpuMultivector> {
393 bivectors.iter().map(exp_bivector_simd).collect()
394 }
395
396 #[must_use]
398 pub fn rotor_slerp(a: &[GpuMultivector], b: &[GpuMultivector], t: f32) -> Vec<GpuMultivector> {
399 a.iter()
400 .zip(b.iter())
401 .map(|(a, b)| rotor_slerp_simd(a, b, t))
402 .collect()
403 }
404
405 pub fn normalize(mvs: &[GpuMultivector]) -> Vec<GpuMultivector> {
407 mvs.iter().map(normalize_simd).collect()
408 }
409
410 #[must_use]
412 pub fn from_ga3(mvs: &[GA3]) -> Vec<GpuMultivector> {
413 mvs.iter().map(std::convert::Into::into).collect()
414 }
415
416 #[must_use]
418 pub fn to_ga3(mvs: &[GpuMultivector]) -> Vec<GA3> {
419 mvs.iter().map(|mv| (*mv).into()).collect()
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 #![allow(clippy::float_cmp)]
426 use super::*;
429
430 #[test]
431 fn test_addition_simd() {
432 let a = GpuMultivector::vector(1.0, 2.0, 3.0);
433 let b = GpuMultivector::vector(4.0, 5.0, 6.0);
434 let result = addition_simd(&a, &b);
435 assert_eq!(result.get_vector(), (5.0, 7.0, 9.0));
436 }
437
438 #[test]
439 fn test_subtraction_simd() {
440 let a = GpuMultivector::vector(5.0, 7.0, 9.0);
441 let b = GpuMultivector::vector(1.0, 2.0, 3.0);
442 let result = subtraction_simd(&a, &b);
443 assert_eq!(result.get_vector(), (4.0, 5.0, 6.0));
444 }
445
446 #[test]
447 fn test_scalar_mul_simd() {
448 let a = GpuMultivector::vector(1.0, 2.0, 3.0);
449 let result = scalar_mul_simd(&a, 2.0);
450 assert_eq!(result.get_vector(), (2.0, 4.0, 6.0));
451 }
452
453 #[test]
454 fn test_geometric_product_scalars() {
455 let a = GpuMultivector::scalar(3.0);
456 let b = GpuMultivector::scalar(4.0);
457 let result = geometric_product_simd(&a, &b);
458 assert!((result.get_scalar() - 12.0).abs() < 1e-6);
459 }
460
461 #[test]
462 fn test_geometric_product_vectors() {
463 let e1 = GpuMultivector {
465 coeffs: [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
466 };
467 let result = geometric_product_simd(&e1, &e1);
468 assert!((result.get_scalar() - 1.0).abs() < 1e-6);
469 }
470
471 #[test]
472 fn test_geometric_product_bivector() {
473 let e1 = GpuMultivector {
475 coeffs: [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
476 };
477 let e2 = GpuMultivector {
478 coeffs: [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0],
479 };
480 let result = geometric_product_simd(&e1, &e2);
481 assert!((result.coeffs[3] - 1.0).abs() < 1e-6); }
483
484 #[test]
485 fn test_reverse_simd() {
486 let mut a = GpuMultivector::zero();
487 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);
493 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); }
498
499 #[test]
500 fn test_norm_simd() {
501 let v = GpuMultivector::vector(3.0, 4.0, 0.0);
502 let n = norm_simd(&v);
503 assert!((n - 5.0).abs() < 1e-6);
504 }
505
506 #[test]
507 fn test_normalize_simd() {
508 let v = GpuMultivector::vector(3.0, 4.0, 0.0);
509 let normalized = normalize_simd(&v);
510 let (x, y, z) = normalized.get_vector();
511 assert!((x - 0.6).abs() < 1e-6);
512 assert!((y - 0.8).abs() < 1e-6);
513 assert!(z.abs() < 1e-6);
514 }
515
516 #[test]
517 fn test_lerp_simd() {
518 let a = GpuMultivector::scalar(0.0);
519 let b = GpuMultivector::scalar(10.0);
520 let mid = lerp_simd(&a, &b, 0.5);
521 assert!((mid.get_scalar() - 5.0).abs() < 1e-6);
522 }
523
524 #[test]
525 fn test_exp_bivector_small() {
526 let mut b = GpuMultivector::zero();
528 b.coeffs[3] = 0.001; let result = exp_bivector_simd(&b);
530 assert!((result.coeffs[0] - 1.0).abs() < 0.01);
531 }
532
533 #[test]
534 fn test_sandwich_identity() {
535 let rotor = GpuMultivector::scalar(1.0);
537 let v = GpuMultivector::vector(1.0, 2.0, 3.0);
538 let result = sandwich_simd(&v, &rotor);
539 let (x, y, z) = result.get_vector();
540 assert!((x - 1.0).abs() < 1e-6);
541 assert!((y - 2.0).abs() < 1e-6);
542 assert!((z - 3.0).abs() < 1e-6);
543 }
544
545 #[test]
546 fn test_batch_operations() {
547 let a = vec![GpuMultivector::scalar(1.0), GpuMultivector::scalar(2.0)];
548 let b = vec![GpuMultivector::scalar(3.0), GpuMultivector::scalar(4.0)];
549
550 let results = SimdBatch::addition(&a, &b);
551 assert!((results[0].get_scalar() - 4.0).abs() < 1e-6);
552 assert!((results[1].get_scalar() - 6.0).abs() < 1e-6);
553 }
554}