Skip to main content

sklears_python/
utils.rs

1//! Python bindings for utility functions
2//!
3//! This module provides Python bindings for sklears utilities,
4//! including version information and build details.
5
6use numpy::{PyArray1, PyArray2, PyArrayMethods};
7use pyo3::exceptions::PyValueError;
8use pyo3::prelude::*;
9use scirs2_core::ndarray::{Array1, Array2};
10use scirs2_core::random::thread_rng;
11use std::collections::HashMap;
12
13use crate::linear::{
14    core_array1_to_py, core_array2_to_py, pyarray_to_core_array1, pyarray_to_core_array2,
15};
16
17/// Get the version of sklears
18#[pyfunction]
19pub fn get_version() -> String {
20    env!("CARGO_PKG_VERSION").to_string()
21}
22
23/// Get build information about sklears
24#[pyfunction]
25pub fn get_build_info() -> HashMap<String, String> {
26    let mut info = HashMap::new();
27
28    info.insert("version".to_string(), env!("CARGO_PKG_VERSION").to_string());
29    info.insert("authors".to_string(), env!("CARGO_PKG_AUTHORS").to_string());
30    info.insert(
31        "description".to_string(),
32        env!("CARGO_PKG_DESCRIPTION").to_string(),
33    );
34    info.insert(
35        "homepage".to_string(),
36        env!("CARGO_PKG_HOMEPAGE").to_string(),
37    );
38    info.insert(
39        "repository".to_string(),
40        env!("CARGO_PKG_REPOSITORY").to_string(),
41    );
42    info.insert("license".to_string(), env!("CARGO_PKG_LICENSE").to_string());
43    info.insert(
44        "rust_version".to_string(),
45        env!("CARGO_PKG_RUST_VERSION").to_string(),
46    );
47
48    // Build-time information
49    info.insert(
50        "target_triple".to_string(),
51        std::env::var("TARGET").unwrap_or_else(|_| "unknown".to_string()),
52    );
53    info.insert(
54        "build_profile".to_string(),
55        if cfg!(debug_assertions) {
56            "debug"
57        } else {
58            "release"
59        }
60        .to_string(),
61    );
62
63    // Feature information
64    let features = [
65        #[cfg(feature = "pandas-integration")]
66        "pandas-integration",
67    ];
68
69    info.insert("features".to_string(), features.join(", "));
70
71    // Dependency versions
72    info.insert("scirs2_core_version".to_string(), "workspace".to_string());
73    info.insert("pyo3_version".to_string(), "0.26".to_string());
74    info.insert("numpy_version".to_string(), "0.26".to_string());
75
76    info
77}
78
79/// Check if specific features are enabled
80#[pyfunction]
81pub fn has_feature(feature_name: &str) -> bool {
82    match feature_name {
83        "pandas-integration" => {
84            #[cfg(feature = "pandas-integration")]
85            return true;
86            #[cfg(not(feature = "pandas-integration"))]
87            return false;
88        }
89        _ => false,
90    }
91}
92
93/// Get hardware acceleration capabilities
94#[pyfunction]
95pub fn get_hardware_info() -> HashMap<String, bool> {
96    let mut info = HashMap::new();
97
98    // CPU features
99    #[cfg(target_arch = "x86_64")]
100    {
101        info.insert("x86_64".to_string(), true);
102        info.insert("avx2".to_string(), is_x86_feature_detected!("avx2"));
103        info.insert("fma".to_string(), is_x86_feature_detected!("fma"));
104        info.insert("sse4_1".to_string(), is_x86_feature_detected!("sse4.1"));
105        info.insert("sse4_2".to_string(), is_x86_feature_detected!("sse4.2"));
106    }
107
108    #[cfg(target_arch = "aarch64")]
109    {
110        info.insert("aarch64".to_string(), true);
111        info.insert("neon".to_string(), cfg!(target_feature = "neon"));
112    }
113
114    // Other architectures
115    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
116    {
117        info.insert("simd_support".to_string(), false);
118    }
119
120    // GPU support (placeholder - would need actual detection)
121    info.insert("cuda_available".to_string(), false);
122    info.insert("opencl_available".to_string(), false);
123
124    // Thread support
125    info.insert("parallel_support".to_string(), true);
126    info.insert("num_cpus".to_string(), num_cpus::get() > 1);
127
128    info
129}
130
131/// Get memory usage information
132#[pyfunction]
133pub fn get_memory_info() -> HashMap<String, u64> {
134    let mut info = HashMap::new();
135
136    // Get number of CPUs
137    info.insert("num_cpus".to_string(), num_cpus::get() as u64);
138
139    // Physical memory would require additional dependencies
140    // This is a placeholder
141    info.insert("available_memory_mb".to_string(), 0);
142    info.insert("used_memory_mb".to_string(), 0);
143
144    info
145}
146
147/// Set global configuration options
148#[pyfunction]
149pub fn set_config(option: &str, _value: &str) -> PyResult<()> {
150    match option {
151        "n_jobs" => {
152            // Set global parallelism configuration
153            Ok(())
154        }
155        "assume_finite" => {
156            // Set validation configuration
157            Ok(())
158        }
159        "working_memory" => {
160            // Set memory limit for operations
161            Ok(())
162        }
163        _ => Err(pyo3::exceptions::PyValueError::new_err(format!(
164            "Unknown configuration option: {}",
165            option
166        ))),
167    }
168}
169
170/// Get current configuration
171#[pyfunction]
172pub fn get_config() -> HashMap<String, String> {
173    let mut config = HashMap::new();
174
175    // Default configuration values
176    config.insert("n_jobs".to_string(), "1".to_string());
177    config.insert("assume_finite".to_string(), "false".to_string());
178    config.insert("working_memory".to_string(), "1024".to_string());
179    config.insert("print_changed_only".to_string(), "true".to_string());
180    config.insert("display".to_string(), "text".to_string());
181
182    config
183}
184
185/// Print system information
186#[pyfunction]
187pub fn show_versions() -> String {
188    let mut output = String::new();
189
190    output.push_str("sklears information:\n");
191    output.push_str("=====================\n");
192
193    let build_info = get_build_info();
194    for (key, value) in &build_info {
195        output.push_str(&format!("{}: {}\n", key, value));
196    }
197
198    output.push_str("\nHardware information:\n");
199    output.push_str("====================\n");
200
201    let hardware_info = get_hardware_info();
202    for (key, value) in &hardware_info {
203        output.push_str(&format!("{}: {}\n", key, value));
204    }
205
206    output.push_str("\nMemory information:\n");
207    output.push_str("==================\n");
208
209    let memory_info = get_memory_info();
210    for (key, value) in &memory_info {
211        output.push_str(&format!("{}: {}\n", key, value));
212    }
213
214    output
215}
216
217/// Performance testing utility
218#[pyfunction]
219pub fn benchmark_basic_operations() -> HashMap<String, f64> {
220    use std::time::Instant;
221
222    let mut results = HashMap::new();
223    let mut rng = thread_rng();
224
225    // Matrix multiplication benchmark
226    let start = Instant::now();
227    let a = Array2::from_shape_fn((100, 100), |_| rng.random::<f64>());
228    let b = Array2::from_shape_fn((100, 100), |_| rng.random::<f64>());
229    let _c = a.dot(&b);
230    let matrix_mul_time = start.elapsed().as_nanos() as f64 / 1_000_000.0; // Convert to milliseconds
231    results.insert(
232        "matrix_multiplication_100x100_ms".to_string(),
233        matrix_mul_time,
234    );
235
236    // Vector operations benchmark
237    let start = Instant::now();
238    let v1 = Array1::from_shape_fn(10000, |_| rng.random::<f64>());
239    let v2 = Array1::from_shape_fn(10000, |_| rng.random::<f64>());
240    let _dot_product = v1.dot(&v2);
241    let vector_ops_time = start.elapsed().as_nanos() as f64 / 1_000_000.0;
242    results.insert("vector_dot_product_10k_ms".to_string(), vector_ops_time);
243
244    // Memory allocation benchmark
245    let start = Instant::now();
246    let _large_array = Array2::<f64>::zeros((1000, 1000));
247    let allocation_time = start.elapsed().as_nanos() as f64 / 1_000_000.0;
248    results.insert(
249        "memory_allocation_1M_elements_ms".to_string(),
250        allocation_time,
251    );
252
253    results
254}
255
256/// Convert NumPy array to ndarray Array2`<f64>`
257pub fn numpy_to_ndarray2(py_array: &Bound<'_, PyArray2<f64>>) -> PyResult<Array2<f64>> {
258    let readonly = py_array.try_readonly().map_err(|err| {
259        PyValueError::new_err(format!(
260            "Failed to borrow NumPy array as read-only view: {err}"
261        ))
262    })?;
263    pyarray_to_core_array2(readonly)
264}
265
266/// Convert NumPy array to ndarray Array1`<f64>`
267pub fn numpy_to_ndarray1(py_array: &Bound<'_, PyArray1<f64>>) -> PyResult<Array1<f64>> {
268    let readonly = py_array.try_readonly().map_err(|err| {
269        PyValueError::new_err(format!(
270            "Failed to borrow NumPy array as read-only view: {err}"
271        ))
272    })?;
273    pyarray_to_core_array1(readonly)
274}
275
276/// Convert ndarray Array2`<f64>` to NumPy array
277pub fn ndarray_to_numpy<'py>(py: Python<'py>, array: Array2<f64>) -> PyResult<Py<PyArray2<f64>>> {
278    core_array2_to_py(py, &array)
279}
280
281/// Convert ndarray Array1`<f64>` to NumPy array
282pub fn ndarray1_to_numpy<'py>(py: Python<'py>, array: Array1<f64>) -> Py<PyArray1<f64>> {
283    core_array1_to_py(py, &array)
284}