1#![warn(missing_docs)]
103#![warn(clippy::all)]
104#![allow(clippy::module_name_repetitions)]
105#![allow(clippy::must_use_candidate)]
106#![allow(clippy::needless_range_loop)]
108#![allow(clippy::multiple_bound_locations)]
110
111pub mod blas;
112pub mod conversions;
113pub mod lapack;
114
115#[cfg(feature = "parallel")]
116pub mod parallel;
117
118#[cfg(feature = "sparse")]
119pub mod sparse;
120
121pub use ndarray::{
123 Array1, Array2, ArrayD, ArrayView1, ArrayView2, ArrayViewD, ArrayViewMut1, ArrayViewMut2,
124 ArrayViewMutD, IxDyn,
125};
126
127pub use num_complex::{Complex32, Complex64};
129
130pub mod prelude {
137 pub use crate::conversions::{
139 array_view_mut_to_mat_mut, array_view_to_mat_ref, array_view_to_mat_ref_or_transposed,
140 array_view1_as_slice, array_view1_as_slice_mut, array1_to_vec, array2_into_mat,
141 array2_to_mat, filled_f, is_column_major, is_row_major, mat_ref_to_array2, mat_to_array2,
142 mat_to_array2_c, slice_to_array1, to_column_major, zeros_f,
143 };
144
145 pub use crate::conversions::{
147 array_view_mutd_to_mat_mut, array_viewd_to_mat_ref, array_viewd_to_mat_ref_or_transposed,
148 array2_to_arrayd, arrayd_into_mat, arrayd_to_array2, arrayd_to_mat, mat_ref_to_arrayd,
149 mat_to_arrayd,
150 };
151
152 pub use crate::blas::{
154 asum_ndarray, axpy_ndarray, dot_ndarray, dot_view, nrm2_ndarray, scal_ndarray,
155 };
156
157 pub use crate::blas::{
159 asum_c32_ndarray, asum_c64_ndarray, axpy_c32_ndarray, axpy_c64_ndarray, dotc_c32_ndarray,
160 dotc_c64_ndarray, dotu_c32_ndarray, dotu_c64_ndarray, nrm2_c32_ndarray, nrm2_c64_ndarray,
161 scal_c32_ndarray, scal_c64_ndarray,
162 };
163
164 pub use crate::blas::{Transpose, gemv_ndarray, matvec, matvec_t};
166
167 pub use crate::blas::{gemm_ndarray, matmul, matmul_c, matmul_into};
169
170 pub use crate::blas::{frobenius_norm, norm_1, norm_inf, norm_max};
172
173 pub use crate::blas::{
175 frobenius_norm_c32, frobenius_norm_c64, norm_1_c32, norm_1_c64, norm_inf_c32, norm_inf_c64,
176 norm_max_c32, norm_max_c64,
177 };
178
179 pub use crate::blas::{eye, eye_f, trace, transpose};
181
182 pub use crate::blas::{
184 conj_transpose_c32, conj_transpose_c64, eye_c32, eye_c64, trace_c32, trace_c64,
185 };
186
187 pub use crate::lapack::{
189 CholeskyResult, LuResult, QrResult, SvdResult, SymEvdResult, cholesky_ndarray,
190 eig_symmetric, eigvals_symmetric, lu_ndarray, qr_ndarray, svd_ndarray, svd_truncated,
191 };
192
193 pub use crate::lapack::{
195 RandomizedSvdResult, low_rank_approx_ndarray, rsvd_ndarray, rsvd_power_ndarray,
196 };
197
198 pub use crate::lapack::{SchurResult, schur_ndarray};
200
201 pub use crate::lapack::{Eigenvalue, GeneralEvdResult, eig_ndarray, eigvals_ndarray};
203
204 pub use crate::lapack::{
206 tridiag_solve_multiple_ndarray, tridiag_solve_ndarray, tridiag_solve_spd_ndarray,
207 };
208
209 pub use crate::lapack::{lstsq_ndarray, solve_multiple_ndarray, solve_ndarray};
211
212 pub use crate::lapack::{cond_ndarray, det_ndarray, inv_ndarray, pinv_ndarray, rank_ndarray};
214
215 pub use crate::lapack::{LapackError, LapackResult};
217
218 #[cfg(feature = "parallel")]
220 pub use crate::parallel::{gemm_par_ndarray, matmul_par};
221
222 #[cfg(feature = "sparse")]
224 pub use crate::sparse::{
225 SparseNdarrayError, array2_to_csc, array2_to_csc_with_tolerance, array2_to_csr,
226 array2_to_csr_with_tolerance, csc_to_array2, csr_to_array2, sparse_solve_ndarray,
227 sparse_solve_ndarray_with_options, spmv_full_ndarray, spmv_ndarray,
228 };
229}
230
231#[cfg(test)]
232mod tests {
233 use super::prelude::*;
234 use ndarray::{Array2, array};
235
236 #[test]
237 fn test_full_workflow() {
238 let a = Array2::from_shape_fn((3, 3), |(i, j)| (i * 3 + j + 1) as f64);
240 let b = Array2::from_shape_fn((3, 3), |(i, j)| ((i + j) % 3 + 1) as f64);
241
242 let c = matmul(&a, &b);
244 assert_eq!(c.dim(), (3, 3));
245
246 let x = array![1.0f64, 2.0, 3.0];
248 let y = matvec(&a, &x);
249 assert_eq!(y.len(), 3);
250
251 let fnorm = frobenius_norm(&a);
253 assert!(fnorm > 0.0);
254
255 let symmetric = array![[4.0f64, 1.0, 0.0], [1.0, 4.0, 1.0], [0.0, 1.0, 4.0]];
257 let rhs = array![5.0f64, 6.0, 5.0];
258 let solution = solve_ndarray(&symmetric, &rhs).unwrap();
259 assert_eq!(solution.len(), 3);
260
261 let residual = matvec(&symmetric, &solution);
263 for i in 0..3 {
264 assert!((residual[i] - rhs[i]).abs() < 1e-10);
265 }
266 }
267
268 #[test]
269 fn test_decomposition_workflow() {
270 let a = array![[4.0f64, 2.0, 1.0], [2.0, 5.0, 2.0], [1.0, 2.0, 4.0]];
271
272 let lu = lu_ndarray(&a).unwrap();
274 let det = lu.det();
275 assert!(det.abs() > 1e-10); let qr = qr_ndarray(&a).unwrap();
279 assert_eq!(qr.q.dim().0, 3);
280 assert_eq!(qr.r.dim().1, 3);
281
282 let svd = svd_ndarray(&a).unwrap();
284 assert_eq!(svd.s.len(), 3);
285
286 let evd = eig_symmetric(&a).unwrap();
288 assert_eq!(evd.eigenvalues.len(), 3);
289
290 let chol = cholesky_ndarray(&a).unwrap();
292 assert_eq!(chol.l.dim(), (3, 3));
293 }
294
295 #[test]
296 fn test_blas_operations() {
297 let x = array![1.0f64, 2.0, 3.0];
299 let y = array![4.0f64, 5.0, 6.0];
300
301 let d = dot_ndarray(&x, &y);
302 assert!((d - 32.0).abs() < 1e-10);
303
304 let n = nrm2_ndarray(&x);
305 assert!((n - 14.0f64.sqrt()).abs() < 1e-10);
306
307 let s = asum_ndarray(&x);
308 assert!((s - 6.0).abs() < 1e-10);
309
310 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
312 let v = array![1.0f64, 1.0];
313 let result = matvec(&a, &v);
314 assert!((result[0] - 3.0).abs() < 1e-10);
315 assert!((result[1] - 7.0).abs() < 1e-10);
316
317 let b = array![[1.0f64, 0.0], [0.0, 1.0]];
319 let c = matmul(&a, &b);
320 for i in 0..2 {
321 for j in 0..2 {
322 assert!((c[[i, j]] - a[[i, j]]).abs() < 1e-10);
323 }
324 }
325 }
326
327 #[test]
328 fn test_column_major_efficiency() {
329 let col_major: Array2<f64> = zeros_f(10, 10);
331 assert!(is_column_major(&col_major));
332
333 let row_major: Array2<f64> = Array2::zeros((10, 10));
334 assert!(!is_column_major(&row_major));
335 assert!(is_row_major(&row_major));
336
337 let converted = to_column_major(&row_major);
339 assert!(is_column_major(&converted));
340 }
341
342 #[test]
343 fn test_identity_operations() {
344 let id: Array2<f64> = eye(3);
345
346 let tr = trace(&id);
348 assert!((tr - 3.0).abs() < 1e-15);
349
350 let fnorm = frobenius_norm(&id);
352 assert!((fnorm - 3.0f64.sqrt()).abs() < 1e-15);
353
354 let det = det_ndarray(&id).unwrap();
356 assert!((det - 1.0).abs() < 1e-10);
357
358 let inv = inv_ndarray(&id).unwrap();
360 for i in 0..3 {
361 for j in 0..3 {
362 let expected = if i == j { 1.0 } else { 0.0 };
363 assert!((inv[[i, j]] - expected).abs() < 1e-10);
364 }
365 }
366
367 let cond = cond_ndarray(&id).unwrap();
369 assert!((cond - 1.0).abs() < 1e-10);
370
371 let r = rank_ndarray(&id).unwrap();
373 assert_eq!(r, 3);
374 }
375
376 #[test]
377 fn test_large_matrices() {
378 let n = 100;
379 let a: Array2<f64> = zeros_f(n, n);
380 let mut a = a.mapv(|_| 0.0);
381
382 for i in 0..n {
384 a[[i, i]] = 10.0;
385 if i > 0 {
386 a[[i, i - 1]] = 1.0;
387 }
388 if i < n - 1 {
389 a[[i, i + 1]] = 1.0;
390 }
391 }
392
393 let x: ndarray::Array1<f64> = ndarray::Array1::ones(n);
395 let y = matvec(&a, &x);
396 assert_eq!(y.len(), n);
397
398 let id: Array2<f64> = eye(n);
400 let c = matmul(&a, &id);
401 for i in 0..n {
402 for j in 0..n {
403 assert!((c[[i, j]] - a[[i, j]]).abs() < 1e-10);
404 }
405 }
406 }
407
408 #[test]
409 fn test_numerical_accuracy() {
410 let n = 5;
412 let mut h: Array2<f64> = Array2::zeros((n, n));
413 for i in 0..n {
414 for j in 0..n {
415 h[[i, j]] = 1.0 / ((i + j + 1) as f64);
416 }
417 }
418
419 let svd = svd_ndarray(&h).unwrap();
421 assert_eq!(svd.s.len(), n);
422
423 for s in svd.s.iter() {
425 assert!(*s > 0.0);
426 }
427
428 let cond = cond_ndarray(&h).unwrap();
430 assert!(cond > 1000.0);
431 }
432
433 #[test]
434 fn test_arrayd_integration() {
435 use ndarray::{ArrayD, IxDyn};
436
437 let arr_d = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
439
440 let mat = arrayd_to_mat(&arr_d);
442 assert_eq!(mat.shape(), (3, 4));
443
444 let recovered = mat_to_arrayd(&mat);
445 assert_eq!(recovered.shape(), &[3, 4]);
446
447 for i in 0..3 {
449 for j in 0..4 {
450 assert!((arr_d[[i, j].as_ref()] - recovered[[i, j].as_ref()]).abs() < 1e-15);
451 }
452 }
453
454 let arr_2 = arrayd_to_array2(&arr_d);
456 let fnorm = frobenius_norm(&arr_2);
457 assert!(fnorm > 0.0);
458
459 let result_d = array2_to_arrayd(&arr_2);
461 assert_eq!(result_d.shape(), &[3, 4]);
462 }
463
464 #[test]
465 fn test_arrayd_matrix_operations() {
466 use ndarray::{ArrayD, IxDyn};
467
468 let a = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] + idx[1] + 1) as f64);
470 let b = ArrayD::from_shape_fn(IxDyn(&[4, 2]), |idx| (idx[0] * idx[1] + 1) as f64);
471
472 let a2 = arrayd_to_array2(&a);
474 let b2 = arrayd_to_array2(&b);
475
476 let c2 = matmul(&a2, &b2);
477 assert_eq!(c2.dim(), (3, 2));
478
479 let c_d = array2_to_arrayd(&c2);
481 assert_eq!(c_d.shape(), &[3, 2]);
482 }
483}