1#[derive(Debug, Clone)]
18pub struct NumpyEntry {
19 pub numpy_path: &'static str,
21 pub rust_path: &'static str,
23 pub notes: &'static str,
25 pub numpy_example: &'static str,
27 pub rust_example: &'static str,
29}
30
31pub fn numpy_table() -> &'static [NumpyEntry] {
33 &NUMPY_TABLE
34}
35
36pub fn search_numpy(query: &str) -> Vec<&'static NumpyEntry> {
38 let lower = query.to_lowercase();
39 NUMPY_TABLE
40 .iter()
41 .filter(|e| e.numpy_path.to_lowercase().contains(&lower))
42 .collect()
43}
44
45static NUMPY_TABLE: [NumpyEntry; 42] = [
50 NumpyEntry {
52 numpy_path: "numpy.array",
53 rust_path: "ndarray::array! or ndarray::Array::from_vec",
54 notes: "Use the array![] macro for literals, or Array::from_vec / from_shape_vec for dynamic data.",
55 numpy_example: r#"import numpy as np
56a = np.array([1.0, 2.0, 3.0])
57b = np.array([[1, 2], [3, 4]])"#,
58 rust_example: r#"use ndarray::{array, Array1, Array2};
59let a = array![1.0, 2.0, 3.0];
60let b = array![[1.0, 2.0], [3.0, 4.0]];"#,
61 },
62 NumpyEntry {
63 numpy_path: "numpy.zeros",
64 rust_path: "ndarray::Array::zeros",
65 notes: "Create an array filled with zeros.",
66 numpy_example: r#"a = np.zeros((3, 4))"#,
67 rust_example: r#"use ndarray::Array2;
68let a = Array2::<f64>::zeros((3, 4));"#,
69 },
70 NumpyEntry {
71 numpy_path: "numpy.ones",
72 rust_path: "ndarray::Array::ones",
73 notes: "Create an array filled with ones.",
74 numpy_example: r#"a = np.ones((3, 4))"#,
75 rust_example: r#"use ndarray::Array2;
76let a = Array2::<f64>::ones((3, 4));"#,
77 },
78 NumpyEntry {
79 numpy_path: "numpy.eye",
80 rust_path: "ndarray::Array2::eye",
81 notes: "Identity matrix.",
82 numpy_example: r#"I = np.eye(3)"#,
83 rust_example: r#"use ndarray::Array2;
84let eye = Array2::<f64>::eye(3);"#,
85 },
86 NumpyEntry {
87 numpy_path: "numpy.full",
88 rust_path: "ndarray::Array::from_elem",
89 notes: "Create an array filled with a given value.",
90 numpy_example: r#"a = np.full((3, 4), 7.0)"#,
91 rust_example: r#"use ndarray::Array2;
92let a = Array2::<f64>::from_elem((3, 4), 7.0);"#,
93 },
94 NumpyEntry {
95 numpy_path: "numpy.arange",
96 rust_path: "ndarray::Array::range or ndarray::Array::linspace",
97 notes: "arange with step: use Array::range(start, end, step).",
98 numpy_example: r#"a = np.arange(0, 10, 0.5)"#,
99 rust_example: r#"use ndarray::Array1;
100let a = Array1::range(0.0, 10.0, 0.5);"#,
101 },
102 NumpyEntry {
103 numpy_path: "numpy.linspace",
104 rust_path: "ndarray::Array::linspace",
105 notes: "Evenly spaced values over an interval.",
106 numpy_example: r#"a = np.linspace(0, 1, 50)"#,
107 rust_example: r#"use ndarray::Array1;
108let a = Array1::linspace(0.0, 1.0, 50);"#,
109 },
110 NumpyEntry {
111 numpy_path: "numpy.empty",
112 rust_path: "ndarray::Array::uninit or Array::zeros",
113 notes: "Rust does not have uninitialized arrays in safe code. Use zeros() or from_elem().",
114 numpy_example: r#"a = np.empty((3, 4))"#,
115 rust_example: r#"use ndarray::Array2;
116// No direct equivalent; use zeros instead:
117let a = Array2::<f64>::zeros((3, 4));"#,
118 },
119 NumpyEntry {
121 numpy_path: "numpy.reshape",
122 rust_path: "ndarray::Array::into_shape_with_order",
123 notes: "Reshape an array. The new shape must have the same total number of elements.",
124 numpy_example: r#"b = a.reshape((2, 3))"#,
125 rust_example: r#"let b = a.into_shape_with_order((2, 3))?;"#,
126 },
127 NumpyEntry {
128 numpy_path: "numpy.transpose",
129 rust_path: "ndarray::Array::t() or .reversed_axes()",
130 notes: "Transpose. .t() returns a view; .reversed_axes() consumes the array.",
131 numpy_example: r#"b = a.T
132b = np.transpose(a)"#,
133 rust_example: r#"let b = a.t(); // transposed view
134let b = a.reversed_axes(); // consumes a"#,
135 },
136 NumpyEntry {
137 numpy_path: "numpy.concatenate",
138 rust_path: "ndarray::concatenate or ndarray::stack",
139 notes: "Join arrays along an existing axis. Use ndarray::concatenate![] macro.",
140 numpy_example: r#"c = np.concatenate([a, b], axis=0)"#,
141 rust_example: r#"use ndarray::concatenate;
142use ndarray::Axis;
143let c = concatenate(Axis(0), &[a.view(), b.view()])?;"#,
144 },
145 NumpyEntry {
146 numpy_path: "numpy.stack",
147 rust_path: "ndarray::stack",
148 notes: "Stack arrays along a new axis.",
149 numpy_example: r#"c = np.stack([a, b], axis=0)"#,
150 rust_example: r#"use ndarray::{stack, Axis};
151let c = stack(Axis(0), &[a.view(), b.view()])?;"#,
152 },
153 NumpyEntry {
154 numpy_path: "numpy.split",
155 rust_path: "ndarray slicing",
156 notes: "No direct split function; use array slicing with s![] macro.",
157 numpy_example: r#"parts = np.split(a, 3, axis=0)"#,
158 rust_example: r#"use ndarray::s;
159let part0 = a.slice(s![0..n, ..]);
160let part1 = a.slice(s![n..2*n, ..]);"#,
161 },
162 NumpyEntry {
163 numpy_path: "numpy.flatten",
164 rust_path: "ndarray::Array::into_raw_vec or .iter()",
165 notes: "Flatten to 1-D. Use .into_raw_vec() for owned, or .iter() for iteration.",
166 numpy_example: r#"flat = a.flatten()"#,
167 rust_example: r#"let flat: Vec<f64> = a.into_raw_vec();
168// or iterate:
169let flat: Array1<f64> = a.iter().cloned().collect();"#,
170 },
171 NumpyEntry {
172 numpy_path: "numpy.squeeze",
173 rust_path: "ndarray: remove_axis",
174 notes: "Remove size-1 axes. Use .index_axis(Axis(n), 0) to remove a specific axis.",
175 numpy_example: r#"b = np.squeeze(a)"#,
176 rust_example: r#"use ndarray::Axis;
177let b = a.index_axis(Axis(0), 0); // remove axis 0 if size==1"#,
178 },
179 NumpyEntry {
181 numpy_path: "numpy.dot",
182 rust_path: "ndarray .dot() method",
183 notes: "Matrix/vector dot product. Array2.dot(&Array2), Array1.dot(&Array1).",
184 numpy_example: r#"c = np.dot(a, b)"#,
185 rust_example: r#"let c = a.dot(&b);"#,
186 },
187 NumpyEntry {
188 numpy_path: "numpy.matmul",
189 rust_path: "ndarray .dot() method",
190 notes: "Same as np.dot for 2-D arrays. For batched, see scirs2_linalg::batch_matmul.",
191 numpy_example: r#"c = np.matmul(a, b)
192c = a @ b"#,
193 rust_example: r#"let c = a.dot(&b);
194// Batched: use scirs2_linalg::prelude::batch_matmul"#,
195 },
196 NumpyEntry {
197 numpy_path: "numpy.sum",
198 rust_path: "ndarray .sum() or .sum_axis()",
199 notes: "Sum all elements or along an axis.",
200 numpy_example: r#"s = np.sum(a)
201s = np.sum(a, axis=0)"#,
202 rust_example: r#"let s = a.sum();
203let s = a.sum_axis(ndarray::Axis(0));"#,
204 },
205 NumpyEntry {
206 numpy_path: "numpy.mean",
207 rust_path: "ndarray .mean() or scirs2_stats::mean",
208 notes: "Mean value. ndarray's .mean() returns Option; scirs2_stats::mean returns Result.",
209 numpy_example: r#"m = np.mean(a)"#,
210 rust_example: r#"let m = a.mean(); // returns Option<f64>
211// or:
212use scirs2_stats::mean;
213let m = mean(&a.view())?;"#,
214 },
215 NumpyEntry {
216 numpy_path: "numpy.var",
217 rust_path: "ndarray .var(ddof) or scirs2_stats::var",
218 notes: "Variance. ndarray uses .var(ddof); scirs2_stats::var(x, ddof, workers).",
219 numpy_example: r#"v = np.var(a, ddof=1)"#,
220 rust_example: r#"let v = a.var(1.0);
221// or:
222use scirs2_stats::var;
223let v = var(&a.view(), 1, None)?;"#,
224 },
225 NumpyEntry {
226 numpy_path: "numpy.std",
227 rust_path: "ndarray .std(ddof) or scirs2_stats::std",
228 notes: "Standard deviation.",
229 numpy_example: r#"s = np.std(a, ddof=1)"#,
230 rust_example: r#"let s = a.std(1.0);
231// or:
232use scirs2_stats::std;
233let s = std(&a.view(), 1, None)?;"#,
234 },
235 NumpyEntry {
236 numpy_path: "numpy.max / numpy.min",
237 rust_path: "ndarray: a.iter().cloned().fold() or a.fold()",
238 notes: "No built-in max/min on ndarray; iterate or use scirs2_core utilities.",
239 numpy_example: r#"mx = np.max(a)
240mn = np.min(a)"#,
241 rust_example: r#"let mx = a.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
242let mn = a.iter().cloned().fold(f64::INFINITY, f64::min);"#,
243 },
244 NumpyEntry {
245 numpy_path: "numpy.abs",
246 rust_path: "ndarray .mapv(f64::abs)",
247 notes: "Element-wise absolute value.",
248 numpy_example: r#"b = np.abs(a)"#,
249 rust_example: r#"let b = a.mapv(f64::abs);"#,
250 },
251 NumpyEntry {
252 numpy_path: "numpy.sqrt",
253 rust_path: "ndarray .mapv(f64::sqrt)",
254 notes: "Element-wise square root.",
255 numpy_example: r#"b = np.sqrt(a)"#,
256 rust_example: r#"let b = a.mapv(f64::sqrt);"#,
257 },
258 NumpyEntry {
259 numpy_path: "numpy.exp",
260 rust_path: "ndarray .mapv(f64::exp)",
261 notes: "Element-wise exponential.",
262 numpy_example: r#"b = np.exp(a)"#,
263 rust_example: r#"let b = a.mapv(f64::exp);"#,
264 },
265 NumpyEntry {
266 numpy_path: "numpy.log",
267 rust_path: "ndarray .mapv(f64::ln)",
268 notes: "Element-wise natural logarithm. Note: Rust uses ln(), not log().",
269 numpy_example: r#"b = np.log(a)"#,
270 rust_example: r#"let b = a.mapv(f64::ln);"#,
271 },
272 NumpyEntry {
273 numpy_path: "numpy.sin / numpy.cos / numpy.tan",
274 rust_path: "ndarray .mapv(f64::sin) / .mapv(f64::cos) / .mapv(f64::tan)",
275 notes: "Element-wise trigonometric functions.",
276 numpy_example: r#"b = np.sin(a)"#,
277 rust_example: r#"let b = a.mapv(f64::sin);"#,
278 },
279 NumpyEntry {
280 numpy_path: "numpy.clip",
281 rust_path: "ndarray .mapv(|x| x.clamp(lo, hi))",
282 notes: "Clip values to a range.",
283 numpy_example: r#"b = np.clip(a, 0, 1)"#,
284 rust_example: r#"let b = a.mapv(|x: f64| x.clamp(0.0, 1.0));"#,
285 },
286 NumpyEntry {
287 numpy_path: "numpy.where",
288 rust_path: "ndarray .mapv() with conditional or Zip",
289 notes: "Conditional element selection. Use mapv or ndarray::Zip for element-wise conditions.",
290 numpy_example: r#"b = np.where(a > 0, a, 0)"#,
291 rust_example: r#"let b = a.mapv(|x: f64| if x > 0.0 { x } else { 0.0 });"#,
292 },
293 NumpyEntry {
295 numpy_path: "numpy.linalg.det",
296 rust_path: "scirs2_linalg::prelude::det",
297 notes: "Matrix determinant.",
298 numpy_example: r#"d = np.linalg.det(a)"#,
299 rust_example: r#"use scirs2_linalg::prelude::det;
300let d = det(&a.view())?;"#,
301 },
302 NumpyEntry {
303 numpy_path: "numpy.linalg.inv",
304 rust_path: "scirs2_linalg::prelude::inv",
305 notes: "Matrix inverse.",
306 numpy_example: r#"a_inv = np.linalg.inv(a)"#,
307 rust_example: r#"use scirs2_linalg::prelude::inv;
308let a_inv = inv(&a.view())?;"#,
309 },
310 NumpyEntry {
311 numpy_path: "numpy.linalg.solve",
312 rust_path: "scirs2_linalg::prelude::solve",
313 notes: "Solve Ax = b.",
314 numpy_example: r#"x = np.linalg.solve(a, b)"#,
315 rust_example: r#"use scirs2_linalg::prelude::solve;
316let x = solve(&a.view(), &b.view())?;"#,
317 },
318 NumpyEntry {
319 numpy_path: "numpy.linalg.eig",
320 rust_path: "scirs2_linalg::prelude::eig",
321 notes: "Eigenvalue decomposition.",
322 numpy_example: r#"vals, vecs = np.linalg.eig(a)"#,
323 rust_example: r#"use scirs2_linalg::prelude::eig;
324let (vals, vecs) = eig(&a.view())?;"#,
325 },
326 NumpyEntry {
327 numpy_path: "numpy.linalg.svd",
328 rust_path: "scirs2_linalg::prelude::svd",
329 notes: "Singular Value Decomposition.",
330 numpy_example: r#"U, s, Vt = np.linalg.svd(a)"#,
331 rust_example: r#"use scirs2_linalg::prelude::svd;
332let (u, s, vt) = svd(&a.view())?;"#,
333 },
334 NumpyEntry {
335 numpy_path: "numpy.linalg.norm",
336 rust_path: "scirs2_linalg::prelude::vector_norm / matrix_norm",
337 notes: "Vector or matrix norm. Use vector_norm for 1-D, matrix_norm for 2-D.",
338 numpy_example: r#"n = np.linalg.norm(a)"#,
339 rust_example: r#"use scirs2_linalg::prelude::{vector_norm, matrix_norm};
340let n = vector_norm(&v.view(), 2.0)?;
341let n = matrix_norm(&m.view(), "fro")?;"#,
342 },
343 NumpyEntry {
344 numpy_path: "numpy.linalg.lstsq",
345 rust_path: "scirs2_linalg::prelude::lstsq",
346 notes: "Least-squares solution.",
347 numpy_example: r#"x, res, rank, sv = np.linalg.lstsq(a, b, rcond=None)"#,
348 rust_example: r#"use scirs2_linalg::prelude::lstsq;
349let result = lstsq(&a.view(), &b.view())?;"#,
350 },
351 NumpyEntry {
353 numpy_path: "numpy indexing: a[i, j]",
354 rust_path: "ndarray: a[[i, j]] or a.get([i, j])",
355 notes: "Direct indexing uses [[i, j]]. .get() returns Option for bounds checking.",
356 numpy_example: r#"val = a[2, 3]"#,
357 rust_example: r#"let val = a[[2, 3]];
358// Safe version:
359let val = a.get([2, 3]);"#,
360 },
361 NumpyEntry {
362 numpy_path: "numpy slicing: a[1:3, :]",
363 rust_path: "ndarray: a.slice(s![1..3, ..])",
364 notes: "Use the s![] macro for slicing. Supports ranges, steps, and negative indices.",
365 numpy_example: r#"b = a[1:3, :]
366c = a[::2, :]"#,
367 rust_example: r#"use ndarray::s;
368let b = a.slice(s![1..3, ..]);
369let c = a.slice(s![..;2, ..]);"#,
370 },
371 NumpyEntry {
372 numpy_path: "numpy boolean indexing: a[a > 0]",
373 rust_path: "ndarray: filtering with iterators",
374 notes: "No direct boolean indexing; use .iter().filter() or .mapv() with conditions.",
375 numpy_example: r#"b = a[a > 0]"#,
376 rust_example: r#"let b: Vec<f64> = a.iter().filter(|&&x| x > 0.0).cloned().collect();"#,
377 },
378 NumpyEntry {
380 numpy_path: "numpy.random.rand",
381 rust_path: "scirs2_core::random",
382 notes: "Uniform [0, 1) random array. Use scirs2_core random utilities.",
383 numpy_example: r#"a = np.random.rand(3, 4)"#,
384 rust_example: r#"use scirs2_core::random;
385// Generate uniform random values using scirs2_core random module"#,
386 },
387 NumpyEntry {
388 numpy_path: "numpy.random.randn",
389 rust_path: "scirs2_core::random",
390 notes: "Standard normal random array.",
391 numpy_example: r#"a = np.random.randn(3, 4)"#,
392 rust_example: r#"use scirs2_core::random;
393// Generate normal random values using scirs2_core random module"#,
394 },
395 NumpyEntry {
396 numpy_path: "numpy.random.seed",
397 rust_path: "scirs2_core::random (seed-based RNG)",
398 notes: "Use seeded RNG from scirs2_core::random for reproducibility.",
399 numpy_example: r#"np.random.seed(42)"#,
400 rust_example: r#"use scirs2_core::random;
401// Create seeded RNG for reproducible results"#,
402 },
403];
404
405#[cfg(test)]
406mod tests {
407 use super::*;
408
409 #[test]
410 fn test_numpy_equiv_table_not_empty() {
411 assert!(!numpy_table().is_empty());
412 assert_eq!(numpy_table().len(), 42);
413 }
414
415 #[test]
416 fn test_search_numpy() {
417 let results = search_numpy("zeros");
418 assert!(!results.is_empty());
419 assert!(results.iter().any(|e| e.numpy_path == "numpy.zeros"));
420 }
421
422 #[test]
423 fn test_search_numpy_case_insensitive() {
424 let upper = search_numpy("ZEROS");
425 let lower = search_numpy("zeros");
426 assert_eq!(upper.len(), lower.len());
427 }
428
429 #[test]
430 fn test_search_numpy_linalg() {
431 let results = search_numpy("linalg");
432 assert!(results.len() >= 5);
433 }
434
435 #[test]
436 fn test_all_rust_paths_non_empty() {
437 for entry in numpy_table() {
438 assert!(
439 !entry.rust_path.is_empty(),
440 "Empty rust_path for {}",
441 entry.numpy_path
442 );
443 }
444 }
445
446 #[test]
447 fn test_no_duplicate_numpy_paths() {
448 let table = numpy_table();
449 let mut seen = std::collections::HashSet::new();
450 for entry in table {
451 assert!(
452 seen.insert(entry.numpy_path),
453 "Duplicate numpy_path: {}",
454 entry.numpy_path
455 );
456 }
457 }
458}