1use numpy::{PyArray1, PyArray2, PyArrayMethods};
7use pyo3::exceptions::PyValueError;
8use pyo3::prelude::*;
9use pyo3::types::PyDict;
10use scirs2_core::ndarray::{Array1, Array2};
11use scirs2_core::random::thread_rng;
12use std::collections::HashMap;
13
14use crate::linear::{
15 core_array1_to_py, core_array2_to_py, pyarray_to_core_array1, pyarray_to_core_array2,
16};
17
18#[pyfunction]
20pub fn get_version() -> String {
21 env!("CARGO_PKG_VERSION").to_string()
22}
23
24#[pyfunction]
26pub fn get_build_info() -> HashMap<String, String> {
27 let mut info = HashMap::new();
28
29 info.insert("version".to_string(), env!("CARGO_PKG_VERSION").to_string());
30 info.insert("authors".to_string(), env!("CARGO_PKG_AUTHORS").to_string());
31 info.insert(
32 "description".to_string(),
33 env!("CARGO_PKG_DESCRIPTION").to_string(),
34 );
35 info.insert(
36 "homepage".to_string(),
37 env!("CARGO_PKG_HOMEPAGE").to_string(),
38 );
39 info.insert(
40 "repository".to_string(),
41 env!("CARGO_PKG_REPOSITORY").to_string(),
42 );
43 info.insert("license".to_string(), env!("CARGO_PKG_LICENSE").to_string());
44 info.insert(
45 "rust_version".to_string(),
46 env!("CARGO_PKG_RUST_VERSION").to_string(),
47 );
48
49 info.insert(
51 "target_triple".to_string(),
52 std::env::var("TARGET").unwrap_or_else(|_| "unknown".to_string()),
53 );
54 info.insert(
55 "build_profile".to_string(),
56 if cfg!(debug_assertions) {
57 "debug"
58 } else {
59 "release"
60 }
61 .to_string(),
62 );
63
64 let features: [&str; 0] = [];
66
67 info.insert("features".to_string(), features.join(", "));
68
69 info.insert("scirs2_core_version".to_string(), "workspace".to_string());
71 info.insert("pyo3_version".to_string(), "0.26".to_string());
72 info.insert("numpy_version".to_string(), "0.26".to_string());
73
74 info
75}
76
77#[pyfunction]
79pub fn has_feature(feature_name: &str) -> bool {
80 let _ = feature_name;
81 false
82}
83
84#[pyfunction]
93pub fn get_hardware_info(py: Python<'_>) -> PyResult<Py<PyDict>> {
94 let info = PyDict::new(py);
95
96 #[cfg(target_arch = "x86_64")]
98 {
99 info.set_item("x86_64", true)?;
100 info.set_item("avx2", is_x86_feature_detected!("avx2"))?;
101 info.set_item("fma", is_x86_feature_detected!("fma"))?;
102 info.set_item("sse4_1", is_x86_feature_detected!("sse4.1"))?;
103 info.set_item("sse4_2", is_x86_feature_detected!("sse4.2"))?;
104 }
105
106 #[cfg(target_arch = "aarch64")]
107 {
108 info.set_item("aarch64", true)?;
109 info.set_item("neon", cfg!(target_feature = "neon"))?;
110 }
111
112 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
114 {
115 info.set_item("simd_support", false)?;
116 }
117
118 #[cfg(feature = "gpu")]
124 info.set_item(
125 "cuda_available",
126 sklears_core::gpu::GpuUtils::is_gpu_available(),
127 )?;
128 #[cfg(not(feature = "gpu"))]
129 info.set_item("cuda_available", false)?;
130 info.set_item("opencl_available", false)?;
133
134 info.set_item("parallel_support", true)?;
136 info.set_item("num_cpus", num_cpus::get())?;
139
140 Ok(info.into())
141}
142
143#[pyfunction]
145pub fn get_memory_info() -> HashMap<String, u64> {
146 let mut info = HashMap::new();
147
148 info.insert("num_cpus".to_string(), num_cpus::get() as u64);
150
151 info.insert("available_memory_mb".to_string(), 0);
154 info.insert("used_memory_mb".to_string(), 0);
155
156 info
157}
158
159#[pyfunction]
161pub fn set_config(option: &str, _value: &str) -> PyResult<()> {
162 match option {
163 "n_jobs" => {
164 Ok(())
166 }
167 "assume_finite" => {
168 Ok(())
170 }
171 "working_memory" => {
172 Ok(())
174 }
175 _ => Err(pyo3::exceptions::PyValueError::new_err(format!(
176 "Unknown configuration option: {}",
177 option
178 ))),
179 }
180}
181
182#[pyfunction]
184pub fn get_config() -> HashMap<String, String> {
185 let mut config = HashMap::new();
186
187 config.insert("n_jobs".to_string(), "1".to_string());
189 config.insert("assume_finite".to_string(), "false".to_string());
190 config.insert("working_memory".to_string(), "1024".to_string());
191 config.insert("print_changed_only".to_string(), "true".to_string());
192 config.insert("display".to_string(), "text".to_string());
193
194 config
195}
196
197#[pyfunction]
199pub fn show_versions(py: Python<'_>) -> PyResult<String> {
200 let mut output = String::new();
201
202 output.push_str("sklears information:\n");
203 output.push_str("=====================\n");
204
205 let build_info = get_build_info();
206 for (key, value) in &build_info {
207 output.push_str(&format!("{}: {}\n", key, value));
208 }
209
210 output.push_str("\nHardware information:\n");
211 output.push_str("====================\n");
212
213 let hardware_info = get_hardware_info(py)?;
214 for (key, value) in hardware_info.bind(py).iter() {
215 output.push_str(&format!("{}: {}\n", key, value));
216 }
217
218 output.push_str("\nMemory information:\n");
219 output.push_str("==================\n");
220
221 let memory_info = get_memory_info();
222 for (key, value) in &memory_info {
223 output.push_str(&format!("{}: {}\n", key, value));
224 }
225
226 Ok(output)
227}
228
229#[pyfunction]
231pub fn benchmark_basic_operations() -> HashMap<String, f64> {
232 use std::time::Instant;
233
234 let mut results = HashMap::new();
235 let mut rng = thread_rng();
236
237 let start = Instant::now();
239 let a = Array2::from_shape_fn((100, 100), |_| rng.random::<f64>());
240 let b = Array2::from_shape_fn((100, 100), |_| rng.random::<f64>());
241 let _c = a.dot(&b);
242 let matrix_mul_time = start.elapsed().as_nanos() as f64 / 1_000_000.0; results.insert(
244 "matrix_multiplication_100x100_ms".to_string(),
245 matrix_mul_time,
246 );
247
248 let start = Instant::now();
250 let v1 = Array1::from_shape_fn(10000, |_| rng.random::<f64>());
251 let v2 = Array1::from_shape_fn(10000, |_| rng.random::<f64>());
252 let _dot_product = v1.dot(&v2);
253 let vector_ops_time = start.elapsed().as_nanos() as f64 / 1_000_000.0;
254 results.insert("vector_dot_product_10k_ms".to_string(), vector_ops_time);
255
256 let start = Instant::now();
258 let _large_array = Array2::<f64>::zeros((1000, 1000));
259 let allocation_time = start.elapsed().as_nanos() as f64 / 1_000_000.0;
260 results.insert(
261 "memory_allocation_1M_elements_ms".to_string(),
262 allocation_time,
263 );
264
265 results
266}
267
268pub fn numpy_to_ndarray2(py_array: &Bound<'_, PyArray2<f64>>) -> PyResult<Array2<f64>> {
270 let readonly = py_array.try_readonly().map_err(|err| {
271 PyValueError::new_err(format!(
272 "Failed to borrow NumPy array as read-only view: {err}"
273 ))
274 })?;
275 pyarray_to_core_array2(readonly)
276}
277
278pub fn numpy_to_ndarray1(py_array: &Bound<'_, PyArray1<f64>>) -> PyResult<Array1<f64>> {
280 let readonly = py_array.try_readonly().map_err(|err| {
281 PyValueError::new_err(format!(
282 "Failed to borrow NumPy array as read-only view: {err}"
283 ))
284 })?;
285 pyarray_to_core_array1(readonly)
286}
287
288pub fn ndarray_to_numpy<'py>(py: Python<'py>, array: Array2<f64>) -> PyResult<Py<PyArray2<f64>>> {
290 core_array2_to_py(py, &array)
291}
292
293pub fn ndarray1_to_numpy<'py>(py: Python<'py>, array: Array1<f64>) -> Py<PyArray1<f64>> {
295 core_array1_to_py(py, &array)
296}