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
98pub use ndarray::{
100 Array1, Array2, ArrayD, ArrayView1, ArrayView2, ArrayViewD, ArrayViewMut1, ArrayViewMut2,
101 ArrayViewMutD, IxDyn,
102};
103
104pub use num_complex::{Complex32, Complex64};
106
107pub mod prelude {
114 pub use crate::conversions::{
116 array_view_mut_to_mat_mut, array_view_to_mat_ref, array_view_to_mat_ref_or_transposed,
117 array_view1_as_slice, array_view1_as_slice_mut, array1_to_vec, array2_into_mat,
118 array2_to_mat, filled_f, is_column_major, is_row_major, mat_ref_to_array2, mat_to_array2,
119 mat_to_array2_c, slice_to_array1, to_column_major, zeros_f,
120 };
121
122 pub use crate::conversions::{
124 array_view_mutd_to_mat_mut, array_viewd_to_mat_ref, array_viewd_to_mat_ref_or_transposed,
125 array2_to_arrayd, arrayd_into_mat, arrayd_to_array2, arrayd_to_mat, mat_ref_to_arrayd,
126 mat_to_arrayd,
127 };
128
129 pub use crate::blas::{
131 asum_ndarray, axpy_ndarray, dot_ndarray, dot_view, nrm2_ndarray, scal_ndarray,
132 };
133
134 pub use crate::blas::{
136 asum_c32_ndarray, asum_c64_ndarray, axpy_c32_ndarray, axpy_c64_ndarray, dotc_c32_ndarray,
137 dotc_c64_ndarray, dotu_c32_ndarray, dotu_c64_ndarray, nrm2_c32_ndarray, nrm2_c64_ndarray,
138 scal_c32_ndarray, scal_c64_ndarray,
139 };
140
141 pub use crate::blas::{Transpose, gemv_ndarray, matvec, matvec_t};
143
144 pub use crate::blas::{gemm_ndarray, matmul, matmul_c, matmul_into};
146
147 pub use crate::blas::{frobenius_norm, norm_1, norm_inf, norm_max};
149
150 pub use crate::blas::{
152 frobenius_norm_c32, frobenius_norm_c64, norm_1_c32, norm_1_c64, norm_inf_c32, norm_inf_c64,
153 norm_max_c32, norm_max_c64,
154 };
155
156 pub use crate::blas::{eye, eye_f, trace, transpose};
158
159 pub use crate::blas::{
161 conj_transpose_c32, conj_transpose_c64, eye_c32, eye_c64, trace_c32, trace_c64,
162 };
163
164 pub use crate::lapack::{
166 CholeskyResult, LuResult, QrResult, SvdResult, SymEvdResult, cholesky_ndarray,
167 eig_symmetric, eigvals_symmetric, lu_ndarray, qr_ndarray, svd_ndarray, svd_truncated,
168 };
169
170 pub use crate::lapack::{
172 RandomizedSvdResult, low_rank_approx_ndarray, rsvd_ndarray, rsvd_power_ndarray,
173 };
174
175 pub use crate::lapack::{SchurResult, schur_ndarray};
177
178 pub use crate::lapack::{Eigenvalue, GeneralEvdResult, eig_ndarray, eigvals_ndarray};
180
181 pub use crate::lapack::{
183 tridiag_solve_multiple_ndarray, tridiag_solve_ndarray, tridiag_solve_spd_ndarray,
184 };
185
186 pub use crate::lapack::{lstsq_ndarray, solve_multiple_ndarray, solve_ndarray};
188
189 pub use crate::lapack::{cond_ndarray, det_ndarray, inv_ndarray, pinv_ndarray, rank_ndarray};
191
192 pub use crate::lapack::{LapackError, LapackResult};
194}
195
196#[cfg(test)]
197mod tests {
198 use super::prelude::*;
199 use ndarray::{Array2, array};
200
201 #[test]
202 fn test_full_workflow() {
203 let a = Array2::from_shape_fn((3, 3), |(i, j)| (i * 3 + j + 1) as f64);
205 let b = Array2::from_shape_fn((3, 3), |(i, j)| ((i + j) % 3 + 1) as f64);
206
207 let c = matmul(&a, &b);
209 assert_eq!(c.dim(), (3, 3));
210
211 let x = array![1.0f64, 2.0, 3.0];
213 let y = matvec(&a, &x);
214 assert_eq!(y.len(), 3);
215
216 let fnorm = frobenius_norm(&a);
218 assert!(fnorm > 0.0);
219
220 let symmetric = array![[4.0f64, 1.0, 0.0], [1.0, 4.0, 1.0], [0.0, 1.0, 4.0]];
222 let rhs = array![5.0f64, 6.0, 5.0];
223 let solution = solve_ndarray(&symmetric, &rhs).unwrap();
224 assert_eq!(solution.len(), 3);
225
226 let residual = matvec(&symmetric, &solution);
228 for i in 0..3 {
229 assert!((residual[i] - rhs[i]).abs() < 1e-10);
230 }
231 }
232
233 #[test]
234 fn test_decomposition_workflow() {
235 let a = array![[4.0f64, 2.0, 1.0], [2.0, 5.0, 2.0], [1.0, 2.0, 4.0]];
236
237 let lu = lu_ndarray(&a).unwrap();
239 let det = lu.det();
240 assert!(det.abs() > 1e-10); let qr = qr_ndarray(&a).unwrap();
244 assert_eq!(qr.q.dim().0, 3);
245 assert_eq!(qr.r.dim().1, 3);
246
247 let svd = svd_ndarray(&a).unwrap();
249 assert_eq!(svd.s.len(), 3);
250
251 let evd = eig_symmetric(&a).unwrap();
253 assert_eq!(evd.eigenvalues.len(), 3);
254
255 let chol = cholesky_ndarray(&a).unwrap();
257 assert_eq!(chol.l.dim(), (3, 3));
258 }
259
260 #[test]
261 fn test_blas_operations() {
262 let x = array![1.0f64, 2.0, 3.0];
264 let y = array![4.0f64, 5.0, 6.0];
265
266 let d = dot_ndarray(&x, &y);
267 assert!((d - 32.0).abs() < 1e-10);
268
269 let n = nrm2_ndarray(&x);
270 assert!((n - 14.0f64.sqrt()).abs() < 1e-10);
271
272 let s = asum_ndarray(&x);
273 assert!((s - 6.0).abs() < 1e-10);
274
275 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
277 let v = array![1.0f64, 1.0];
278 let result = matvec(&a, &v);
279 assert!((result[0] - 3.0).abs() < 1e-10);
280 assert!((result[1] - 7.0).abs() < 1e-10);
281
282 let b = array![[1.0f64, 0.0], [0.0, 1.0]];
284 let c = matmul(&a, &b);
285 for i in 0..2 {
286 for j in 0..2 {
287 assert!((c[[i, j]] - a[[i, j]]).abs() < 1e-10);
288 }
289 }
290 }
291
292 #[test]
293 fn test_column_major_efficiency() {
294 let col_major: Array2<f64> = zeros_f(10, 10);
296 assert!(is_column_major(&col_major));
297
298 let row_major: Array2<f64> = Array2::zeros((10, 10));
299 assert!(!is_column_major(&row_major));
300 assert!(is_row_major(&row_major));
301
302 let converted = to_column_major(&row_major);
304 assert!(is_column_major(&converted));
305 }
306
307 #[test]
308 fn test_identity_operations() {
309 let id: Array2<f64> = eye(3);
310
311 let tr = trace(&id);
313 assert!((tr - 3.0).abs() < 1e-15);
314
315 let fnorm = frobenius_norm(&id);
317 assert!((fnorm - 3.0f64.sqrt()).abs() < 1e-15);
318
319 let det = det_ndarray(&id).unwrap();
321 assert!((det - 1.0).abs() < 1e-10);
322
323 let inv = inv_ndarray(&id).unwrap();
325 for i in 0..3 {
326 for j in 0..3 {
327 let expected = if i == j { 1.0 } else { 0.0 };
328 assert!((inv[[i, j]] - expected).abs() < 1e-10);
329 }
330 }
331
332 let cond = cond_ndarray(&id).unwrap();
334 assert!((cond - 1.0).abs() < 1e-10);
335
336 let r = rank_ndarray(&id).unwrap();
338 assert_eq!(r, 3);
339 }
340
341 #[test]
342 fn test_large_matrices() {
343 let n = 100;
344 let a: Array2<f64> = zeros_f(n, n);
345 let mut a = a.mapv(|_| 0.0);
346
347 for i in 0..n {
349 a[[i, i]] = 10.0;
350 if i > 0 {
351 a[[i, i - 1]] = 1.0;
352 }
353 if i < n - 1 {
354 a[[i, i + 1]] = 1.0;
355 }
356 }
357
358 let x: ndarray::Array1<f64> = ndarray::Array1::ones(n);
360 let y = matvec(&a, &x);
361 assert_eq!(y.len(), n);
362
363 let id: Array2<f64> = eye(n);
365 let c = matmul(&a, &id);
366 for i in 0..n {
367 for j in 0..n {
368 assert!((c[[i, j]] - a[[i, j]]).abs() < 1e-10);
369 }
370 }
371 }
372
373 #[test]
374 fn test_numerical_accuracy() {
375 let n = 5;
377 let mut h: Array2<f64> = Array2::zeros((n, n));
378 for i in 0..n {
379 for j in 0..n {
380 h[[i, j]] = 1.0 / ((i + j + 1) as f64);
381 }
382 }
383
384 let svd = svd_ndarray(&h).unwrap();
386 assert_eq!(svd.s.len(), n);
387
388 for s in svd.s.iter() {
390 assert!(*s > 0.0);
391 }
392
393 let cond = cond_ndarray(&h).unwrap();
395 assert!(cond > 1000.0);
396 }
397
398 #[test]
399 fn test_arrayd_integration() {
400 use ndarray::{ArrayD, IxDyn};
401
402 let arr_d = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
404
405 let mat = arrayd_to_mat(&arr_d);
407 assert_eq!(mat.shape(), (3, 4));
408
409 let recovered = mat_to_arrayd(&mat);
410 assert_eq!(recovered.shape(), &[3, 4]);
411
412 for i in 0..3 {
414 for j in 0..4 {
415 assert!((arr_d[[i, j].as_ref()] - recovered[[i, j].as_ref()]).abs() < 1e-15);
416 }
417 }
418
419 let arr_2 = arrayd_to_array2(&arr_d);
421 let fnorm = frobenius_norm(&arr_2);
422 assert!(fnorm > 0.0);
423
424 let result_d = array2_to_arrayd(&arr_2);
426 assert_eq!(result_d.shape(), &[3, 4]);
427 }
428
429 #[test]
430 fn test_arrayd_matrix_operations() {
431 use ndarray::{ArrayD, IxDyn};
432
433 let a = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] + idx[1] + 1) as f64);
435 let b = ArrayD::from_shape_fn(IxDyn(&[4, 2]), |idx| (idx[0] * idx[1] + 1) as f64);
436
437 let a2 = arrayd_to_array2(&a);
439 let b2 = arrayd_to_array2(&b);
440
441 let c2 = matmul(&a2, &b2);
442 assert_eq!(c2.dim(), (3, 2));
443
444 let c_d = array2_to_arrayd(&c2);
446 assert_eq!(c_d.shape(), &[3, 2]);
447 }
448}