1use 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#[pyfunction]
19pub fn get_version() -> String {
20 env!("CARGO_PKG_VERSION").to_string()
21}
22
23#[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 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 let features: [&str; 0] = [];
65
66 info.insert("features".to_string(), features.join(", "));
67
68 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#[pyfunction]
78pub fn has_feature(feature_name: &str) -> bool {
79 let _ = feature_name;
80 false
81}
82
83#[pyfunction]
85pub fn get_hardware_info() -> HashMap<String, bool> {
86 let mut info = HashMap::new();
87
88 #[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 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
106 {
107 info.insert("simd_support".to_string(), false);
108 }
109
110 info.insert("cuda_available".to_string(), false);
112 info.insert("opencl_available".to_string(), false);
113
114 info.insert("parallel_support".to_string(), true);
116 info.insert("num_cpus".to_string(), num_cpus::get() > 1);
117
118 info
119}
120
121#[pyfunction]
123pub fn get_memory_info() -> HashMap<String, u64> {
124 let mut info = HashMap::new();
125
126 info.insert("num_cpus".to_string(), num_cpus::get() as u64);
128
129 info.insert("available_memory_mb".to_string(), 0);
132 info.insert("used_memory_mb".to_string(), 0);
133
134 info
135}
136
137#[pyfunction]
139pub fn set_config(option: &str, _value: &str) -> PyResult<()> {
140 match option {
141 "n_jobs" => {
142 Ok(())
144 }
145 "assume_finite" => {
146 Ok(())
148 }
149 "working_memory" => {
150 Ok(())
152 }
153 _ => Err(pyo3::exceptions::PyValueError::new_err(format!(
154 "Unknown configuration option: {}",
155 option
156 ))),
157 }
158}
159
160#[pyfunction]
162pub fn get_config() -> HashMap<String, String> {
163 let mut config = HashMap::new();
164
165 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#[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#[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 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; results.insert(
222 "matrix_multiplication_100x100_ms".to_string(),
223 matrix_mul_time,
224 );
225
226 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 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
246pub 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
256pub 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
266pub fn ndarray_to_numpy<'py>(py: Python<'py>, array: Array2<f64>) -> PyResult<Py<PyArray2<f64>>> {
268 core_array2_to_py(py, &array)
269}
270
271pub fn ndarray1_to_numpy<'py>(py: Python<'py>, array: Array1<f64>) -> Py<PyArray1<f64>> {
273 core_array1_to_py(py, &array)
274}