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 = [
65 #[cfg(feature = "pandas-integration")]
66 "pandas-integration",
67 ];
68
69 info.insert("features".to_string(), features.join(", "));
70
71 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#[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#[pyfunction]
95pub fn get_hardware_info() -> HashMap<String, bool> {
96 let mut info = HashMap::new();
97
98 #[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 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
116 {
117 info.insert("simd_support".to_string(), false);
118 }
119
120 info.insert("cuda_available".to_string(), false);
122 info.insert("opencl_available".to_string(), false);
123
124 info.insert("parallel_support".to_string(), true);
126 info.insert("num_cpus".to_string(), num_cpus::get() > 1);
127
128 info
129}
130
131#[pyfunction]
133pub fn get_memory_info() -> HashMap<String, u64> {
134 let mut info = HashMap::new();
135
136 info.insert("num_cpus".to_string(), num_cpus::get() as u64);
138
139 info.insert("available_memory_mb".to_string(), 0);
142 info.insert("used_memory_mb".to_string(), 0);
143
144 info
145}
146
147#[pyfunction]
149pub fn set_config(option: &str, _value: &str) -> PyResult<()> {
150 match option {
151 "n_jobs" => {
152 Ok(())
154 }
155 "assume_finite" => {
156 Ok(())
158 }
159 "working_memory" => {
160 Ok(())
162 }
163 _ => Err(pyo3::exceptions::PyValueError::new_err(format!(
164 "Unknown configuration option: {}",
165 option
166 ))),
167 }
168}
169
170#[pyfunction]
172pub fn get_config() -> HashMap<String, String> {
173 let mut config = HashMap::new();
174
175 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#[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#[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 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; results.insert(
232 "matrix_multiplication_100x100_ms".to_string(),
233 matrix_mul_time,
234 );
235
236 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 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
256pub 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
266pub 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
276pub fn ndarray_to_numpy<'py>(py: Python<'py>, array: Array2<f64>) -> PyResult<Py<PyArray2<f64>>> {
278 core_array2_to_py(py, &array)
279}
280
281pub fn ndarray1_to_numpy<'py>(py: Python<'py>, array: Array1<f64>) -> Py<PyArray1<f64>> {
283 core_array1_to_py(py, &array)
284}