1use num_complex::{Complex32, Complex64};
4
5use super::traits::Scalar;
6
7#[cfg(feature = "f16")]
8use half::f16;
9
10#[cfg(feature = "f128")]
11use super::extended::QuadFloat;
12
13pub trait HasFastFma: Scalar {}
23
24impl HasFastFma for f32 {}
25impl HasFastFma for f64 {}
26impl HasFastFma for Complex32 {}
27impl HasFastFma for Complex64 {}
28
29pub trait SimdCompatible: Scalar {
34 const SIMD_WIDTH: usize;
36
37 #[inline]
39 fn use_simd_for(len: usize) -> bool {
40 len >= Self::SIMD_WIDTH * 2
41 }
42}
43
44impl SimdCompatible for f32 {
45 #[cfg(target_arch = "x86_64")]
46 const SIMD_WIDTH: usize = 8; #[cfg(target_arch = "aarch64")]
49 const SIMD_WIDTH: usize = 4; #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
52 const SIMD_WIDTH: usize = 4;
53}
54
55impl SimdCompatible for f64 {
56 #[cfg(target_arch = "x86_64")]
57 const SIMD_WIDTH: usize = 4; #[cfg(target_arch = "aarch64")]
60 const SIMD_WIDTH: usize = 2; #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
63 const SIMD_WIDTH: usize = 2;
64}
65
66impl SimdCompatible for Complex32 {
67 #[cfg(target_arch = "x86_64")]
69 const SIMD_WIDTH: usize = 4;
70
71 #[cfg(target_arch = "aarch64")]
72 const SIMD_WIDTH: usize = 2;
73
74 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
75 const SIMD_WIDTH: usize = 2;
76}
77
78impl SimdCompatible for Complex64 {
79 #[cfg(target_arch = "x86_64")]
80 const SIMD_WIDTH: usize = 2;
81
82 #[cfg(target_arch = "aarch64")]
83 const SIMD_WIDTH: usize = 1;
84
85 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
86 const SIMD_WIDTH: usize = 1;
87}
88
89pub trait ScalarBatch: Scalar + SimdCompatible {
98 fn dot_batch(x: &[Self], y: &[Self]) -> Self;
106
107 fn sum_batch(x: &[Self]) -> Self;
109
110 fn asum_batch(x: &[Self]) -> Self::Real;
112
113 fn iamax_batch(x: &[Self]) -> usize;
115
116 fn scale_batch(alpha: Self, x: &mut [Self]);
118
119 fn axpy_batch(alpha: Self, x: &[Self], y: &mut [Self]);
121
122 fn fma_batch(a: &[Self], b: &[Self], c: &[Self], out: &mut [Self]);
124}
125
126impl ScalarBatch for f32 {
127 #[inline]
128 fn dot_batch(x: &[Self], y: &[Self]) -> Self {
129 debug_assert_eq!(x.len(), y.len());
130 let mut sum = 0.0f32;
131 for i in 0..x.len() {
132 sum = x[i].mul_add(y[i], sum);
133 }
134 sum
135 }
136
137 #[inline]
138 fn sum_batch(x: &[Self]) -> Self {
139 x.iter().copied().sum()
140 }
141
142 #[inline]
143 fn asum_batch(x: &[Self]) -> Self::Real {
144 x.iter().map(|&v| v.abs()).sum()
145 }
146
147 #[inline]
148 fn iamax_batch(x: &[Self]) -> usize {
149 let Some(mut max_val) = x.first().map(|v| v.abs()) else {
153 return 0;
154 };
155 let mut max_idx = 0;
156 for (i, xi) in x.iter().enumerate().skip(1) {
157 let val = xi.abs();
158 if val > max_val {
159 max_val = val;
160 max_idx = i;
161 }
162 }
163 max_idx
164 }
165
166 #[inline]
167 fn scale_batch(alpha: Self, x: &mut [Self]) {
168 for xi in x.iter_mut() {
169 *xi *= alpha;
170 }
171 }
172
173 #[inline]
174 fn axpy_batch(alpha: Self, x: &[Self], y: &mut [Self]) {
175 debug_assert_eq!(x.len(), y.len());
176 for i in 0..x.len() {
177 y[i] = alpha.mul_add(x[i], y[i]);
178 }
179 }
180
181 #[inline]
182 fn fma_batch(a: &[Self], b: &[Self], c: &[Self], out: &mut [Self]) {
183 debug_assert_eq!(a.len(), b.len());
184 debug_assert_eq!(a.len(), c.len());
185 debug_assert_eq!(a.len(), out.len());
186 for i in 0..a.len() {
187 out[i] = a[i].mul_add(b[i], c[i]);
188 }
189 }
190}
191
192impl ScalarBatch for f64 {
193 #[inline]
194 fn dot_batch(x: &[Self], y: &[Self]) -> Self {
195 debug_assert_eq!(x.len(), y.len());
196 let mut sum = 0.0f64;
197 for i in 0..x.len() {
198 sum = x[i].mul_add(y[i], sum);
199 }
200 sum
201 }
202
203 #[inline]
204 fn sum_batch(x: &[Self]) -> Self {
205 x.iter().copied().sum()
206 }
207
208 #[inline]
209 fn asum_batch(x: &[Self]) -> Self::Real {
210 x.iter().map(|&v| v.abs()).sum()
211 }
212
213 #[inline]
214 fn iamax_batch(x: &[Self]) -> usize {
215 let Some(mut max_val) = x.first().map(|v| v.abs()) else {
219 return 0;
220 };
221 let mut max_idx = 0;
222 for (i, xi) in x.iter().enumerate().skip(1) {
223 let val = xi.abs();
224 if val > max_val {
225 max_val = val;
226 max_idx = i;
227 }
228 }
229 max_idx
230 }
231
232 #[inline]
233 fn scale_batch(alpha: Self, x: &mut [Self]) {
234 for xi in x.iter_mut() {
235 *xi *= alpha;
236 }
237 }
238
239 #[inline]
240 fn axpy_batch(alpha: Self, x: &[Self], y: &mut [Self]) {
241 debug_assert_eq!(x.len(), y.len());
242 for i in 0..x.len() {
243 y[i] = alpha.mul_add(x[i], y[i]);
244 }
245 }
246
247 #[inline]
248 fn fma_batch(a: &[Self], b: &[Self], c: &[Self], out: &mut [Self]) {
249 debug_assert_eq!(a.len(), b.len());
250 debug_assert_eq!(a.len(), c.len());
251 debug_assert_eq!(a.len(), out.len());
252 for i in 0..a.len() {
253 out[i] = a[i].mul_add(b[i], c[i]);
254 }
255 }
256}
257
258impl ScalarBatch for Complex32 {
259 #[inline]
260 fn dot_batch(x: &[Self], y: &[Self]) -> Self {
261 debug_assert_eq!(x.len(), y.len());
262 let mut sum = Complex32::new(0.0, 0.0);
263 for i in 0..x.len() {
264 sum += x[i] * y[i];
265 }
266 sum
267 }
268
269 #[inline]
270 fn sum_batch(x: &[Self]) -> Self {
271 x.iter().copied().sum()
272 }
273
274 #[inline]
275 fn asum_batch(x: &[Self]) -> Self::Real {
276 x.iter().map(|z| z.re.abs() + z.im.abs()).sum()
277 }
278
279 #[inline]
280 fn iamax_batch(x: &[Self]) -> usize {
281 let Some(mut max_val) = x.first().map(|z| z.re.abs() + z.im.abs()) else {
286 return 0;
287 };
288 let mut max_idx = 0;
289 for (i, z) in x.iter().enumerate().skip(1) {
290 let val = z.re.abs() + z.im.abs();
291 if val > max_val {
292 max_val = val;
293 max_idx = i;
294 }
295 }
296 max_idx
297 }
298
299 #[inline]
300 fn scale_batch(alpha: Self, x: &mut [Self]) {
301 for xi in x.iter_mut() {
302 *xi *= alpha;
303 }
304 }
305
306 #[inline]
307 fn axpy_batch(alpha: Self, x: &[Self], y: &mut [Self]) {
308 debug_assert_eq!(x.len(), y.len());
309 for i in 0..x.len() {
310 y[i] += alpha * x[i];
311 }
312 }
313
314 #[inline]
315 fn fma_batch(a: &[Self], b: &[Self], c: &[Self], out: &mut [Self]) {
316 debug_assert_eq!(a.len(), b.len());
317 debug_assert_eq!(a.len(), c.len());
318 debug_assert_eq!(a.len(), out.len());
319 for i in 0..a.len() {
320 out[i] = a[i] * b[i] + c[i];
321 }
322 }
323}
324
325impl ScalarBatch for Complex64 {
326 #[inline]
327 fn dot_batch(x: &[Self], y: &[Self]) -> Self {
328 debug_assert_eq!(x.len(), y.len());
329 let mut sum = Complex64::new(0.0, 0.0);
330 for i in 0..x.len() {
331 sum += x[i] * y[i];
332 }
333 sum
334 }
335
336 #[inline]
337 fn sum_batch(x: &[Self]) -> Self {
338 x.iter().copied().sum()
339 }
340
341 #[inline]
342 fn asum_batch(x: &[Self]) -> Self::Real {
343 x.iter().map(|z| z.re.abs() + z.im.abs()).sum()
344 }
345
346 #[inline]
347 fn iamax_batch(x: &[Self]) -> usize {
348 let Some(mut max_val) = x.first().map(|z| z.re.abs() + z.im.abs()) else {
353 return 0;
354 };
355 let mut max_idx = 0;
356 for (i, z) in x.iter().enumerate().skip(1) {
357 let val = z.re.abs() + z.im.abs();
358 if val > max_val {
359 max_val = val;
360 max_idx = i;
361 }
362 }
363 max_idx
364 }
365
366 #[inline]
367 fn scale_batch(alpha: Self, x: &mut [Self]) {
368 for xi in x.iter_mut() {
369 *xi *= alpha;
370 }
371 }
372
373 #[inline]
374 fn axpy_batch(alpha: Self, x: &[Self], y: &mut [Self]) {
375 debug_assert_eq!(x.len(), y.len());
376 for i in 0..x.len() {
377 y[i] += alpha * x[i];
378 }
379 }
380
381 #[inline]
382 fn fma_batch(a: &[Self], b: &[Self], c: &[Self], out: &mut [Self]) {
383 debug_assert_eq!(a.len(), b.len());
384 debug_assert_eq!(a.len(), c.len());
385 debug_assert_eq!(a.len(), out.len());
386 for i in 0..a.len() {
387 out[i] = a[i] * b[i] + c[i];
388 }
389 }
390}
391
392#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub enum ScalarClass {
398 RealF32,
400 RealF64,
402 ComplexF32,
404 ComplexF64,
406 RealF16,
408 RealF128,
410 Other,
412}
413
414pub trait ScalarClassify: Scalar {
416 const CLASS: ScalarClass;
418
419 const PRECISION_LEVEL: u8;
421
422 const STORAGE_BYTES: usize = core::mem::size_of::<Self>();
424}
425
426impl ScalarClassify for f32 {
427 const CLASS: ScalarClass = ScalarClass::RealF32;
428 const PRECISION_LEVEL: u8 = 2;
429}
430
431impl ScalarClassify for f64 {
432 const CLASS: ScalarClass = ScalarClass::RealF64;
433 const PRECISION_LEVEL: u8 = 3;
434}
435
436impl ScalarClassify for Complex32 {
437 const CLASS: ScalarClass = ScalarClass::ComplexF32;
438 const PRECISION_LEVEL: u8 = 2;
439}
440
441impl ScalarClassify for Complex64 {
442 const CLASS: ScalarClass = ScalarClass::ComplexF64;
443 const PRECISION_LEVEL: u8 = 3;
444}
445
446#[cfg(feature = "f16")]
447impl ScalarClassify for f16 {
448 const CLASS: ScalarClass = ScalarClass::RealF16;
449 const PRECISION_LEVEL: u8 = 1;
450}
451
452#[cfg(feature = "f128")]
453impl ScalarClassify for QuadFloat {
454 const CLASS: ScalarClass = ScalarClass::RealF128;
455 const PRECISION_LEVEL: u8 = 4;
456}
457
458pub trait UnrollHints: Scalar {
463 const UNROLL_FACTOR: usize;
465
466 const BLOCK_SIZE: usize;
468
469 const PREFER_STREAMING: bool;
471}
472
473impl UnrollHints for f32 {
474 const UNROLL_FACTOR: usize = 8;
475 const BLOCK_SIZE: usize = 64;
476 const PREFER_STREAMING: bool = true;
477}
478
479impl UnrollHints for f64 {
480 const UNROLL_FACTOR: usize = 4;
481 const BLOCK_SIZE: usize = 32;
482 const PREFER_STREAMING: bool = true;
483}
484
485impl UnrollHints for Complex32 {
486 const UNROLL_FACTOR: usize = 4;
487 const BLOCK_SIZE: usize = 32;
488 const PREFER_STREAMING: bool = true;
489}
490
491impl UnrollHints for Complex64 {
492 const UNROLL_FACTOR: usize = 2;
493 const BLOCK_SIZE: usize = 16;
494 const PREFER_STREAMING: bool = true;
495}
496
497pub trait ExtendedPrecision: Scalar {
502 type Accumulator: Scalar;
504
505 fn to_accumulator(self) -> Self::Accumulator;
507
508 fn from_accumulator(acc: Self::Accumulator) -> Self;
510}
511
512impl ExtendedPrecision for f32 {
513 type Accumulator = f64;
514
515 #[inline]
516 fn to_accumulator(self) -> f64 {
517 self as f64
518 }
519
520 #[inline]
521 fn from_accumulator(acc: f64) -> f32 {
522 acc as f32
523 }
524}
525
526#[cfg(feature = "f128")]
531impl ExtendedPrecision for f64 {
532 type Accumulator = QuadFloat;
533
534 #[inline]
535 fn to_accumulator(self) -> QuadFloat {
536 QuadFloat::from(self)
537 }
538
539 #[inline]
540 fn from_accumulator(acc: QuadFloat) -> f64 {
541 let tf = acc.inner();
545 tf.hi() + tf.lo()
546 }
547}
548
549#[cfg(not(feature = "f128"))]
557impl ExtendedPrecision for f64 {
558 type Accumulator = f64;
559
560 #[inline]
561 fn to_accumulator(self) -> f64 {
562 self
563 }
564
565 #[inline]
566 fn from_accumulator(acc: f64) -> f64 {
567 acc
568 }
569}
570
571impl ExtendedPrecision for Complex32 {
572 type Accumulator = Complex64;
573
574 #[inline]
575 fn to_accumulator(self) -> Complex64 {
576 Complex64::new(self.re as f64, self.im as f64)
577 }
578
579 #[inline]
580 fn from_accumulator(acc: Complex64) -> Complex32 {
581 Complex32::new(acc.re as f32, acc.im as f32)
582 }
583}
584
585impl ExtendedPrecision for Complex64 {
586 type Accumulator = Complex64;
587
588 #[inline]
589 fn to_accumulator(self) -> Complex64 {
590 self
591 }
592
593 #[inline]
594 fn from_accumulator(acc: Complex64) -> Complex64 {
595 acc
596 }
597}
598
599#[derive(Debug, Clone, Copy)]
607pub struct KahanSum<T: Scalar> {
608 sum: T,
609 compensation: T,
610}
611
612impl<T: Scalar> Default for KahanSum<T> {
613 fn default() -> Self {
614 Self::new()
615 }
616}
617
618impl<T: Scalar> KahanSum<T> {
619 #[inline]
621 pub fn new() -> Self {
622 Self {
623 sum: T::zero(),
624 compensation: T::zero(),
625 }
626 }
627
628 #[inline]
630 pub fn add(&mut self, value: T) {
631 let y = value - self.compensation;
632 let t = self.sum + y;
633 self.compensation = (t - self.sum) - y;
634 self.sum = t;
635 }
636
637 #[inline]
639 pub fn sum(self) -> T {
640 self.sum
641 }
642}
643
644#[inline]
648pub fn pairwise_sum<T: Scalar>(values: &[T]) -> T {
649 const THRESHOLD: usize = 32;
650
651 if values.is_empty() {
652 return T::zero();
653 }
654 if values.len() <= THRESHOLD {
655 return values.iter().copied().fold(T::zero(), |acc, x| acc + x);
656 }
657
658 let mid = values.len() / 2;
659 pairwise_sum(&values[..mid]) + pairwise_sum(&values[mid..])
660}
661
662#[derive(Debug, Clone, Copy)]
666pub struct KBKSum<T: Scalar> {
667 sum: T,
668 cs: T,
669 ccs: T,
670}
671
672impl<T: Scalar> Default for KBKSum<T> {
673 fn default() -> Self {
674 Self::new()
675 }
676}
677
678impl<T: Scalar> KBKSum<T> {
679 #[inline]
681 pub fn new() -> Self {
682 Self {
683 sum: T::zero(),
684 cs: T::zero(),
685 ccs: T::zero(),
686 }
687 }
688
689 #[inline]
691 pub fn add(&mut self, value: T) {
692 let t = self.sum + value;
693 let c = if Scalar::abs(self.sum) >= Scalar::abs(value) {
694 (self.sum - t) + value
695 } else {
696 (value - t) + self.sum
697 };
698 self.sum = t;
699
700 let t2 = self.cs + c;
701 let cc = if Scalar::abs(self.cs) >= Scalar::abs(c) {
702 (self.cs - t2) + c
703 } else {
704 (c - t2) + self.cs
705 };
706 self.cs = t2;
707 self.ccs += cc;
708 }
709
710 #[inline]
712 pub fn sum(self) -> T {
713 self.sum + self.cs + self.ccs
714 }
715}