use crate::error::StatsResult;
use crate::error_standardization::ErrorMessages;
use scirs2_core::ndarray::{s, Array1, ArrayBase, Data, Ix1};
use scirs2_core::numeric::{Float, NumCast};
use scirs2_core::simd_ops::{AutoOptimizer, PlatformCapabilities, SimdUnifiedOps};
#[allow(dead_code)]
pub fn mean_enhanced<F, D>(x: &ArrayBase<D, Ix1>) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
D: Data<Elem = F>,
{
if x.is_empty() {
return Err(ErrorMessages::empty_array("x"));
}
let n = x.len();
let optimizer = AutoOptimizer::new();
let capabilities = PlatformCapabilities::detect();
let sum = if n < 16 {
x.iter().fold(F::zero(), |acc, &val| acc + val)
} else if n < 1024 || !capabilities.avx2_available {
F::simd_sum(&x.view())
} else {
compensated_simd_sum(x, &optimizer)
};
Ok(sum / F::from(n).expect("Failed to convert to float"))
}
#[allow(dead_code)]
pub fn variance_enhanced<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::iter::Sum<F>,
D: Data<Elem = F>,
{
let n = x.len();
if n == 0 {
return Err(ErrorMessages::empty_array("x"));
}
if n <= ddof {
return Err(ErrorMessages::insufficientdata(
"variance calculation",
ddof + 1,
n,
));
}
let optimizer = AutoOptimizer::new();
if n > 1000 && optimizer.should_use_simd(n) {
welford_variance_simd(x, ddof)
} else {
let mean = mean_enhanced(x)?;
let sum_sq_dev = if optimizer.should_use_simd(n) {
simd_sum_squared_deviations(x, mean)
} else {
x.iter()
.map(|&val| {
let dev = val - mean;
dev * dev
})
.fold(F::zero(), |acc, val| acc + val)
};
Ok(sum_sq_dev / F::from(n - ddof).expect("Failed to convert to float"))
}
}
#[allow(dead_code)]
pub fn correlation_simd_enhanced<F, D1, D2>(
x: &ArrayBase<D1, Ix1>,
y: &ArrayBase<D2, Ix1>,
) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
D1: Data<Elem = F>,
D2: Data<Elem = F>,
{
if x.is_empty() {
return Err(ErrorMessages::empty_array("x"));
}
if y.is_empty() {
return Err(ErrorMessages::empty_array("y"));
}
if x.len() != y.len() {
return Err(ErrorMessages::length_mismatch("x", x.len(), "y", y.len()));
}
let n = x.len();
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(n) {
simd_correlation_full(x, y)
} else {
scalar_correlation_optimized(x, y)
}
}
#[allow(dead_code)]
pub fn comprehensive_stats_simd<F, D>(
x: &ArrayBase<D, Ix1>,
ddof: usize,
) -> StatsResult<ComprehensiveStats<F>>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::fmt::Debug + std::iter::Sum<F>,
D: Data<Elem = F>,
{
let n = x.len();
if n == 0 {
return Err(ErrorMessages::empty_array("x"));
}
if n <= ddof {
return Err(ErrorMessages::insufficientdata(
"comprehensive statistics",
ddof + 1,
n,
));
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(n) && n > 100 {
simd_comprehensive_single_pass(x, ddof)
} else {
let mean = mean_enhanced(x)?;
let variance = variance_enhanced(x, ddof)?;
let std = variance.sqrt();
Ok(ComprehensiveStats {
mean,
variance,
std,
skewness: F::zero(), kurtosis: F::zero(), count: n,
})
}
}
#[derive(Debug, Clone)]
pub struct ComprehensiveStats<F> {
pub mean: F,
pub variance: F,
pub std: F,
pub skewness: F,
pub kurtosis: F,
pub count: usize,
}
#[allow(dead_code)]
fn compensated_simd_sum<F, D>(x: &ArrayBase<D, Ix1>, optimizer: &AutoOptimizer) -> F
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D: Data<Elem = F>,
{
const CHUNK_SIZE: usize = 8192;
let mut sum = F::zero();
let mut compensation = F::zero();
let n = x.len();
let num_full_chunks = n / CHUNK_SIZE;
let remainder = n % CHUNK_SIZE;
for chunk in x.exact_chunks(CHUNK_SIZE) {
let chunk_sum = if optimizer.should_use_simd(chunk.len()) {
F::simd_sum(&chunk.view())
} else {
chunk.iter().fold(F::zero(), |acc, &val| acc + val)
};
let y = chunk_sum - compensation;
let t = sum + y;
compensation = (t - sum) - y;
sum = t;
}
if remainder > 0 {
let remainder_start = num_full_chunks * CHUNK_SIZE;
let remainder_slice = x.slice(s![remainder_start..]);
let remainder_sum = remainder_slice
.iter()
.fold(F::zero(), |acc, &val| acc + val);
let y = remainder_sum - compensation;
let t = sum + y;
sum = t;
}
sum
}
#[allow(dead_code)]
pub fn mean_ultra_simd<F, D>(x: &ArrayBase<D, Ix1>) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
D: Data<Elem = F>,
{
if x.is_empty() {
return Err(ErrorMessages::empty_array("x"));
}
let n = x.len();
let capabilities = PlatformCapabilities::detect();
let sum = if n < 32 {
x.iter().fold(F::zero(), |acc, &val| acc + val)
} else if n < 64 || !capabilities.has_avx2() {
F::simd_sum(&x.view())
} else {
bandwidth_saturated_sum_ultra(x)
};
Ok(sum / F::from(n).expect("Failed to convert to float"))
}
#[allow(dead_code)]
pub fn variance_ultra_simd<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::iter::Sum<F>,
D: Data<Elem = F>,
{
let n = x.len();
if n == 0 {
return Err(ErrorMessages::empty_array("x"));
}
if n <= ddof {
return Err(ErrorMessages::insufficientdata(
"variance calculation",
ddof + 1,
n,
));
}
let capabilities = PlatformCapabilities::detect();
if n >= 128 && capabilities.has_avx2() {
bandwidth_saturated_variance_ultra(x, ddof)
} else if n >= 64 {
let mean = mean_ultra_simd(x)?;
let sum_sq_dev = bandwidth_saturated_sum_squared_deviations_ultra(x, mean);
Ok(sum_sq_dev / F::from(n - ddof).expect("Failed to convert to float"))
} else {
variance_enhanced(x, ddof)
}
}
#[allow(dead_code)]
pub fn correlation_ultra_simd<F, D1, D2>(
x: &ArrayBase<D1, Ix1>,
y: &ArrayBase<D2, Ix1>,
) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync,
D1: Data<Elem = F>,
D2: Data<Elem = F>,
{
if x.is_empty() {
return Err(ErrorMessages::empty_array("x"));
}
if y.is_empty() {
return Err(ErrorMessages::empty_array("y"));
}
if x.len() != y.len() {
return Err(ErrorMessages::length_mismatch("x", x.len(), "y", y.len()));
}
let n = x.len();
let capabilities = PlatformCapabilities::detect();
if n >= 128 && capabilities.has_avx2() {
bandwidth_saturated_correlation_ultra(x, y)
} else if n >= 64 {
simd_correlation_full(x, y)
} else {
scalar_correlation_optimized(x, y)
}
}
#[allow(dead_code)]
pub fn comprehensive_stats_ultra_simd<F, D>(
x: &ArrayBase<D, Ix1>,
ddof: usize,
) -> StatsResult<ComprehensiveStats<F>>
where
F: Float + NumCast + SimdUnifiedOps + Copy + Send + Sync + std::fmt::Debug + std::iter::Sum<F>,
D: Data<Elem = F>,
{
let n = x.len();
if n == 0 {
return Err(ErrorMessages::empty_array("x"));
}
if n <= ddof {
return Err(ErrorMessages::insufficientdata(
"comprehensive statistics",
ddof + 1,
n,
));
}
let capabilities = PlatformCapabilities::detect();
if n >= 256 && capabilities.has_avx2() {
bandwidth_saturated_comprehensive_ultra(x, ddof)
} else if n >= 64 {
simd_comprehensive_single_pass(x, ddof)
} else {
comprehensive_stats_simd(x, ddof)
}
}
#[allow(dead_code)]
fn bandwidth_saturated_sum_ultra<F, D>(x: &ArrayBase<D, Ix1>) -> F
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D: Data<Elem = F>,
{
let n = x.len();
let chunk_size = 16;
let mut total_sum = F::zero();
for chunk_start in (0..n).step_by(chunk_size) {
let chunk_end = (chunk_start + chunk_size).min(n);
let chunk_len = chunk_end - chunk_start;
if chunk_len == chunk_size {
let chunk_data: Array1<f32> = x
.slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
.iter()
.map(|&val| val.to_f64().expect("Operation failed") as f32)
.collect();
let chunk_sum = f32::simd_sum_f32_ultra(&chunk_data.view());
total_sum = total_sum + F::from(chunk_sum as f64).expect("Failed to convert to float");
} else {
for i in chunk_start..chunk_end {
total_sum = total_sum + x[i];
}
}
}
total_sum
}
#[allow(dead_code)]
fn bandwidth_saturated_variance_ultra<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D: Data<Elem = F>,
{
let n = x.len();
let chunk_size = 16;
let mut sum = F::zero();
let mut sum_sq = F::zero();
for chunk_start in (0..n).step_by(chunk_size) {
let chunk_end = (chunk_start + chunk_size).min(n);
let chunk_len = chunk_end - chunk_start;
if chunk_len == chunk_size {
let chunk_data: Array1<f32> = x
.slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
.iter()
.map(|&val| val.to_f64().expect("Operation failed") as f32)
.collect();
let chunk_sum = f32::simd_sum_f32_ultra(&chunk_data.view());
let mut chunk_squared: Array1<f32> = Array1::zeros(chunk_size);
f32::simd_mul_f32_ultra(
&chunk_data.view(),
&chunk_data.view(),
&mut chunk_squared.view_mut(),
);
let chunk_sum_sq = f32::simd_sum_f32_ultra(&chunk_squared.view());
sum = sum + F::from(chunk_sum as f64).expect("Failed to convert to float");
sum_sq = sum_sq + F::from(chunk_sum_sq as f64).expect("Failed to convert to float");
} else {
for i in chunk_start..chunk_end {
let val = x[i];
sum = sum + val;
sum_sq = sum_sq + val * val;
}
}
}
let n_f = F::from(n).expect("Failed to convert to float");
let mean = sum / n_f;
let variance =
(sum_sq - n_f * mean * mean) / F::from(n - ddof).expect("Failed to convert to float");
Ok(variance)
}
#[allow(dead_code)]
fn bandwidth_saturated_sum_squared_deviations_ultra<F, D>(x: &ArrayBase<D, Ix1>, mean: F) -> F
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D: Data<Elem = F>,
{
let n = x.len();
let chunk_size = 16;
let mean_f32 = mean.to_f64().expect("Operation failed") as f32;
let mut total_sum_sq_dev = F::zero();
for chunk_start in (0..n).step_by(chunk_size) {
let chunk_end = (chunk_start + chunk_size).min(n);
let chunk_len = chunk_end - chunk_start;
if chunk_len == chunk_size {
let chunk_data: Array1<f32> = x
.slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
.iter()
.map(|&val| val.to_f64().expect("Operation failed") as f32)
.collect();
let mean_array: Array1<f32> = Array1::from_elem(chunk_size, mean_f32);
let mut deviations: Array1<f32> = Array1::zeros(chunk_size);
f32::simd_sub_f32_ultra(
&chunk_data.view(),
&mean_array.view(),
&mut deviations.view_mut(),
);
let mut squared_deviations: Array1<f32> = Array1::zeros(chunk_size);
f32::simd_mul_f32_ultra(
&deviations.view(),
&deviations.view(),
&mut squared_deviations.view_mut(),
);
let chunk_sum_sq_dev = f32::simd_sum_f32_ultra(&squared_deviations.view());
total_sum_sq_dev = total_sum_sq_dev
+ F::from(chunk_sum_sq_dev as f64).expect("Failed to convert to float");
} else {
for i in chunk_start..chunk_end {
let dev = x[i] - mean;
total_sum_sq_dev = total_sum_sq_dev + dev * dev;
}
}
}
total_sum_sq_dev
}
#[allow(dead_code)]
fn bandwidth_saturated_correlation_ultra<F, D1, D2>(
x: &ArrayBase<D1, Ix1>,
y: &ArrayBase<D2, Ix1>,
) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D1: Data<Elem = F>,
D2: Data<Elem = F>,
{
let n = x.len();
let chunk_size = 16;
let mut sum_x = F::zero();
let mut sum_y = F::zero();
let mut sum_xy = F::zero();
let mut sum_x2 = F::zero();
let mut sum_y2 = F::zero();
for chunk_start in (0..n).step_by(chunk_size) {
let chunk_end = (chunk_start + chunk_size).min(n);
let chunk_len = chunk_end - chunk_start;
if chunk_len == chunk_size {
let x_chunk: Array1<f32> = x
.slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
.iter()
.map(|&val| val.to_f64().expect("Operation failed") as f32)
.collect();
let y_chunk: Array1<f32> = y
.slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
.iter()
.map(|&val| val.to_f64().expect("Operation failed") as f32)
.collect();
let chunk_sum_x = f32::simd_sum_f32_ultra(&x_chunk.view());
let chunk_sum_y = f32::simd_sum_f32_ultra(&y_chunk.view());
let mut xy_products: Array1<f32> = Array1::zeros(chunk_size);
let mut x_squared: Array1<f32> = Array1::zeros(chunk_size);
let mut y_squared: Array1<f32> = Array1::zeros(chunk_size);
f32::simd_mul_f32_ultra(
&x_chunk.view(),
&y_chunk.view(),
&mut xy_products.view_mut(),
);
f32::simd_mul_f32_ultra(&x_chunk.view(), &x_chunk.view(), &mut x_squared.view_mut());
f32::simd_mul_f32_ultra(&y_chunk.view(), &y_chunk.view(), &mut y_squared.view_mut());
let chunk_sum_xy = f32::simd_sum_f32_ultra(&xy_products.view());
let chunk_sum_x2 = f32::simd_sum_f32_ultra(&x_squared.view());
let chunk_sum_y2 = f32::simd_sum_f32_ultra(&y_squared.view());
sum_x = sum_x + F::from(chunk_sum_x as f64).expect("Failed to convert to float");
sum_y = sum_y + F::from(chunk_sum_y as f64).expect("Failed to convert to float");
sum_xy = sum_xy + F::from(chunk_sum_xy as f64).expect("Failed to convert to float");
sum_x2 = sum_x2 + F::from(chunk_sum_x2 as f64).expect("Failed to convert to float");
sum_y2 = sum_y2 + F::from(chunk_sum_y2 as f64).expect("Failed to convert to float");
} else {
for i in chunk_start..chunk_end {
let x_val = x[i];
let y_val = y[i];
sum_x = sum_x + x_val;
sum_y = sum_y + y_val;
sum_xy = sum_xy + x_val * y_val;
sum_x2 = sum_x2 + x_val * x_val;
sum_y2 = sum_y2 + y_val * y_val;
}
}
}
let n_f = F::from(n).expect("Failed to convert to float");
let mean_x = sum_x / n_f;
let mean_y = sum_y / n_f;
let numerator = sum_xy - n_f * mean_x * mean_y;
let denom_x = sum_x2 - n_f * mean_x * mean_x;
let denom_y = sum_y2 - n_f * mean_y * mean_y;
if denom_x <= F::epsilon() || denom_y <= F::epsilon() {
return Err(ErrorMessages::numerical_instability(
"correlation calculation",
"One or both variables have zero variance",
));
}
Ok(numerator / (denom_x * denom_y).sqrt())
}
#[allow(dead_code)]
fn bandwidth_saturated_comprehensive_ultra<F, D>(
x: &ArrayBase<D, Ix1>,
ddof: usize,
) -> StatsResult<ComprehensiveStats<F>>
where
F: Float + NumCast + SimdUnifiedOps + Copy + std::fmt::Debug,
D: Data<Elem = F>,
{
let n = x.len();
let chunk_size = 16;
let mut sum = F::zero();
let mut sum_sq = F::zero();
let mut sum_cube = F::zero();
let mut sum_fourth = F::zero();
for chunk_start in (0..n).step_by(chunk_size) {
let chunk_end = (chunk_start + chunk_size).min(n);
let chunk_len = chunk_end - chunk_start;
if chunk_len == chunk_size {
let chunk_data: Array1<f32> = x
.slice(scirs2_core::ndarray::s![chunk_start..chunk_end])
.iter()
.map(|&val| val.to_f64().expect("Operation failed") as f32)
.collect();
let mut chunk_squared: Array1<f32> = Array1::zeros(chunk_size);
let mut chunk_cubed: Array1<f32> = Array1::zeros(chunk_size);
let mut chunk_fourth: Array1<f32> = Array1::zeros(chunk_size);
f32::simd_mul_f32_ultra(
&chunk_data.view(),
&chunk_data.view(),
&mut chunk_squared.view_mut(),
);
f32::simd_mul_f32_ultra(
&chunk_squared.view(),
&chunk_data.view(),
&mut chunk_cubed.view_mut(),
);
f32::simd_mul_f32_ultra(
&chunk_squared.view(),
&chunk_squared.view(),
&mut chunk_fourth.view_mut(),
);
let chunk_sum = f32::simd_sum_f32_ultra(&chunk_data.view());
let chunk_sum_sq = f32::simd_sum_f32_ultra(&chunk_squared.view());
let chunk_sum_cube = f32::simd_sum_f32_ultra(&chunk_cubed.view());
let chunk_sum_fourth = f32::simd_sum_f32_ultra(&chunk_fourth.view());
sum = sum + F::from(chunk_sum as f64).expect("Failed to convert to float");
sum_sq = sum_sq + F::from(chunk_sum_sq as f64).expect("Failed to convert to float");
sum_cube =
sum_cube + F::from(chunk_sum_cube as f64).expect("Failed to convert to float");
sum_fourth =
sum_fourth + F::from(chunk_sum_fourth as f64).expect("Failed to convert to float");
} else {
for i in chunk_start..chunk_end {
let val = x[i];
let val_sq = val * val;
sum = sum + val;
sum_sq = sum_sq + val_sq;
sum_cube = sum_cube + val_sq * val;
sum_fourth = sum_fourth + val_sq * val_sq;
}
}
}
let n_f = F::from(n).expect("Failed to convert to float");
let mean = sum / n_f;
let mean_sq = mean * mean;
let mean_cube = mean_sq * mean;
let mean_fourth = mean_sq * mean_sq;
let m2 = (sum_sq / n_f) - mean_sq;
let m3 = (sum_cube / n_f)
- F::from(3.0).expect("Failed to convert constant to float") * mean * m2
- mean_cube;
let m4 = (sum_fourth / n_f)
- F::from(4.0).expect("Failed to convert constant to float") * mean * m3
- F::from(6.0).expect("Failed to convert constant to float") * mean_sq * m2
- mean_fourth;
let variance = m2 * n_f / F::from(n - ddof).expect("Failed to convert to float");
let std = variance.sqrt();
let skewness = if m2 > F::epsilon() {
m3 / m2.powf(F::from(1.5).expect("Failed to convert constant to float"))
} else {
F::zero()
};
let kurtosis = if m2 > F::epsilon() {
(m4 / (m2 * m2)) - F::from(3.0).expect("Failed to convert constant to float")
} else {
F::zero()
};
Ok(ComprehensiveStats {
mean,
variance,
std,
skewness,
kurtosis,
count: n,
})
}
#[allow(dead_code)]
fn welford_variance_simd<F, D>(x: &ArrayBase<D, Ix1>, ddof: usize) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D: Data<Elem = F>,
{
let n = x.len();
let mut mean = F::zero();
let mut m2 = F::zero();
const SIMD_CHUNK: usize = 8;
let full_chunks = n / SIMD_CHUNK;
for i in 0..full_chunks {
let start = i * SIMD_CHUNK;
let end = (i + 1) * SIMD_CHUNK;
let chunk = x.slice(scirs2_core::ndarray::s![start..end]);
for (j, &val) in chunk.iter().enumerate() {
let count = F::from(start + j + 1).expect("Failed to convert to float");
let delta = val - mean;
mean = mean + delta / count;
let delta2 = val - mean;
m2 = m2 + delta * delta2;
}
}
for (i, &val) in x.iter().enumerate().skip(full_chunks * SIMD_CHUNK) {
let count = F::from(i + 1).expect("Failed to convert to float");
let delta = val - mean;
mean = mean + delta / count;
let delta2 = val - mean;
m2 = m2 + delta * delta2;
}
Ok(m2 / F::from(n - ddof).expect("Failed to convert to float"))
}
#[allow(dead_code)]
fn simd_sum_squared_deviations<F, D>(x: &ArrayBase<D, Ix1>, mean: F) -> F
where
F: Float + NumCast + SimdUnifiedOps + Copy + std::iter::Sum<F>,
D: Data<Elem = F>,
{
let mean_array = Array1::from_elem(x.len(), mean);
let deviations = F::simd_sub(&x.view(), &mean_array.view());
F::simd_mul(&deviations.view(), &deviations.view()).sum()
}
#[allow(dead_code)]
fn simd_correlation_full<F, D1, D2>(
x: &ArrayBase<D1, Ix1>,
y: &ArrayBase<D2, Ix1>,
) -> StatsResult<F>
where
F: Float + NumCast + SimdUnifiedOps + Copy,
D1: Data<Elem = F>,
D2: Data<Elem = F>,
{
let n = x.len();
let n_f = F::from(n).expect("Failed to convert to float");
let mean_x = F::simd_sum(&x.view()) / n_f;
let mean_y = F::simd_sum(&y.view()) / n_f;
let mean_x_array = Array1::from_elem(n, mean_x);
let mean_y_array = Array1::from_elem(n, mean_y);
let dev_x = F::simd_sub(&x.view(), &mean_x_array.view());
let dev_y = F::simd_sub(&y.view(), &mean_y_array.view());
let sum_xy = F::simd_mul(&dev_x.view(), &dev_y.view()).sum();
let sum_x2 = F::simd_mul(&dev_x.view(), &dev_x.view()).sum();
let sum_y2 = F::simd_mul(&dev_y.view(), &dev_y.view()).sum();
if sum_x2 <= F::epsilon() || sum_y2 <= F::epsilon() {
return Err(ErrorMessages::numerical_instability(
"correlation calculation",
"One or both variables have zero variance",
));
}
Ok(sum_xy / (sum_x2 * sum_y2).sqrt())
}
#[allow(dead_code)]
fn scalar_correlation_optimized<F, D1, D2>(
x: &ArrayBase<D1, Ix1>,
y: &ArrayBase<D2, Ix1>,
) -> StatsResult<F>
where
F: Float + NumCast + Copy,
D1: Data<Elem = F>,
D2: Data<Elem = F>,
{
let n = x.len();
let n_f = F::from(n).expect("Failed to convert to float");
let mut sum_x = F::zero();
let mut sum_y = F::zero();
let mut sum_xy = F::zero();
let mut sum_x2 = F::zero();
let mut sum_y2 = F::zero();
for (x_val, y_val) in x.iter().zip(y.iter()) {
sum_x = sum_x + *x_val;
sum_y = sum_y + *y_val;
sum_xy = sum_xy + (*x_val) * (*y_val);
sum_x2 = sum_x2 + (*x_val) * (*x_val);
sum_y2 = sum_y2 + (*y_val) * (*y_val);
}
let mean_x = sum_x / n_f;
let mean_y = sum_y / n_f;
let numerator = sum_xy - n_f * mean_x * mean_y;
let denom_x = sum_x2 - n_f * mean_x * mean_x;
let denom_y = sum_y2 - n_f * mean_y * mean_y;
if denom_x <= F::epsilon() || denom_y <= F::epsilon() {
return Err(ErrorMessages::numerical_instability(
"correlation calculation",
"One or both variables have zero variance",
));
}
Ok(numerator / (denom_x * denom_y).sqrt())
}
#[allow(dead_code)]
fn simd_comprehensive_single_pass<F, D>(
x: &ArrayBase<D, Ix1>,
ddof: usize,
) -> StatsResult<ComprehensiveStats<F>>
where
F: Float + NumCast + SimdUnifiedOps + Copy + std::fmt::Debug,
D: Data<Elem = F>,
{
let n = x.len();
let n_f = F::from(n).expect("Failed to convert to float");
let mean = F::simd_sum(&x.view()) / n_f;
let mean_array = Array1::from_elem(n, mean);
let deviations = F::simd_sub(&x.view(), &mean_array.view());
let dev_squared = F::simd_mul(&deviations.view(), &deviations.view());
let dev_cubed = F::simd_mul(&dev_squared.view(), &deviations.view());
let dev_fourth = F::simd_mul(&dev_squared.view(), &dev_squared.view());
let m2 = dev_squared.sum();
let m3 = dev_cubed.sum();
let m4 = dev_fourth.sum();
let variance = m2 / F::from(n - ddof).expect("Failed to convert to float");
let std = variance.sqrt();
let skewness = if variance > F::epsilon() {
(m3 / n_f) / variance.powf(F::from(1.5).expect("Failed to convert constant to float"))
} else {
F::zero()
};
let kurtosis = if variance > F::epsilon() {
(m4 / n_f) / (variance * variance)
- F::from(3.0).expect("Failed to convert constant to float")
} else {
F::zero()
};
Ok(ComprehensiveStats {
mean,
variance,
std,
skewness,
kurtosis,
count: n,
})
}