1use crate::error::StatsResult;
8use crate::error_standardization::ErrorMessages;
9use scirs2_core::ndarray::{s, Array1, ArrayBase, Data, Ix1};
10use scirs2_core::numeric::{Float, NumCast};
11use scirs2_core::simd_ops::{AutoOptimizer, PlatformCapabilities, SimdUnifiedOps};
12
13#[allow(dead_code)]
39pub fn mean_enhanced<F, D>(x: &ArrayBase<D, Ix1>) -> StatsResult<F>
40where
41 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
42 D: Data<Elem = F>,
43{
44 if x.is_empty() {
45 return Err(ErrorMessages::empty_array("x"));
46 }
47
48 let n = x.len();
49 let optimizer = AutoOptimizer::new();
50 let capabilities = PlatformCapabilities::detect();
51
52 let sum = if n < 16 {
54 x.iter().fold(F::zero(), |acc, &val| acc + val)
56 } else if n < 1024 || !capabilities.avx2_available {
57 F::simd_sum(&x.view())
59 } else {
60 compensated_simd_sum(x, &optimizer)
62 };
63
64 Ok(sum / F::from(n).unwrap())
65}
66
67#[allow(dead_code)]
81pub fn variance_enhanced<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
82where
83 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::iter::Sum<F>,
84 D: Data<Elem = F>,
85{
86 let n = x.len();
87 if n == 0 {
88 return Err(ErrorMessages::empty_array("x"));
89 }
90 if n <= ddof {
91 return Err(ErrorMessages::insufficientdata(
92 "variance calculation",
93 ddof + 1,
94 n,
95 ));
96 }
97
98 let optimizer = AutoOptimizer::new();
99
100 if n > 1000 && optimizer.should_use_simd(n) {
102 welford_variance_simd(x, ddof)
103 } else {
104 let mean = mean_enhanced(x)?;
106 let sum_sq_dev = if optimizer.should_use_simd(n) {
107 simd_sum_squared_deviations(x, mean)
108 } else {
109 x.iter()
110 .map(|&val| {
111 let dev = val - mean;
112 dev * dev
113 })
114 .fold(F::zero(), |acc, val| acc + val)
115 };
116
117 Ok(sum_sq_dev / F::from(n - ddof).unwrap())
118 }
119}
120
121#[allow(dead_code)]
135pub fn correlation_simd_enhanced<F, D1, D2>(
136 x: &ArrayBase<D1, Ix1>,
137 y: &ArrayBase<D2, Ix1>,
138) -> StatsResult<F>
139where
140 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
141 D1: Data<Elem = F>,
142 D2: Data<Elem = F>,
143{
144 if x.is_empty() {
145 return Err(ErrorMessages::empty_array("x"));
146 }
147 if y.is_empty() {
148 return Err(ErrorMessages::empty_array("y"));
149 }
150 if x.len() != y.len() {
151 return Err(ErrorMessages::length_mismatch("x", x.len(), "y", y.len()));
152 }
153
154 let n = x.len();
155 let optimizer = AutoOptimizer::new();
156
157 if optimizer.should_use_simd(n) {
158 simd_correlation_full(x, y)
160 } else {
161 scalar_correlation_optimized(x, y)
163 }
164}
165
166#[allow(dead_code)]
180pub fn comprehensive_stats_simd<F, D>(
181 x: &ArrayBase<D, Ix1>,
182 ddof: usize,
183) -> StatsResult<ComprehensiveStats<F>>
184where
185 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::fmt::Debug + std::iter::Sum<F>,
186 D: Data<Elem = F>,
187{
188 let n = x.len();
189 if n == 0 {
190 return Err(ErrorMessages::empty_array("x"));
191 }
192 if n <= ddof {
193 return Err(ErrorMessages::insufficientdata(
194 "comprehensive statistics",
195 ddof + 1,
196 n,
197 ));
198 }
199
200 let optimizer = AutoOptimizer::new();
201
202 if optimizer.should_use_simd(n) && n > 100 {
203 simd_comprehensive_single_pass(x, ddof)
204 } else {
205 let mean = mean_enhanced(x)?;
207 let variance = variance_enhanced(x, ddof)?;
208 let std = variance.sqrt();
209
210 Ok(ComprehensiveStats {
212 mean,
213 variance,
214 std,
215 skewness: F::zero(), kurtosis: F::zero(), count: n,
218 })
219 }
220}
221
222#[derive(Debug, Clone)]
224pub struct ComprehensiveStats<F> {
225 pub mean: F,
226 pub variance: F,
227 pub std: F,
228 pub skewness: F,
229 pub kurtosis: F,
230 pub count: usize,
231}
232
233#[allow(dead_code)]
237fn compensated_simd_sum<F, D>(x: &ArrayBase<D, Ix1>, optimizer: &AutoOptimizer) -> F
238where
239 F: Float + NumCast + SimdUnifiedOps + Copy,
240 D: Data<Elem = F>,
241{
242 const CHUNK_SIZE: usize = 8192; let mut sum = F::zero();
245 let mut compensation = F::zero();
246
247 let n = x.len();
249 let num_full_chunks = n / CHUNK_SIZE;
250 let remainder = n % CHUNK_SIZE;
251
252 for chunk in x.exact_chunks(CHUNK_SIZE) {
253 let chunk_sum = if optimizer.should_use_simd(chunk.len()) {
254 F::simd_sum(&chunk.view())
255 } else {
256 chunk.iter().fold(F::zero(), |acc, &val| acc + val)
257 };
258
259 let y = chunk_sum - compensation;
261 let t = sum + y;
262 compensation = (t - sum) - y;
263 sum = t;
264 }
265
266 if remainder > 0 {
268 let remainder_start = num_full_chunks * CHUNK_SIZE;
269 let remainder_slice = x.slice(s![remainder_start..]);
270 let remainder_sum = remainder_slice
271 .iter()
272 .fold(F::zero(), |acc, &val| acc + val);
273
274 let y = remainder_sum - compensation;
276 let t = sum + y;
277 sum = t;
278 }
279
280 sum
281}
282
283#[allow(dead_code)]
296pub fn mean_ultra_simd<F, D>(x: &ArrayBase<D, Ix1>) -> StatsResult<F>
297where
298 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
299 D: Data<Elem = F>,
300{
301 if x.is_empty() {
302 return Err(ErrorMessages::empty_array("x"));
303 }
304
305 let n = x.len();
306 let capabilities = PlatformCapabilities::detect();
307
308 let sum = if n < 32 {
310 x.iter().fold(F::zero(), |acc, &val| acc + val)
312 } else if n < 64 || !capabilities.has_avx2() {
313 F::simd_sum(&x.view())
315 } else {
316 bandwidth_saturated_sum_ultra(x)
318 };
319
320 Ok(sum / F::from(n).unwrap())
321}
322
323#[allow(dead_code)]
328pub fn variance_ultra_simd<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
329where
330 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::iter::Sum<F>,
331 D: Data<Elem = F>,
332{
333 let n = x.len();
334 if n == 0 {
335 return Err(ErrorMessages::empty_array("x"));
336 }
337 if n <= ddof {
338 return Err(ErrorMessages::insufficientdata(
339 "variance calculation",
340 ddof + 1,
341 n,
342 ));
343 }
344
345 let capabilities = PlatformCapabilities::detect();
346
347 if n >= 128 && capabilities.has_avx2() {
348 bandwidth_saturated_variance_ultra(x, ddof)
350 } else if n >= 64 {
351 let mean = mean_ultra_simd(x)?;
353 let sum_sq_dev = bandwidth_saturated_sum_squared_deviations_ultra(x, mean);
354 Ok(sum_sq_dev / F::from(n - ddof).unwrap())
355 } else {
356 variance_enhanced(x, ddof)
358 }
359}
360
361#[allow(dead_code)]
366pub fn correlation_ultra_simd<F, D1, D2>(
367 x: &ArrayBase<D1, Ix1>,
368 y: &ArrayBase<D2, Ix1>,
369) -> StatsResult<F>
370where
371 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
372 D1: Data<Elem = F>,
373 D2: Data<Elem = F>,
374{
375 if x.is_empty() {
376 return Err(ErrorMessages::empty_array("x"));
377 }
378 if y.is_empty() {
379 return Err(ErrorMessages::empty_array("y"));
380 }
381 if x.len() != y.len() {
382 return Err(ErrorMessages::length_mismatch("x", x.len(), "y", y.len()));
383 }
384
385 let n = x.len();
386 let capabilities = PlatformCapabilities::detect();
387
388 if n >= 128 && capabilities.has_avx2() {
389 bandwidth_saturated_correlation_ultra(x, y)
391 } else if n >= 64 {
392 simd_correlation_full(x, y)
394 } else {
395 scalar_correlation_optimized(x, y)
397 }
398}
399
400#[allow(dead_code)]
405pub fn comprehensive_stats_ultra_simd<F, D>(
406 x: &ArrayBase<D, Ix1>,
407 ddof: usize,
408) -> StatsResult<ComprehensiveStats<F>>
409where
410 F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::fmt::Debug + std::iter::Sum<F>,
411 D: Data<Elem = F>,
412{
413 let n = x.len();
414 if n == 0 {
415 return Err(ErrorMessages::empty_array("x"));
416 }
417 if n <= ddof {
418 return Err(ErrorMessages::insufficientdata(
419 "comprehensive statistics",
420 ddof + 1,
421 n,
422 ));
423 }
424
425 let capabilities = PlatformCapabilities::detect();
426
427 if n >= 256 && capabilities.has_avx2() {
428 bandwidth_saturated_comprehensive_ultra(x, ddof)
430 } else if n >= 64 {
431 simd_comprehensive_single_pass(x, ddof)
433 } else {
434 comprehensive_stats_simd(x, ddof)
436 }
437}
438
439#[allow(dead_code)]
443fn bandwidth_saturated_sum_ultra<F, D>(x: &ArrayBase<D, Ix1>) -> F
444where
445 F: Float + NumCast + SimdUnifiedOps + Copy,
446 D: Data<Elem = F>,
447{
448 let n = x.len();
449 let chunk_size = 16; let mut total_sum = F::zero();
452
453 for chunk_start in (0..n).step_by(chunk_size) {
455 let chunk_end = (chunk_start + chunk_size).min(n);
456 let chunk_len = chunk_end - chunk_start;
457
458 if chunk_len == chunk_size {
459 let chunk_data: Array1<f32> = x
461 .slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
462 .iter()
463 .map(|&val| val.to_f64().unwrap() as f32)
464 .collect();
465
466 let chunk_sum = f32::simd_sum_f32_ultra(&chunk_data.view());
468 total_sum = total_sum + F::from(chunk_sum as f64).unwrap();
469 } else {
470 for i in chunk_start..chunk_end {
472 total_sum = total_sum + x[i];
473 }
474 }
475 }
476
477 total_sum
478}
479
480#[allow(dead_code)]
482fn bandwidth_saturated_variance_ultra<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
483where
484 F: Float + NumCast + SimdUnifiedOps + Copy,
485 D: Data<Elem = F>,
486{
487 let n = x.len();
488 let chunk_size = 16;
489
490 let mut sum = F::zero();
491 let mut sum_sq = F::zero();
492
493 for chunk_start in (0..n).step_by(chunk_size) {
495 let chunk_end = (chunk_start + chunk_size).min(n);
496 let chunk_len = chunk_end - chunk_start;
497
498 if chunk_len == chunk_size {
499 let chunk_data: Array1<f32> = x
501 .slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
502 .iter()
503 .map(|&val| val.to_f64().unwrap() as f32)
504 .collect();
505
506 let chunk_sum = f32::simd_sum_f32_ultra(&chunk_data.view());
508
509 let mut chunk_squared: Array1<f32> = Array1::zeros(chunk_size);
511 f32::simd_mul_f32_ultra(
512 &chunk_data.view(),
513 &chunk_data.view(),
514 &mut chunk_squared.view_mut(),
515 );
516 let chunk_sum_sq = f32::simd_sum_f32_ultra(&chunk_squared.view());
517
518 sum = sum + F::from(chunk_sum as f64).unwrap();
519 sum_sq = sum_sq + F::from(chunk_sum_sq as f64).unwrap();
520 } else {
521 for i in chunk_start..chunk_end {
523 let val = x[i];
524 sum = sum + val;
525 sum_sq = sum_sq + val * val;
526 }
527 }
528 }
529
530 let n_f = F::from(n).unwrap();
531 let mean = sum / n_f;
532 let variance = (sum_sq - n_f * mean * mean) / F::from(n - ddof).unwrap();
533
534 Ok(variance)
535}
536
537#[allow(dead_code)]
539fn bandwidth_saturated_sum_squared_deviations_ultra<F, D>(x: &ArrayBase<D, Ix1>, mean: F) -> F
540where
541 F: Float + NumCast + SimdUnifiedOps + Copy,
542 D: Data<Elem = F>,
543{
544 let n = x.len();
545 let chunk_size = 16;
546 let mean_f32 = mean.to_f64().unwrap() as f32;
547
548 let mut total_sum_sq_dev = F::zero();
549
550 for chunk_start in (0..n).step_by(chunk_size) {
551 let chunk_end = (chunk_start + chunk_size).min(n);
552 let chunk_len = chunk_end - chunk_start;
553
554 if chunk_len == chunk_size {
555 let chunk_data: Array1<f32> = x
557 .slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
558 .iter()
559 .map(|&val| val.to_f64().unwrap() as f32)
560 .collect();
561
562 let mean_array: Array1<f32> = Array1::from_elem(chunk_size, mean_f32);
564 let mut deviations: Array1<f32> = Array1::zeros(chunk_size);
565 f32::simd_sub_f32_ultra(
566 &chunk_data.view(),
567 &mean_array.view(),
568 &mut deviations.view_mut(),
569 );
570
571 let mut squared_deviations: Array1<f32> = Array1::zeros(chunk_size);
573 f32::simd_mul_f32_ultra(
574 &deviations.view(),
575 &deviations.view(),
576 &mut squared_deviations.view_mut(),
577 );
578
579 let chunk_sum_sq_dev = f32::simd_sum_f32_ultra(&squared_deviations.view());
581 total_sum_sq_dev = total_sum_sq_dev + F::from(chunk_sum_sq_dev as f64).unwrap();
582 } else {
583 for i in chunk_start..chunk_end {
585 let dev = x[i] - mean;
586 total_sum_sq_dev = total_sum_sq_dev + dev * dev;
587 }
588 }
589 }
590
591 total_sum_sq_dev
592}
593
594#[allow(dead_code)]
596fn bandwidth_saturated_correlation_ultra<F, D1, D2>(
597 x: &ArrayBase<D1, Ix1>,
598 y: &ArrayBase<D2, Ix1>,
599) -> StatsResult<F>
600where
601 F: Float + NumCast + SimdUnifiedOps + Copy,
602 D1: Data<Elem = F>,
603 D2: Data<Elem = F>,
604{
605 let n = x.len();
606 let chunk_size = 16;
607
608 let mut sum_x = F::zero();
609 let mut sum_y = F::zero();
610 let mut sum_xy = F::zero();
611 let mut sum_x2 = F::zero();
612 let mut sum_y2 = F::zero();
613
614 for chunk_start in (0..n).step_by(chunk_size) {
616 let chunk_end = (chunk_start + chunk_size).min(n);
617 let chunk_len = chunk_end - chunk_start;
618
619 if chunk_len == chunk_size {
620 let x_chunk: Array1<f32> = x
622 .slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
623 .iter()
624 .map(|&val| val.to_f64().unwrap() as f32)
625 .collect();
626 let y_chunk: Array1<f32> = y
627 .slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
628 .iter()
629 .map(|&val| val.to_f64().unwrap() as f32)
630 .collect();
631
632 let chunk_sum_x = f32::simd_sum_f32_ultra(&x_chunk.view());
634 let chunk_sum_y = f32::simd_sum_f32_ultra(&y_chunk.view());
635
636 let mut xy_products: Array1<f32> = Array1::zeros(chunk_size);
638 let mut x_squared: Array1<f32> = Array1::zeros(chunk_size);
639 let mut y_squared: Array1<f32> = Array1::zeros(chunk_size);
640
641 f32::simd_mul_f32_ultra(
642 &x_chunk.view(),
643 &y_chunk.view(),
644 &mut xy_products.view_mut(),
645 );
646 f32::simd_mul_f32_ultra(&x_chunk.view(), &x_chunk.view(), &mut x_squared.view_mut());
647 f32::simd_mul_f32_ultra(&y_chunk.view(), &y_chunk.view(), &mut y_squared.view_mut());
648
649 let chunk_sum_xy = f32::simd_sum_f32_ultra(&xy_products.view());
650 let chunk_sum_x2 = f32::simd_sum_f32_ultra(&x_squared.view());
651 let chunk_sum_y2 = f32::simd_sum_f32_ultra(&y_squared.view());
652
653 sum_x = sum_x + F::from(chunk_sum_x as f64).unwrap();
655 sum_y = sum_y + F::from(chunk_sum_y as f64).unwrap();
656 sum_xy = sum_xy + F::from(chunk_sum_xy as f64).unwrap();
657 sum_x2 = sum_x2 + F::from(chunk_sum_x2 as f64).unwrap();
658 sum_y2 = sum_y2 + F::from(chunk_sum_y2 as f64).unwrap();
659 } else {
660 for i in chunk_start..chunk_end {
662 let x_val = x[i];
663 let y_val = y[i];
664 sum_x = sum_x + x_val;
665 sum_y = sum_y + y_val;
666 sum_xy = sum_xy + x_val * y_val;
667 sum_x2 = sum_x2 + x_val * x_val;
668 sum_y2 = sum_y2 + y_val * y_val;
669 }
670 }
671 }
672
673 let n_f = F::from(n).unwrap();
674 let mean_x = sum_x / n_f;
675 let mean_y = sum_y / n_f;
676
677 let numerator = sum_xy - n_f * mean_x * mean_y;
678 let denom_x = sum_x2 - n_f * mean_x * mean_x;
679 let denom_y = sum_y2 - n_f * mean_y * mean_y;
680
681 if denom_x <= F::epsilon() || denom_y <= F::epsilon() {
682 return Err(ErrorMessages::numerical_instability(
683 "correlation calculation",
684 "One or both variables have zero variance",
685 ));
686 }
687
688 Ok(numerator / (denom_x * denom_y).sqrt())
689}
690
691#[allow(dead_code)]
693fn bandwidth_saturated_comprehensive_ultra<F, D>(
694 x: &ArrayBase<D, Ix1>,
695 ddof: usize,
696) -> StatsResult<ComprehensiveStats<F>>
697where
698 F: Float + NumCast + SimdUnifiedOps + Copy + std::fmt::Debug,
699 D: Data<Elem = F>,
700{
701 let n = x.len();
702 let chunk_size = 16;
703
704 let mut sum = F::zero();
705 let mut sum_sq = F::zero();
706 let mut sum_cube = F::zero();
707 let mut sum_fourth = F::zero();
708
709 for chunk_start in (0..n).step_by(chunk_size) {
711 let chunk_end = (chunk_start + chunk_size).min(n);
712 let chunk_len = chunk_end - chunk_start;
713
714 if chunk_len == chunk_size {
715 let chunk_data: Array1<f32> = x
717 .slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
718 .iter()
719 .map(|&val| val.to_f64().unwrap() as f32)
720 .collect();
721
722 let mut chunk_squared: Array1<f32> = Array1::zeros(chunk_size);
724 let mut chunk_cubed: Array1<f32> = Array1::zeros(chunk_size);
725 let mut chunk_fourth: Array1<f32> = Array1::zeros(chunk_size);
726
727 f32::simd_mul_f32_ultra(
728 &chunk_data.view(),
729 &chunk_data.view(),
730 &mut chunk_squared.view_mut(),
731 );
732 f32::simd_mul_f32_ultra(
733 &chunk_squared.view(),
734 &chunk_data.view(),
735 &mut chunk_cubed.view_mut(),
736 );
737 f32::simd_mul_f32_ultra(
738 &chunk_squared.view(),
739 &chunk_squared.view(),
740 &mut chunk_fourth.view_mut(),
741 );
742
743 let chunk_sum = f32::simd_sum_f32_ultra(&chunk_data.view());
745 let chunk_sum_sq = f32::simd_sum_f32_ultra(&chunk_squared.view());
746 let chunk_sum_cube = f32::simd_sum_f32_ultra(&chunk_cubed.view());
747 let chunk_sum_fourth = f32::simd_sum_f32_ultra(&chunk_fourth.view());
748
749 sum = sum + F::from(chunk_sum as f64).unwrap();
751 sum_sq = sum_sq + F::from(chunk_sum_sq as f64).unwrap();
752 sum_cube = sum_cube + F::from(chunk_sum_cube as f64).unwrap();
753 sum_fourth = sum_fourth + F::from(chunk_sum_fourth as f64).unwrap();
754 } else {
755 for i in chunk_start..chunk_end {
757 let val = x[i];
758 let val_sq = val * val;
759 sum = sum + val;
760 sum_sq = sum_sq + val_sq;
761 sum_cube = sum_cube + val_sq * val;
762 sum_fourth = sum_fourth + val_sq * val_sq;
763 }
764 }
765 }
766
767 let n_f = F::from(n).unwrap();
768 let mean = sum / n_f;
769 let mean_sq = mean * mean;
770 let mean_cube = mean_sq * mean;
771 let mean_fourth = mean_sq * mean_sq;
772
773 let m2 = (sum_sq / n_f) - mean_sq;
775 let m3 = (sum_cube / n_f) - F::from(3.0).unwrap() * mean * m2 - mean_cube;
776 let m4 = (sum_fourth / n_f)
777 - F::from(4.0).unwrap() * mean * m3
778 - F::from(6.0).unwrap() * mean_sq * m2
779 - mean_fourth;
780
781 let variance = m2 * n_f / F::from(n - ddof).unwrap();
782 let std = variance.sqrt();
783
784 let skewness = if m2 > F::epsilon() {
786 m3 / m2.powf(F::from(1.5).unwrap())
787 } else {
788 F::zero()
789 };
790
791 let kurtosis = if m2 > F::epsilon() {
792 (m4 / (m2 * m2)) - F::from(3.0).unwrap()
793 } else {
794 F::zero()
795 };
796
797 Ok(ComprehensiveStats {
798 mean,
799 variance,
800 std,
801 skewness,
802 kurtosis,
803 count: n,
804 })
805}
806
807#[allow(dead_code)]
809fn welford_variance_simd<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
810where
811 F: Float + NumCast + SimdUnifiedOps + Copy,
812 D: Data<Elem = F>,
813{
814 let n = x.len();
815 let mut mean = F::zero();
816 let mut m2 = F::zero();
817
818 const SIMD_CHUNK: usize = 8;
820 let full_chunks = n / SIMD_CHUNK;
821
822 for i in 0..full_chunks {
823 let start = i * SIMD_CHUNK;
824 let end = (i + 1) * SIMD_CHUNK;
825 let chunk = x.slice(scirs2_core::ndarray::s![start..end]);
826
827 for (j, &val) in chunk.iter().enumerate() {
829 let count = F::from(start + j + 1).unwrap();
830 let delta = val - mean;
831 mean = mean + delta / count;
832 let delta2 = val - mean;
833 m2 = m2 + delta * delta2;
834 }
835 }
836
837 for (i, &val) in x.iter().enumerate().skip(full_chunks * SIMD_CHUNK) {
839 let count = F::from(i + 1).unwrap();
840 let delta = val - mean;
841 mean = mean + delta / count;
842 let delta2 = val - mean;
843 m2 = m2 + delta * delta2;
844 }
845
846 Ok(m2 / F::from(n - ddof).unwrap())
847}
848
849#[allow(dead_code)]
851fn simd_sum_squared_deviations<F, D>(x: &ArrayBase<D, Ix1>, mean: F) -> F
852where
853 F: Float + NumCast + SimdUnifiedOps + Copy + std::iter::Sum<F>,
854 D: Data<Elem = F>,
855{
856 let mean_array = Array1::from_elem(x.len(), mean);
857 let deviations = F::simd_sub(&x.view(), &mean_array.view());
858 F::simd_mul(&deviations.view(), &deviations.view()).sum()
859}
860
861#[allow(dead_code)]
863fn simd_correlation_full<F, D1, D2>(
864 x: &ArrayBase<D1, Ix1>,
865 y: &ArrayBase<D2, Ix1>,
866) -> StatsResult<F>
867where
868 F: Float + NumCast + SimdUnifiedOps + Copy,
869 D1: Data<Elem = F>,
870 D2: Data<Elem = F>,
871{
872 let n = x.len();
873 let n_f = F::from(n).unwrap();
874
875 let mean_x = F::simd_sum(&x.view()) / n_f;
877 let mean_y = F::simd_sum(&y.view()) / n_f;
878
879 let mean_x_array = Array1::from_elem(n, mean_x);
881 let mean_y_array = Array1::from_elem(n, mean_y);
882
883 let dev_x = F::simd_sub(&x.view(), &mean_x_array.view());
885 let dev_y = F::simd_sub(&y.view(), &mean_y_array.view());
886
887 let sum_xy = F::simd_mul(&dev_x.view(), &dev_y.view()).sum();
889 let sum_x2 = F::simd_mul(&dev_x.view(), &dev_x.view()).sum();
890 let sum_y2 = F::simd_mul(&dev_y.view(), &dev_y.view()).sum();
891
892 if sum_x2 <= F::epsilon() || sum_y2 <= F::epsilon() {
894 return Err(ErrorMessages::numerical_instability(
895 "correlation calculation",
896 "One or both variables have zero variance",
897 ));
898 }
899
900 Ok(sum_xy / (sum_x2 * sum_y2).sqrt())
901}
902
903#[allow(dead_code)]
905fn scalar_correlation_optimized<F, D1, D2>(
906 x: &ArrayBase<D1, Ix1>,
907 y: &ArrayBase<D2, Ix1>,
908) -> StatsResult<F>
909where
910 F: Float + NumCast + Copy,
911 D1: Data<Elem = F>,
912 D2: Data<Elem = F>,
913{
914 let n = x.len();
915 let n_f = F::from(n).unwrap();
916
917 let mut sum_x = F::zero();
919 let mut sum_y = F::zero();
920 let mut sum_xy = F::zero();
921 let mut sum_x2 = F::zero();
922 let mut sum_y2 = F::zero();
923
924 for (x_val, y_val) in x.iter().zip(y.iter()) {
925 sum_x = sum_x + *x_val;
926 sum_y = sum_y + *y_val;
927 sum_xy = sum_xy + (*x_val) * (*y_val);
928 sum_x2 = sum_x2 + (*x_val) * (*x_val);
929 sum_y2 = sum_y2 + (*y_val) * (*y_val);
930 }
931
932 let mean_x = sum_x / n_f;
933 let mean_y = sum_y / n_f;
934
935 let numerator = sum_xy - n_f * mean_x * mean_y;
936 let denom_x = sum_x2 - n_f * mean_x * mean_x;
937 let denom_y = sum_y2 - n_f * mean_y * mean_y;
938
939 if denom_x <= F::epsilon() || denom_y <= F::epsilon() {
940 return Err(ErrorMessages::numerical_instability(
941 "correlation calculation",
942 "One or both variables have zero variance",
943 ));
944 }
945
946 Ok(numerator / (denom_x * denom_y).sqrt())
947}
948
949#[allow(dead_code)]
951fn simd_comprehensive_single_pass<F, D>(
952 x: &ArrayBase<D, Ix1>,
953 ddof: usize,
954) -> StatsResult<ComprehensiveStats<F>>
955where
956 F: Float + NumCast + SimdUnifiedOps + Copy + std::fmt::Debug,
957 D: Data<Elem = F>,
958{
959 let n = x.len();
960 let n_f = F::from(n).unwrap();
961
962 let mean = F::simd_sum(&x.view()) / n_f;
964
965 let mean_array = Array1::from_elem(n, mean);
967 let deviations = F::simd_sub(&x.view(), &mean_array.view());
968
969 let dev_squared = F::simd_mul(&deviations.view(), &deviations.view());
971 let dev_cubed = F::simd_mul(&dev_squared.view(), &deviations.view());
972 let dev_fourth = F::simd_mul(&dev_squared.view(), &dev_squared.view());
973
974 let m2 = dev_squared.sum();
976 let m3 = dev_cubed.sum();
977 let m4 = dev_fourth.sum();
978
979 let variance = m2 / F::from(n - ddof).unwrap();
980 let std = variance.sqrt();
981
982 let skewness = if variance > F::epsilon() {
984 (m3 / n_f) / variance.powf(F::from(1.5).unwrap())
985 } else {
986 F::zero()
987 };
988
989 let kurtosis = if variance > F::epsilon() {
990 (m4 / n_f) / (variance * variance) - F::from(3.0).unwrap()
991 } else {
992 F::zero()
993 };
994
995 Ok(ComprehensiveStats {
996 mean,
997 variance,
998 std,
999 skewness,
1000 kurtosis,
1001 count: n,
1002 })
1003}