1use crate::conversions::array2_to_mat;
7use ndarray::{Array1, Array2, ArrayView1, ShapeBuilder};
8use num_complex::{Complex32, Complex64};
9use oxiblas_blas::level1::{asum, axpy, dot, dotc_c32, dotc_c64, dotu_c32, dotu_c64, nrm2, scal};
10use oxiblas_blas::level2::{GemvTrans, gemv as blas_gemv};
11use oxiblas_blas::level3::{GemmKernel, gemm as blas_gemm};
12use oxiblas_core::scalar::Field;
13use oxiblas_matrix::Mat;
14
15pub fn dot_ndarray<T: Field>(x: &Array1<T>, y: &Array1<T>) -> T {
31 assert_eq!(x.len(), y.len(), "Vector lengths must match");
32
33 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice()) {
35 dot(x_slice, y_slice)
36 } else {
37 let x_vec: Vec<T> = x.iter().cloned().collect();
39 let y_vec: Vec<T> = y.iter().cloned().collect();
40 dot(&x_vec, &y_vec)
41 }
42}
43
44pub fn dot_view<T: Field>(x: &ArrayView1<T>, y: &ArrayView1<T>) -> T {
46 assert_eq!(x.len(), y.len(), "Vector lengths must match");
47
48 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice()) {
49 dot(x_slice, y_slice)
50 } else {
51 let x_vec: Vec<T> = x.iter().cloned().collect();
52 let y_vec: Vec<T> = y.iter().cloned().collect();
53 dot(&x_vec, &y_vec)
54 }
55}
56
57pub fn dotc_c64_ndarray(x: &Array1<Complex64>, y: &Array1<Complex64>) -> Complex64 {
92 assert_eq!(x.len(), y.len(), "Vector lengths must match");
93
94 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice()) {
95 dotc_c64(x_slice, y_slice)
96 } else {
97 let x_vec: Vec<Complex64> = x.iter().copied().collect();
98 let y_vec: Vec<Complex64> = y.iter().copied().collect();
99 dotc_c64(&x_vec, &y_vec)
100 }
101}
102
103pub fn dotc_c32_ndarray(x: &Array1<Complex32>, y: &Array1<Complex32>) -> Complex32 {
110 assert_eq!(x.len(), y.len(), "Vector lengths must match");
111
112 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice()) {
113 dotc_c32(x_slice, y_slice)
114 } else {
115 let x_vec: Vec<Complex32> = x.iter().copied().collect();
116 let y_vec: Vec<Complex32> = y.iter().copied().collect();
117 dotc_c32(&x_vec, &y_vec)
118 }
119}
120
121pub fn dotu_c64_ndarray(x: &Array1<Complex64>, y: &Array1<Complex64>) -> Complex64 {
131 assert_eq!(x.len(), y.len(), "Vector lengths must match");
132
133 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice()) {
134 dotu_c64(x_slice, y_slice)
135 } else {
136 let x_vec: Vec<Complex64> = x.iter().copied().collect();
137 let y_vec: Vec<Complex64> = y.iter().copied().collect();
138 dotu_c64(&x_vec, &y_vec)
139 }
140}
141
142pub fn dotu_c32_ndarray(x: &Array1<Complex32>, y: &Array1<Complex32>) -> Complex32 {
149 assert_eq!(x.len(), y.len(), "Vector lengths must match");
150
151 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice()) {
152 dotu_c32(x_slice, y_slice)
153 } else {
154 let x_vec: Vec<Complex32> = x.iter().copied().collect();
155 let y_vec: Vec<Complex32> = y.iter().copied().collect();
156 dotu_c32(&x_vec, &y_vec)
157 }
158}
159
160pub fn nrm2_c64_ndarray(x: &Array1<Complex64>) -> f64 {
172 let mut components = Vec::with_capacity(x.len() * 2);
173 for xi in x.iter() {
174 components.push(xi.re);
175 components.push(xi.im);
176 }
177 nrm2(&components)
178}
179
180pub fn nrm2_c32_ndarray(x: &Array1<Complex32>) -> f32 {
187 let mut components = Vec::with_capacity(x.len() * 2);
188 for xi in x.iter() {
189 components.push(xi.re);
190 components.push(xi.im);
191 }
192 nrm2(&components)
193}
194
195pub fn asum_c64_ndarray(x: &Array1<Complex64>) -> f64 {
199 let mut sum = 0.0f64;
200 for xi in x.iter() {
201 sum += xi.norm();
202 }
203 sum
204}
205
206pub fn asum_c32_ndarray(x: &Array1<Complex32>) -> f32 {
210 let mut sum = 0.0f32;
211 for xi in x.iter() {
212 sum += xi.norm();
213 }
214 sum
215}
216
217pub fn nrm2_ndarray<T: Field + oxiblas_core::scalar::Real>(x: &Array1<T>) -> T {
221 if let Some(slice) = x.as_slice() {
222 nrm2(slice)
223 } else {
224 let vec: Vec<T> = x.iter().cloned().collect();
225 nrm2(&vec)
226 }
227}
228
229pub fn asum_ndarray<T: Field + oxiblas_core::scalar::Real>(x: &Array1<T>) -> T {
233 if let Some(slice) = x.as_slice() {
234 asum(slice)
235 } else {
236 let vec: Vec<T> = x.iter().cloned().collect();
237 asum(&vec)
238 }
239}
240
241pub fn axpy_ndarray<T: Field>(alpha: T, x: &Array1<T>, y: &mut Array1<T>) {
248 assert_eq!(x.len(), y.len(), "Vector lengths must match");
249
250 if let (Some(x_slice), Some(y_slice)) = (x.as_slice(), y.as_slice_mut()) {
251 axpy(alpha, x_slice, y_slice);
252 } else {
253 for (yi, xi) in y.iter_mut().zip(x.iter()) {
255 *yi = alpha * (*xi) + *yi;
256 }
257 }
258}
259
260pub fn scal_ndarray<T: Field>(alpha: T, x: &mut Array1<T>) {
262 if let Some(slice) = x.as_slice_mut() {
263 scal(alpha, slice);
264 } else {
265 for xi in x.iter_mut() {
266 *xi = alpha * (*xi);
267 }
268 }
269}
270
271#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub enum Transpose {
278 NoTrans,
280 Trans,
282 ConjTrans,
284}
285
286impl From<Transpose> for GemvTrans {
287 fn from(t: Transpose) -> Self {
288 match t {
289 Transpose::NoTrans => GemvTrans::NoTrans,
290 Transpose::Trans => GemvTrans::Trans,
291 Transpose::ConjTrans => GemvTrans::ConjTrans,
292 }
293 }
294}
295
296pub fn gemv_ndarray<T: Field + Clone>(
309 trans: Transpose,
310 alpha: T,
311 a: &Array2<T>,
312 x: &Array1<T>,
313 beta: T,
314 y: &mut Array1<T>,
315) where
316 T: bytemuck::Zeroable,
317{
318 let a_mat = array2_to_mat(a);
319 let (m, n) = a.dim();
320
321 let (x_len, y_len) = match trans {
323 Transpose::NoTrans => (n, m),
324 Transpose::Trans | Transpose::ConjTrans => (m, n),
325 };
326
327 assert_eq!(x.len(), x_len, "x dimension mismatch");
328 assert_eq!(y.len(), y_len, "y dimension mismatch");
329
330 let x_vec: Vec<T> = x.iter().cloned().collect();
332
333 if let Some(y_slice) = y.as_slice_mut() {
334 blas_gemv(trans.into(), alpha, a_mat.as_ref(), &x_vec, beta, y_slice);
335 } else {
336 let mut y_vec: Vec<T> = y.iter().cloned().collect();
337 blas_gemv(
338 trans.into(),
339 alpha,
340 a_mat.as_ref(),
341 &x_vec,
342 beta,
343 &mut y_vec,
344 );
345 for (yi, val) in y.iter_mut().zip(y_vec) {
346 *yi = val;
347 }
348 }
349}
350
351pub fn matvec<T: Field + Clone>(a: &Array2<T>, x: &Array1<T>) -> Array1<T>
355where
356 T: bytemuck::Zeroable,
357{
358 let (m, _n) = a.dim();
359 let mut y = Array1::zeros(m);
360 gemv_ndarray(Transpose::NoTrans, T::one(), a, x, T::zero(), &mut y);
361 y
362}
363
364pub fn matvec_t<T: Field + Clone>(a: &Array2<T>, x: &Array1<T>) -> Array1<T>
366where
367 T: bytemuck::Zeroable,
368{
369 let (_m, n) = a.dim();
370 let mut y = Array1::zeros(n);
371 gemv_ndarray(Transpose::Trans, T::one(), a, x, T::zero(), &mut y);
372 y
373}
374
375pub fn gemm_ndarray<T: Field + GemmKernel>(
391 alpha: T,
392 a: &Array2<T>,
393 b: &Array2<T>,
394 beta: T,
395 c: &mut Array2<T>,
396) where
397 T: bytemuck::Zeroable + Clone,
398{
399 let a_mat = array2_to_mat(a);
400 let b_mat = array2_to_mat(b);
401
402 let (m, n) = c.dim();
403 let mut c_mat: Mat<T> = Mat::zeros(m, n);
404
405 if beta != T::zero() {
407 for i in 0..m {
408 for j in 0..n {
409 c_mat[(i, j)] = c[[i, j]];
410 }
411 }
412 }
413
414 blas_gemm(alpha, a_mat.as_ref(), b_mat.as_ref(), beta, c_mat.as_mut());
415
416 for i in 0..m {
418 for j in 0..n {
419 c[[i, j]] = c_mat[(i, j)];
420 }
421 }
422}
423
424pub fn matmul<T: Field + GemmKernel>(a: &Array2<T>, b: &Array2<T>) -> Array2<T>
428where
429 T: bytemuck::Zeroable + Clone,
430{
431 let (m, k1) = a.dim();
432 let (k2, n) = b.dim();
433 assert_eq!(k1, k2, "Inner dimensions must match: {} vs {}", k1, k2);
434
435 let a_mat = array2_to_mat(a);
436 let b_mat = array2_to_mat(b);
437 let mut c_mat: Mat<T> = Mat::zeros(m, n);
438
439 blas_gemm(
440 T::one(),
441 a_mat.as_ref(),
442 b_mat.as_ref(),
443 T::zero(),
444 c_mat.as_mut(),
445 );
446
447 Array2::from_shape_fn((m, n).f(), |(i, j)| c_mat[(i, j)])
449}
450
451pub fn matmul_c<T: Field + GemmKernel>(a: &Array2<T>, b: &Array2<T>) -> Array2<T>
453where
454 T: bytemuck::Zeroable + Clone,
455{
456 let (m, k1) = a.dim();
457 let (k2, n) = b.dim();
458 assert_eq!(k1, k2, "Inner dimensions must match");
459
460 let a_mat = array2_to_mat(a);
461 let b_mat = array2_to_mat(b);
462 let mut c_mat: Mat<T> = Mat::zeros(m, n);
463
464 blas_gemm(
465 T::one(),
466 a_mat.as_ref(),
467 b_mat.as_ref(),
468 T::zero(),
469 c_mat.as_mut(),
470 );
471
472 Array2::from_shape_fn((m, n), |(i, j)| c_mat[(i, j)])
474}
475
476pub fn matmul_into<T: Field + GemmKernel>(a: &Array2<T>, b: &Array2<T>, c: &mut Array2<T>)
478where
479 T: bytemuck::Zeroable + Clone,
480{
481 gemm_ndarray(T::one(), a, b, T::zero(), c);
482}
483
484pub fn frobenius_norm<T: Field + oxiblas_core::scalar::Real>(a: &Array2<T>) -> T {
499 if let Some(slice) = a.as_slice() {
500 nrm2(slice)
501 } else {
502 let vec: Vec<T> = a.iter().copied().collect();
504 nrm2(&vec)
505 }
506}
507
508pub fn norm_1(a: &Array2<f64>) -> f64 {
512 let (nrows, ncols) = a.dim();
513 let mut max_sum = 0.0f64;
514
515 for j in 0..ncols {
516 let mut col_sum = 0.0f64;
517 for i in 0..nrows {
518 col_sum += a[[i, j]].abs();
519 }
520 if col_sum > max_sum {
521 max_sum = col_sum;
522 }
523 }
524
525 max_sum
526}
527
528pub fn norm_inf(a: &Array2<f64>) -> f64 {
532 let (nrows, ncols) = a.dim();
533 let mut max_sum = 0.0f64;
534
535 for i in 0..nrows {
536 let mut row_sum = 0.0f64;
537 for j in 0..ncols {
538 row_sum += a[[i, j]].abs();
539 }
540 if row_sum > max_sum {
541 max_sum = row_sum;
542 }
543 }
544
545 max_sum
546}
547
548pub fn norm_max(a: &Array2<f64>) -> f64 {
552 let mut max_val = 0.0f64;
553 for val in a.iter() {
554 let abs_val = val.abs();
555 if abs_val > max_val {
556 max_val = abs_val;
557 }
558 }
559 max_val
560}
561
562pub fn trace<T: Field>(a: &Array2<T>) -> T {
568 let (nrows, ncols) = a.dim();
569 assert_eq!(nrows, ncols, "Matrix must be square for trace");
570
571 let mut sum = T::zero();
572 for i in 0..nrows {
573 sum += a[[i, i]];
574 }
575 sum
576}
577
578pub fn transpose<T: Clone>(a: &Array2<T>) -> Array2<T> {
580 a.t().to_owned()
581}
582
583pub fn eye<T: Field>(n: usize) -> Array2<T>
585where
586 T: Clone,
587{
588 let mut result = Array2::zeros((n, n));
589 for i in 0..n {
590 result[[i, i]] = T::one();
591 }
592 result
593}
594
595pub fn eye_f<T: Field>(n: usize) -> Array2<T>
597where
598 T: Clone,
599{
600 let mut result: Array2<T> = Array2::from_shape_fn((n, n).f(), |_| T::zero());
601 for i in 0..n {
602 result[[i, i]] = T::one();
603 }
604 result
605}
606
607pub fn conj_transpose_c64(a: &Array2<Complex64>) -> Array2<Complex64> {
634 let (m, n) = a.dim();
635 Array2::from_shape_fn((n, m), |(i, j)| a[[j, i]].conj())
636}
637
638pub fn conj_transpose_c32(a: &Array2<Complex32>) -> Array2<Complex32> {
642 let (m, n) = a.dim();
643 Array2::from_shape_fn((n, m), |(i, j)| a[[j, i]].conj())
644}
645
646pub fn frobenius_norm_c64(a: &Array2<Complex64>) -> f64 {
655 let mut components = Vec::with_capacity(a.len() * 2);
656 for val in a.iter() {
657 components.push(val.re);
658 components.push(val.im);
659 }
660 nrm2(&components)
661}
662
663pub fn frobenius_norm_c32(a: &Array2<Complex32>) -> f32 {
670 let mut components = Vec::with_capacity(a.len() * 2);
671 for val in a.iter() {
672 components.push(val.re);
673 components.push(val.im);
674 }
675 nrm2(&components)
676}
677
678pub fn norm_1_c64(a: &Array2<Complex64>) -> f64 {
682 let (nrows, ncols) = a.dim();
683 let mut max_sum = 0.0f64;
684
685 for j in 0..ncols {
686 let mut col_sum = 0.0f64;
687 for i in 0..nrows {
688 col_sum += a[[i, j]].norm();
689 }
690 if col_sum > max_sum {
691 max_sum = col_sum;
692 }
693 }
694
695 max_sum
696}
697
698pub fn norm_1_c32(a: &Array2<Complex32>) -> f32 {
700 let (nrows, ncols) = a.dim();
701 let mut max_sum = 0.0f32;
702
703 for j in 0..ncols {
704 let mut col_sum = 0.0f32;
705 for i in 0..nrows {
706 col_sum += a[[i, j]].norm();
707 }
708 if col_sum > max_sum {
709 max_sum = col_sum;
710 }
711 }
712
713 max_sum
714}
715
716pub fn norm_inf_c64(a: &Array2<Complex64>) -> f64 {
720 let (nrows, ncols) = a.dim();
721 let mut max_sum = 0.0f64;
722
723 for i in 0..nrows {
724 let mut row_sum = 0.0f64;
725 for j in 0..ncols {
726 row_sum += a[[i, j]].norm();
727 }
728 if row_sum > max_sum {
729 max_sum = row_sum;
730 }
731 }
732
733 max_sum
734}
735
736pub fn norm_inf_c32(a: &Array2<Complex32>) -> f32 {
738 let (nrows, ncols) = a.dim();
739 let mut max_sum = 0.0f32;
740
741 for i in 0..nrows {
742 let mut row_sum = 0.0f32;
743 for j in 0..ncols {
744 row_sum += a[[i, j]].norm();
745 }
746 if row_sum > max_sum {
747 max_sum = row_sum;
748 }
749 }
750
751 max_sum
752}
753
754pub fn norm_max_c64(a: &Array2<Complex64>) -> f64 {
758 let mut max_val = 0.0f64;
759 for val in a.iter() {
760 let abs_val = val.norm();
761 if abs_val > max_val {
762 max_val = abs_val;
763 }
764 }
765 max_val
766}
767
768pub fn norm_max_c32(a: &Array2<Complex32>) -> f32 {
770 let mut max_val = 0.0f32;
771 for val in a.iter() {
772 let abs_val = val.norm();
773 if abs_val > max_val {
774 max_val = abs_val;
775 }
776 }
777 max_val
778}
779
780pub fn trace_c64(a: &Array2<Complex64>) -> Complex64 {
782 let (nrows, ncols) = a.dim();
783 assert_eq!(nrows, ncols, "Matrix must be square for trace");
784
785 let mut sum = Complex64::new(0.0, 0.0);
786 for i in 0..nrows {
787 sum += a[[i, i]];
788 }
789 sum
790}
791
792pub fn trace_c32(a: &Array2<Complex32>) -> Complex32 {
794 let (nrows, ncols) = a.dim();
795 assert_eq!(nrows, ncols, "Matrix must be square for trace");
796
797 let mut sum = Complex32::new(0.0, 0.0);
798 for i in 0..nrows {
799 sum += a[[i, i]];
800 }
801 sum
802}
803
804pub fn scal_c64_ndarray(alpha: Complex64, x: &mut Array1<Complex64>) {
806 for xi in x.iter_mut() {
807 *xi = alpha * (*xi);
808 }
809}
810
811pub fn scal_c32_ndarray(alpha: Complex32, x: &mut Array1<Complex32>) {
813 for xi in x.iter_mut() {
814 *xi = alpha * (*xi);
815 }
816}
817
818pub fn axpy_c64_ndarray(alpha: Complex64, x: &Array1<Complex64>, y: &mut Array1<Complex64>) {
820 assert_eq!(x.len(), y.len(), "Vector lengths must match");
821
822 for (yi, xi) in y.iter_mut().zip(x.iter()) {
823 *yi = alpha * (*xi) + *yi;
824 }
825}
826
827pub fn axpy_c32_ndarray(alpha: Complex32, x: &Array1<Complex32>, y: &mut Array1<Complex32>) {
829 assert_eq!(x.len(), y.len(), "Vector lengths must match");
830
831 for (yi, xi) in y.iter_mut().zip(x.iter()) {
832 *yi = alpha * (*xi) + *yi;
833 }
834}
835
836pub fn eye_c64(n: usize) -> Array2<Complex64> {
838 let mut result: Array2<Complex64> = Array2::from_elem((n, n), Complex64::new(0.0, 0.0));
839 for i in 0..n {
840 result[[i, i]] = Complex64::new(1.0, 0.0);
841 }
842 result
843}
844
845pub fn eye_c32(n: usize) -> Array2<Complex32> {
847 let mut result: Array2<Complex32> = Array2::from_elem((n, n), Complex32::new(0.0, 0.0));
848 for i in 0..n {
849 result[[i, i]] = Complex32::new(1.0, 0.0);
850 }
851 result
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use ndarray::array;
858
859 #[test]
860 fn test_dot_ndarray() {
861 let x = array![1.0f64, 2.0, 3.0];
862 let y = array![4.0f64, 5.0, 6.0];
863 let d = dot_ndarray(&x, &y);
864 assert!((d - 32.0).abs() < 1e-10);
865 }
866
867 #[test]
868 fn test_nrm2_ndarray() {
869 let x = array![3.0f64, 4.0];
870 let norm = nrm2_ndarray(&x);
871 assert!((norm - 5.0).abs() < 1e-10);
872 }
873
874 #[test]
875 fn test_asum_ndarray() {
876 let x = array![-1.0f64, 2.0, -3.0];
877 let sum = asum_ndarray(&x);
878 assert!((sum - 6.0).abs() < 1e-10);
879 }
880
881 #[test]
882 fn test_axpy_ndarray() {
883 let x = array![1.0f64, 2.0, 3.0];
884 let mut y = array![4.0f64, 5.0, 6.0];
885 axpy_ndarray(2.0, &x, &mut y);
886 assert!((y[0] - 6.0).abs() < 1e-10);
887 assert!((y[1] - 9.0).abs() < 1e-10);
888 assert!((y[2] - 12.0).abs() < 1e-10);
889 }
890
891 #[test]
892 fn test_scal_ndarray() {
893 let mut x = array![1.0f64, 2.0, 3.0];
894 scal_ndarray(2.0, &mut x);
895 assert!((x[0] - 2.0).abs() < 1e-10);
896 assert!((x[1] - 4.0).abs() < 1e-10);
897 assert!((x[2] - 6.0).abs() < 1e-10);
898 }
899
900 #[test]
901 fn test_gemv_notrans() {
902 let a = Array2::from_shape_fn((2, 3), |(i, j)| (i * 3 + j + 1) as f64);
903 let x = array![1.0f64, 1.0, 1.0];
904 let mut y = array![0.0f64, 0.0];
905
906 gemv_ndarray(Transpose::NoTrans, 1.0, &a, &x, 0.0, &mut y);
907
908 assert!((y[0] - 6.0).abs() < 1e-10);
911 assert!((y[1] - 15.0).abs() < 1e-10);
912 }
913
914 #[test]
915 fn test_gemv_trans() {
916 let a = Array2::from_shape_fn((2, 3), |(i, j)| (i * 3 + j + 1) as f64);
917 let x = array![1.0f64, 1.0];
918 let mut y = array![0.0f64, 0.0, 0.0];
919
920 gemv_ndarray(Transpose::Trans, 1.0, &a, &x, 0.0, &mut y);
921
922 assert!((y[0] - 5.0).abs() < 1e-10);
926 assert!((y[1] - 7.0).abs() < 1e-10);
927 assert!((y[2] - 9.0).abs() < 1e-10);
928 }
929
930 #[test]
931 fn test_matvec() {
932 let a = Array2::from_shape_fn((2, 3), |(i, j)| (i * 3 + j + 1) as f64);
933 let x = array![1.0f64, 2.0, 3.0];
934 let y = matvec(&a, &x);
935
936 assert!((y[0] - 14.0).abs() < 1e-10);
939 assert!((y[1] - 32.0).abs() < 1e-10);
940 }
941
942 #[test]
943 fn test_matmul() {
944 let a = Array2::from_shape_fn((2, 3), |_| 1.0f64);
945 let b = Array2::from_shape_fn((3, 2), |_| 2.0f64);
946 let c = matmul(&a, &b);
947
948 assert_eq!(c.dim(), (2, 2));
949 for i in 0..2 {
950 for j in 0..2 {
951 assert!((c[[i, j]] - 6.0).abs() < 1e-10);
952 }
953 }
954 }
955
956 #[test]
957 fn test_gemm_ndarray() {
958 let a = Array2::from_shape_fn((2, 3), |_| 1.0f64);
959 let b = Array2::from_shape_fn((3, 2), |_| 2.0f64);
960 let mut c = Array2::from_shape_fn((2, 2), |_| 1.0f64);
961
962 gemm_ndarray(1.0, &a, &b, 1.0, &mut c);
963
964 for i in 0..2 {
966 for j in 0..2 {
967 assert!((c[[i, j]] - 7.0).abs() < 1e-10);
968 }
969 }
970 }
971
972 #[test]
973 fn test_frobenius_norm() {
974 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
975 let norm = frobenius_norm(&a);
976 assert!((norm - 30.0f64.sqrt()).abs() < 1e-10);
978 }
979
980 #[test]
981 fn test_frobenius_norm_overflow_prevention() {
982 let large = 1e200f64;
986 let a = array![[large, large], [large, large]];
987 let norm = frobenius_norm(&a);
988 let expected = 2.0 * large; assert!(norm.is_finite(), "norm should be finite, got {}", norm);
990 assert!(
991 (norm - expected).abs() / expected < 1e-10,
992 "expected {}, got {}",
993 expected,
994 norm
995 );
996 }
997
998 #[test]
999 fn test_norm_1() {
1000 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
1001 let norm = norm_1(&a);
1002 assert!((norm - 6.0).abs() < 1e-10);
1004 }
1005
1006 #[test]
1007 fn test_norm_inf() {
1008 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
1009 let norm = norm_inf(&a);
1010 assert!((norm - 7.0).abs() < 1e-10);
1012 }
1013
1014 #[test]
1015 fn test_trace() {
1016 let a = array![[1.0f64, 2.0], [3.0, 4.0]];
1017 let tr = trace(&a);
1018 assert!((tr - 5.0).abs() < 1e-10);
1019 }
1020
1021 #[test]
1022 fn test_eye() {
1023 let id: Array2<f64> = eye(3);
1024 for i in 0..3 {
1025 for j in 0..3 {
1026 if i == j {
1027 assert!((id[[i, j]] - 1.0).abs() < 1e-15);
1028 } else {
1029 assert!(id[[i, j]].abs() < 1e-15);
1030 }
1031 }
1032 }
1033 }
1034
1035 #[test]
1036 fn test_transpose() {
1037 let a = array![[1.0f64, 2.0, 3.0], [4.0, 5.0, 6.0]];
1038 let at = transpose(&a);
1039 assert_eq!(at.dim(), (3, 2));
1040 assert!((at[[0, 0]] - 1.0).abs() < 1e-15);
1041 assert!((at[[2, 1]] - 6.0).abs() < 1e-15);
1042 }
1043
1044 #[test]
1049 fn test_dotc_c64_ndarray() {
1050 let x = array![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)];
1055 let y = array![Complex64::new(5.0, 6.0), Complex64::new(7.0, 8.0)];
1056
1057 let result = dotc_c64_ndarray(&x, &y);
1058 assert!((result.re - 70.0).abs() < 1e-10);
1059 assert!((result.im - (-8.0)).abs() < 1e-10);
1060 }
1061
1062 #[test]
1063 fn test_dotc_c32_ndarray() {
1064 let x = array![Complex32::new(1.0, 2.0), Complex32::new(3.0, 4.0)];
1065 let y = array![Complex32::new(5.0, 6.0), Complex32::new(7.0, 8.0)];
1066
1067 let result = dotc_c32_ndarray(&x, &y);
1068 assert!((result.re - 70.0).abs() < 1e-5);
1069 assert!((result.im - (-8.0)).abs() < 1e-5);
1070 }
1071
1072 #[test]
1073 fn test_dotu_c64_ndarray() {
1074 let x = array![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)];
1079 let y = array![Complex64::new(5.0, 6.0), Complex64::new(7.0, 8.0)];
1080
1081 let result = dotu_c64_ndarray(&x, &y);
1082 assert!((result.re - (-18.0)).abs() < 1e-10);
1083 assert!((result.im - 68.0).abs() < 1e-10);
1084 }
1085
1086 #[test]
1087 fn test_dotu_c32_ndarray() {
1088 let x = array![Complex32::new(1.0, 2.0), Complex32::new(3.0, 4.0)];
1089 let y = array![Complex32::new(5.0, 6.0), Complex32::new(7.0, 8.0)];
1090
1091 let result = dotu_c32_ndarray(&x, &y);
1092 assert!((result.re - (-18.0)).abs() < 1e-5);
1093 assert!((result.im - 68.0).abs() < 1e-5);
1094 }
1095
1096 #[test]
1097 fn test_dotc_c64_self_inner_product() {
1098 let x = array![
1100 Complex64::new(1.0, 2.0),
1101 Complex64::new(3.0, 4.0),
1102 Complex64::new(5.0, 6.0)
1103 ];
1104
1105 let result = dotc_c64_ndarray(&x, &x);
1106
1107 assert!(result.im.abs() < 1e-10);
1109
1110 assert!((result.re - 91.0).abs() < 1e-10);
1112 }
1113
1114 #[test]
1115 fn test_nrm2_c64_ndarray() {
1116 let x = array![Complex64::new(3.0, 4.0)]; let norm = nrm2_c64_ndarray(&x);
1119 assert!((norm - 5.0).abs() < 1e-10);
1120
1121 let x = array![Complex64::new(1.0, 0.0), Complex64::new(0.0, 1.0)];
1122 let norm = nrm2_c64_ndarray(&x);
1123 assert!((norm - 2.0f64.sqrt()).abs() < 1e-10);
1125 }
1126
1127 #[test]
1128 fn test_nrm2_c32_ndarray() {
1129 let x = array![Complex32::new(3.0, 4.0)];
1130 let norm = nrm2_c32_ndarray(&x);
1131 assert!((norm - 5.0).abs() < 1e-5);
1132 }
1133
1134 #[test]
1135 fn test_nrm2_c64_ndarray_overflow_prevention() {
1136 let large = 1e200f64;
1140 let x = array![Complex64::new(large, 0.0), Complex64::new(0.0, large)];
1141 let norm = nrm2_c64_ndarray(&x);
1142 let expected = 2.0f64.sqrt() * large;
1143 assert!(norm.is_finite(), "norm should be finite, got {}", norm);
1144 assert!(
1145 (norm - expected).abs() / expected < 1e-10,
1146 "expected {}, got {}",
1147 expected,
1148 norm
1149 );
1150 }
1151
1152 #[test]
1153 fn test_nrm2_c32_ndarray_overflow_prevention() {
1154 let large = 1e30f32; let x = array![Complex32::new(large, 0.0), Complex32::new(0.0, large)];
1156 let norm = nrm2_c32_ndarray(&x);
1157 let expected = 2.0f32.sqrt() * large;
1158 assert!(norm.is_finite(), "norm should be finite, got {}", norm);
1159 assert!(
1160 (norm - expected).abs() / expected < 1e-5,
1161 "expected {}, got {}",
1162 expected,
1163 norm
1164 );
1165 }
1166
1167 #[test]
1168 fn test_asum_c64_ndarray() {
1169 let x = array![Complex64::new(3.0, 4.0), Complex64::new(5.0, 12.0)];
1171 let sum = asum_c64_ndarray(&x);
1173 assert!((sum - 18.0).abs() < 1e-10);
1174 }
1175
1176 #[test]
1177 fn test_asum_c32_ndarray() {
1178 let x = array![Complex32::new(3.0, 4.0), Complex32::new(5.0, 12.0)];
1179 let sum = asum_c32_ndarray(&x);
1180 assert!((sum - 18.0).abs() < 1e-5);
1181 }
1182
1183 #[test]
1184 fn test_conj_transpose_c64() {
1185 let a = array![
1186 [Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)],
1187 [Complex64::new(5.0, 6.0), Complex64::new(7.0, 8.0)]
1188 ];
1189
1190 let ah = conj_transpose_c64(&a);
1191 assert_eq!(ah.dim(), (2, 2));
1192
1193 assert!((ah[[0, 0]].re - 1.0).abs() < 1e-10);
1195 assert!((ah[[0, 0]].im - (-2.0)).abs() < 1e-10);
1196
1197 assert!((ah[[0, 1]].re - 5.0).abs() < 1e-10);
1199 assert!((ah[[0, 1]].im - (-6.0)).abs() < 1e-10);
1200
1201 assert!((ah[[1, 0]].re - 3.0).abs() < 1e-10);
1203 assert!((ah[[1, 0]].im - (-4.0)).abs() < 1e-10);
1204
1205 assert!((ah[[1, 1]].re - 7.0).abs() < 1e-10);
1207 assert!((ah[[1, 1]].im - (-8.0)).abs() < 1e-10);
1208 }
1209
1210 #[test]
1211 fn test_conj_transpose_c64_rectangular() {
1212 let a = array![
1213 [
1214 Complex64::new(1.0, 1.0),
1215 Complex64::new(2.0, 2.0),
1216 Complex64::new(3.0, 3.0)
1217 ],
1218 [
1219 Complex64::new(4.0, 4.0),
1220 Complex64::new(5.0, 5.0),
1221 Complex64::new(6.0, 6.0)
1222 ]
1223 ];
1224
1225 let ah = conj_transpose_c64(&a);
1226 assert_eq!(ah.dim(), (3, 2));
1227
1228 assert!((ah[[2, 1]].re - 6.0).abs() < 1e-10);
1230 assert!((ah[[2, 1]].im - (-6.0)).abs() < 1e-10);
1231 }
1232
1233 #[test]
1234 fn test_frobenius_norm_c64() {
1235 let a = array![
1236 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 1.0)],
1237 [Complex64::new(0.0, 1.0), Complex64::new(1.0, 0.0)]
1238 ];
1239 let norm = frobenius_norm_c64(&a);
1241 assert!((norm - 2.0).abs() < 1e-10);
1242 }
1243
1244 #[test]
1245 fn test_frobenius_norm_c32() {
1246 let a = array![
1247 [Complex32::new(3.0, 4.0)] ];
1249 let norm = frobenius_norm_c32(&a);
1250 assert!((norm - 5.0).abs() < 1e-5);
1251 }
1252
1253 #[test]
1254 fn test_frobenius_norm_c64_overflow_prevention() {
1255 let large = 1e200f64;
1256 let a = array![[Complex64::new(large, 0.0), Complex64::new(0.0, large)]];
1257 let norm = frobenius_norm_c64(&a);
1258 let expected = 2.0f64.sqrt() * large;
1259 assert!(norm.is_finite(), "norm should be finite, got {}", norm);
1260 assert!(
1261 (norm - expected).abs() / expected < 1e-10,
1262 "expected {}, got {}",
1263 expected,
1264 norm
1265 );
1266 }
1267
1268 #[test]
1269 fn test_frobenius_norm_c32_overflow_prevention() {
1270 let large = 1e30f32;
1271 let a = array![[Complex32::new(large, 0.0), Complex32::new(0.0, large)]];
1272 let norm = frobenius_norm_c32(&a);
1273 let expected = 2.0f32.sqrt() * large;
1274 assert!(norm.is_finite(), "norm should be finite, got {}", norm);
1275 assert!(
1276 (norm - expected).abs() / expected < 1e-5,
1277 "expected {}, got {}",
1278 expected,
1279 norm
1280 );
1281 }
1282
1283 #[test]
1284 fn test_norm_1_c64() {
1285 let a = array![
1286 [Complex64::new(3.0, 4.0), Complex64::new(0.0, 1.0)],
1287 [Complex64::new(0.0, 0.0), Complex64::new(5.0, 12.0)]
1288 ];
1289 let norm = norm_1_c64(&a);
1292 assert!((norm - 14.0).abs() < 1e-10);
1293 }
1294
1295 #[test]
1296 fn test_norm_inf_c64() {
1297 let a = array![
1298 [Complex64::new(3.0, 4.0), Complex64::new(0.0, 1.0)],
1299 [Complex64::new(0.0, 0.0), Complex64::new(5.0, 12.0)]
1300 ];
1301 let norm = norm_inf_c64(&a);
1304 assert!((norm - 13.0).abs() < 1e-10);
1305 }
1306
1307 #[test]
1308 fn test_norm_max_c64() {
1309 let a = array![
1310 [Complex64::new(1.0, 0.0), Complex64::new(3.0, 4.0)],
1311 [Complex64::new(5.0, 12.0), Complex64::new(0.0, 1.0)]
1312 ];
1313 let max = norm_max_c64(&a);
1315 assert!((max - 13.0).abs() < 1e-10);
1316 }
1317
1318 #[test]
1319 fn test_trace_c64() {
1320 let a = array![
1321 [Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)],
1322 [Complex64::new(5.0, 6.0), Complex64::new(7.0, 8.0)]
1323 ];
1324 let tr = trace_c64(&a);
1326 assert!((tr.re - 8.0).abs() < 1e-10);
1327 assert!((tr.im - 10.0).abs() < 1e-10);
1328 }
1329
1330 #[test]
1331 fn test_trace_c32() {
1332 let a = array![
1333 [Complex32::new(1.0, 2.0), Complex32::new(3.0, 4.0)],
1334 [Complex32::new(5.0, 6.0), Complex32::new(7.0, 8.0)]
1335 ];
1336 let tr = trace_c32(&a);
1337 assert!((tr.re - 8.0).abs() < 1e-5);
1338 assert!((tr.im - 10.0).abs() < 1e-5);
1339 }
1340
1341 #[test]
1342 fn test_scal_c64_ndarray() {
1343 let mut x = array![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)];
1344 let alpha = Complex64::new(2.0, 0.0);
1345 scal_c64_ndarray(alpha, &mut x);
1346
1347 assert!((x[0].re - 2.0).abs() < 1e-10);
1348 assert!((x[0].im - 4.0).abs() < 1e-10);
1349 assert!((x[1].re - 6.0).abs() < 1e-10);
1350 assert!((x[1].im - 8.0).abs() < 1e-10);
1351 }
1352
1353 #[test]
1354 fn test_scal_c64_ndarray_complex_alpha() {
1355 let mut x = array![Complex64::new(1.0, 0.0)];
1356 let alpha = Complex64::new(0.0, 1.0); scal_c64_ndarray(alpha, &mut x);
1358
1359 assert!((x[0].re - 0.0).abs() < 1e-10);
1361 assert!((x[0].im - 1.0).abs() < 1e-10);
1362 }
1363
1364 #[test]
1365 fn test_axpy_c64_ndarray() {
1366 let x = array![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)];
1367 let mut y = array![Complex64::new(5.0, 6.0), Complex64::new(7.0, 8.0)];
1368 let alpha = Complex64::new(2.0, 0.0);
1369
1370 axpy_c64_ndarray(alpha, &x, &mut y);
1371
1372 assert!((y[0].re - 7.0).abs() < 1e-10);
1374 assert!((y[0].im - 10.0).abs() < 1e-10);
1375
1376 assert!((y[1].re - 13.0).abs() < 1e-10);
1378 assert!((y[1].im - 16.0).abs() < 1e-10);
1379 }
1380
1381 #[test]
1382 fn test_axpy_c32_ndarray() {
1383 let x = array![Complex32::new(1.0, 2.0)];
1384 let mut y = array![Complex32::new(3.0, 4.0)];
1385 let alpha = Complex32::new(0.0, 1.0); axpy_c32_ndarray(alpha, &x, &mut y);
1388
1389 assert!((y[0].re - 1.0).abs() < 1e-5);
1391 assert!((y[0].im - 5.0).abs() < 1e-5);
1392 }
1393
1394 #[test]
1395 fn test_eye_c64() {
1396 let id = eye_c64(3);
1397 assert_eq!(id.dim(), (3, 3));
1398
1399 for i in 0..3 {
1400 for j in 0..3 {
1401 if i == j {
1402 assert!((id[[i, j]].re - 1.0).abs() < 1e-10);
1403 assert!(id[[i, j]].im.abs() < 1e-10);
1404 } else {
1405 assert!(id[[i, j]].re.abs() < 1e-10);
1406 assert!(id[[i, j]].im.abs() < 1e-10);
1407 }
1408 }
1409 }
1410 }
1411
1412 #[test]
1413 fn test_eye_c32() {
1414 let id = eye_c32(2);
1415 assert_eq!(id.dim(), (2, 2));
1416 assert!((id[[0, 0]].re - 1.0).abs() < 1e-5);
1417 assert!((id[[1, 1]].re - 1.0).abs() < 1e-5);
1418 assert!(id[[0, 1]].re.abs() < 1e-5);
1419 assert!(id[[1, 0]].re.abs() < 1e-5);
1420 }
1421
1422 #[test]
1423 fn test_dotc_c64_large() {
1424 let n = 1000;
1426 let x: Array1<Complex64> =
1427 Array1::from_shape_fn(n, |i| Complex64::new(i as f64, (i as f64) * 0.5));
1428 let y: Array1<Complex64> =
1429 Array1::from_shape_fn(n, |i| Complex64::new(1.0, 0.1 * i as f64));
1430
1431 let result = dotc_c64_ndarray(&x, &y);
1432
1433 let expected: Complex64 = x.iter().zip(y.iter()).map(|(xi, yi)| xi.conj() * yi).sum();
1435 assert!((result.re - expected.re).abs() < 1e-6);
1436 assert!((result.im - expected.im).abs() < 1e-6);
1437 }
1438
1439 #[test]
1440 fn test_dotu_c64_large() {
1441 let n = 1000;
1442 let x: Array1<Complex64> = Array1::from_shape_fn(n, |i| {
1443 Complex64::new((i % 100) as f64, ((i + 50) % 100) as f64)
1444 });
1445 let y: Array1<Complex64> = Array1::from_shape_fn(n, |i| {
1446 Complex64::new(((i + 25) % 100) as f64, ((i + 75) % 100) as f64)
1447 });
1448
1449 let result = dotu_c64_ndarray(&x, &y);
1450
1451 let expected: Complex64 = x.iter().zip(y.iter()).map(|(xi, yi)| xi * yi).sum();
1452 assert!((result.re - expected.re).abs() < 1e-6);
1453 assert!((result.im - expected.im).abs() < 1e-6);
1454 }
1455
1456 #[test]
1457 fn test_hermitian_property() {
1458 let a = array![
1460 [Complex64::new(2.0, 0.0), Complex64::new(1.0, 1.0)],
1461 [Complex64::new(1.0, -1.0), Complex64::new(3.0, 0.0)]
1462 ];
1463
1464 let ah = conj_transpose_c64(&a);
1465
1466 for i in 0..2 {
1468 for j in 0..2 {
1469 assert!((a[[i, j]].re - ah[[i, j]].re).abs() < 1e-10);
1470 assert!((a[[i, j]].im - ah[[i, j]].im).abs() < 1e-10);
1471 }
1472 }
1473 }
1474}