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: [&str; 0] = [];
65
66    info.insert("features".to_string(), features.join(", "));
67
68    // Dependency versions
69    info.insert("scirs2_core_version".to_string(), "workspace".to_string());
70    info.insert("pyo3_version".to_string(), "0.26".to_string());
71    info.insert("numpy_version".to_string(), "0.26".to_string());
72
73    info
74}
75
76/// Check if specific features are enabled
77#[pyfunction]
78pub fn has_feature(feature_name: &str) -> bool {
79    let _ = feature_name;
80    false
81}
82
83/// Get hardware acceleration capabilities
84#[pyfunction]
85pub fn get_hardware_info() -> HashMap<String, bool> {
86    let mut info = HashMap::new();
87
88    // CPU features
89    #[cfg(target_arch = "x86_64")]
90    {
91        info.insert("x86_64".to_string(), true);
92        info.insert("avx2".to_string(), is_x86_feature_detected!("avx2"));
93        info.insert("fma".to_string(), is_x86_feature_detected!("fma"));
94        info.insert("sse4_1".to_string(), is_x86_feature_detected!("sse4.1"));
95        info.insert("sse4_2".to_string(), is_x86_feature_detected!("sse4.2"));
96    }
97
98    #[cfg(target_arch = "aarch64")]
99    {
100        info.insert("aarch64".to_string(), true);
101        info.insert("neon".to_string(), cfg!(target_feature = "neon"));
102    }
103
104    // Other architectures
105    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
106    {
107        info.insert("simd_support".to_string(), false);
108    }
109
110    // GPU support (placeholder - would need actual detection)
111    info.insert("cuda_available".to_string(), false);
112    info.insert("opencl_available".to_string(), false);
113
114    // Thread support
115    info.insert("parallel_support".to_string(), true);
116    info.insert("num_cpus".to_string(), num_cpus::get() > 1);
117
118    info
119}
120
121/// Get memory usage information
122#[pyfunction]
123pub fn get_memory_info() -> HashMap<String, u64> {
124    let mut info = HashMap::new();
125
126    // Get number of CPUs
127    info.insert("num_cpus".to_string(), num_cpus::get() as u64);
128
129    // Physical memory would require additional dependencies
130    // This is a placeholder
131    info.insert("available_memory_mb".to_string(), 0);
132    info.insert("used_memory_mb".to_string(), 0);
133
134    info
135}
136
137/// Set global configuration options
138#[pyfunction]
139pub fn set_config(option: &str, _value: &str) -> PyResult<()> {
140    match option {
141        "n_jobs" => {
142            // Set global parallelism configuration
143            Ok(())
144        }
145        "assume_finite" => {
146            // Set validation configuration
147            Ok(())
148        }
149        "working_memory" => {
150            // Set memory limit for operations
151            Ok(())
152        }
153        _ => Err(pyo3::exceptions::PyValueError::new_err(format!(
154            "Unknown configuration option: {}",
155            option
156        ))),
157    }
158}
159
160/// Get current configuration
161#[pyfunction]
162pub fn get_config() -> HashMap<String, String> {
163    let mut config = HashMap::new();
164
165    // Default configuration values
166    config.insert("n_jobs".to_string(), "1".to_string());
167    config.insert("assume_finite".to_string(), "false".to_string());
168    config.insert("working_memory".to_string(), "1024".to_string());
169    config.insert("print_changed_only".to_string(), "true".to_string());
170    config.insert("display".to_string(), "text".to_string());
171
172    config
173}
174
175/// Print system information
176#[pyfunction]
177pub fn show_versions() -> String {
178    let mut output = String::new();
179
180    output.push_str("sklears information:\n");
181    output.push_str("=====================\n");
182
183    let build_info = get_build_info();
184    for (key, value) in &build_info {
185        output.push_str(&format!("{}: {}\n", key, value));
186    }
187
188    output.push_str("\nHardware information:\n");
189    output.push_str("====================\n");
190
191    let hardware_info = get_hardware_info();
192    for (key, value) in &hardware_info {
193        output.push_str(&format!("{}: {}\n", key, value));
194    }
195
196    output.push_str("\nMemory information:\n");
197    output.push_str("==================\n");
198
199    let memory_info = get_memory_info();
200    for (key, value) in &memory_info {
201        output.push_str(&format!("{}: {}\n", key, value));
202    }
203
204    output
205}
206
207/// Performance testing utility
208#[pyfunction]
209pub fn benchmark_basic_operations() -> HashMap<String, f64> {
210    use std::time::Instant;
211
212    let mut results = HashMap::new();
213    let mut rng = thread_rng();
214
215    // Matrix multiplication benchmark
216    let start = Instant::now();
217    let a = Array2::from_shape_fn((100, 100), |_| rng.random::<f64>());
218    let b = Array2::from_shape_fn((100, 100), |_| rng.random::<f64>());
219    let _c = a.dot(&b);
220    let matrix_mul_time = start.elapsed().as_nanos() as f64 / 1_000_000.0; // Convert to milliseconds
221    results.insert(
222        "matrix_multiplication_100x100_ms".to_string(),
223        matrix_mul_time,
224    );
225
226    // Vector operations benchmark
227    let start = Instant::now();
228    let v1 = Array1::from_shape_fn(10000, |_| rng.random::<f64>());
229    let v2 = Array1::from_shape_fn(10000, |_| rng.random::<f64>());
230    let _dot_product = v1.dot(&v2);
231    let vector_ops_time = start.elapsed().as_nanos() as f64 / 1_000_000.0;
232    results.insert("vector_dot_product_10k_ms".to_string(), vector_ops_time);
233
234    // Memory allocation benchmark
235    let start = Instant::now();
236    let _large_array = Array2::<f64>::zeros((1000, 1000));
237    let allocation_time = start.elapsed().as_nanos() as f64 / 1_000_000.0;
238    results.insert(
239        "memory_allocation_1M_elements_ms".to_string(),
240        allocation_time,
241    );
242
243    results
244}
245
246/// Convert NumPy array to ndarray Array2`<f64>`
247pub fn numpy_to_ndarray2(py_array: &Bound<'_, PyArray2<f64>>) -> PyResult<Array2<f64>> {
248    let readonly = py_array.try_readonly().map_err(|err| {
249        PyValueError::new_err(format!(
250            "Failed to borrow NumPy array as read-only view: {err}"
251        ))
252    })?;
253    pyarray_to_core_array2(readonly)
254}
255
256/// Convert NumPy array to ndarray Array1`<f64>`
257pub fn numpy_to_ndarray1(py_array: &Bound<'_, PyArray1<f64>>) -> PyResult<Array1<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_array1(readonly)
264}
265
266/// Convert ndarray Array2`<f64>` to NumPy array
267pub fn ndarray_to_numpy<'py>(py: Python<'py>, array: Array2<f64>) -> PyResult<Py<PyArray2<f64>>> {
268    core_array2_to_py(py, &array)
269}
270
271/// Convert ndarray Array1`<f64>` to NumPy array
272pub fn ndarray1_to_numpy<'py>(py: Python<'py>, array: Array1<f64>) -> Py<PyArray1<f64>> {
273    core_array1_to_py(py, &array)
274}