use ndarray::{
Array1, Array2, ArrayD, ArrayView1, ArrayView2, ArrayViewD, ArrayViewMut1, ArrayViewMut2,
ArrayViewMutD, IxDyn, ShapeBuilder,
};
use oxiblas_core::scalar::Field;
use oxiblas_matrix::{Mat, MatMut, MatRef};
pub fn array2_to_mat<T: Field + Clone>(arr: &Array2<T>) -> Mat<T>
where
T: bytemuck::Zeroable,
{
let (nrows, ncols) = arr.dim();
let strides = arr.strides();
if strides[0] == 1 && strides[1] == nrows as isize {
let mut mat = Mat::zeros(nrows, ncols);
for i in 0..nrows {
for j in 0..ncols {
mat[(i, j)] = arr[[i, j]];
}
}
mat
} else {
let mut mat = Mat::zeros(nrows, ncols);
for i in 0..nrows {
for j in 0..ncols {
mat[(i, j)] = arr[[i, j]];
}
}
mat
}
}
pub fn array2_into_mat<T: Field + Clone>(arr: Array2<T>) -> Mat<T>
where
T: bytemuck::Zeroable,
{
array2_to_mat(&arr)
}
pub fn mat_to_array2<T: Field + Clone>(mat: &Mat<T>) -> Array2<T> {
let (nrows, ncols) = mat.shape();
Array2::from_shape_fn((nrows, ncols).f(), |(i, j)| mat[(i, j)])
}
pub fn mat_ref_to_array2<T: Field + Clone>(mat: MatRef<'_, T>) -> Array2<T> {
let (nrows, ncols) = (mat.nrows(), mat.ncols());
Array2::from_shape_fn((nrows, ncols).f(), |(i, j)| mat[(i, j)])
}
pub fn mat_to_array2_c<T: Field + Clone>(mat: &Mat<T>) -> Array2<T> {
let (nrows, ncols) = mat.shape();
Array2::from_shape_fn((nrows, ncols), |(i, j)| mat[(i, j)])
}
pub fn arrayd_to_mat<T: Field + Clone>(arr: &ArrayD<T>) -> Mat<T>
where
T: bytemuck::Zeroable,
{
assert_eq!(
arr.ndim(),
2,
"ArrayD must be 2-dimensional for matrix conversion"
);
let shape = arr.shape();
let nrows = shape[0];
let ncols = shape[1];
let mut mat = Mat::zeros(nrows, ncols);
for i in 0..nrows {
for j in 0..ncols {
mat[(i, j)] = arr[[i, j].as_ref()];
}
}
mat
}
pub fn arrayd_into_mat<T: Field + Clone>(arr: ArrayD<T>) -> Mat<T>
where
T: bytemuck::Zeroable,
{
arrayd_to_mat(&arr)
}
pub fn mat_to_arrayd<T: Field + Clone>(mat: &Mat<T>) -> ArrayD<T> {
let (nrows, ncols) = mat.shape();
let mut arr = ArrayD::from_elem(IxDyn(&[nrows, ncols]), T::zero());
for i in 0..nrows {
for j in 0..ncols {
arr[[i, j].as_ref()] = mat[(i, j)];
}
}
arr
}
pub fn mat_ref_to_arrayd<T: Field + Clone>(mat: MatRef<'_, T>) -> ArrayD<T> {
let (nrows, ncols) = (mat.nrows(), mat.ncols());
let mut arr = ArrayD::from_elem(IxDyn(&[nrows, ncols]), T::zero());
for i in 0..nrows {
for j in 0..ncols {
arr[[i, j].as_ref()] = mat[(i, j)];
}
}
arr
}
pub fn arrayd_to_array2<T: Clone>(arr: &ArrayD<T>) -> Array2<T> {
assert_eq!(arr.ndim(), 2, "ArrayD must be 2-dimensional");
let shape = arr.shape();
Array2::from_shape_fn((shape[0], shape[1]), |(i, j)| arr[[i, j].as_ref()].clone())
}
pub fn array2_to_arrayd<T: Clone>(arr: &Array2<T>) -> ArrayD<T> {
let (nrows, ncols) = arr.dim();
let mut result = ArrayD::from_elem(IxDyn(&[nrows, ncols]), arr[[0, 0]].clone());
for i in 0..nrows {
for j in 0..ncols {
result[[i, j].as_ref()] = arr[[i, j]].clone();
}
}
result
}
pub fn array_viewd_to_mat_ref<'a, T: Field>(arr: &'a ArrayViewD<'a, T>) -> Option<MatRef<'a, T>> {
if arr.ndim() != 2 {
return None;
}
let shape = arr.shape();
let nrows = shape[0];
let ncols = shape[1];
let strides = arr.strides();
if strides[0] == 1 {
let col_stride = strides[1] as usize;
let ptr = arr.as_ptr();
Some(MatRef::new(ptr, nrows, ncols, col_stride))
} else {
None
}
}
pub fn array_viewd_to_mat_ref_or_transposed<'a, T: Field>(
arr: &'a ArrayViewD<'a, T>,
) -> Option<(MatRef<'a, T>, bool)> {
if arr.ndim() != 2 {
return None;
}
let shape = arr.shape();
let nrows = shape[0];
let ncols = shape[1];
let strides = arr.strides();
if strides[0] == 1 {
let col_stride = strides[1] as usize;
let ptr = arr.as_ptr();
Some((MatRef::new(ptr, nrows, ncols, col_stride), false))
} else if strides[1] == 1 {
let row_stride = strides[0] as usize;
let ptr = arr.as_ptr();
Some((MatRef::new(ptr, ncols, nrows, row_stride), true))
} else {
None
}
}
pub fn array_view_mutd_to_mat_mut<'a, T: Field>(
arr: &'a mut ArrayViewMutD<'a, T>,
) -> Option<MatMut<'a, T>> {
if arr.ndim() != 2 {
return None;
}
let shape = arr.shape();
let nrows = shape[0];
let ncols = shape[1];
let strides = arr.strides();
if strides[0] == 1 {
let col_stride = strides[1] as usize;
let ptr = arr.as_mut_ptr();
Some(MatMut::new(ptr, nrows, ncols, col_stride))
} else {
None
}
}
pub fn array_view_to_mat_ref<'a, T: Field>(arr: &'a ArrayView2<'a, T>) -> Option<MatRef<'a, T>> {
let (nrows, ncols) = arr.dim();
let strides = arr.strides();
if strides[0] == 1 {
let col_stride = strides[1] as usize;
let ptr = arr.as_ptr();
Some(MatRef::new(ptr, nrows, ncols, col_stride))
} else {
None
}
}
pub fn array_view_to_mat_ref_or_transposed<'a, T: Field>(
arr: &'a ArrayView2<'a, T>,
) -> Option<(MatRef<'a, T>, bool)> {
let (nrows, ncols) = arr.dim();
let strides = arr.strides();
if strides[0] == 1 {
let col_stride = strides[1] as usize;
let ptr = arr.as_ptr();
Some((MatRef::new(ptr, nrows, ncols, col_stride), false))
} else if strides[1] == 1 {
let row_stride = strides[0] as usize;
let ptr = arr.as_ptr();
Some((MatRef::new(ptr, ncols, nrows, row_stride), true))
} else {
None
}
}
pub fn array_view_mut_to_mat_mut<'a, T: Field>(
arr: &'a mut ArrayViewMut2<'a, T>,
) -> Option<MatMut<'a, T>> {
let (nrows, ncols) = arr.dim();
let strides = arr.strides();
if strides[0] == 1 {
let col_stride = strides[1] as usize;
let ptr = arr.as_mut_ptr();
Some(MatMut::new(ptr, nrows, ncols, col_stride))
} else {
None
}
}
pub fn array1_to_vec<T: Clone>(arr: &Array1<T>) -> Vec<T> {
arr.iter().cloned().collect()
}
pub fn slice_to_array1<T: Clone>(slice: &[T]) -> Array1<T> {
Array1::from_vec(slice.to_vec())
}
pub fn array_view1_as_slice<'a, T>(arr: &'a ArrayView1<'a, T>) -> Option<&'a [T]> {
arr.as_slice()
}
pub fn array_view1_as_slice_mut<'a, T>(arr: &'a mut ArrayViewMut1<'a, T>) -> Option<&'a mut [T]> {
arr.as_slice_mut()
}
pub fn zeros_f<T: Clone + Default>(nrows: usize, ncols: usize) -> Array2<T> {
Array2::from_shape_fn((nrows, ncols).f(), |_| T::default())
}
pub fn filled_f<T: Clone>(nrows: usize, ncols: usize, value: T) -> Array2<T> {
Array2::from_shape_fn((nrows, ncols).f(), |_| value.clone())
}
pub fn is_column_major<T>(arr: &Array2<T>) -> bool {
let strides = arr.strides();
let (nrows, _) = arr.dim();
strides[0] == 1 && strides[1] == nrows as isize
}
pub fn is_row_major<T>(arr: &Array2<T>) -> bool {
let strides = arr.strides();
let (_, ncols) = arr.dim();
strides[0] == ncols as isize && strides[1] == 1
}
pub fn to_column_major<T: Clone + Default>(arr: &Array2<T>) -> Array2<T> {
let (nrows, ncols) = arr.dim();
let mut result = zeros_f(nrows, ncols);
for i in 0..nrows {
for j in 0..ncols {
result[[i, j]] = arr[[i, j]].clone();
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::Array2;
#[test]
fn test_array2_to_mat_rowmajor() {
let arr = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
let mat = array2_to_mat(&arr);
assert_eq!(mat.shape(), (3, 4));
for i in 0..3 {
for j in 0..4 {
assert_eq!(mat[(i, j)], arr[[i, j]]);
}
}
}
#[test]
fn test_array2_to_mat_colmajor() {
let arr: Array2<f64> = Array2::from_shape_fn((3, 4).f(), |(i, j)| (i * 4 + j) as f64);
assert!(is_column_major(&arr));
let mat = array2_to_mat(&arr);
assert_eq!(mat.shape(), (3, 4));
for i in 0..3 {
for j in 0..4 {
assert_eq!(mat[(i, j)], arr[[i, j]]);
}
}
}
#[test]
fn test_mat_to_array2() {
let mat: Mat<f64> = Mat::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
let arr = mat_to_array2(&mat);
assert_eq!(arr.dim(), (2, 3));
assert_eq!(arr[[0, 0]], 1.0);
assert_eq!(arr[[1, 2]], 6.0);
}
#[test]
fn test_roundtrip() {
let original = Array2::from_shape_fn((5, 7), |(i, j)| (i * 7 + j) as f64);
let mat = array2_to_mat(&original);
let recovered = mat_to_array2(&mat);
for i in 0..5 {
for j in 0..7 {
assert!((original[[i, j]] - recovered[[i, j]]).abs() < 1e-15);
}
}
}
#[test]
fn test_is_column_major() {
let col_major: Array2<f64> = Array2::zeros((3, 4).f());
let row_major: Array2<f64> = Array2::zeros((3, 4));
assert!(is_column_major(&col_major));
assert!(!is_column_major(&row_major));
assert!(is_row_major(&row_major));
assert!(!is_row_major(&col_major));
}
#[test]
fn test_to_column_major() {
let row_major = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
let col_major = to_column_major(&row_major);
assert!(is_column_major(&col_major));
for i in 0..3 {
for j in 0..4 {
assert_eq!(row_major[[i, j]], col_major[[i, j]]);
}
}
}
#[test]
fn test_array_view_to_mat_ref_or_transposed() {
let col_major: Array2<f64> = Array2::from_shape_fn((3, 4).f(), |(i, j)| (i * 4 + j) as f64);
let view = col_major.view();
let (mat_ref, transposed) = array_view_to_mat_ref_or_transposed(&view).unwrap();
assert!(!transposed);
assert_eq!(mat_ref.shape(), (3, 4));
let row_major: Array2<f64> = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
let view = row_major.view();
let (mat_ref, transposed) = array_view_to_mat_ref_or_transposed(&view).unwrap();
assert!(transposed);
assert_eq!(mat_ref.shape(), (4, 3));
}
#[test]
fn test_arrayd_to_mat() {
let arr = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
let mat = arrayd_to_mat(&arr);
assert_eq!(mat.shape(), (3, 4));
for i in 0..3 {
for j in 0..4 {
assert_eq!(mat[(i, j)], arr[[i, j].as_ref()]);
}
}
}
#[test]
fn test_mat_to_arrayd() {
let mat: Mat<f64> = Mat::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
let arr = mat_to_arrayd(&mat);
assert_eq!(arr.ndim(), 2);
assert_eq!(arr.shape(), &[2, 3]);
assert_eq!(arr[[0, 0].as_ref()], 1.0);
assert_eq!(arr[[1, 2].as_ref()], 6.0);
}
#[test]
fn test_arrayd_roundtrip() {
let original = ArrayD::from_shape_fn(IxDyn(&[5, 7]), |idx| (idx[0] * 7 + idx[1]) as f64);
let mat = arrayd_to_mat(&original);
let recovered = mat_to_arrayd(&mat);
assert_eq!(recovered.shape(), original.shape());
for i in 0..5 {
for j in 0..7 {
assert!((original[[i, j].as_ref()] - recovered[[i, j].as_ref()]).abs() < 1e-15);
}
}
}
#[test]
fn test_arrayd_to_array2() {
let arr_d = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
let arr_2 = arrayd_to_array2(&arr_d);
assert_eq!(arr_2.dim(), (3, 4));
for i in 0..3 {
for j in 0..4 {
assert_eq!(arr_2[[i, j]], arr_d[[i, j].as_ref()]);
}
}
}
#[test]
fn test_array2_to_arrayd() {
let arr_2: Array2<f64> = Array2::from_shape_fn((3, 4), |(i, j)| (i * 4 + j) as f64);
let arr_d = array2_to_arrayd(&arr_2);
assert_eq!(arr_d.ndim(), 2);
assert_eq!(arr_d.shape(), &[3, 4]);
for i in 0..3 {
for j in 0..4 {
assert_eq!(arr_d[[i, j].as_ref()], arr_2[[i, j]]);
}
}
}
#[test]
fn test_array_viewd_to_mat_ref() {
let arr = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
let view = arr.view();
let result = array_viewd_to_mat_ref(&view);
assert!(result.is_none());
}
#[test]
fn test_array_viewd_to_mat_ref_or_transposed() {
let arr = ArrayD::from_shape_fn(IxDyn(&[3, 4]), |idx| (idx[0] * 4 + idx[1]) as f64);
let view = arr.view();
let result = array_viewd_to_mat_ref_or_transposed(&view);
assert!(result.is_some());
let (mat_ref, transposed) = result.unwrap();
assert!(transposed); assert_eq!(mat_ref.shape(), (4, 3)); }
#[test]
#[should_panic(expected = "2-dimensional")]
fn test_arrayd_to_mat_wrong_dim() {
let arr = ArrayD::from_shape_fn(IxDyn(&[2, 3, 4]), |idx| idx[0] as f64);
let _ = arrayd_to_mat(&arr);
}
#[test]
fn test_array_viewd_wrong_dim() {
let arr = ArrayD::from_shape_fn(IxDyn(&[2, 3, 4]), |idx| idx[0] as f64);
let view = arr.view();
assert!(array_viewd_to_mat_ref(&view).is_none());
assert!(array_viewd_to_mat_ref_or_transposed(&view).is_none());
}
}