scirs2_metrics/optimization/
hardware.rs

1//! Hardware acceleration utilities for metrics computation
2//!
3//! This module provides hardware-accelerated implementations of common metrics
4//! using unified SIMD operations from scirs2-core for improved performance
5//! and cross-platform compatibility.
6
7use crate::error::{MetricsError, Result};
8use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
9use scirs2_core::numeric::{Float, One, Zero};
10use scirs2_core::parallel_ops::*;
11use scirs2_core::simd_ops::{AutoOptimizer, PlatformCapabilities, SimdUnifiedOps};
12use statrs::statistics::Statistics;
13
14/// Configuration for hardware acceleration
15#[derive(Debug, Clone)]
16pub struct HardwareAccelConfig {
17    /// Enable SIMD vectorization
18    pub enable_simd: bool,
19    /// Enable GPU acceleration (when available)
20    pub enable_gpu: bool,
21    /// Minimum data size to use hardware acceleration
22    pub min_data_size: usize,
23    /// Preferred vector width for SIMD operations
24    pub vector_width: VectorWidth,
25    /// GPU memory threshold for offloading
26    pub gpu_memory_threshold: usize,
27}
28
29/// Vector width for SIMD operations
30#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum VectorWidth {
32    /// 128-bit vectors (SSE)
33    V128,
34    /// 256-bit vectors (AVX)
35    V256,
36    /// 512-bit vectors (AVX-512)
37    V512,
38    /// Auto-detect best available
39    Auto,
40}
41
42impl Default for HardwareAccelConfig {
43    fn default() -> Self {
44        Self {
45            enable_simd: true,
46            enable_gpu: false, // Disabled by default until GPU backend is ready
47            min_data_size: 1000,
48            vector_width: VectorWidth::Auto,
49            gpu_memory_threshold: 1024 * 1024, // 1MB
50        }
51    }
52}
53
54impl HardwareAccelConfig {
55    /// Create new hardware acceleration configuration
56    pub fn new() -> Self {
57        Self::default()
58    }
59
60    /// Enable/disable SIMD vectorization
61    pub fn with_simd_enabled(mut self, enabled: bool) -> Self {
62        self.enable_simd = enabled;
63        self
64    }
65
66    /// Enable/disable GPU acceleration
67    pub fn with_gpu_enabled(mut self, enabled: bool) -> Self {
68        self.enable_gpu = enabled;
69        self
70    }
71
72    /// Set minimum data size for hardware acceleration
73    pub fn with_min_data_size(mut self, size: usize) -> Self {
74        self.min_data_size = size;
75        self
76    }
77
78    /// Set preferred vector width
79    pub fn with_vector_width(mut self, width: VectorWidth) -> Self {
80        self.vector_width = width;
81        self
82    }
83
84    /// Set GPU memory threshold
85    pub fn with_gpu_memory_threshold(mut self, threshold: usize) -> Self {
86        self.gpu_memory_threshold = threshold;
87        self
88    }
89}
90
91/// Hardware capabilities detector (using core platform capabilities)
92#[derive(Debug)]
93pub struct HardwareCapabilities {
94    pub has_sse: bool,
95    pub has_sse2: bool,
96    pub has_sse3: bool,
97    pub has_ssse3: bool,
98    pub has_sse41: bool,
99    pub has_sse42: bool,
100    pub has_avx: bool,
101    pub has_avx2: bool,
102    pub has_avx512f: bool,
103    pub has_fma: bool,
104    pub has_gpu: bool,
105    pub gpu_memory: Option<usize>,
106    // Store individual capabilities instead of the entire struct
107    pub simd_available: bool,
108    pub avx2_available: bool,
109    pub avx512_available: bool,
110    pub gpu_available: bool,
111}
112
113impl HardwareCapabilities {
114    /// Detect hardware capabilities using core platform detection
115    pub fn detect() -> Self {
116        let core_caps = PlatformCapabilities::detect();
117
118        Self {
119            has_sse: true, // Assume SSE is available if we're on x86_64 (legacy compatibility)
120            has_sse2: core_caps.simd_available,
121            has_sse3: core_caps.simd_available,
122            has_ssse3: core_caps.simd_available,
123            has_sse41: core_caps.simd_available,
124            has_sse42: core_caps.simd_available,
125            has_avx: core_caps.avx2_available,
126            has_avx2: core_caps.avx2_available,
127            has_avx512f: core_caps.avx512_available,
128            has_fma: core_caps.simd_available, // FMA is typically available with modern SIMD
129            has_gpu: core_caps.gpu_available,
130            gpu_memory: None, // GPU memory detection not implemented in core yet
131            simd_available: core_caps.simd_available,
132            avx2_available: core_caps.avx2_available,
133            avx512_available: core_caps.avx512_available,
134            gpu_available: core_caps.gpu_available,
135        }
136    }
137
138    /// Get optimal vector width for current hardware
139    pub fn optimal_vector_width(&self) -> VectorWidth {
140        if self.avx512_available {
141            VectorWidth::V512
142        } else if self.avx2_available {
143            VectorWidth::V256
144        } else {
145            VectorWidth::V128 // Default for SIMD or fallback
146        }
147    }
148
149    /// Check if SIMD is available
150    pub fn simd_available(&self) -> bool {
151        self.simd_available
152    }
153}
154
155/// SIMD-accelerated distance computations
156pub struct SimdDistanceMetrics {
157    config: HardwareAccelConfig,
158    capabilities: HardwareCapabilities,
159}
160
161impl SimdDistanceMetrics {
162    /// Create new SIMD distance metrics calculator
163    pub fn new() -> Self {
164        Self {
165            config: HardwareAccelConfig::default(),
166            capabilities: HardwareCapabilities::detect(),
167        }
168    }
169
170    /// Create with custom configuration
171    pub fn with_config(config: HardwareAccelConfig) -> Self {
172        Self {
173            config,
174            capabilities: HardwareCapabilities::detect(),
175        }
176    }
177
178    /// Compute Euclidean distance using SIMD
179    pub fn euclidean_distance_simd<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
180    where
181        F: Float + SimdUnifiedOps + std::fmt::Debug + 'static,
182    {
183        if a.len() != b.len() {
184            return Err(MetricsError::InvalidInput(
185                "Arrays must have the same length".to_string(),
186            ));
187        }
188
189        if !self.config.enable_simd
190            || !self.capabilities.simd_available()
191            || a.len() < self.config.min_data_size
192        {
193            // Fallback to standard implementation
194            return self.euclidean_distance_standard(a, b);
195        }
196
197        // Use unified SIMD operations for distance calculation
198        let diff = F::simd_sub(&a.view(), &b.view());
199        let squared_diff = F::simd_mul(&diff.view(), &diff.view());
200        let distance = F::simd_sum(&squared_diff.view()).sqrt();
201        Ok(distance)
202    }
203
204    /// Compute Manhattan distance using SIMD
205    pub fn manhattan_distance_simd<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
206    where
207        F: Float + SimdUnifiedOps + std::fmt::Debug + 'static + std::iter::Sum,
208    {
209        if a.len() != b.len() {
210            return Err(MetricsError::InvalidInput(
211                "Arrays must have the same length".to_string(),
212            ));
213        }
214
215        if !self.config.enable_simd
216            || !self.capabilities.simd_available()
217            || a.len() < self.config.min_data_size
218        {
219            return self.manhattan_distance_standard(a, b);
220        }
221
222        // Use unified SIMD operations for Manhattan distance
223        let diff = F::simd_sub(&a.view(), &b.view());
224        let abs_diff = F::simd_abs(&diff.view());
225        let distance = F::simd_sum(&abs_diff.view());
226        Ok(distance)
227    }
228
229    /// Compute cosine distance using SIMD
230    pub fn cosine_distance_simd<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
231    where
232        F: Float + SimdUnifiedOps + std::fmt::Debug + PartialEq + 'static,
233    {
234        if a.len() != b.len() {
235            return Err(MetricsError::InvalidInput(
236                "Arrays must have the same length".to_string(),
237            ));
238        }
239
240        if !self.config.enable_simd
241            || !self.capabilities.simd_available()
242            || a.len() < self.config.min_data_size
243        {
244            return self.cosine_distance_standard(a, b);
245        }
246
247        // For cosine distance, we need dot product and norms
248        let dot_product = self.dot_product_simd(a, b)?;
249        let norm_a = self.euclidean_norm_simd(a)?;
250        let norm_b = self.euclidean_norm_simd(b)?;
251
252        if norm_a == F::zero() || norm_b == F::zero() {
253            return Ok(F::one()); // Maximum distance
254        }
255
256        let cosine_similarity = dot_product / (norm_a * norm_b);
257        Ok(F::one() - cosine_similarity)
258    }
259
260    /// Compute dot product using SIMD
261    pub fn dot_product_simd<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
262    where
263        F: Float + SimdUnifiedOps + std::fmt::Debug + 'static,
264    {
265        if a.len() != b.len() {
266            return Err(MetricsError::InvalidInput(
267                "Arrays must have the same length".to_string(),
268            ));
269        }
270
271        if !self.config.enable_simd
272            || !self.capabilities.simd_available()
273            || a.len() < self.config.min_data_size
274        {
275            return Ok(a.dot(b));
276        }
277
278        // Use unified SIMD operations for dot product
279        let dot_product = F::simd_dot(&a.view(), &b.view());
280        Ok(dot_product)
281    }
282
283    /// Compute Euclidean norm using SIMD
284    pub fn euclidean_norm_simd<F>(&self, a: &Array1<F>) -> Result<F>
285    where
286        F: Float + SimdUnifiedOps + std::fmt::Debug + 'static,
287    {
288        if !self.config.enable_simd
289            || !self.capabilities.simd_available()
290            || a.len() < self.config.min_data_size
291        {
292            return Ok(a.dot(a).sqrt());
293        }
294
295        let norm_squared = self.dot_product_simd(a, a)?;
296        Ok(norm_squared.sqrt())
297    }
298
299    // Private implementation methods
300
301    fn euclidean_distance_standard<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
302    where
303        F: Float + std::fmt::Debug + 'static,
304    {
305        let diff = a - b;
306        Ok(diff.dot(&diff).sqrt())
307    }
308
309    fn manhattan_distance_standard<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
310    where
311        F: Float + std::fmt::Debug + std::iter::Sum,
312    {
313        let sum: F = a.iter().zip(b.iter()).map(|(x, y)| (*x - *y).abs()).sum();
314        Ok(sum)
315    }
316
317    fn cosine_distance_standard<F>(&self, a: &Array1<F>, b: &Array1<F>) -> Result<F>
318    where
319        F: Float + std::fmt::Debug + Zero + One + 'static,
320    {
321        let dot_product = a.dot(b);
322        let norm_a = a.dot(a).sqrt();
323        let norm_b = b.dot(b).sqrt();
324
325        if norm_a == F::zero() || norm_b == F::zero() {
326            return Ok(F::one());
327        }
328
329        let cosine_similarity = dot_product / (norm_a * norm_b);
330        Ok(F::one() - cosine_similarity)
331    }
332}
333
334impl Default for SimdDistanceMetrics {
335    fn default() -> Self {
336        Self::new()
337    }
338}
339
340/// SIMD-accelerated statistical computations
341pub struct SimdStatistics {
342    config: HardwareAccelConfig,
343    capabilities: HardwareCapabilities,
344}
345
346impl SimdStatistics {
347    /// Create new SIMD statistics calculator
348    pub fn new() -> Self {
349        Self {
350            config: HardwareAccelConfig::default(),
351            capabilities: HardwareCapabilities::detect(),
352        }
353    }
354
355    /// Create with custom configuration
356    pub fn with_config(config: HardwareAccelConfig) -> Self {
357        Self {
358            config,
359            capabilities: HardwareCapabilities::detect(),
360        }
361    }
362
363    /// Compute mean using SIMD
364    pub fn mean_simd(&self, data: &Array1<f64>) -> Result<f64> {
365        if !self.config.enable_simd
366            || !self.capabilities.simd_available()
367            || data.len() < self.config.min_data_size
368        {
369            return Ok(data.mean().unwrap_or(0.0));
370        }
371
372        let sum = self.sum_simd(data)?;
373        Ok(sum / data.len() as f64)
374    }
375
376    /// Compute variance using SIMD
377    pub fn variance_simd(&self, data: &Array1<f64>) -> Result<f64> {
378        if data.len() < 2 {
379            return Ok(0.0);
380        }
381
382        if !self.config.enable_simd
383            || !self.capabilities.simd_available()
384            || data.len() < self.config.min_data_size
385        {
386            let mean = data.mean().unwrap_or(0.0);
387            let var =
388                data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (data.len() - 1) as f64;
389            return Ok(var);
390        }
391
392        let mean = self.mean_simd(data)?;
393        let sum_squared_diff = self.sum_squared_differences_simd(data, mean)?;
394        Ok(sum_squared_diff / (data.len() - 1) as f64)
395    }
396
397    /// Compute standard deviation using SIMD
398    pub fn std_simd(&self, data: &Array1<f64>) -> Result<f64> {
399        let variance = self.variance_simd(data)?;
400        Ok(variance.sqrt())
401    }
402
403    /// Compute sum using SIMD
404    pub fn sum_simd(&self, data: &Array1<f64>) -> Result<f64> {
405        if !self.config.enable_simd
406            || !self.capabilities.simd_available()
407            || data.len() < self.config.min_data_size
408        {
409            return Ok(data.sum());
410        }
411
412        // Use unified SIMD operations for sum
413        let sum = f64::simd_sum(&data.view());
414        Ok(sum)
415    }
416
417    /// Compute sum of squared differences from mean using SIMD
418    pub fn sum_squared_differences_simd(&self, data: &Array1<f64>, mean: f64) -> Result<f64> {
419        if !self.config.enable_simd
420            || !self.capabilities.simd_available()
421            || data.len() < self.config.min_data_size
422        {
423            let sum = data.iter().map(|x| (x - mean).powi(2)).sum::<f64>();
424            return Ok(sum);
425        }
426
427        // Use unified SIMD operations for sum of squared differences
428        let mean_array = Array1::from_elem(data.len(), mean);
429        let diff = f64::simd_sub(&data.view(), &mean_array.view());
430        let squared = f64::simd_mul(&diff.view(), &diff.view());
431        let sum = f64::simd_sum(&squared.view());
432        Ok(sum)
433    }
434}
435
436impl Default for SimdStatistics {
437    fn default() -> Self {
438        Self::new()
439    }
440}
441
442/// Matrix operations with hardware acceleration
443pub struct HardwareAcceleratedMatrix {
444    config: HardwareAccelConfig,
445    capabilities: HardwareCapabilities,
446}
447
448impl HardwareAcceleratedMatrix {
449    /// Create new hardware-accelerated matrix operations
450    pub fn new() -> Self {
451        Self {
452            config: HardwareAccelConfig::default(),
453            capabilities: HardwareCapabilities::detect(),
454        }
455    }
456
457    /// Create with custom configuration
458    pub fn with_config(config: HardwareAccelConfig) -> Self {
459        Self {
460            config,
461            capabilities: HardwareCapabilities::detect(),
462        }
463    }
464
465    /// Matrix-vector multiplication with hardware acceleration
466    pub fn matvec_accelerated(
467        &self,
468        matrix: &Array2<f64>,
469        vector: &Array1<f64>,
470    ) -> Result<Array1<f64>> {
471        let (rows, cols) = matrix.dim();
472        if cols != vector.len() {
473            return Err(MetricsError::InvalidInput(
474                "Matrix columns must match vector length".to_string(),
475            ));
476        }
477
478        if !self.config.enable_simd
479            || !self.capabilities.simd_available()
480            || rows * cols < self.config.min_data_size
481        {
482            // Fallback to standard implementation
483            return Ok(matrix.dot(vector));
484        }
485
486        let mut result = Array1::zeros(rows);
487        let simd_distances = SimdDistanceMetrics::with_config(self.config.clone());
488
489        // Compute dot product for each row
490        for (i, row) in matrix.rows().into_iter().enumerate() {
491            result[i] = simd_distances.dot_product_simd(&row.to_owned(), vector)?;
492        }
493
494        Ok(result)
495    }
496
497    /// Pairwise distance matrix computation with hardware acceleration
498    pub fn pairwise_distances_accelerated(
499        &self,
500        data: &Array2<f64>,
501        metric: &str,
502    ) -> Result<Array2<f64>> {
503        let n_samples = data.nrows();
504        let mut distances = Array2::zeros((n_samples, n_samples));
505
506        if !self.config.enable_simd || !self.capabilities.simd_available() {
507            // Fallback to standard implementation
508            return self.pairwise_distances_standard(data, metric);
509        }
510
511        let simd_distances = SimdDistanceMetrics::with_config(self.config.clone());
512
513        for i in 0..n_samples {
514            for j in (i + 1)..n_samples {
515                let row_i = data.row(i).to_owned();
516                let row_j = data.row(j).to_owned();
517
518                let distance = match metric {
519                    "euclidean" => simd_distances.euclidean_distance_simd(&row_i, &row_j)?,
520                    "manhattan" => simd_distances.manhattan_distance_simd(&row_i, &row_j)?,
521                    "cosine" => simd_distances.cosine_distance_simd(&row_i, &row_j)?,
522                    _ => {
523                        return Err(MetricsError::InvalidInput(format!(
524                            "Unsupported metric: {}",
525                            metric
526                        )))
527                    }
528                };
529
530                distances[[i, j]] = distance;
531                distances[[j, i]] = distance; // Symmetric
532            }
533        }
534
535        Ok(distances)
536    }
537
538    /// Correlation matrix computation with hardware acceleration
539    pub fn correlation_matrix_accelerated(&self, data: &Array2<f64>) -> Result<Array2<f64>> {
540        let (_n_samples, n_features) = data.dim();
541        let mut correlation_matrix = Array2::zeros((n_features, n_features));
542
543        if !self.config.enable_simd || !self.capabilities.simd_available() {
544            return self.correlation_matrix_standard(data);
545        }
546
547        let simd_stats = SimdStatistics::with_config(self.config.clone());
548
549        // Compute means for each feature
550        let mut means = Array1::zeros(n_features);
551        for j in 0..n_features {
552            let column = data.column(j).to_owned();
553            means[j] = simd_stats.mean_simd(&column)?;
554        }
555
556        // Compute correlations
557        for i in 0..n_features {
558            for j in (i + 1)..n_features {
559                let col_i = data.column(i).to_owned();
560                let col_j = data.column(j).to_owned();
561
562                let correlation =
563                    self.compute_correlation_simd(&col_i, &col_j, means[i], means[j], &simd_stats)?;
564
565                correlation_matrix[[i, j]] = correlation;
566                correlation_matrix[[j, i]] = correlation;
567            }
568            // Diagonal elements are 1.0
569            correlation_matrix[[i, i]] = 1.0;
570        }
571
572        Ok(correlation_matrix)
573    }
574
575    // Private helper methods
576
577    fn pairwise_distances_standard(&self, data: &Array2<f64>, metric: &str) -> Result<Array2<f64>> {
578        let n_samples = data.nrows();
579        let mut distances = Array2::zeros((n_samples, n_samples));
580
581        for i in 0..n_samples {
582            for j in (i + 1)..n_samples {
583                let row_i = data.row(i);
584                let row_j = data.row(j);
585
586                let distance = match metric {
587                    "euclidean" => {
588                        let diff = &row_i.to_owned() - &row_j.to_owned();
589                        diff.dot(&diff).sqrt()
590                    }
591                    "manhattan" => row_i
592                        .iter()
593                        .zip(row_j.iter())
594                        .map(|(a, b)| (a - b).abs())
595                        .sum(),
596                    "cosine" => {
597                        let dot_product = row_i.dot(&row_j);
598                        let norm_i = row_i.dot(&row_i).sqrt();
599                        let norm_j = row_j.dot(&row_j).sqrt();
600                        if norm_i == 0.0 || norm_j == 0.0 {
601                            1.0
602                        } else {
603                            1.0 - dot_product / (norm_i * norm_j)
604                        }
605                    }
606                    _ => {
607                        return Err(MetricsError::InvalidInput(format!(
608                            "Unsupported metric: {}",
609                            metric
610                        )))
611                    }
612                };
613
614                distances[[i, j]] = distance;
615                distances[[j, i]] = distance;
616            }
617        }
618
619        Ok(distances)
620    }
621
622    fn correlation_matrix_standard(&self, data: &Array2<f64>) -> Result<Array2<f64>> {
623        let (n_samples, n_features) = data.dim();
624        let mut correlation_matrix = Array2::zeros((n_features, n_features));
625
626        // Compute means
627        let means: Array1<f64> = data.mean_axis(Axis(0)).unwrap();
628
629        for i in 0..n_features {
630            for j in (i + 1)..n_features {
631                let col_i = data.column(i);
632                let col_j = data.column(j);
633
634                let mean_i = means[i];
635                let mean_j = means[j];
636
637                // Compute covariance and standard deviations
638                let mut cov = 0.0;
639                let mut var_i = 0.0;
640                let mut var_j = 0.0;
641
642                for k in 0..n_samples {
643                    let diff_i = col_i[k] - mean_i;
644                    let diff_j = col_j[k] - mean_j;
645                    cov += diff_i * diff_j;
646                    var_i += diff_i * diff_i;
647                    var_j += diff_j * diff_j;
648                }
649
650                let correlation = if var_i > 1e-10 && var_j > 1e-10 {
651                    cov / (var_i * var_j).sqrt()
652                } else {
653                    0.0
654                };
655
656                correlation_matrix[[i, j]] = correlation;
657                correlation_matrix[[j, i]] = correlation;
658            }
659            correlation_matrix[[i, i]] = 1.0;
660        }
661
662        Ok(correlation_matrix)
663    }
664
665    fn compute_correlation_simd(
666        &self,
667        col_i: &Array1<f64>,
668        col_j: &Array1<f64>,
669        mean_i: f64,
670        mean_j: f64,
671        simd_stats: &SimdStatistics,
672    ) -> Result<f64> {
673        // Compute centered vectors
674        let centered_i = col_i.mapv(|x| x - mean_i);
675        let centered_j = col_j.mapv(|x| x - mean_j);
676
677        // Compute covariance using SIMD dot product
678        let simd_distances = SimdDistanceMetrics::with_config(self.config.clone());
679        let covariance = simd_distances.dot_product_simd(&centered_i, &centered_j)?;
680
681        // Compute standard deviations using SIMD
682        let var_i = simd_stats.sum_squared_differences_simd(col_i, mean_i)?;
683        let var_j = simd_stats.sum_squared_differences_simd(col_j, mean_j)?;
684
685        if var_i > 1e-10 && var_j > 1e-10 {
686            Ok(covariance / (var_i * var_j).sqrt())
687        } else {
688            Ok(0.0)
689        }
690    }
691}
692
693impl Default for HardwareAcceleratedMatrix {
694    fn default() -> Self {
695        Self::new()
696    }
697}
698
699#[cfg(test)]
700mod tests {
701    use super::*;
702    use scirs2_core::ndarray::Array;
703
704    #[test]
705    fn test_hardware_capabilities() {
706        let capabilities = HardwareCapabilities::detect();
707        println!("Hardware capabilities: {:?}", capabilities);
708
709        // Just verify detection works - actual capabilities depend on hardware
710        assert!(
711            capabilities.optimal_vector_width() != VectorWidth::V512 || capabilities.has_avx512f
712        );
713    }
714
715    #[test]
716    fn test_simd_distance_metrics() {
717        let metrics = SimdDistanceMetrics::new();
718        let a = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
719        let b = Array1::from_vec(vec![2.0, 3.0, 4.0, 5.0, 6.0]);
720
721        // Test Euclidean distance
722        let euclidean = metrics.euclidean_distance_simd(&a, &b).unwrap();
723        let expected_euclidean = (5.0_f64).sqrt(); // sqrt(5 * 1^2)
724        assert!((euclidean - expected_euclidean).abs() < 1e-10);
725
726        // Test Manhattan distance
727        let manhattan = metrics.manhattan_distance_simd(&a, &b).unwrap();
728        assert!((manhattan - 5.0).abs() < 1e-10);
729
730        // Test dot product
731        let dot = metrics.dot_product_simd(&a, &b).unwrap();
732        let expected_dot = 70.0; // 1*2 + 2*3 + 3*4 + 4*5 + 5*6
733        assert!((dot - expected_dot).abs() < 1e-10);
734    }
735
736    #[test]
737    fn test_simd_statistics() {
738        let stats = SimdStatistics::new();
739        let data = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
740
741        // Test mean
742        let mean = stats.mean_simd(&data).unwrap();
743        assert!((mean - 3.0).abs() < 1e-10);
744
745        // Test sum
746        let sum = stats.sum_simd(&data).unwrap();
747        assert!((sum - 15.0).abs() < 1e-10);
748
749        // Test variance
750        let variance = stats.variance_simd(&data).unwrap();
751        let expected_variance = 2.5; // Sample variance
752        assert!((variance - expected_variance).abs() < 1e-10);
753    }
754
755    #[test]
756    fn test_hardware_accelerated_matrix() {
757        let matrix_ops = HardwareAcceleratedMatrix::new();
758
759        // Test matrix-vector multiplication
760        let matrix =
761            Array2::from_shape_vec((3, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
762                .unwrap();
763        let vector = Array1::from_vec(vec![1.0, 2.0, 3.0]);
764
765        let result = matrix_ops.matvec_accelerated(&matrix, &vector).unwrap();
766        let expected = Array1::from_vec(vec![14.0, 32.0, 50.0]); // 1*1+2*2+3*3, etc.
767
768        for (actual, expected) in result.iter().zip(expected.iter()) {
769            assert!((actual - expected).abs() < 1e-10);
770        }
771
772        // Test pairwise distances
773        let data = Array2::from_shape_vec((3, 2), vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0]).unwrap();
774
775        let distances = matrix_ops
776            .pairwise_distances_accelerated(&data, "euclidean")
777            .unwrap();
778
779        // Check symmetry
780        assert!((distances[[0, 1]] - distances[[1, 0]]).abs() < 1e-10);
781        assert!((distances[[0, 2]] - distances[[2, 0]]).abs() < 1e-10);
782        assert!((distances[[1, 2]] - distances[[2, 1]]).abs() < 1e-10);
783
784        // Check diagonal is zero
785        assert!(distances[[0, 0]].abs() < 1e-10);
786        assert!(distances[[1, 1]].abs() < 1e-10);
787        assert!(distances[[2, 2]].abs() < 1e-10);
788    }
789
790    #[test]
791    fn test_config_builder() {
792        let config = HardwareAccelConfig::new()
793            .with_simd_enabled(true)
794            .with_gpu_enabled(false)
795            .with_min_data_size(500)
796            .with_vector_width(VectorWidth::V256)
797            .with_gpu_memory_threshold(2048);
798
799        assert!(config.enable_simd);
800        assert!(!config.enable_gpu);
801        assert_eq!(config.min_data_size, 500);
802        assert_eq!(config.gpu_memory_threshold, 2048);
803    }
804}