Skip to main content

sklears_simd/
lib.rs

1//! SIMD-optimized operations for sklears
2//!
3//! This crate provides SIMD-accelerated implementations of common machine learning operations.
4//!
5//! ## SciRS2 Policy Compliance
6//! - SIMD operations delegated to scirs2-core's backend
7//! - Works on stable Rust (no nightly features required)
8//! - Platform-specific optimizations handled by ndarray/BLAS
9
10// Note: no-std feature is temporarily disabled until implementation is complete
11#![cfg_attr(feature = "no-std", no_std)]
12// The simd_feature_detected! macro wraps is_x86_feature_detected! with different
13// feature strings. Clippy cannot distinguish these macro invocations and fires
14// false-positive "same condition" / "simplified bool" lints. These are intentional
15// SIMD dispatch patterns and the lints are suppressed at crate level.
16#![allow(clippy::ifs_same_cond)]
17#![allow(clippy::nonminimal_bool)]
18#![allow(clippy::eq_op)]
19
20#[cfg(feature = "no-std")]
21extern crate alloc;
22
23// No-std compatible print macros (no-op for tests)
24#[cfg(feature = "no-std")]
25#[macro_export]
26macro_rules! println {
27    ($($arg:tt)*) => {{}};
28}
29
30#[cfg(feature = "no-std")]
31#[macro_export]
32macro_rules! eprintln {
33    ($($arg:tt)*) => {{}};
34}
35
36// Conditional SIMD feature detection macro
37// In no-std mode, always return false (use scalar fallback)
38// In std mode, use the actual is_x86_feature_detected! macro
39#[cfg(all(
40    any(target_arch = "x86", target_arch = "x86_64"),
41    not(feature = "no-std")
42))]
43#[macro_export]
44macro_rules! simd_feature_detected {
45    ($feature:tt) => {
46        std::arch::is_x86_feature_detected!($feature)
47    };
48}
49
50#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "no-std"))]
51#[macro_export]
52#[allow(unused_macros)]
53macro_rules! simd_feature_detected {
54    ($feature:tt) => {
55        false
56    };
57}
58
59#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
60#[macro_export]
61#[allow(unused_macros)]
62macro_rules! simd_feature_detected {
63    ($feature:tt) => {
64        false
65    };
66}
67
68// Re-export for use in submodules
69
70pub mod activation;
71pub mod adaptive_optimization;
72pub mod advanced_optimizations;
73pub mod allocator;
74pub mod approximate;
75pub mod audio_processing;
76pub mod batch_operations;
77pub mod benchmark_framework;
78pub mod bit_operations;
79pub mod clustering;
80pub mod comprehensive_benchmarks;
81pub mod compression;
82pub mod custom_accelerator;
83pub mod distance;
84pub mod distributions;
85pub mod energy_benchmarks;
86pub mod error_correction;
87pub mod external_integration;
88pub mod fluent;
89pub mod fpga;
90pub mod half_precision;
91pub mod image_processing;
92pub mod intrinsics;
93pub mod kernels;
94pub mod loss;
95pub mod matrix;
96pub mod memory;
97pub mod middleware;
98pub mod neuromorphic;
99#[cfg(feature = "no-std")]
100pub mod no_std;
101pub mod optimization;
102pub mod optimization_hints;
103pub mod performance_hooks;
104pub mod performance_monitor;
105pub mod plugin_architecture;
106pub mod profiling;
107pub mod quantum;
108pub mod reduction;
109pub mod regression;
110#[cfg(target_arch = "riscv64")]
111pub mod riscv_vector;
112pub mod safe_simd;
113pub mod safety;
114pub mod search;
115pub mod signal_processing;
116pub mod sorting;
117pub mod target;
118pub mod tpu;
119pub mod traits;
120pub mod validation;
121pub mod vector;
122
123// Re-export key types and functions
124pub use clustering::LinkageType;
125
126/// Platform-specific SIMD capabilities
127#[derive(Debug, Clone, Copy)]
128pub struct SimdCapabilities {
129    pub sse: bool,
130    pub sse2: bool,
131    pub sse3: bool,
132    pub ssse3: bool,
133    pub sse41: bool,
134    pub sse42: bool,
135    pub avx: bool,
136    pub avx2: bool,
137    pub avx512: bool,
138    pub neon: bool,
139    pub riscv_vector: bool,
140    pub riscv_vlen: usize,
141}
142
143impl SimdCapabilities {
144    /// Detect available SIMD instructions on the current platform
145    pub fn detect() -> Self {
146        Self {
147            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
148            sse: simd_feature_detected!("sse"),
149            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
150            sse2: simd_feature_detected!("sse2"),
151            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
152            sse3: simd_feature_detected!("sse3"),
153            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
154            ssse3: simd_feature_detected!("ssse3"),
155            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
156            sse41: simd_feature_detected!("sse4.1"),
157            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
158            sse42: simd_feature_detected!("sse4.2"),
159            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
160            avx: simd_feature_detected!("avx"),
161            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
162            avx2: simd_feature_detected!("avx2"),
163            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
164            avx512: simd_feature_detected!("avx512f"),
165            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
166            sse: false,
167            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
168            sse2: false,
169            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
170            sse3: false,
171            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
172            ssse3: false,
173            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
174            sse41: false,
175            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
176            sse42: false,
177            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
178            avx: false,
179            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
180            avx2: false,
181            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
182            avx512: false,
183            #[cfg(target_arch = "aarch64")]
184            neon: true,
185            #[cfg(not(target_arch = "aarch64"))]
186            neon: false,
187
188            #[cfg(target_arch = "riscv64")]
189            riscv_vector: {
190                #[cfg(target_arch = "riscv64")]
191                {
192                    crate::riscv_vector::RiscVVectorCaps::detect().available
193                }
194                #[cfg(not(target_arch = "riscv64"))]
195                {
196                    false
197                }
198            },
199            #[cfg(not(target_arch = "riscv64"))]
200            riscv_vector: false,
201
202            #[cfg(target_arch = "riscv64")]
203            riscv_vlen: {
204                #[cfg(target_arch = "riscv64")]
205                {
206                    crate::riscv_vector::RiscVVectorCaps::detect().vlen
207                }
208                #[cfg(not(target_arch = "riscv64"))]
209                {
210                    0
211                }
212            },
213            #[cfg(not(target_arch = "riscv64"))]
214            riscv_vlen: 0,
215        }
216    }
217
218    /// Get the best available SIMD width for f32 operations
219    pub fn best_f32_width(&self) -> usize {
220        if self.avx512 {
221            16 // 512 bits / 32 bits
222        } else if self.avx2 || self.avx {
223            8 // 256 bits / 32 bits
224        } else if self.sse || self.neon {
225            4 // 128 bits / 32 bits
226        } else if self.riscv_vector && self.riscv_vlen > 0 {
227            self.riscv_vlen / 32 // VLEN bits / 32 bits per f32
228        } else {
229            1 // Scalar fallback
230        }
231    }
232
233    /// Get the best available SIMD width for f64 operations
234    pub fn best_f64_width(&self) -> usize {
235        if self.avx512 {
236            8 // 512 bits / 64 bits
237        } else if self.avx2 || self.avx {
238            4 // 256 bits / 64 bits
239        } else if self.sse2 || self.neon {
240            2 // 128 bits / 64 bits
241        } else if self.riscv_vector && self.riscv_vlen > 0 {
242            self.riscv_vlen / 64 // VLEN bits / 64 bits per f64
243        } else {
244            1 // Scalar fallback
245        }
246    }
247
248    /// Get the platform name for current SIMD capabilities
249    pub fn platform_name(&self) -> &'static str {
250        if self.avx512 {
251            "AVX-512"
252        } else if self.avx2 {
253            "AVX2"
254        } else if self.avx {
255            "AVX"
256        } else if self.sse42 {
257            "SSE4.2"
258        } else if self.sse41 {
259            "SSE4.1"
260        } else if self.ssse3 {
261            "SSSE3"
262        } else if self.sse3 {
263            "SSE3"
264        } else if self.sse2 {
265            "SSE2"
266        } else if self.sse {
267            "SSE"
268        } else if self.neon {
269            "NEON"
270        } else if self.riscv_vector {
271            "RISC-V Vector"
272        } else {
273            "Scalar"
274        }
275    }
276}
277
278/// Global SIMD capabilities detection
279pub static SIMD_CAPS: once_cell::sync::Lazy<SimdCapabilities> =
280    once_cell::sync::Lazy::new(SimdCapabilities::detect);
281
282#[allow(non_snake_case)]
283#[cfg(all(test, not(feature = "no-std")))]
284mod tests {
285    use super::*;
286
287    #[test]
288    fn test_simd_detection() {
289        let caps = SimdCapabilities::detect();
290        println!("SIMD Capabilities: {:?}", caps);
291
292        // At least one width should be available
293        assert!(caps.best_f32_width() >= 1);
294        assert!(caps.best_f64_width() >= 1);
295    }
296}