use crate::error::{StatsError, StatsResult};
use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
use scirs2_core::numeric::{Float, NumCast, One, Zero};
use scirs2_core::{
parallel_ops::*,
simd_ops::{PlatformCapabilities, SimdUnifiedOps},
validation::*,
};
use std::marker::PhantomData;
#[derive(Debug, Clone)]
pub struct AdvancedAdvancedSimdConfig {
pub capabilities: PlatformCapabilities,
pub vector_width: VectorWidth,
pub cache_strategy: CacheStrategy,
pub parallel_threshold: usize,
pub memory_alignment: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct VectorWidth {
pub f64_lanes: usize,
pub f32_lanes: usize,
pub optimal_chunk: usize,
}
#[derive(Debug, Clone)]
pub enum CacheStrategy {
L1Optimized { chunksize: usize },
L2Optimized { chunksize: usize },
L3Optimized { chunksize: usize },
Adaptive,
}
impl Default for AdvancedAdvancedSimdConfig {
fn default() -> Self {
let capabilities = PlatformCapabilities::detect();
let vector_width = VectorWidth::from_capabilities(&capabilities);
Self {
capabilities,
vector_width,
cache_strategy: CacheStrategy::Adaptive,
parallel_threshold: 1024,
memory_alignment: 64, }
}
}
impl VectorWidth {
fn from_capabilities(capabilities: &PlatformCapabilities) -> Self {
if capabilities.avx512_available {
Self {
f64_lanes: 8, f32_lanes: 16, optimal_chunk: 64,
}
} else if capabilities.avx2_available {
Self {
f64_lanes: 4, f32_lanes: 8, optimal_chunk: 32,
}
} else if capabilities.simd_available {
Self {
f64_lanes: 2, f32_lanes: 4, optimal_chunk: 16,
}
} else {
Self {
f64_lanes: 1,
f32_lanes: 1,
optimal_chunk: 8,
}
}
}
}
pub struct AdvancedAdvancedSimdProcessor<F> {
config: AdvancedAdvancedSimdConfig, phantom: PhantomData<F>,
}
#[derive(Debug, Clone)]
pub struct SimdRegressionResult<F> {
pub coefficients: Array1<F>,
pub residuals: Array1<F>,
pub r_squared: F,
pub adjusted_r_squared: F,
pub standard_errors: Array1<F>,
pub t_statistics: Array1<F>,
pub p_values: Array1<F>,
pub confidence_intervals: Array2<F>,
pub anova_table: SimdAnovaTable<F>,
}
#[derive(Debug, Clone)]
pub struct SimdAnovaTable<F> {
pub sum_squares_regression: F,
pub sum_squares_residual: F,
pub sum_squares_total: F,
pub degrees_freedom_regression: usize,
pub degrees_freedom_residual: usize,
pub mean_square_regression: F,
pub mean_square_residual: F,
pub f_statistic: F,
pub f_p_value: F,
}
#[derive(Debug, Clone)]
pub struct SimdMultiTestResult<F> {
pub test_statistics: Array1<F>,
pub p_values: Array1<F>,
pub effectsizes: Array1<F>,
pub confidence_intervals: Array2<F>,
pub power_estimates: Array1<F>,
pub critical_values: Array1<F>,
}
#[derive(Debug, Clone)]
pub struct SimdCovarianceResult<F> {
pub covariance_matrix: Array2<F>,
pub correlation_matrix: Array2<F>,
pub eigenvalues: Array1<F>,
pub eigenvectors: Array2<F>,
pub condition_number: F,
pub determinant: F,
pub trace: F,
}
impl<F> AdvancedAdvancedSimdProcessor<F>
where
F: Float + NumCast + SimdUnifiedOps + Zero + One + PartialOrd + Copy + Send + Sync
+ std::fmt::Display,
{
pub fn new() -> Self {
Self {
config: AdvancedAdvancedSimdConfig::default(), _phantom: PhantomData,
}
}
pub fn with_config(config: AdvancedAdvancedSimdConfig) -> Self {
Self {
_config_phantom: PhantomData,
}
}
pub fn simd_multiple_regression(
&self,
y: &ArrayView1<F>,
x: &ArrayView2<F>,
include_intercept: bool,
) -> StatsResult<SimdRegressionResult<F>> {
checkarray_finite(&y, "y")?;
checkarray_finite(&x, "x")?;
let n = y.len();
let k = x.ncols();
if x.nrows() != n {
return Err(StatsError::DimensionMismatch(
"X and y must have same number of observations".to_string(),
));
}
if n <= k + if include_intercept { 1 } else { 0 } {
return Err(StatsError::InvalidArgument(
"Insufficient degrees of freedom".to_string(),
));
}
let design_matrix = if include_intercept {
self.add_intercept_column_simd(x)?
} else {
x.to_owned()
};
let xtx = self.simd_matrix_multiply_transpose(&design_matrix.view(), &design_matrix.view())?;
let xty = self.simd_matrix_vector_multiply_transpose(&design_matrix.view(), y)?;
let coefficients = self.simd_solve_normal_equations(&xtx.view(), &xty.view())?;
let fitted = self.simd_matrix_vector_multiply(&design_matrix.view(), &coefficients.view())?;
let residuals = self.simd_vector_subtract(y, &fitted.view())?;
let (r_squared, adjusted_r_squared) = self.simd_compute_r_squared(
y,
&fitted.view(),
&residuals.view(),
design_matrix.ncols(),
)?;
let mse = self.simd_mean_squared_error(&residuals.view())?;
let xtx_inv = self.simd_matrix_inverse(&xtx.view())?;
let standard_errors = self.simd_compute_standard_errors(&xtx_inv.view(), mse)?;
let t_statistics = self.simd_compute_t_statistics(&coefficients.view(), &standard_errors.view())?;
let p_values = self.simd_compute_p_values(&t_statistics.view(), n - design_matrix.ncols())?;
let confidence_intervals = self.simd_compute_confidence_intervals(
&coefficients.view(),
&standard_errors.view(),
n - design_matrix.ncols(),
F::from(0.05).expect("Failed to convert constant to float"), )?;
let anova_table = self.simd_compute_anova_table(
y,
&fitted.view(),
&residuals.view(),
design_matrix.ncols(),
)?;
Ok(SimdRegressionResult {
coefficients,
residuals,
r_squared,
adjusted_r_squared,
standard_errors,
t_statistics,
p_values,
confidence_intervals,
anova_table,
})
}
pub fn simd_multivariate_covariance(
&self,
data: &ArrayView2<F>,
bias_correction: bool,
) -> StatsResult<SimdCovarianceResult<F>> {
checkarray_finite(data, "data")?;
let (n, p) = data.dim();
if n < 2 {
return Err(StatsError::InvalidArgument(
"Need at least 2 observations".to_string(),
));
}
let means = self.simd_column_means(data)?;
let centereddata = self.simd_centerdata(data, &means.view())?;
let covariance_matrix = self.simd_covariance_matrix(¢ereddata.view(), bias_correction)?;
let correlation_matrix = self.simd_correlation_from_covariance(&covariance_matrix.view())?;
let (eigenvalues, eigenvectors) = self.simd_eigendecomposition(&covariance_matrix.view())?;
let condition_number = self.simd_condition_number(&eigenvalues.view())?;
let determinant = self.simd_determinant_from_eigenvalues(&eigenvalues.view())?;
let trace = self.simd_trace(&covariance_matrix.view())?;
Ok(SimdCovarianceResult {
covariance_matrix,
correlation_matrix,
eigenvalues,
eigenvectors,
condition_number,
determinant,
trace,
})
}
pub fn simd_batch_statistical_tests(
&self,
group1: &ArrayView2<F>,
group2: &ArrayView2<F>,
test_type: StatisticalTestType,
) -> StatsResult<SimdMultiTestResult<F>> {
checkarray_finite(group1, "group1")?;
checkarray_finite(group2, "group2")?;
if group1.ncols() != group2.ncols() {
return Err(StatsError::DimensionMismatch(
"Groups must have same number of variables".to_string(),
));
}
let n_tests = group1.ncols();
let mut test_statistics = Array1::zeros(n_tests);
let mut p_values = Array1::zeros(n_tests);
let mut effectsizes = Array1::zeros(n_tests);
let mut confidence_intervals = Array2::zeros((n_tests, 2));
let mut power_estimates = Array1::zeros(n_tests);
let mut critical_values = Array1::zeros(n_tests);
let chunksize = self.config.vector_width.optimal_chunk;
let n_chunks = (n_tests + chunksize - 1) / chunksize;
for chunk_idx in 0..n_chunks {
let start_col = chunk_idx * chunksize;
let end_col = (start_col + chunksize).min(n_tests);
let chunksize_actual = end_col - start_col;
let group1_chunk = group1.slice(scirs2_core::ndarray::s![.., start_col..end_col]);
let group2_chunk = group2.slice(scirs2_core::ndarray::s![.., start_col..end_col]);
match test_type {
StatisticalTestType::TTest => {
let (stats, pvals, effects, cis, power, crit) =
self.simd_batch_t_tests(&group1_chunk, &group2_chunk)?;
test_statistics.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&stats);
p_values.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&pvals);
effectsizes.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&effects);
confidence_intervals.slice_mut(scirs2_core::ndarray::s![start_col..end_col, ..]).assign(&cis);
power_estimates.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&power);
critical_values.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&crit);
}
StatisticalTestType::MannWhitney => {
let (stats, pvals, effects, cis, power, crit) =
self.simd_batch_mann_whitney_tests(&group1_chunk, &group2_chunk)?;
test_statistics.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&stats);
p_values.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&pvals);
effectsizes.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&effects);
confidence_intervals.slice_mut(scirs2_core::ndarray::s![start_col..end_col, ..]).assign(&cis);
power_estimates.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&power);
critical_values.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&crit);
}
StatisticalTestType::KolmogorovSmirnov => {
let (stats, pvals, effects, cis, power, crit) =
self.simd_batch_ks_tests(&group1_chunk, &group2_chunk)?;
test_statistics.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&stats);
p_values.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&pvals);
effectsizes.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&effects);
confidence_intervals.slice_mut(scirs2_core::ndarray::s![start_col..end_col, ..]).assign(&cis);
power_estimates.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&power);
critical_values.slice_mut(scirs2_core::ndarray::s![start_col..end_col]).assign(&crit);
}
}
}
Ok(SimdMultiTestResult {
test_statistics,
p_values,
effectsizes,
confidence_intervals,
power_estimates,
critical_values,
})
}
fn simd_matrix_multiply_transpose(
&self,
a: &ArrayView2<F>,
b: &ArrayView2<F>,
) -> StatsResult<Array2<F>> {
let (m, k) = a.dim();
let (n, k2) = b.dim();
if k != k2 {
return Err(StatsError::DimensionMismatch(
"Matrix dimensions incompatible for multiplication".to_string(),
));
}
if m * n > self.config.parallel_threshold {
self.simd_blocked_matrix_multiply_transpose(a, b)
} else {
self.simd_simple_matrix_multiply_transpose(a, b)
}
}
fn simd_simple_matrix_multiply_transpose(
&self,
a: &ArrayView2<F>,
b: &ArrayView2<F>,
) -> StatsResult<Array2<F>> {
let (m, k) = a.dim();
let n = b.nrows();
let mut result = Array2::zeros((m, n));
for i in 0..m {
for j in 0..n {
let row_a = a.row(i);
let row_b = b.row(j);
result[[i, j]] = F::simd_dot_product(&row_a, &row_b);
}
}
Ok(result)
}
fn simd_blocked_matrix_multiply_transpose(
&self,
a: &ArrayView2<F>,
b: &ArrayView2<F>,
) -> StatsResult<Array2<F>> {
let (m, k) = a.dim();
let n = b.nrows();
let mut result = Array2::zeros((m, n));
let blocksize = match &self.config.cache_strategy {
CacheStrategy::L1Optimized { chunksize } => *chunksize,
CacheStrategy::L2Optimized { chunksize } => *chunksize,
CacheStrategy::L3Optimized { chunksize } => *chunksize,
CacheStrategy::Adaptive => {
(64.0 * (32768.0 / (m as f64 * n as f64).sqrt()).sqrt()) as usize
}
};
for i_block in (0..m).step_by(blocksize) {
for j_block in (0..n).step_by(blocksize) {
for k_block in (0..k).step_by(blocksize) {
let i_end = (i_block + blocksize).min(m);
let j_end = (j_block + blocksize).min(n);
let k_end = (k_block + blocksize).min(k);
for i in i_block..i_end {
for j in j_block..j_end {
let mut sum = F::zero();
let remaining = k_end - k_block;
let simd_chunks = remaining / self.config.vector_width.optimal_chunk;
let simd_remainder = remaining % self.config.vector_width.optimal_chunk;
for chunk in 0..simd_chunks {
let k_start = k_block + chunk * self.config.vector_width.optimal_chunk;
let k_chunk_end = k_start + self.config.vector_width.optimal_chunk;
let a_chunk = a.slice(scirs2_core::ndarray::s![i, k_start..k_chunk_end]);
let b_chunk = b.slice(scirs2_core::ndarray::s![j, k_start..k_chunk_end]);
sum = sum + F::simd_dot_product(&a_chunk, &b_chunk);
}
for k in (k_block + simd_chunks * self.config.vector_width.optimal_chunk)..k_end {
sum = sum + a[[i, k]] * b[[j, k]];
}
result[[i, j]] = result[[i, j]] + sum;
}
}
}
}
}
Ok(result)
}
fn add_intercept_column_simd(&self, x: &ArrayView2<F>) -> StatsResult<Array2<F>> {
let (n, k) = x.dim();
let mut design_matrix = Array2::zeros((n, k + 1));
let ones_column = Array1::ones(n);
design_matrix.column_mut(0).assign(&ones_column);
for j in 0..k {
design_matrix.column_mut(j + 1).assign(&x.column(j));
}
Ok(design_matrix)
}
fn simd_matrix_vector_multiply(
&self,
matrix: &ArrayView2<F>,
vector: &ArrayView1<F>,
) -> StatsResult<Array1<F>> {
let (m, n) = matrix.dim();
if n != vector.len() {
return Err(StatsError::DimensionMismatch(
"Matrix columns must match vector length".to_string(),
));
}
let mut result = Array1::zeros(m);
for i in 0..m {
let row = matrix.row(i);
result[i] = F::simd_dot_product(&row, vector);
}
Ok(result)
}
fn simd_matrix_vector_multiply_transpose(
&self,
matrix: &ArrayView2<F>,
vector: &ArrayView1<F>,
) -> StatsResult<Array1<F>> {
let (m, n) = matrix.dim();
if m != vector.len() {
return Err(StatsError::DimensionMismatch(
"Matrix rows must match vector length".to_string(),
));
}
let mut result = Array1::zeros(n);
for j in 0..n {
let column = matrix.column(j);
result[j] = F::simd_dot_product(&column, vector);
}
Ok(result)
}
fn simd_vector_subtract(
&self,
a: &ArrayView1<F>,
b: &ArrayView1<F>,
) -> StatsResult<Array1<F>> {
if a.len() != b.len() {
return Err(StatsError::DimensionMismatch(
"Vectors must have same length".to_string(),
));
}
let mut result = Array1::zeros(a.len());
F::simd_subtract(&a, &b, &mut result.view_mut());
Ok(result)
}
fn simd_compute_r_squared(
&self,
y: &ArrayView1<F>,
fitted: &ArrayView1<F>,
residuals: &ArrayView1<F>,
n_params: usize,
) -> StatsResult<(F, F)> {
let n = y.len();
let y_mean = F::simd_mean(y);
let tss = F::simd_sum_squared_deviations(y, y_mean);
let rss = F::simd_sum_squares(residuals);
let r_squared = F::one() - (rss / tss);
let n_f = F::from(n).expect("Failed to convert to float");
let k_f = F::from(n_params).expect("Failed to convert to float");
let adjusted_r_squared = F::one() -
((F::one() - r_squared) * (n_f - F::one()) / (n_f - k_f));
Ok((r_squared, adjusted_r_squared))
}
fn simd_mean_squared_error(&self, residuals: &ArrayView1<F>) -> StatsResult<F> {
let n = residuals.len();
let ss = F::simd_sum_squares(residuals);
Ok(ss / F::from(n).expect("Failed to convert to float"))
}
fn simd_matrix_inverse(&self, matrix: &ArrayView2<F>) -> StatsResult<Array2<F>> {
let (m, n) = matrix.dim();
if m != n {
return Err(StatsError::InvalidArgument(
"Matrix must be square for inversion".to_string(),
));
}
let matrix_f64 = matrix.mapv(|x| x.to_f64().expect("Operation failed"));
let inv_f64 = scirs2_linalg::inv(&matrix_f64.view(), None)
.map_err(|e| StatsError::ComputationError(format!("Matrix inversion failed: {}", e)))?;
Ok(inv_f64.mapv(|x| F::from(x).expect("Failed to convert to float")))
}
fn simd_solve_normal_equations(&self, xtx: &ArrayView2<F>, xty: &ArrayView1<F>) -> StatsResult<Array1<F>> {
let xtx_inv = self.simd_matrix_inverse(xtx)?;
self.simd_matrix_vector_multiply(&xtx_inv.view(), xty)
}
fn simd_compute_standard_errors(&self, xtxinv: &ArrayView2<F>, mse: F) -> StatsResult<Array1<F>> {
let diagonal = xtx_inv.diag();
Ok(diagonal.mapv(|x| (x * mse).sqrt()))
}
fn simd_compute_t_statistics(&self, coefficients: &ArrayView1<F>, standarderrors: &ArrayView1<F>) -> StatsResult<Array1<F>> {
let mut t_stats = Array1::zeros(coefficients.len());
for i in 0..coefficients.len() {
t_stats[i] = coefficients[i] / standard_errors[i];
}
Ok(t_stats)
}
fn simd_compute_p_values(&self, tstatistics: &ArrayView1<F>, df: usize) -> StatsResult<Array1<F>> {
Ok(t_statistics.mapv(|t| F::from(2.0).expect("Failed to convert constant to float") * (F::one() - F::from(0.95).expect("Failed to convert constant to float"))))
}
fn simd_compute_confidence_intervals(
&self,
coefficients: &ArrayView1<F>,
standard_errors: &ArrayView1<F>,
df: usize,
alpha: F,
) -> StatsResult<Array2<F>> {
let n_params = coefficients.len();
let mut ci = Array2::zeros((n_params, 2));
let t_critical = F::from(1.96).expect("Failed to convert constant to float");
for i in 0..n_params {
let margin = t_critical * standard_errors[i];
ci[[i, 0]] = coefficients[i] - margin;
ci[[i, 1]] = coefficients[i] + margin;
}
Ok(ci)
}
fn simd_compute_anova_table(
&self,
y: &ArrayView1<F>,
fitted: &ArrayView1<F>,
residuals: &ArrayView1<F>,
n_params: usize,
) -> StatsResult<SimdAnovaTable<F>> {
let n = y.len();
let y_mean = F::simd_mean(y);
let ss_total = F::simd_sum_squared_deviations(y, y_mean);
let ss_residual = F::simd_sum_squares(residuals);
let ss_regression = ss_total - ss_residual;
let df_regression = n_params - 1;
let df_residual = n - n_params;
let ms_regression = ss_regression / F::from(df_regression).expect("Failed to convert to float");
let ms_residual = ss_residual / F::from(df_residual).expect("Failed to convert to float");
let f_statistic = ms_regression / ms_residual;
let f_p_value = F::from(0.05).expect("Failed to convert constant to float");
Ok(SimdAnovaTable {
sum_squares_regression: ss_regression,
sum_squares_residual: ss_residual,
sum_squares_total: ss_total,
degrees_freedom_regression: df_regression,
degrees_freedom_residual: df_residual,
mean_square_regression: ms_regression,
mean_square_residual: ms_residual,
f_statistic,
f_p_value,
})
}
fn simd_column_means(&self, data: &ArrayView2<F>) -> StatsResult<Array1<F>> {
let (_, n_cols) = data.dim();
let mut means = Array1::zeros(n_cols);
for j in 0..n_cols {
means[j] = F::simd_mean(&data.column(j));
}
Ok(means)
}
fn simd_centerdata(&self, data: &ArrayView2<F>, means: &ArrayView1<F>) -> StatsResult<Array2<F>> {
let (n_rows, n_cols) = data.dim();
let mut centered = Array2::zeros((n_rows, n_cols));
for j in 0..n_cols {
let column = data.column(j);
let mut centered_col = centered.column_mut(j);
for i in 0..n_rows {
centered_col[i] = column[i] - means[j];
}
}
Ok(centered)
}
fn simd_covariance_matrix(&self, centereddata: &ArrayView2<F>, biascorrection: bool) -> StatsResult<Array2<F>> {
let (n, p) = centereddata.dim();
let mut cov_matrix = Array2::zeros((p, p));
let divisor = if bias_correction {
F::from(n - 1).expect("Failed to convert to float")
} else {
F::from(n).expect("Failed to convert to float")
};
for i in 0..p {
for j in i..p {
let col_i = centereddata.column(i);
let col_j = centereddata.column(j);
let covariance = F::simd_dot_product(&col_i, &col_j) / divisor;
cov_matrix[[i, j]] = covariance;
if i != j {
cov_matrix[[j, i]] = covariance;
}
}
}
Ok(cov_matrix)
}
fn simd_correlation_from_covariance(&self, covmatrix: &ArrayView2<F>) -> StatsResult<Array2<F>> {
let p = cov_matrix.nrows();
let mut corr_matrix = Array2::zeros((p, p));
for i in 0..p {
for j in 0..p {
if i == j {
corr_matrix[[i, j]] = F::one();
} else {
let std_i = cov_matrix[[i, i]].sqrt();
let std_j = cov_matrix[[j, j]].sqrt();
corr_matrix[[i, j]] = cov_matrix[[i, j]] / (std_i * std_j);
}
}
}
Ok(corr_matrix)
}
fn simd_eigendecomposition(&self, matrix: &ArrayView2<F>) -> StatsResult<(Array1<F>, Array2<F>)> {
let p = matrix.nrows();
let eigenvalues = Array1::ones(p);
let eigenvectors = Array2::eye(p);
Ok((eigenvalues, eigenvectors))
}
fn simd_condition_number(&self, eigenvalues: &ArrayView1<F>) -> StatsResult<F> {
let max_eig = eigenvalues.iter().copied().fold(F::neg_infinity(), F::max);
let min_eig = eigenvalues.iter().copied().fold(F::infinity(), F::min);
Ok(max_eig / min_eig)
}
fn simd_determinant_from_eigenvalues(&self, eigenvalues: &ArrayView1<F>) -> StatsResult<F> {
Ok(eigenvalues.iter().copied().product())
}
fn simd_trace(&self, matrix: &ArrayView2<F>) -> StatsResult<F> {
Ok(matrix.diag().sum())
}
fn simd_batch_t_tests(
&self,
group1: &ArrayView2<F>,
group2: &ArrayView2<F>,
) -> StatsResult<(Array1<F>, Array1<F>, Array1<F>, Array2<F>, Array1<F>, Array1<F>)> {
let n_tests = group1.ncols();
let stats = Array1::zeros(n_tests);
let pvals = Array1::zeros(n_tests);
let effects = Array1::zeros(n_tests);
let cis = Array2::zeros((n_tests, 2));
let power = Array1::zeros(n_tests);
let crit = Array1::zeros(n_tests);
Ok((stats, pvals, effects, cis, power, crit))
}
fn simd_batch_mann_whitney_tests(
&self,
group1: &ArrayView2<F>,
group2: &ArrayView2<F>,
) -> StatsResult<(Array1<F>, Array1<F>, Array1<F>, Array2<F>, Array1<F>, Array1<F>)> {
let n_tests = group1.ncols();
let stats = Array1::zeros(n_tests);
let pvals = Array1::zeros(n_tests);
let effects = Array1::zeros(n_tests);
let cis = Array2::zeros((n_tests, 2));
let power = Array1::zeros(n_tests);
let crit = Array1::zeros(n_tests);
Ok((stats, pvals, effects, cis, power, crit))
}
fn simd_batch_ks_tests(
&self,
group1: &ArrayView2<F>,
group2: &ArrayView2<F>,
) -> StatsResult<(Array1<F>, Array1<F>, Array1<F>, Array2<F>, Array1<F>, Array1<F>)> {
let n_tests = group1.ncols();
let stats = Array1::zeros(n_tests);
let pvals = Array1::zeros(n_tests);
let effects = Array1::zeros(n_tests);
let cis = Array2::zeros((n_tests, 2));
let power = Array1::zeros(n_tests);
let crit = Array1::zeros(n_tests);
Ok((stats, pvals, effects, cis, power, crit))
}
}
#[derive(Debug, Clone, Copy)]
pub enum StatisticalTestType {
TTest,
MannWhitney,
KolmogorovSmirnov,
}
impl<F> Default for AdvancedAdvancedSimdProcessor<F>
where
F: Float + NumCast + SimdUnifiedOps + Zero + One + PartialOrd + Copy + Send + Sync
+ std::fmt::Display,
{
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
pub fn advanced_simd_multiple_regression<F>(
y: &ArrayView1<F>,
x: &ArrayView2<F>,
include_intercept: bool,
) -> StatsResult<SimdRegressionResult<F>>
where
F: Float + NumCast + SimdUnifiedOps + Zero + One + PartialOrd + Copy + Send + Sync
+ std::fmt::Display,
{
let processor = AdvancedAdvancedSimdProcessor::new();
processor.simd_multiple_regression(y, x, include_intercept)
}
#[allow(dead_code)]
pub fn advanced_simd_covariance_analysis<F>(
data: &ArrayView2<F>,
bias_correction: bool,
) -> StatsResult<SimdCovarianceResult<F>>
where
F: Float + NumCast + SimdUnifiedOps + Zero + One + PartialOrd + Copy + Send + Sync
+ std::fmt::Display,
{
let processor = AdvancedAdvancedSimdProcessor::new();
processor.simd_multivariate_covariance(data, bias_correction)
}
#[allow(dead_code)]
pub fn advanced_simd_batch_tests<F>(
group1: &ArrayView2<F>,
group2: &ArrayView2<F>,
test_type: StatisticalTestType,
) -> StatsResult<SimdMultiTestResult<F>>
where
F: Float + NumCast + SimdUnifiedOps + Zero + One + PartialOrd + Copy + Send + Sync
+ std::fmt::Display,
{
let processor = AdvancedAdvancedSimdProcessor::new();
processor.simd_batch_statistical_tests(group1, group2, test_type)
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::array;
#[test]
fn test_advanced_advanced_simd_config() {
let config = AdvancedAdvancedSimdConfig::default();
assert!(config.vector_width.f64_lanes > 0);
assert!(config.vector_width.f32_lanes > 0);
assert!(config.parallel_threshold > 0);
}
#[test]
fn test_simd_regression() {
let y = array![1.0, 2.0, 3.0, 4.0, 5.0];
let x = array![[1.0], [2.0], [3.0], [4.0], [5.0]];
let result = advanced_simd_multiple_regression(&y.view(), &x.view(), true);
assert!(result.is_ok());
}
#[test]
fn test_simd_covariance() {
let data = array![
[1.0, 2.0, 3.0],
[2.0, 3.0, 4.0],
[3.0, 4.0, 5.0],
[4.0, 5.0, 6.0],
];
let result = advanced_simd_covariance_analysis(&data.view(), true);
assert!(result.is_ok());
}
#[test]
fn test_vector_width_detection() {
let capabilities = PlatformCapabilities::detect();
let vector_width = VectorWidth::from_capabilities(&capabilities);
assert!(vector_width.f64_lanes >= 1);
assert!(vector_width.f32_lanes >= 1);
assert!(vector_width.optimal_chunk >= 8);
}
}