use faer::{Mat, MatRef, concat};
use crate::prelude::*;
use crate::utils::vec_utils::flatten_vector;
pub fn nested_vector_to_faer_mat<T>(nested_vec: Vec<Vec<T>>, col_wise: bool) -> Mat<T>
where
T: BixverseFloat,
{
let (nrow, ncol) = if col_wise {
(nested_vec[0].len(), nested_vec.len())
} else {
(nested_vec.len(), nested_vec[0].len())
};
let data = flatten_vector(nested_vec);
if col_wise {
Mat::from_fn(nrow, ncol, |i, j| data[i + j * nrow])
} else {
Mat::from_fn(nrow, ncol, |i, j| data[j + i * ncol])
}
}
pub fn faer_diagonal_from_vec<T>(vec: Vec<T>) -> Mat<T>
where
T: BixverseFloat,
{
let len = vec.len();
Mat::from_fn(
len,
len,
|row, col| if row == col { vec[row] } else { T::zero() },
)
}
pub fn upper_triangle_indices(n_dim: usize, offset: usize) -> (Vec<usize>, Vec<usize>) {
if offset >= n_dim {
return (Vec::new(), Vec::new());
}
assert!(offset <= 1, "The offset should be 0 or 1");
let total_elements: usize = (0..n_dim)
.map(|row| n_dim.saturating_sub(row + offset))
.sum();
let mut row_indices = Vec::with_capacity(total_elements);
let mut col_indices = Vec::with_capacity(total_elements);
for row in 0..n_dim {
let start_col = row + offset;
if start_col < n_dim {
let end_col = n_dim;
let elements_in_row = end_col - start_col;
row_indices.extend(std::iter::repeat_n(row, elements_in_row));
col_indices.extend(start_col..end_col);
}
}
(row_indices, col_indices)
}
pub fn upper_triangle_to_sym_faer<T>(data: &[T], shift: usize, n: usize) -> faer::Mat<T>
where
T: BixverseFloat,
{
let mut mat = Mat::<T>::zeros(n, n);
let mut idx = 0;
for i in 0..n {
for j in i..n {
if shift == 1 && i == j {
mat[(i, j)] = T::one();
} else {
mat[(i, j)] = data[idx];
mat[(j, i)] = data[idx];
idx += 1;
}
}
}
mat
}
pub fn faer_mat_to_upper_triangle<T>(x: MatRef<T>, shift: usize) -> Vec<T>
where
T: BixverseFloat,
{
assert!(shift <= 1, "The shift should be 0 or 1");
let n = x.ncols();
let total_elements = if shift == 0 {
n * (n + 1) / 2
} else {
n * (n - 1) / 2
};
let mut vals = Vec::with_capacity(total_elements);
for i in 0..n {
let start_j = i + shift;
for j in start_j..n {
vals.push(*x.get(i, j));
}
}
vals
}
pub fn mat_rm_row<T>(x: MatRef<T>, idx_to_remove: usize) -> Mat<T>
where
T: BixverseFloat,
{
assert!(
idx_to_remove <= x.nrows(),
"The specified index is larger than the matrix"
);
let total_rows = x.nrows();
if idx_to_remove == 0 {
x.subrows(1, total_rows - 1).to_owned()
} else if idx_to_remove == total_rows - 1 {
x.subrows(0, total_rows - 1).to_owned()
} else {
let upper = x.subrows(0, idx_to_remove);
let lower = x.subrows(idx_to_remove + 1, total_rows - idx_to_remove - 1);
concat![[upper], [lower]]
}
}
#[allow(dead_code)]
pub fn rowbind_matrices<T>(matrices: Vec<Mat<T>>) -> Mat<T>
where
T: BixverseFloat,
{
let ncols = matrices[0].ncols();
let total_row = matrices.iter().map(|m| m.nrows()).sum();
let mut result: Mat<T> = Mat::zeros(total_row, ncols);
let mut row_offset = 0;
for matrix in matrices {
assert_eq!(
matrix.ncols(),
ncols,
"All matrices must have the same number of columns"
);
let nrows = matrix.nrows();
for i in 0..nrows {
for j in 0..ncols {
result[(row_offset + i, j)] = matrix[(i, j)]
}
}
row_offset += nrows;
}
result
}
pub fn colbind_matrices<T>(matrices: &[Mat<T>]) -> Mat<T>
where
T: BixverseFloat,
{
let nrows = matrices[0].nrows();
let total_col = matrices.iter().map(|m| m.ncols()).sum();
let mut result: Mat<T> = Mat::zeros(nrows, total_col);
let mut col_offset = 0;
for matrix in matrices {
assert_eq!(
matrix.nrows(),
nrows,
"All matrices must have the same number of columns"
);
let ncols = matrix.ncols();
for i in 0..nrows {
for j in 0..ncols {
result[(i, col_offset + j)] = matrix[(i, j)]
}
}
col_offset += ncols;
}
result
}
pub fn mat_to_flat_row_major<T>(mat: MatRef<T>) -> Vec<T>
where
T: BixverseFloat,
{
let n = mat.nrows();
let d = mat.ncols();
let mut flat = Vec::with_capacity(n * d);
for row in 0..n {
for col in 0..d {
flat.push(mat[(row, col)]);
}
}
flat
}
pub fn flat_row_major_to_mat<T>(flat: &[T], nrows: usize, ncols: usize) -> Mat<T>
where
T: BixverseFloat,
{
assert_eq!(
flat.len(),
nrows * ncols,
"Flat data size doesn't match dimensions"
);
Mat::from_fn(nrows, ncols, |i, j| flat[i * ncols + j])
}
#[cfg(test)]
mod tests_matrix_utils {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_mat_to_flat_row_major() {
let mat = Mat::from_fn(2, 3, |i, j| (i * 3 + j) as f32);
let flat = mat_to_flat_row_major(mat.as_ref());
assert_eq!(flat, vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
}
#[test]
fn test_flat_row_major_to_mat() {
let flat = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0];
let mat = flat_row_major_to_mat(&flat, 2, 3);
assert_eq!(mat.nrows(), 2);
assert_eq!(mat.ncols(), 3);
for i in 0..2 {
for j in 0..3 {
assert_eq!(mat[(i, j)], (i * 3 + j) as f32);
}
}
}
#[test]
fn test_round_trip() {
let original = Mat::from_fn(3, 4, |i, j| (i as f32) * 0.5 + (j as f32) * 0.1);
let flat = mat_to_flat_row_major(original.as_ref());
let restored = flat_row_major_to_mat(&flat, 3, 4);
for i in 0..3 {
for j in 0..4 {
assert_relative_eq!(original[(i, j)], restored[(i, j)], epsilon = 1e-7);
}
}
}
}