use crate::linear_algebra::{Matrix, Vector};
use crate::scalar::Numeric;
use crate::utils::error_codes::CalcError;
#[derive(Debug, Clone, Copy)]
#[must_use]
pub struct Svd<const M: usize, const N: usize, T = f64> {
pub(crate) u: Matrix<M, N, T>,
pub(crate) singular_values: Vector<N, T>,
pub(crate) v: Matrix<N, N, T>,
}
impl<const M: usize, const N: usize, T: Numeric> Matrix<M, N, T> {
pub fn svd(self) -> Result<Svd<M, N, T>, CalcError> {
if M < N {
return Err(CalcError::Underdetermined);
}
for r in 0..M {
for c in 0..N {
if !self[(r, c)].is_finite() {
return Err(CalcError::NonFiniteValue);
}
}
}
let mut u = self;
let mut v = Matrix::<N, N, T>::identity();
let max_sweeps = 60;
for _ in 0..max_sweeps {
let mut off_max = T::ZERO;
for p in 0..N {
for q in (p + 1)..N {
let cp = u.column(p);
let cq = u.column(q);
let alpha = cp.norm_squared();
let beta = cq.norm_squared();
let gamma = cp.dot(cq);
if alpha == T::ZERO || beta == T::ZERO {
continue;
}
let scale = (alpha * beta).sqrt();
let off = gamma.abs() / scale;
if off > off_max {
off_max = off;
}
if gamma.abs() <= T::EPSILON * scale {
continue;
}
let zeta = (beta - alpha) / (T::TWO * gamma);
let sign = if zeta < T::ZERO { -T::ONE } else { T::ONE };
let t = sign / (zeta.abs() + (T::ONE + zeta * zeta).sqrt());
let c = T::ONE / (T::ONE + t * t).sqrt();
let s = c * t;
for i in 0..M {
let up = u[(i, p)];
let uq = u[(i, q)];
u[(i, p)] = c * up - s * uq;
u[(i, q)] = s * up + c * uq;
}
for i in 0..N {
let vp = v[(i, p)];
let vq = v[(i, q)];
v[(i, p)] = c * vp - s * vq;
v[(i, q)] = s * vp + c * vq;
}
}
}
if off_max <= T::EPSILON {
break;
}
}
let mut singular_values = Vector::<N, T>::zeros();
for k in 0..N {
let sigma = u.column(k).norm();
singular_values[k] = sigma;
if sigma > T::ZERO {
for i in 0..M {
u[(i, k)] /= sigma;
}
}
}
for k in 0..N {
let mut top = k;
for j in (k + 1)..N {
if singular_values[j] > singular_values[top] {
top = j;
}
}
if top != k {
let tmp = singular_values[k];
singular_values[k] = singular_values[top];
singular_values[top] = tmp;
for i in 0..M {
let tmp = u[(i, k)];
u[(i, k)] = u[(i, top)];
u[(i, top)] = tmp;
}
for i in 0..N {
let tmp = v[(i, k)];
v[(i, k)] = v[(i, top)];
v[(i, top)] = tmp;
}
}
}
for k in 0..N {
let mut row = 0;
let mut best = T::ZERO;
for i in 0..M {
let mag = u[(i, k)].abs();
if mag > best {
best = mag;
row = i;
}
}
if u[(row, k)] < T::ZERO {
for i in 0..M {
u[(i, k)] = -u[(i, k)];
}
for i in 0..N {
v[(i, k)] = -v[(i, k)];
}
}
}
Ok(Svd {
u,
singular_values,
v,
})
}
pub fn pseudo_inverse(self) -> Result<Matrix<N, M, T>, CalcError> {
if M >= N {
Ok(self.svd()?.pseudo_inverse())
} else {
Ok(self.transpose().svd()?.pseudo_inverse().transpose())
}
}
}
impl<const M: usize, const N: usize, T: Numeric> Svd<M, N, T> {
pub fn singular_values(&self) -> Vector<N, T> {
self.singular_values
}
pub fn u(&self) -> Matrix<M, N, T> {
self.u
}
pub fn v(&self) -> Matrix<N, N, T> {
self.v
}
#[inline]
#[must_use]
pub fn rank(&self, tol: T) -> usize {
let mut count = 0;
for k in 0..N {
if self.singular_values[k] > tol {
count += 1;
}
}
count
}
#[inline]
#[must_use]
pub fn condition_number(&self) -> T {
if N == 0 {
return T::INFINITY;
}
let smallest = self.singular_values[N - 1];
if smallest <= T::ZERO {
T::INFINITY
} else {
self.singular_values[0] / smallest
}
}
fn default_tol(&self) -> T {
if N == 0 {
return T::ZERO;
}
T::from_usize(M.max(N)) * T::EPSILON * self.singular_values[0]
}
pub fn pseudo_inverse_tol(&self, tol: T) -> Matrix<N, M, T> {
Matrix::from_fn(|i, j| {
let mut acc = T::ZERO;
for k in 0..N {
let sigma = self.singular_values[k];
if sigma > tol {
acc += self.v[(i, k)] * self.u[(j, k)] / sigma;
}
}
acc
})
}
pub fn pseudo_inverse(&self) -> Matrix<N, M, T> {
self.pseudo_inverse_tol(self.default_tol())
}
pub fn solve(&self, b: Vector<M, T>) -> Vector<N, T> {
let tol = self.default_tol();
let mut z = Vector::<N, T>::zeros();
for k in 0..N {
let sigma = self.singular_values[k];
if sigma > tol {
z[k] = self.u.column(k).dot(b) / sigma;
}
}
self.v * z
}
}