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