use scirs2_core::ndarray::{Array2, ArrayView2};
use scirs2_core::numeric::{Float, NumAssign, One};
use std::iter::Sum;
use crate::eigen::eig;
use crate::error::{LinalgError, LinalgResult};
use crate::solve::solve_multiple;
use crate::validation::validate_decomposition;
#[allow(dead_code)]
pub fn spectral_radius<F>(a: &ArrayView2<F>, workers: Option<usize>) -> LinalgResult<F>
where
F: Float + NumAssign + Sum + One + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
{
use crate::parallel;
parallel::configure_workers(workers);
validate_decomposition(a, "Spectral radius computation", true)?;
let (eigenvals, _) = eig(a, None)?;
let mut max_abs = F::zero();
for &val in eigenvals.iter() {
let abs_val = (val.re * val.re + val.im * val.im).sqrt();
if abs_val > max_abs {
max_abs = abs_val;
}
}
Ok(max_abs)
}
#[allow(dead_code)]
pub fn spectral_condition_number<F>(a: &ArrayView2<F>, workers: Option<usize>) -> LinalgResult<F>
where
F: Float + NumAssign + Sum + One + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
{
use crate::parallel;
parallel::configure_workers(workers);
validate_decomposition(a, "Spectral condition number computation", true)?;
let (eigenvals, _) = eig(a, None)?;
let mut max_abs = F::zero();
let mut min_abs = F::infinity();
for &val in eigenvals.iter() {
let abs_val = (val.re * val.re + val.im * val.im).sqrt();
if abs_val > max_abs {
max_abs = abs_val;
}
if abs_val < min_abs && abs_val > F::epsilon() {
min_abs = abs_val;
}
}
if min_abs < F::epsilon() {
return Ok(F::infinity());
}
Ok(max_abs / min_abs)
}
#[allow(dead_code)]
pub fn polar_decomposition<F>(a: &ArrayView2<F>, side: &str) -> LinalgResult<(Array2<F>, Array2<F>)>
where
F: Float + NumAssign + Sum + One + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
{
use super::exponential::sqrtm;
validate_decomposition(a, "Polar decomposition", false)?;
let (m, n) = a.dim();
match side {
"right" => {
let mut aha = Array2::<F>::zeros((n, n));
for i in 0..n {
for j in 0..n {
for k in 0..m {
aha[[i, j]] += a[[k, i]] * a[[k, j]]; }
}
}
let p = sqrtm(&aha.view(), 50, F::from(1e-12).expect("Operation failed"))?;
let p_inv = solve_multiple(&p.view(), &Array2::eye(n).view(), None)?;
let mut u = Array2::<F>::zeros((m, n));
for i in 0..m {
for j in 0..n {
for k in 0..n {
u[[i, j]] += a[[i, k]] * p_inv[[k, j]];
}
}
}
Ok((u, p))
}
"left" => {
let mut aah = Array2::<F>::zeros((m, m));
for i in 0..m {
for j in 0..m {
for k in 0..n {
aah[[i, j]] += a[[i, k]] * a[[j, k]]; }
}
}
let p = sqrtm(&aah.view(), 50, F::from(1e-12).expect("Operation failed"))?;
let p_inv = solve_multiple(&p.view(), &Array2::eye(m).view(), None)?;
let mut u = Array2::<F>::zeros((m, n));
for i in 0..m {
for j in 0..n {
for k in 0..m {
u[[i, j]] += p_inv[[i, k]] * a[[k, j]];
}
}
}
Ok((p, u))
}
_ => Err(LinalgError::InvalidInputError(format!(
"Invalid side '{}'. Must be 'left' or 'right'",
side
))),
}
}
#[allow(dead_code)]
pub fn geometric_mean_spd<F>(a: &ArrayView2<F>, b: &ArrayView2<F>, t: F) -> LinalgResult<Array2<F>>
where
F: Float + NumAssign + Sum + One + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
{
use super::exponential::sqrtm;
use super::fractional::spdmatrix_function;
validate_decomposition(a, "Geometric mean computation (matrix A)", true)?;
validate_decomposition(b, "Geometric mean computation (matrix B)", true)?;
if a.dim() != b.dim() {
return Err(LinalgError::ShapeError(
"Matrices must have the same dimensions".to_string(),
));
}
let n = a.nrows();
for i in 0..n {
for j in 0..n {
if (a[[i, j]] - a[[j, i]]).abs() > F::epsilon() {
return Err(LinalgError::InvalidInputError(
"Matrix A must be symmetric".to_string(),
));
}
if (b[[i, j]] - b[[j, i]]).abs() > F::epsilon() {
return Err(LinalgError::InvalidInputError(
"Matrix B must be symmetric".to_string(),
));
}
}
}
let a_half = spdmatrix_function(a, |x| x.sqrt(), true)?;
let a_neg_half = spdmatrix_function(a, |x| F::one() / x.sqrt(), true)?;
let mut temp1 = Array2::<F>::zeros((n, n));
for i in 0..n {
for j in 0..n {
for k in 0..n {
temp1[[i, j]] += a_neg_half[[i, k]] * b[[k, j]];
}
}
}
let mut similarity = Array2::<F>::zeros((n, n));
for i in 0..n {
for j in 0..n {
for k in 0..n {
similarity[[i, j]] += temp1[[i, k]] * a_neg_half[[k, j]];
}
}
}
let powered_similarity = spdmatrix_function(&similarity.view(), |x| x.powf(t), true)?;
let mut temp2 = Array2::<F>::zeros((n, n));
for i in 0..n {
for j in 0..n {
for k in 0..n {
temp2[[i, j]] += a_half[[i, k]] * powered_similarity[[k, j]];
}
}
}
let mut result = Array2::<F>::zeros((n, n));
for i in 0..n {
for j in 0..n {
for k in 0..n {
result[[i, j]] += temp2[[i, k]] * a_half[[k, j]];
}
}
}
Ok(result)
}
#[allow(dead_code)]
pub fn tikhonov_regularization<F>(
a: &ArrayView2<F>,
lambda: F,
identity_like: bool,
) -> LinalgResult<Array2<F>>
where
F: Float + NumAssign + Sum + One + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
{
let (m, n) = a.dim();
if identity_like && m != n {
return Err(LinalgError::ShapeError(
"Matrix must be square for identity-like regularization".to_string(),
));
}
let mut result = a.to_owned();
if identity_like {
for i in 0..n {
result[[i, i]] += lambda;
}
} else {
for i in 0..m {
for j in 0..n {
result[[i, j]] += lambda;
}
}
}
Ok(result)
}
#[allow(dead_code)]
pub fn nuclear_norm<F>(a: &ArrayView2<F>, workers: Option<usize>) -> LinalgResult<F>
where
F: Float + NumAssign + Sum + One + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
{
use crate::parallel;
parallel::configure_workers(workers);
validate_decomposition(a, "Nuclear norm computation", false)?;
let (m, n) = a.dim();
if m == n {
let (eigenvals, _) = eig(a, None)?;
let mut sum = F::zero();
for &val in eigenvals.iter() {
sum += (val.re * val.re + val.im * val.im).sqrt();
}
Ok(sum)
} else {
Err(LinalgError::ImplementationError(
"Nuclear norm for non-square matrices requires SVD (not yet implemented)".to_string(),
))
}
}