mod bidiagonal;
mod dc;
mod dqds;
mod qr;
mod secular;
mod small;
use super::properties::range_exponent;
use super::{Matrix, Scalar};
use crate::math::complex::Float;
use bidiagonal::{apply_adjoint_right, reduce, triangularize};
#[derive(Clone, Debug)]
pub struct Svd<T: Scalar> {
u: Matrix<T>,
s: Vec<T::Real>,
v: Matrix<T>,
}
impl<T: Scalar> Svd<T> {
pub fn u(&self) -> Matrix<T> {
self.u.clone()
}
pub fn singular_values(&self) -> &[T::Real] {
&self.s
}
pub fn v(&self) -> Matrix<T> {
self.v.clone()
}
}
impl<T: Scalar> Matrix<T> {
pub fn svd(&self) -> Svd<T> {
let (s, uv) = svd(self, true);
let (u, v) = uv.unwrap_or_default();
Svd { u, s, v }
}
pub fn singular_values(&self) -> Vec<T::Real> {
svd(self, false).0
}
}
#[allow(clippy::type_complexity)]
pub(crate) fn svd<T: Scalar>(
a: &Matrix<T>,
want_uv: bool,
) -> (Vec<T::Real>, Option<(Matrix<T>, Matrix<T>)>) {
let (mut s, uv, k) = svd_scaled(a, want_uv);
if k != 0 {
let down = T::Real::TWO.powi(-k);
for x in &mut s {
*x *= down;
}
}
(s, uv)
}
#[allow(clippy::type_complexity)]
pub(crate) fn svd_scaled<T: Scalar>(
a: &Matrix<T>,
want_uv: bool,
) -> (Vec<T::Real>, Option<(Matrix<T>, Matrix<T>)>, i32) {
let (m, n) = a.shape();
let wide = m < n;
let (rows, cols) = if wide { (n, m) } else { (m, n) };
let swap = |uv: Option<(Matrix<T>, Matrix<T>)>| {
if wide { uv.map(|(u, v)| (v, u)) } else { uv }
};
if a.data.iter().any(|x| !x.is_finite()) {
let nan = T::from_parts(T::Real::NAN, T::Real::NAN);
let uv = want_uv.then(|| {
(
Matrix::from_fn(rows, cols, |_, _| nan),
Matrix::from_fn(cols, cols, |_, _| nan),
)
});
return (vec![T::Real::NAN; cols], swap(uv), 0);
}
let k = range_exponent(&a.data);
let mut x = if wide {
adjoint(&a.data, m, n).data
} else {
a.data.clone()
};
if k != 0 {
let up = T::Real::TWO.powi(k);
for x in &mut x {
*x = x.scale(up);
}
}
let (s, uv) = decompose(x, rows, cols, want_uv);
(s, swap(uv), k)
}
#[allow(clippy::type_complexity)]
fn decompose<T: Scalar>(
mut a: Vec<T>,
m: usize,
n: usize,
want_uv: bool,
) -> (Vec<T::Real>, Option<(Matrix<T>, Matrix<T>)>) {
if n > 0
&& if want_uv {
2 * m >= 5 * n
} else {
4 * m >= 7 * n
}
{
let taus = triangularize(&mut a, m, n);
let mut r = vec![T::zero(); n * n];
for j in 0..n {
r[j * n..j * n + j + 1].copy_from_slice(&a[j * m..j * m + j + 1]);
}
let (s, uv) = decompose(r, n, n, want_uv);
let uv = uv.map(|(ur, v)| {
let mut x = vec![T::zero(); n * m];
for j in 0..n {
for (i, x) in x[j * n..(j + 1) * n].iter_mut().enumerate() {
*x = ur.data[j + i * n].conj();
}
}
apply_adjoint_right(&a, m, m, &taus, &mut x, n);
(adjoint(&x, n, m), v)
});
return (s, uv);
}
if n <= small::largest::<T>(want_uv) {
return small::decompose(a, m, n, want_uv);
}
let (mut d, mut e, reduced) = reduce(a, m, n);
if !want_uv {
if !dqds::singular_values(&mut d, &e) {
e.push(T::Real::ZERO);
qr::bidiagonal_qr::<_, qr::Columns<_>, qr::Columns<_>>(&mut d, &mut e, None, None);
}
return (d, None);
}
let mut ub = vec![T::Real::ZERO; n * n];
let mut vb = vec![T::Real::ZERO; n * n];
dc::bidiagonal_svd(&mut d, &mut e, &mut ub, &mut vb, T::gemm_real);
let mut x = vec![T::zero(); n * m];
let mut y = vec![T::zero(); n * n];
for j in 0..n {
for i in 0..n {
x[i + j * n] = T::from_real(ub[j + i * n]);
y[i + j * n] = T::from_real(vb[j + i * n]);
}
}
reduced.apply_q_right(&mut x, n);
reduced.apply_p_right(&mut y, n);
(d, Some((adjoint(&x, n, m), adjoint(&y, n, n))))
}
fn adjoint<T: Scalar>(x: &[T], rows: usize, cols: usize) -> Matrix<T> {
const TILE: usize = 16;
let mut a = Matrix::zeros(cols, rows);
for j0 in (0..rows).step_by(TILE) {
for i0 in (0..cols).step_by(TILE) {
for j in j0..(j0 + TILE).min(rows) {
for i in i0..(i0 + TILE).min(cols) {
a.data[i + j * cols] = x[j + i * rows].conj();
}
}
}
}
a
}