use matrix::{Matrix, BaseMatrix, BaseMatrixMut, MatrixSlice, MatrixSliceMut};
use error::{Error, ErrorKind};
use std::any::Any;
use std::cmp;
use libnum::{Float, Signed};
fn correct_svd_signs<T>(mut b: Matrix<T>,
mut u: Matrix<T>,
mut v: Matrix<T>)
-> (Matrix<T>, Matrix<T>, Matrix<T>)
where T: Any + Float + Signed
{
{
let ref mut shortest_matrix = if u.rows() <= v.rows() { &mut u } else { &mut v };
let column_length = shortest_matrix.rows();
let num_singular_values = cmp::min(b.rows(), b.cols());
for i in 0..num_singular_values {
if b[[i, i]] < T::zero() {
b[[i, i]] = b[[i, i]].abs();
let mut column = shortest_matrix.sub_slice_mut([0, i], column_length, 1);
column *= -T::one();
}
}
}
(b, u, v)
}
fn sort_svd<T>(mut b: Matrix<T>,
mut u: Matrix<T>,
mut v: Matrix<T>)
-> (Matrix<T>, Matrix<T>, Matrix<T>)
where T: Any + Float + Signed
{
assert!(u.cols() == b.cols() && b.cols() == v.cols());
let mut indexed_sorted_values: Vec<_> = b.diag().cloned().enumerate().collect();
indexed_sorted_values.sort_by(|&(_, ref x), &(_, ref y)| {
x.partial_cmp(y)
.expect("All singular values should be finite, and thus sortable.")
.reverse()
});
for (i, &(_, value)) in indexed_sorted_values.iter().enumerate() {
b[[i, i]] = value;
}
let swappable_pairs = indexed_sorted_values.into_iter()
.enumerate()
.map(|(new_index, (old_index, _))| (old_index, new_index))
.filter(|&(old_index, new_index)| old_index < new_index);
for (old_index, new_index) in swappable_pairs {
u.swap_cols(old_index, new_index);
v.swap_cols(old_index, new_index);
}
(b, u, v)
}
impl<T: Any + Float + Signed> Matrix<T> {
pub fn svd(self) -> Result<(Matrix<T>, Matrix<T>, Matrix<T>), Error> {
let (b, u, v) = try!(self.svd_unordered());
Ok(sort_svd(b, u, v))
}
fn svd_unordered(self) -> Result<(Matrix<T>, Matrix<T>, Matrix<T>), Error> {
let (b, u, v) = try!(self.svd_golub_reinsch());
Ok(correct_svd_signs(b, u, v))
}
fn svd_golub_reinsch(mut self) -> Result<(Matrix<T>, Matrix<T>, Matrix<T>), Error> {
let mut flipped = false;
if self.cols > self.rows {
self = self.transpose();
flipped = true;
}
let eps = T::from(3.0).unwrap() * T::epsilon();
let n = self.cols;
let (mut b, mut u, mut v) = try!(self.bidiagonal_decomp()
.map_err(|_| Error::new(ErrorKind::DecompFailure, "Could not compute SVD.")));
loop {
let mut q = 0;
let mut on_lower = true;
let mut p = 0;
let mut on_middle = false;
for i in (0..n - 1).rev() {
let (b_ii, b_sup_diag, diag_abs_sum): (T, T, T);
unsafe {
b_ii = *b.get_unchecked([i, i]);
b_sup_diag = b.get_unchecked([i, i + 1]).abs();
diag_abs_sum = eps * (b_ii.abs() + b.get_unchecked([i + 1, i + 1]).abs());
}
if b_sup_diag <= diag_abs_sum {
if on_lower {
q += 1;
} else if on_middle {
on_middle = false;
p = i + 1;
}
unsafe {
*b.get_unchecked_mut([i, i + 1]) = T::zero();
}
} else {
if on_lower {
on_middle = true;
on_lower = false;
}
}
}
if q == n - 1 {
break;
}
for i in p..n - q - 1 {
let (b_ii, b_sup_diag): (T, T);
unsafe {
b_ii = *b.get_unchecked([i, i]);
b_sup_diag = *b.get_unchecked([i, i + 1]);
}
if b_ii.abs() < eps {
let (c, s) = Matrix::<T>::givens_rot(b_ii, b_sup_diag);
let givens = Matrix::new(2, 2, vec![c, s, -s, c]);
let b_i = MatrixSliceMut::from_matrix(&mut b, [i, i], 1, 2);
let zerod_line = &b_i * givens;
b_i.set_to(zerod_line.as_slice());
}
}
unsafe {
try!(Matrix::<T>::golub_kahan_svd_step(&mut b, &mut u, &mut v, p, q)
.map_err(|_| Error::new(ErrorKind::DecompFailure, "Could not compute SVD.")));
}
}
if flipped {
Ok((b.transpose(), v, u))
} else {
Ok((b, u, v))
}
}
unsafe fn golub_kahan_svd_step(b: &mut Matrix<T>,
u: &mut Matrix<T>,
v: &mut Matrix<T>,
p: usize,
q: usize)
-> Result<(), Error> {
let n = b.rows();
let c: Matrix<T>;
{
let y = MatrixSlice::from_matrix(&b, [n - q - 2, n - q - 2], 2, 2).into_matrix();
if n - q - p - 2 > 0 {
let x = MatrixSlice::from_matrix(&b, [p, n - q - 2], n - q - p - 2, 2);
c = x.into_matrix().transpose() * x + y.transpose() * y;
} else {
c = y.transpose() * y;
}
}
let c_eigs = try!(c.clone().eigenvalues());
let lambda: T;
if (c_eigs[0] - *c.get_unchecked([1, 1])).abs() <
(c_eigs[1] - *c.get_unchecked([1, 1])).abs() {
lambda = c_eigs[0];
} else {
lambda = c_eigs[1];
}
let b_pp = *b.get_unchecked([p, p]);
let mut alpha = (b_pp * b_pp) - lambda;
let mut beta = b_pp * *b.get_unchecked([p, p + 1]);
for k in p..n - q - 1 {
let (c, s) = Matrix::<T>::givens_rot(alpha, beta);
let givens_mat = Matrix::new(2, 2, vec![c, s, -s, c]);
{
let b_block = MatrixSliceMut::from_matrix(b,
[k.saturating_sub(1), k],
cmp::min(3, n - k.saturating_sub(1)),
2);
let transformed = &b_block * &givens_mat;
b_block.set_to(transformed.as_slice());
let v_block = MatrixSliceMut::from_matrix(v, [0, k], n, 2);
let transformed = &v_block * &givens_mat;
v_block.set_to(transformed.as_slice());
}
alpha = *b.get_unchecked([k, k]);
beta = *b.get_unchecked([k + 1, k]);
let (c, s) = Matrix::<T>::givens_rot(alpha, beta);
let givens_mat = Matrix::new(2, 2, vec![c, -s, s, c]);
{
let b_block = MatrixSliceMut::from_matrix(b, [k, k], 2, cmp::min(3, n - k));
let transformed = &givens_mat * &b_block;
b_block.set_to(transformed.as_slice());
let m = u.rows();
let u_block = MatrixSliceMut::from_matrix(u, [0, k], m, 2);
let transformed = &u_block * givens_mat.transpose();
u_block.set_to(transformed.as_slice());
}
if k + 2 < n - q {
alpha = *b.get_unchecked([k, k + 1]);
beta = *b.get_unchecked([k, k + 2]);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use matrix::{Matrix, BaseMatrix};
use vector::Vector;
use super::sort_svd;
fn validate_svd(mat: &Matrix<f64>, b: &Matrix<f64>, u: &Matrix<f64>, v: &Matrix<f64>) {
for (idx, row) in b.row_iter().enumerate() {
assert!(!row.iter().take(idx).any(|&x| x > 1e-10));
assert!(!row.iter().skip(idx + 1).any(|&x| x > 1e-10));
assert!(row[idx] >= 0.0);
}
let recovered = u * b * v.transpose();
assert_eq!(recovered.rows(), mat.rows());
assert_eq!(recovered.cols(), mat.cols());
assert!(!mat.data()
.iter()
.zip(recovered.data().iter())
.any(|(&x, &y)| (x - y).abs() > 1e-10));
let ref u_transposed = u.transpose();
let ref v_transposed = v.transpose();
let ref mat_transposed = mat.transpose();
let mut singular_triplets = u_transposed.row_iter().zip(b.diag()).zip(v_transposed.row_iter())
.map(|((u_col, singular_value), v_col)| (Vector::new(u_col.raw_slice()), singular_value, Vector::new(v_col.raw_slice())));
assert!(singular_triplets.by_ref()
.map(|(ref u, sigma, ref v)| mat * v - u * sigma)
.flat_map(|v| v.into_vec().into_iter())
.all(|x| x.abs() < 1e-10));
assert!(singular_triplets.by_ref()
.map(|(ref u, sigma, ref v)| mat_transposed * u - v * sigma)
.flat_map(|v| v.into_vec().into_iter())
.all(|x| x.abs() < 1e-10));
}
#[test]
fn test_sort_svd() {
let u = matrix![1.0, 2.0, 3.0;
4.0, 5.0, 6.0];
let b = matrix![4.0, 0.0, 0.0;
0.0, 8.0, 0.0;
0.0, 0.0, 2.0];
let v = matrix![21.0, 22.0, 23.0;
24.0, 25.0, 26.0;
27.0, 28.0, 29.0];
let (b, u, v) = sort_svd(b, u, v);
assert_eq!(b.data(), &vec![8.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 2.0]);
assert_eq!(u.data(), &vec![2.0, 1.0, 3.0, 5.0, 4.0, 6.0]);
assert_eq!(v.data(),
&vec![22.0, 21.0, 23.0, 25.0, 24.0, 26.0, 28.0, 27.0, 29.0]);
}
#[test]
fn test_svd_tall_matrix() {
let mat = matrix![3.61833700244349288, -3.28382346228211697, 1.97968027781346501, -0.41869628192662156;
3.96046289599926427, 0.70730060716580723, -2.80552479438772817, -1.45283286109873933;
1.44435028724617442, 1.27749196276785826, -1.09858397535426366, -0.03159619816434689;
1.13455445826500667, 0.81521390274755756, 3.99123446373437263, -2.83025703359666192;
-3.30895752093770579, -0.04979044289857298, 3.03248594516832792, 3.85962479743330977];
let (b, u, v) = mat.clone().svd().unwrap();
let expected_values = vec![8.0, 6.0, 4.0, 2.0];
validate_svd(&mat, &b, &u, &v);
assert!(expected_values.iter()
.zip(b.diag())
.all(|(expected, actual)| (expected - actual).abs() < 1e-14));
}
#[test]
fn test_svd_short_matrix() {
let mat = matrix![3.61833700244349288, 3.96046289599926427, 1.44435028724617442, 1.13455445826500645, -3.30895752093770579;
-3.28382346228211697, 0.70730060716580723, 1.27749196276785826, 0.81521390274755756, -0.04979044289857298;
1.97968027781346545, -2.80552479438772817, -1.09858397535426366, 3.99123446373437263, 3.03248594516832792;
-0.41869628192662156, -1.45283286109873933, -0.03159619816434689, -2.83025703359666192, 3.85962479743330977];
let (b, u, v) = mat.clone().svd().unwrap();
let expected_values = vec![8.0, 6.0, 4.0, 2.0];
validate_svd(&mat, &b, &u, &v);
assert!(expected_values.iter()
.zip(b.diag())
.all(|(expected, actual)| (expected - actual).abs() < 1e-14));
}
#[test]
fn test_svd_square_matrix() {
let mat = matrix![1.0, 2.0, 3.0, 4.0, 5.0;
2.0, 4.0, 1.0, 2.0, 1.0;
3.0, 1.0, 7.0, 1.0, 1.0;
4.0, 2.0, 1.0, -1.0, 3.0;
5.0, 1.0, 1.0, 3.0, 2.0];
let expected_values = vec![12.1739747429271112,
5.2681047320525831,
4.4942269799769843,
2.9279675877385123,
2.8758200827412224];
let (b, u, v) = mat.clone().svd().unwrap();
validate_svd(&mat, &b, &u, &v);
assert!(expected_values.iter()
.zip(b.diag())
.all(|(expected, actual)| (expected - actual).abs() < 1e-12));
}
}