1#![warn(missing_docs)]
86#![warn(clippy::all)]
87#![allow(clippy::module_name_repetitions)]
88#![allow(clippy::must_use_candidate)]
89#![allow(clippy::needless_range_loop)]
91#![allow(clippy::multiple_bound_locations)]
93
94pub mod blas;
95pub mod conversions;
96pub mod lapack;
97
98#[cfg(feature = "parallel")]
99pub mod parallel;
100
101#[cfg(feature = "sparse")]
102pub mod sparse;
103
104pub use ndarray::{
106 Array1, Array2, ArrayD, ArrayView1, ArrayView2, ArrayViewD, ArrayViewMut1, ArrayViewMut2,
107 ArrayViewMutD, IxDyn,
108};
109
110pub use num_complex::{Complex32, Complex64};
112
113pub mod prelude {
120 pub use crate::conversions::{
122 array_view_mut_to_mat_mut, array_view_to_mat_ref, array_view_to_mat_ref_or_transposed,
123 array_view1_as_slice, array_view1_as_slice_mut, array1_to_vec, array2_into_mat,
124 array2_to_mat, filled_f, is_column_major, is_row_major, mat_ref_to_array2, mat_to_array2,
125 mat_to_array2_c, slice_to_array1, to_column_major, zeros_f,
126 };
127
128 pub use crate::conversions::{
130 array_view_mutd_to_mat_mut, array_viewd_to_mat_ref, array_viewd_to_mat_ref_or_transposed,
131 array2_to_arrayd, arrayd_into_mat, arrayd_to_array2, arrayd_to_mat, mat_ref_to_arrayd,
132 mat_to_arrayd,
133 };
134
135 pub use crate::blas::{
137 asum_ndarray, axpy_ndarray, dot_ndarray, dot_view, nrm2_ndarray, scal_ndarray,
138 };
139
140 pub use crate::blas::{
142 asum_c32_ndarray, asum_c64_ndarray, axpy_c32_ndarray, axpy_c64_ndarray, dotc_c32_ndarray,
143 dotc_c64_ndarray, dotu_c32_ndarray, dotu_c64_ndarray, nrm2_c32_ndarray, nrm2_c64_ndarray,
144 scal_c32_ndarray, scal_c64_ndarray,
145 };
146
147 pub use crate::blas::{Transpose, gemv_ndarray, matvec, matvec_t};
149
150 pub use crate::blas::{gemm_ndarray, matmul, matmul_c, matmul_into};
152
153 pub use crate::blas::{frobenius_norm, norm_1, norm_inf, norm_max};
155
156 pub use crate::blas::{
158 frobenius_norm_c32, frobenius_norm_c64, norm_1_c32, norm_1_c64, norm_inf_c32, norm_inf_c64,
159 norm_max_c32, norm_max_c64,
160 };
161
162 pub use crate::blas::{eye, eye_f, trace, transpose};
164
165 pub use crate::blas::{
167 conj_transpose_c32, conj_transpose_c64, eye_c32, eye_c64, trace_c32, trace_c64,
168 };
169
170 pub use crate::lapack::{
172 CholeskyResult, LuResult, QrResult, SvdResult, SymEvdResult, cholesky_ndarray,
173 eig_symmetric, eigvals_symmetric, lu_ndarray, qr_ndarray, svd_ndarray, svd_truncated,
174 };
175
176 pub use crate::lapack::{
178 RandomizedSvdResult, low_rank_approx_ndarray, rsvd_ndarray, rsvd_power_ndarray,
179 };
180
181 pub use crate::lapack::{SchurResult, schur_ndarray};
183
184 pub use crate::lapack::{Eigenvalue, GeneralEvdResult, eig_ndarray, eigvals_ndarray};
186
187 pub use crate::lapack::{
189 tridiag_solve_multiple_ndarray, tridiag_solve_ndarray, tridiag_solve_spd_ndarray,
190 };
191
192 pub use crate::lapack::{lstsq_ndarray, solve_multiple_ndarray, solve_ndarray};
194
195 pub use crate::lapack::{cond_ndarray, det_ndarray, inv_ndarray, pinv_ndarray, rank_ndarray};
197
198 pub use crate::lapack::{LapackError, LapackResult};
200
201 #[cfg(feature = "parallel")]
203 pub use crate::parallel::{gemm_par_ndarray, matmul_par};
204
205 #[cfg(feature = "sparse")]
207 pub use crate::sparse::{
208 SparseNdarrayError, array2_to_csc, array2_to_csr, csc_to_array2, csr_to_array2,
209 sparse_solve_ndarray, sparse_solve_ndarray_with_options, spmv_full_ndarray, spmv_ndarray,
210 };
211}
212
213#[cfg(test)]
214mod tests {
215 use super::prelude::*;
216 use ndarray::{Array2, array};
217
218 #[test]
219 fn test_full_workflow() {
220 let a = Array2::from_shape_fn((3, 3), |(i, j)| (i * 3 + j + 1) as f64);
222 let b = Array2::from_shape_fn((3, 3), |(i, j)| ((i + j) % 3 + 1) as f64);
223
224 let c = matmul(&a, &b);
226 assert_eq!(c.dim(), (3, 3));
227
228 let x = array![1.0f64, 2.0, 3.0];
230 let y = matvec(&a, &x);
231 assert_eq!(y.len(), 3);
232
233 let fnorm = frobenius_norm(&a);
235 assert!(fnorm > 0.0);
236
237 let symmetric = array![[4.0f64, 1.0, 0.0], [1.0, 4.0, 1.0], [0.0, 1.0, 4.0]];
239 let rhs = array![5.0f64, 6.0, 5.0];
240 let solution = solve_ndarray(&symmetric, &rhs).unwrap();
241 assert_eq!(solution.len(), 3);
242
243 let residual = matvec(&symmetric, &solution);
245 for i in 0..3 {
246 assert!((residual[i] - rhs[i]).abs() < 1e-10);
247 }
248 }
249
250 #[test]
251 fn test_decomposition_workflow() {
252 let a = array![[4.0f64, 2.0, 1.0], [2.0, 5.0, 2.0], [1.0, 2.0, 4.0]];
253
254 let lu = lu_ndarray(&a).unwrap();
256 let det = lu.det();
257 assert!(det.abs() > 1e-10); let qr = qr_ndarray(&a).unwrap();
261 assert_eq!(qr.q.dim().0, 3);
262 assert_eq!(qr.r.dim().1, 3);
263
264 let svd = svd_ndarray(&a).unwrap();
266 assert_eq!(svd.s.len(), 3);
267
268 let evd = eig_symmetric(&a).unwrap();
270 assert_eq!(evd.eigenvalues.len(), 3);
271
272 let chol = cholesky_ndarray(&a).unwrap();
274 assert_eq!(chol.l.dim(), (3, 3));
275 }
276
277 #[test]
278 fn test_blas_operations() {
279 let x = array![1.0f64, 2.0, 3.0];
281 let y = array![4.0f64, 5.0, 6.0];
282
283 let d = dot_ndarray(&x, &y);
284 assert!((d - 32.0).abs() < 1e-10);
285
286 let n = nrm2_ndarray(&x);
287 assert!((n - 14.0f64.sqrt()).abs() < 1e-10);
288
289 let s = asum_ndarray(&x);
290 assert!((s - 6.0).abs() < 1e-10);
291
292 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
294 let v = array![1.0f64, 1.0];
295 let result = matvec(&a, &v);
296 assert!((result[0] - 3.0).abs() < 1e-10);
297 assert!((result[1] - 7.0).abs() < 1e-10);
298
299 let b = array![[1.0f64, 0.0], [0.0, 1.0]];
301 let c = matmul(&a, &b);
302 for i in 0..2 {
303 for j in 0..2 {
304 assert!((c[[i, j]] - a[[i, j]]).abs() < 1e-10);
305 }
306 }
307 }
308
309 #[test]
310 fn test_column_major_efficiency() {
311 let col_major: Array2<f64> = zeros_f(10, 10);
313 assert!(is_column_major(&col_major));
314
315 let row_major: Array2<f64> = Array2::zeros((10, 10));
316 assert!(!is_column_major(&row_major));
317 assert!(is_row_major(&row_major));
318
319 let converted = to_column_major(&row_major);
321 assert!(is_column_major(&converted));
322 }
323
324 #[test]
325 fn test_identity_operations() {
326 let id: Array2<f64> = eye(3);
327
328 let tr = trace(&id);
330 assert!((tr - 3.0).abs() < 1e-15);
331
332 let fnorm = frobenius_norm(&id);
334 assert!((fnorm - 3.0f64.sqrt()).abs() < 1e-15);
335
336 let det = det_ndarray(&id).unwrap();
338 assert!((det - 1.0).abs() < 1e-10);
339
340 let inv = inv_ndarray(&id).unwrap();
342 for i in 0..3 {
343 for j in 0..3 {
344 let expected = if i == j { 1.0 } else { 0.0 };
345 assert!((inv[[i, j]] - expected).abs() < 1e-10);
346 }
347 }
348
349 let cond = cond_ndarray(&id).unwrap();
351 assert!((cond - 1.0).abs() < 1e-10);
352
353 let r = rank_ndarray(&id).unwrap();
355 assert_eq!(r, 3);
356 }
357
358 #[test]
359 fn test_large_matrices() {
360 let n = 100;
361 let a: Array2<f64> = zeros_f(n, n);
362 let mut a = a.mapv(|_| 0.0);
363
364 for i in 0..n {
366 a[[i, i]] = 10.0;
367 if i > 0 {
368 a[[i, i - 1]] = 1.0;
369 }
370 if i < n - 1 {
371 a[[i, i + 1]] = 1.0;
372 }
373 }
374
375 let x: ndarray::Array1<f64> = ndarray::Array1::ones(n);
377 let y = matvec(&a, &x);
378 assert_eq!(y.len(), n);
379
380 let id: Array2<f64> = eye(n);
382 let c = matmul(&a, &id);
383 for i in 0..n {
384 for j in 0..n {
385 assert!((c[[i, j]] - a[[i, j]]).abs() < 1e-10);
386 }
387 }
388 }
389
390 #[test]
391 fn test_numerical_accuracy() {
392 let n = 5;
394 let mut h: Array2<f64> = Array2::zeros((n, n));
395 for i in 0..n {
396 for j in 0..n {
397 h[[i, j]] = 1.0 / ((i + j + 1) as f64);
398 }
399 }
400
401 let svd = svd_ndarray(&h).unwrap();
403 assert_eq!(svd.s.len(), n);
404
405 for s in svd.s.iter() {
407 assert!(*s > 0.0);
408 }
409
410 let cond = cond_ndarray(&h).unwrap();
412 assert!(cond > 1000.0);
413 }
414
415 #[test]
416 fn test_arrayd_integration() {
417 use ndarray::{ArrayD, IxDyn};
418
419 let arr_d = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
421
422 let mat = arrayd_to_mat(&arr_d);
424 assert_eq!(mat.shape(), (3, 4));
425
426 let recovered = mat_to_arrayd(&mat);
427 assert_eq!(recovered.shape(), &[3, 4]);
428
429 for i in 0..3 {
431 for j in 0..4 {
432 assert!((arr_d[[i, j].as_ref()] - recovered[[i, j].as_ref()]).abs() < 1e-15);
433 }
434 }
435
436 let arr_2 = arrayd_to_array2(&arr_d);
438 let fnorm = frobenius_norm(&arr_2);
439 assert!(fnorm > 0.0);
440
441 let result_d = array2_to_arrayd(&arr_2);
443 assert_eq!(result_d.shape(), &[3, 4]);
444 }
445
446 #[test]
447 fn test_arrayd_matrix_operations() {
448 use ndarray::{ArrayD, IxDyn};
449
450 let a = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] + idx[1] + 1) as f64);
452 let b = ArrayD::from_shape_fn(IxDyn(&[4, 2]), |idx| (idx[0] * idx[1] + 1) as f64);
453
454 let a2 = arrayd_to_array2(&a);
456 let b2 = arrayd_to_array2(&b);
457
458 let c2 = matmul(&a2, &b2);
459 assert_eq!(c2.dim(), (3, 2));
460
461 let c_d = array2_to_arrayd(&c2);
463 assert_eq!(c_d.shape(), &[3, 2]);
464 }
465}