#[allow(unused_imports)] use crate::array::Array;
#[allow(unused_imports)] use crate::error::{NumRs2Error, Result};
#[allow(unused_imports)] use num_traits::Float;
#[allow(unused_imports)] use std::fmt::Debug;
#[cfg(all(feature = "matrix_decomp", feature = "lapack"))]
pub fn matrix_rank<T: Float + Clone + Debug>(a: &Array<T>, tol: Option<T>) -> Result<usize> {
let shape = a.shape();
if shape.len() != 2 {
return Err(NumRs2Error::DimensionMismatch(
"matrix_rank requires a 2D matrix".to_string(),
));
}
let (_, s, _) = crate::new_modules::matrix_decomp::svd(a)?;
let tol_val = match tol {
Some(t) => t,
None => {
let m = shape[0];
let n = shape[1];
let max_dim = std::cmp::max(m, n);
let eps = T::epsilon();
let s_data = s.to_vec();
let max_s = s_data
.iter()
.fold(T::zero(), |max, &val| if val > max { val } else { max });
T::from(max_dim).unwrap_or_else(|| T::one()) * eps * max_s
}
};
let s_data = s.to_vec();
let rank = s_data.iter().filter(|&&val| val > tol_val).count();
Ok(rank)
}
#[cfg(all(feature = "matrix_decomp", feature = "lapack"))]
pub fn qr<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::SubAssign
+ std::fmt::Display,
>(
a: &Array<T>,
) -> Result<(Array<T>, Array<T>)> {
crate::new_modules::matrix_decomp::qr(a)
}
#[cfg(not(feature = "matrix_decomp"))]
pub fn qr<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::SubAssign
+ std::fmt::Display,
>(
a: &Array<T>,
) -> Result<(Array<T>, Array<T>)> {
a.qr()
}
#[cfg(all(feature = "matrix_decomp", feature = "lapack"))]
pub fn cholesky<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::fmt::Display,
>(
a: &Array<T>,
) -> Result<Array<T>> {
crate::new_modules::matrix_decomp::cholesky(a)
}
#[cfg(not(feature = "matrix_decomp"))]
pub fn cholesky<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::SubAssign
+ std::fmt::Display,
>(
a: &Array<T>,
) -> Result<Array<T>> {
a.cholesky()
}
#[cfg(all(feature = "matrix_decomp", feature = "lapack"))]
pub fn eig<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::SubAssign
+ std::fmt::Display
+ 'static,
>(
a: &Array<T>,
sort: Option<&str>,
) -> Result<(Array<T>, Array<T>)> {
let (eigenvalues, eigenvectors) = a.eig()?;
if sort.is_none() {
return Ok((eigenvalues, eigenvectors));
}
let sort_option = sort.expect("sort option already checked for None");
if sort_option != "asc" && sort_option != "desc" {
return Err(NumRs2Error::InvalidOperation(format!(
"Invalid sort option: {}. Must be 'asc', 'desc', or None",
sort_option
)));
}
let evec_shape = eigenvectors.shape();
let evals_data = eigenvalues.to_vec();
let n = evals_data.len();
let mut indices: Vec<usize> = (0..n).collect();
indices.sort_by(|&i, &j| {
let a_abs = num_traits::Float::abs(evals_data[i]);
let b_abs = num_traits::Float::abs(evals_data[j]);
if sort_option == "asc" {
a_abs
.partial_cmp(&b_abs)
.unwrap_or(std::cmp::Ordering::Equal)
} else {
b_abs
.partial_cmp(&a_abs)
.unwrap_or(std::cmp::Ordering::Equal)
}
});
let mut sorted_evals = Vec::with_capacity(n);
for &idx in &indices {
sorted_evals.push(evals_data[idx]);
}
let evecs_data = eigenvectors.to_vec();
let eigvec_size = evec_shape[0];
let mut sorted_evecs = Vec::with_capacity(evecs_data.len());
for &idx in &indices {
for i in 0..eigvec_size {
let evec_idx = i * n + idx;
sorted_evecs.push(evecs_data[evec_idx]);
}
}
let sorted_eigenvalues = Array::from_vec(sorted_evals);
let sorted_eigenvectors = Array::from_vec(sorted_evecs).reshape(&evec_shape);
Ok((sorted_eigenvalues, sorted_eigenvectors))
}
#[cfg(not(feature = "matrix_decomp"))]
pub fn eig<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::SubAssign
+ std::fmt::Display,
>(
a: &Array<T>,
sort: Option<&str>,
) -> Result<(Array<T>, Array<T>)> {
let (eigenvalues, eigenvectors) = a.eig()?;
if sort.is_none() {
return Ok((eigenvalues, eigenvectors));
}
let sort_option = sort.expect("sort option already checked for None");
if sort_option != "asc" && sort_option != "desc" {
return Err(NumRs2Error::InvalidOperation(format!(
"Invalid sort option: {}. Must be 'asc', 'desc', or None",
sort_option
)));
}
let evec_shape = eigenvectors.shape();
let evals_data = eigenvalues.to_vec();
let n = evals_data.len();
let mut indices: Vec<usize> = (0..n).collect();
indices.sort_by(|&i, &j| {
let a_abs = num_traits::Float::abs(evals_data[i]);
let b_abs = num_traits::Float::abs(evals_data[j]);
if sort_option == "asc" {
a_abs
.partial_cmp(&b_abs)
.unwrap_or(std::cmp::Ordering::Equal)
} else {
b_abs
.partial_cmp(&a_abs)
.unwrap_or(std::cmp::Ordering::Equal)
}
});
let mut sorted_evals = Vec::with_capacity(n);
for &idx in &indices {
sorted_evals.push(evals_data[idx]);
}
let evecs_data = eigenvectors.to_vec();
let eigvec_size = evec_shape[0];
let mut sorted_evecs = Vec::with_capacity(evecs_data.len());
for &idx in &indices {
for i in 0..eigvec_size {
let evec_idx = i * n + idx;
sorted_evecs.push(evecs_data[evec_idx]);
}
}
let sorted_eigenvalues = Array::from_vec(sorted_evals);
let sorted_eigenvectors = Array::from_vec(sorted_evecs).reshape(&evec_shape);
Ok((sorted_eigenvalues, sorted_eigenvectors))
}
#[cfg(all(feature = "matrix_decomp", feature = "lapack"))]
pub fn svd<T: Float + Clone + Debug>(a: &Array<T>) -> Result<(Array<T>, Array<T>, Array<T>)> {
let (u, s_vec, vt) = crate::new_modules::matrix_decomp::svd(a)?;
let m = u.shape()[0];
let n = vt.shape()[0];
let k = s_vec.len();
let mut s = Array::zeros(&[m, n]);
for i in 0..k.min(m).min(n) {
let val = s_vec.get(&[i])?;
if let Some(t_val) = num_traits::NumCast::from(val) {
s.set(&[i, i], t_val)?;
}
}
Ok((u, s, vt))
}
#[cfg(not(feature = "matrix_decomp"))]
pub fn svd<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::SubAssign
+ std::fmt::Display,
>(
a: &Array<T>,
) -> Result<(Array<T>, Array<T>, Array<T>)> {
a.svd()
}
#[cfg(not(feature = "matrix_decomp"))]
pub fn matrix_rank<
T: Float
+ Clone
+ Debug
+ std::ops::AddAssign
+ std::ops::MulAssign
+ std::ops::SubAssign
+ std::fmt::Display,
>(
a: &Array<T>,
tol: Option<T>,
) -> Result<usize> {
let shape = a.shape();
if shape.len() != 2 {
return Err(NumRs2Error::DimensionMismatch(
"matrix_rank requires a 2D matrix".to_string(),
));
}
let (_, s, _) = svd(a)?;
let tol_val = match tol {
Some(t) => t,
None => {
let m = shape[0];
let n = shape[1];
let max_dim = if m > n { m } else { n };
let eps = T::epsilon();
let s_data = s.to_vec();
let max_s = s_data
.iter()
.fold(T::zero(), |max, &val| if val > max { val } else { max });
T::from(max_dim).unwrap_or_else(|| T::one()) * eps * max_s
}
};
let s_data = s.to_vec();
let rank = s_data.iter().filter(|&&val| val > tol_val).count();
Ok(rank)
}