1use ndarray::{
8 Array1, Array2, ArrayD, ArrayView1, ArrayView2, ArrayViewD, ArrayViewMut1, ArrayViewMut2,
9 ArrayViewMutD, IxDyn, ShapeBuilder,
10};
11use oxiblas_core::scalar::Field;
12use oxiblas_matrix::{Mat, MatMut, MatRef};
13
14pub fn array2_to_mat<T: Field + Clone>(arr: &Array2<T>) -> Mat<T>
23where
24 T: bytemuck::Zeroable,
25{
26 let (nrows, ncols) = arr.dim();
27 let strides = arr.strides();
28
29 if strides[0] == 1 && strides[1] == nrows as isize {
31 let mut mat = Mat::zeros(nrows, ncols);
33 for i in 0..nrows {
35 for j in 0..ncols {
36 mat[(i, j)] = arr[[i, j]];
37 }
38 }
39 mat
40 } else {
41 let mut mat = Mat::zeros(nrows, ncols);
43 for i in 0..nrows {
44 for j in 0..ncols {
45 mat[(i, j)] = arr[[i, j]];
46 }
47 }
48 mat
49 }
50}
51
52pub fn array2_into_mat<T: Field + Clone>(arr: Array2<T>) -> Mat<T>
57where
58 T: bytemuck::Zeroable,
59{
60 array2_to_mat(&arr)
63}
64
65pub fn mat_to_array2<T: Field + Clone>(mat: &Mat<T>) -> Array2<T> {
69 let (nrows, ncols) = mat.shape();
70 Array2::from_shape_fn((nrows, ncols).f(), |(i, j)| mat[(i, j)])
72}
73
74pub fn mat_ref_to_array2<T: Field + Clone>(mat: MatRef<'_, T>) -> Array2<T> {
78 let (nrows, ncols) = (mat.nrows(), mat.ncols());
79 Array2::from_shape_fn((nrows, ncols).f(), |(i, j)| mat[(i, j)])
80}
81
82pub fn mat_to_array2_c<T: Field + Clone>(mat: &Mat<T>) -> Array2<T> {
84 let (nrows, ncols) = mat.shape();
85 Array2::from_shape_fn((nrows, ncols), |(i, j)| mat[(i, j)])
86}
87
88pub fn arrayd_to_mat<T: Field + Clone>(arr: &ArrayD<T>) -> Mat<T>
107where
108 T: bytemuck::Zeroable,
109{
110 assert_eq!(
111 arr.ndim(),
112 2,
113 "ArrayD must be 2-dimensional for matrix conversion"
114 );
115 let shape = arr.shape();
116 let nrows = shape[0];
117 let ncols = shape[1];
118
119 let mut mat = Mat::zeros(nrows, ncols);
120 for i in 0..nrows {
121 for j in 0..ncols {
122 mat[(i, j)] = arr[[i, j].as_ref()];
123 }
124 }
125 mat
126}
127
128pub fn arrayd_into_mat<T: Field + Clone>(arr: ArrayD<T>) -> Mat<T>
133where
134 T: bytemuck::Zeroable,
135{
136 arrayd_to_mat(&arr)
137}
138
139pub fn mat_to_arrayd<T: Field + Clone>(mat: &Mat<T>) -> ArrayD<T> {
143 let (nrows, ncols) = mat.shape();
144 let mut arr = ArrayD::from_elem(IxDyn(&[nrows, ncols]), T::zero());
145 for i in 0..nrows {
146 for j in 0..ncols {
147 arr[[i, j].as_ref()] = mat[(i, j)];
148 }
149 }
150 arr
151}
152
153pub fn mat_ref_to_arrayd<T: Field + Clone>(mat: MatRef<'_, T>) -> ArrayD<T> {
155 let (nrows, ncols) = (mat.nrows(), mat.ncols());
156 let mut arr = ArrayD::from_elem(IxDyn(&[nrows, ncols]), T::zero());
157 for i in 0..nrows {
158 for j in 0..ncols {
159 arr[[i, j].as_ref()] = mat[(i, j)];
160 }
161 }
162 arr
163}
164
165pub fn arrayd_to_array2<T: Clone>(arr: &ArrayD<T>) -> Array2<T> {
170 assert_eq!(arr.ndim(), 2, "ArrayD must be 2-dimensional");
171 let shape = arr.shape();
172 Array2::from_shape_fn((shape[0], shape[1]), |(i, j)| arr[[i, j].as_ref()].clone())
173}
174
175pub fn array2_to_arrayd<T: Clone>(arr: &Array2<T>) -> ArrayD<T> {
177 let (nrows, ncols) = arr.dim();
178 let mut result = ArrayD::from_elem(IxDyn(&[nrows, ncols]), arr[[0, 0]].clone());
179 for i in 0..nrows {
180 for j in 0..ncols {
181 result[[i, j].as_ref()] = arr[[i, j]].clone();
182 }
183 }
184 result
185}
186
187pub fn array_viewd_to_mat_ref<'a, T: Field>(arr: &'a ArrayViewD<'a, T>) -> Option<MatRef<'a, T>> {
197 if arr.ndim() != 2 {
198 return None;
199 }
200
201 let shape = arr.shape();
202 let nrows = shape[0];
203 let ncols = shape[1];
204 let strides = arr.strides();
205
206 if strides[0] == 1 {
208 let col_stride = strides[1] as usize;
209 let ptr = arr.as_ptr();
210 Some(MatRef::new(ptr, nrows, ncols, col_stride))
211 } else {
212 None
213 }
214}
215
216pub fn array_viewd_to_mat_ref_or_transposed<'a, T: Field>(
223 arr: &'a ArrayViewD<'a, T>,
224) -> Option<(MatRef<'a, T>, bool)> {
225 if arr.ndim() != 2 {
226 return None;
227 }
228
229 let shape = arr.shape();
230 let nrows = shape[0];
231 let ncols = shape[1];
232 let strides = arr.strides();
233
234 if strides[0] == 1 {
235 let col_stride = strides[1] as usize;
237 let ptr = arr.as_ptr();
238 Some((MatRef::new(ptr, nrows, ncols, col_stride), false))
239 } else if strides[1] == 1 {
240 let row_stride = strides[0] as usize;
242 let ptr = arr.as_ptr();
243 Some((MatRef::new(ptr, ncols, nrows, row_stride), true))
244 } else {
245 None
246 }
247}
248
249pub fn array_view_mutd_to_mat_mut<'a, T: Field>(
259 arr: &'a mut ArrayViewMutD<'a, T>,
260) -> Option<MatMut<'a, T>> {
261 if arr.ndim() != 2 {
262 return None;
263 }
264
265 let shape = arr.shape();
266 let nrows = shape[0];
267 let ncols = shape[1];
268 let strides = arr.strides();
269
270 if strides[0] == 1 {
271 let col_stride = strides[1] as usize;
272 let ptr = arr.as_mut_ptr();
273 Some(MatMut::new(ptr, nrows, ncols, col_stride))
274 } else {
275 None
276 }
277}
278
279pub fn array_view_to_mat_ref<'a, T: Field>(arr: &'a ArrayView2<'a, T>) -> Option<MatRef<'a, T>> {
292 let (nrows, ncols) = arr.dim();
293 let strides = arr.strides();
294
295 if strides[0] == 1 {
297 let col_stride = strides[1] as usize;
298 let ptr = arr.as_ptr();
299 Some(MatRef::new(ptr, nrows, ncols, col_stride))
300 } else {
301 None
302 }
303}
304
305pub fn array_view_to_mat_ref_or_transposed<'a, T: Field>(
313 arr: &'a ArrayView2<'a, T>,
314) -> Option<(MatRef<'a, T>, bool)> {
315 let (nrows, ncols) = arr.dim();
316 let strides = arr.strides();
317
318 if strides[0] == 1 {
319 let col_stride = strides[1] as usize;
321 let ptr = arr.as_ptr();
322 Some((MatRef::new(ptr, nrows, ncols, col_stride), false))
323 } else if strides[1] == 1 {
324 let row_stride = strides[0] as usize;
326 let ptr = arr.as_ptr();
327 Some((MatRef::new(ptr, ncols, nrows, row_stride), true))
329 } else {
330 None
332 }
333}
334
335pub fn array_view_mut_to_mat_mut<'a, T: Field>(
345 arr: &'a mut ArrayViewMut2<'a, T>,
346) -> Option<MatMut<'a, T>> {
347 let (nrows, ncols) = arr.dim();
348 let strides = arr.strides();
349
350 if strides[0] == 1 {
351 let col_stride = strides[1] as usize;
352 let ptr = arr.as_mut_ptr();
353 Some(MatMut::new(ptr, nrows, ncols, col_stride))
354 } else {
355 None
356 }
357}
358
359pub fn array1_to_vec<T: Clone>(arr: &Array1<T>) -> Vec<T> {
365 arr.iter().cloned().collect()
366}
367
368pub fn slice_to_array1<T: Clone>(slice: &[T]) -> Array1<T> {
370 Array1::from_vec(slice.to_vec())
371}
372
373pub fn array_view1_as_slice<'a, T>(arr: &'a ArrayView1<'a, T>) -> Option<&'a [T]> {
375 arr.as_slice()
376}
377
378pub fn array_view1_as_slice_mut<'a, T>(arr: &'a mut ArrayViewMut1<'a, T>) -> Option<&'a mut [T]> {
380 arr.as_slice_mut()
381}
382
383pub fn zeros_f<T: Clone + Default>(nrows: usize, ncols: usize) -> Array2<T> {
392 Array2::from_shape_fn((nrows, ncols).f(), |_| T::default())
393}
394
395pub fn filled_f<T: Clone>(nrows: usize, ncols: usize, value: T) -> Array2<T> {
397 Array2::from_shape_fn((nrows, ncols).f(), |_| value.clone())
398}
399
400pub fn is_column_major<T>(arr: &Array2<T>) -> bool {
402 let strides = arr.strides();
403 let (nrows, _) = arr.dim();
404 strides[0] == 1 && strides[1] == nrows as isize
405}
406
407pub fn is_row_major<T>(arr: &Array2<T>) -> bool {
409 let strides = arr.strides();
410 let (_, ncols) = arr.dim();
411 strides[0] == ncols as isize && strides[1] == 1
412}
413
414pub fn to_column_major<T: Clone + Default>(arr: &Array2<T>) -> Array2<T> {
416 let (nrows, ncols) = arr.dim();
417 let mut result = zeros_f(nrows, ncols);
418 for i in 0..nrows {
419 for j in 0..ncols {
420 result[[i, j]] = arr[[i, j]].clone();
421 }
422 }
423 result
424}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429 use ndarray::Array2;
430
431 #[test]
432 fn test_array2_to_mat_rowmajor() {
433 let arr = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
434 let mat = array2_to_mat(&arr);
435
436 assert_eq!(mat.shape(), (3, 4));
437 for i in 0..3 {
438 for j in 0..4 {
439 assert_eq!(mat[(i, j)], arr[[i, j]]);
440 }
441 }
442 }
443
444 #[test]
445 fn test_array2_to_mat_colmajor() {
446 let arr: Array2<f64> = Array2::from_shape_fn((3, 4).f(), |(i, j)| (i * 4 + j) as f64);
447 assert!(is_column_major(&arr));
448
449 let mat = array2_to_mat(&arr);
450 assert_eq!(mat.shape(), (3, 4));
451 for i in 0..3 {
452 for j in 0..4 {
453 assert_eq!(mat[(i, j)], arr[[i, j]]);
454 }
455 }
456 }
457
458 #[test]
459 fn test_mat_to_array2() {
460 let mat: Mat<f64> = Mat::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
461 let arr = mat_to_array2(&mat);
462
463 assert_eq!(arr.dim(), (2, 3));
464 assert_eq!(arr[[0, 0]], 1.0);
465 assert_eq!(arr[[1, 2]], 6.0);
466 }
467
468 #[test]
469 fn test_roundtrip() {
470 let original = Array2::from_shape_fn((5, 7), |(i, j)| (i * 7 + j) as f64);
471 let mat = array2_to_mat(&original);
472 let recovered = mat_to_array2(&mat);
473
474 for i in 0..5 {
475 for j in 0..7 {
476 assert!((original[[i, j]] - recovered[[i, j]]).abs() < 1e-15);
477 }
478 }
479 }
480
481 #[test]
482 fn test_is_column_major() {
483 let col_major: Array2<f64> = Array2::zeros((3, 4).f());
484 let row_major: Array2<f64> = Array2::zeros((3, 4));
485
486 assert!(is_column_major(&col_major));
487 assert!(!is_column_major(&row_major));
488 assert!(is_row_major(&row_major));
489 assert!(!is_row_major(&col_major));
490 }
491
492 #[test]
493 fn test_to_column_major() {
494 let row_major = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
495 let col_major = to_column_major(&row_major);
496
497 assert!(is_column_major(&col_major));
498 for i in 0..3 {
499 for j in 0..4 {
500 assert_eq!(row_major[[i, j]], col_major[[i, j]]);
501 }
502 }
503 }
504
505 #[test]
506 fn test_array_view_to_mat_ref_or_transposed() {
507 let col_major: Array2<f64> = Array2::from_shape_fn((3, 4).f(), |(i, j)| (i * 4 + j) as f64);
509 let view = col_major.view();
510 let (mat_ref, transposed) = array_view_to_mat_ref_or_transposed(&view).unwrap();
511 assert!(!transposed);
512 assert_eq!(mat_ref.shape(), (3, 4));
513
514 let row_major: Array2<f64> = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
516 let view = row_major.view();
517 let (mat_ref, transposed) = array_view_to_mat_ref_or_transposed(&view).unwrap();
518 assert!(transposed);
519 assert_eq!(mat_ref.shape(), (4, 3));
521 }
522
523 #[test]
528 fn test_arrayd_to_mat() {
529 let arr = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
530 let mat = arrayd_to_mat(&arr);
531
532 assert_eq!(mat.shape(), (3, 4));
533 for i in 0..3 {
534 for j in 0..4 {
535 assert_eq!(mat[(i, j)], arr[[i, j].as_ref()]);
536 }
537 }
538 }
539
540 #[test]
541 fn test_mat_to_arrayd() {
542 let mat: Mat<f64> = Mat::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
543 let arr = mat_to_arrayd(&mat);
544
545 assert_eq!(arr.ndim(), 2);
546 assert_eq!(arr.shape(), &[2, 3]);
547 assert_eq!(arr[[0, 0].as_ref()], 1.0);
548 assert_eq!(arr[[1, 2].as_ref()], 6.0);
549 }
550
551 #[test]
552 fn test_arrayd_roundtrip() {
553 let original = ArrayD::from_shape_fn(IxDyn(&[5, 7]), |idx| (idx[0] * 7 + idx[1]) as f64);
554 let mat = arrayd_to_mat(&original);
555 let recovered = mat_to_arrayd(&mat);
556
557 assert_eq!(recovered.shape(), original.shape());
558 for i in 0..5 {
559 for j in 0..7 {
560 assert!((original[[i, j].as_ref()] - recovered[[i, j].as_ref()]).abs() < 1e-15);
561 }
562 }
563 }
564
565 #[test]
566 fn test_arrayd_to_array2() {
567 let arr_d = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
568 let arr_2 = arrayd_to_array2(&arr_d);
569
570 assert_eq!(arr_2.dim(), (3, 4));
571 for i in 0..3 {
572 for j in 0..4 {
573 assert_eq!(arr_2[[i, j]], arr_d[[i, j].as_ref()]);
574 }
575 }
576 }
577
578 #[test]
579 fn test_array2_to_arrayd() {
580 let arr_2: Array2<f64> = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
581 let arr_d = array2_to_arrayd(&arr_2);
582
583 assert_eq!(arr_d.ndim(), 2);
584 assert_eq!(arr_d.shape(), &[3, 4]);
585 for i in 0..3 {
586 for j in 0..4 {
587 assert_eq!(arr_d[[i, j].as_ref()], arr_2[[i, j]]);
588 }
589 }
590 }
591
592 #[test]
593 fn test_array_viewd_to_mat_ref() {
594 let arr = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
595 let view = arr.view();
596
597 let result = array_viewd_to_mat_ref(&view);
600 assert!(result.is_none());
602 }
603
604 #[test]
605 fn test_array_viewd_to_mat_ref_or_transposed() {
606 let arr = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
608 let view = arr.view();
609
610 let result = array_viewd_to_mat_ref_or_transposed(&view);
611 assert!(result.is_some());
612 let (mat_ref, transposed) = result.unwrap();
613 assert!(transposed); assert_eq!(mat_ref.shape(), (4, 3)); }
616
617 #[test]
618 #[should_panic(expected = "2-dimensional")]
619 fn test_arrayd_to_mat_wrong_dim() {
620 let arr = ArrayD::from_shape_fn(IxDyn(&[2, 3, 4]), |idx| idx[0] as f64);
621 let _ = arrayd_to_mat(&arr);
622 }
623
624 #[test]
625 fn test_array_viewd_wrong_dim() {
626 let arr = ArrayD::from_shape_fn(IxDyn(&[2, 3, 4]), |idx| idx[0] as f64);
628 let view = arr.view();
629
630 assert!(array_viewd_to_mat_ref(&view).is_none());
632 assert!(array_viewd_to_mat_ref_or_transposed(&view).is_none());
633 }
634}