nalgebra/src/linalg/svd.rs

739 lines
25 KiB
Rust
Raw Normal View History

2021-07-17 17:36:14 +08:00
use std::fmt;
#[cfg(feature = "serde-serialize-no-std")]
2018-10-22 13:00:10 +08:00
use serde::{Deserialize, Serialize};
2019-03-18 18:23:19 +08:00
use approx::AbsDiffEq;
2020-03-21 19:16:46 +08:00
use num::{One, Zero};
2019-03-23 21:29:07 +08:00
use crate::allocator::Allocator;
2021-04-11 17:00:38 +08:00
use crate::base::{DefaultAllocator, Matrix, Matrix2x3, OMatrix, OVector, Vector2};
2019-03-23 21:29:07 +08:00
use crate::constraint::{SameNumberOfRows, ShapeConstraint};
2021-04-11 17:00:38 +08:00
use crate::dimension::{Dim, DimDiff, DimMin, DimMinimum, DimSub, U1};
2021-07-17 17:36:14 +08:00
use crate::storage::{Owned, Storage};
2020-03-21 19:16:46 +08:00
use simba::scalar::{ComplexField, RealField};
2020-03-21 19:16:46 +08:00
use crate::linalg::givens::GivensRotation;
2019-03-23 21:29:07 +08:00
use crate::linalg::symmetric_eigen;
use crate::linalg::Bidiagonal;
/// Singular Value Decomposition of a general matrix.
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde-serialize-no-std",
2018-10-22 13:00:10 +08:00
serde(bound(
2021-04-11 17:00:38 +08:00
serialize = "DefaultAllocator: Allocator<T::RealField, DimMinimum<R, C>> +
Allocator<T, DimMinimum<R, C>, C> +
Allocator<T, R, DimMinimum<R, C>>,
OMatrix<T, R, DimMinimum<R, C>>: Serialize,
OMatrix<T, DimMinimum<R, C>, C>: Serialize,
OVector<T::RealField, DimMinimum<R, C>>: Serialize"
2018-10-22 13:00:10 +08:00
))
)]
#[cfg_attr(
feature = "serde-serialize-no-std",
2018-10-22 13:00:10 +08:00
serde(bound(
2021-04-11 17:00:38 +08:00
deserialize = "DefaultAllocator: Allocator<T::RealField, DimMinimum<R, C>> +
Allocator<T, DimMinimum<R, C>, C> +
Allocator<T, R, DimMinimum<R, C>>,
OMatrix<T, R, DimMinimum<R, C>>: Deserialize<'de>,
OMatrix<T, DimMinimum<R, C>, C>: Deserialize<'de>,
OVector<T::RealField, DimMinimum<R, C>>: Deserialize<'de>"
2018-10-22 13:00:10 +08:00
))
)]
2021-04-11 17:00:38 +08:00
pub struct SVD<T: ComplexField, R: DimMin<C>, C: Dim>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, DimMinimum<R, C>, C>
+ Allocator<T, R, DimMinimum<R, C>>
+ Allocator<T::RealField, DimMinimum<R, C>>,
2018-02-02 19:26:35 +08:00
{
/// The left-singular vectors `U` of this SVD.
2021-04-11 17:00:38 +08:00
pub u: Option<OMatrix<T, R, DimMinimum<R, C>>>,
/// The right-singular vectors `V^t` of this SVD.
2021-04-11 17:00:38 +08:00
pub v_t: Option<OMatrix<T, DimMinimum<R, C>, C>>,
/// The singular values of this SVD.
2021-04-11 17:00:38 +08:00
pub singular_values: OVector<T::RealField, DimMinimum<R, C>>,
}
2021-04-11 17:00:38 +08:00
impl<T: ComplexField, R: DimMin<C>, C: Dim> Copy for SVD<T, R, C>
2018-02-02 19:26:35 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, DimMinimum<R, C>, C>
+ Allocator<T, R, DimMinimum<R, C>>
+ Allocator<T::RealField, DimMinimum<R, C>>,
2021-07-17 17:36:14 +08:00
Owned<T, R, DimMinimum<R, C>>: Copy,
Owned<T, DimMinimum<R, C>, C>: Copy,
Owned<T::RealField, DimMinimum<R, C>>: Copy,
{
}
impl<T: ComplexField, R: DimMin<C>, C: Dim> Clone for SVD<T, R, C>
where
DefaultAllocator: Allocator<T, DimMinimum<R, C>, C>
+ Allocator<T, R, DimMinimum<R, C>>
+ Allocator<T::RealField, DimMinimum<R, C>>,
Owned<T, R, DimMinimum<R, C>>: Clone,
Owned<T, DimMinimum<R, C>, C>: Clone,
Owned<T::RealField, DimMinimum<R, C>>: Clone,
{
fn clone(&self) -> Self {
Self {
u: self.u.clone(),
v_t: self.v_t.clone(),
singular_values: self.singular_values.clone(),
}
}
}
impl<T: ComplexField, R: DimMin<C>, C: Dim> fmt::Debug for SVD<T, R, C>
where
DefaultAllocator: Allocator<T, DimMinimum<R, C>, C>
+ Allocator<T, R, DimMinimum<R, C>>
+ Allocator<T::RealField, DimMinimum<R, C>>,
Owned<T, R, DimMinimum<R, C>>: fmt::Debug,
Owned<T, DimMinimum<R, C>, C>: fmt::Debug,
Owned<T::RealField, DimMinimum<R, C>>: fmt::Debug,
2020-03-21 19:16:46 +08:00
{
2021-07-17 17:36:14 +08:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SVD")
.field("u", &self.u)
.field("v_t", &self.v_t)
.field("singular_values", &self.singular_values)
.finish()
}
2020-03-21 19:16:46 +08:00
}
2021-04-11 17:00:38 +08:00
impl<T: ComplexField, R: DimMin<C>, C: Dim> SVD<T, R, C>
2018-02-02 19:26:35 +08:00
where
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, R, C>
+ Allocator<T, C>
+ Allocator<T, R>
+ Allocator<T, DimDiff<DimMinimum<R, C>, U1>>
+ Allocator<T, DimMinimum<R, C>, C>
+ Allocator<T, R, DimMinimum<R, C>>
+ Allocator<T, DimMinimum<R, C>>
+ Allocator<T::RealField, DimMinimum<R, C>>
+ Allocator<T::RealField, DimDiff<DimMinimum<R, C>, U1>>,
2018-02-02 19:26:35 +08:00
{
/// Computes the Singular Value Decomposition of `matrix` using implicit shift.
2021-04-11 17:00:38 +08:00
pub fn new(matrix: OMatrix<T, R, C>, compute_u: bool, compute_v: bool) -> Self {
2020-03-21 19:16:46 +08:00
Self::try_new(
matrix,
compute_u,
compute_v,
2021-04-11 17:00:38 +08:00
T::RealField::default_epsilon(),
2020-03-21 19:16:46 +08:00
0,
)
.unwrap()
}
/// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift.
///
/// # Arguments
///
/// * `compute_u` set this to `true` to enable the computation of left-singular vectors.
/// * `compute_v` set this to `true` to enable the computation of right-singular vectors.
2018-09-24 12:48:42 +08:00
/// * `eps` tolerance used to determine when a value converged to 0.
/// * `max_niter` maximum total number of iterations performed by the algorithm. If this
/// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm
/// continues indefinitely until convergence.
2018-02-02 19:26:35 +08:00
pub fn try_new(
2021-04-11 17:00:38 +08:00
mut matrix: OMatrix<T, R, C>,
2018-02-02 19:26:35 +08:00
compute_u: bool,
compute_v: bool,
2021-04-11 17:00:38 +08:00
eps: T::RealField,
2018-02-02 19:26:35 +08:00
max_niter: usize,
2020-04-06 00:49:48 +08:00
) -> Option<Self> {
2018-02-02 19:26:35 +08:00
assert!(
2020-11-16 19:11:24 +08:00
!matrix.is_empty(),
2018-02-02 19:26:35 +08:00
"Cannot compute the SVD of an empty matrix."
);
let (nrows, ncols) = matrix.data.shape();
let min_nrows_ncols = nrows.min(ncols);
let dim = min_nrows_ncols.value();
2019-03-18 18:23:19 +08:00
let m_amax = matrix.camax();
if !m_amax.is_zero() {
2019-03-18 18:23:19 +08:00
matrix.unscale_mut(m_amax);
}
2021-06-18 15:45:37 +08:00
let bi_matrix = Bidiagonal::new(matrix);
let mut u = if compute_u { Some(bi_matrix.u()) } else { None };
let mut v_t = if compute_v {
Some(bi_matrix.v_t())
} else {
None
};
let mut diagonal = bi_matrix.diagonal();
let mut off_diagonal = bi_matrix.off_diagonal();
let mut niter = 0;
2020-03-21 19:16:46 +08:00
let (mut start, mut end) = Self::delimit_subproblem(
&mut diagonal,
&mut off_diagonal,
&mut u,
&mut v_t,
2021-06-18 15:45:37 +08:00
bi_matrix.is_upper_diagonal(),
2020-03-21 19:16:46 +08:00
dim - 1,
eps,
);
while end != start {
let subdim = end - start + 1;
// Solve the subproblem.
2021-06-18 15:45:37 +08:00
#[allow(clippy::comparison_chain)]
if subdim > 2 {
let m = end - 1;
let n = end;
let mut vec;
{
2019-03-18 18:23:19 +08:00
let dm = diagonal[m];
let dn = diagonal[n];
let fm = off_diagonal[m];
2019-03-18 18:23:19 +08:00
let tmm = dm * dm + off_diagonal[m - 1] * off_diagonal[m - 1];
let tmn = dm * fm;
let tnn = dn * dn + fm * fm;
let shift = symmetric_eigen::wilkinson_shift(tmm, tnn, tmn);
2018-02-02 19:26:35 +08:00
vec = Vector2::new(
2019-03-18 18:23:19 +08:00
diagonal[start] * diagonal[start] - shift,
diagonal[start] * off_diagonal[start],
2018-02-02 19:26:35 +08:00
);
}
2018-02-02 19:26:35 +08:00
for k in start..n {
let m12 = if k == n - 1 {
2021-04-11 17:00:38 +08:00
T::RealField::zero()
2018-02-02 19:26:35 +08:00
} else {
2019-03-18 18:23:19 +08:00
off_diagonal[k + 1]
2018-02-02 19:26:35 +08:00
};
let mut subm = Matrix2x3::new(
2019-03-18 18:23:19 +08:00
diagonal[k],
off_diagonal[k],
2021-04-11 17:00:38 +08:00
T::RealField::zero(),
T::RealField::zero(),
2019-03-18 18:23:19 +08:00
diagonal[k + 1],
2018-02-02 19:26:35 +08:00
m12,
);
2019-03-18 18:23:19 +08:00
if let Some((rot1, norm1)) = GivensRotation::cancel_y(&vec) {
2020-03-21 19:16:46 +08:00
rot1.inverse()
2021-04-11 17:00:38 +08:00
.rotate_rows(&mut subm.fixed_columns_mut::<2>(0));
let rot1 = GivensRotation::new_unchecked(rot1.c(), T::from_real(rot1.s()));
if k > start {
// This is not the first iteration.
2019-03-18 18:23:19 +08:00
off_diagonal[k - 1] = norm1;
}
let v = Vector2::new(subm[(0, 0)], subm[(1, 0)]);
2020-11-15 23:57:49 +08:00
// TODO: does the case `v.y == 0` ever happen?
2020-03-21 19:16:46 +08:00
let (rot2, norm2) = GivensRotation::cancel_y(&v)
.unwrap_or((GivensRotation::identity(), subm[(0, 0)]));
2021-04-11 17:00:38 +08:00
rot2.rotate(&mut subm.fixed_columns_mut::<2>(1));
let rot2 = GivensRotation::new_unchecked(rot2.c(), T::from_real(rot2.s()));
subm[(0, 0)] = norm2;
if let Some(ref mut v_t) = v_t {
2021-06-18 15:45:37 +08:00
if bi_matrix.is_upper_diagonal() {
2021-04-11 17:00:38 +08:00
rot1.rotate(&mut v_t.fixed_rows_mut::<2>(k));
2018-02-02 19:26:35 +08:00
} else {
2021-04-11 17:00:38 +08:00
rot2.rotate(&mut v_t.fixed_rows_mut::<2>(k));
}
}
if let Some(ref mut u) = u {
2021-06-18 15:45:37 +08:00
if bi_matrix.is_upper_diagonal() {
2021-04-11 17:00:38 +08:00
rot2.inverse().rotate_rows(&mut u.fixed_columns_mut::<2>(k));
2018-02-02 19:26:35 +08:00
} else {
2021-04-11 17:00:38 +08:00
rot1.inverse().rotate_rows(&mut u.fixed_columns_mut::<2>(k));
}
}
2020-10-11 17:23:05 +08:00
diagonal[k] = subm[(0, 0)];
2019-03-18 18:23:19 +08:00
diagonal[k + 1] = subm[(1, 1)];
2020-10-11 17:23:05 +08:00
off_diagonal[k] = subm[(0, 1)];
if k != n - 1 {
2019-03-18 18:23:19 +08:00
off_diagonal[k + 1] = subm[(1, 2)];
}
vec.x = subm[(0, 1)];
vec.y = subm[(0, 2)];
2018-02-02 19:26:35 +08:00
} else {
break;
}
}
2018-02-02 19:26:35 +08:00
} else if subdim == 2 {
// Solve the remaining 2x2 subproblem.
let (u2, s, v2) = compute_2x2_uptrig_svd(
2019-03-18 18:23:19 +08:00
diagonal[start],
off_diagonal[start],
diagonal[start + 1],
2021-06-18 15:45:37 +08:00
compute_u && bi_matrix.is_upper_diagonal()
|| compute_v && !bi_matrix.is_upper_diagonal(),
compute_v && bi_matrix.is_upper_diagonal()
|| compute_u && !bi_matrix.is_upper_diagonal(),
2018-02-02 19:26:35 +08:00
);
2021-04-11 17:00:38 +08:00
let u2 = u2.map(|u2| GivensRotation::new_unchecked(u2.c(), T::from_real(u2.s())));
let v2 = v2.map(|v2| GivensRotation::new_unchecked(v2.c(), T::from_real(v2.s())));
2020-10-11 17:23:05 +08:00
diagonal[start] = s[0];
2019-03-18 18:23:19 +08:00
diagonal[start + 1] = s[1];
2021-04-11 17:00:38 +08:00
off_diagonal[start] = T::RealField::zero();
if let Some(ref mut u) = u {
2021-06-18 15:45:37 +08:00
let rot = if bi_matrix.is_upper_diagonal() {
2018-02-02 19:26:35 +08:00
u2.unwrap()
} else {
v2.unwrap()
};
2021-04-11 17:00:38 +08:00
rot.rotate_rows(&mut u.fixed_columns_mut::<2>(start));
}
if let Some(ref mut v_t) = v_t {
2021-06-18 15:45:37 +08:00
let rot = if bi_matrix.is_upper_diagonal() {
2018-02-02 19:26:35 +08:00
v2.unwrap()
} else {
u2.unwrap()
};
2021-04-11 17:00:38 +08:00
rot.inverse().rotate(&mut v_t.fixed_rows_mut::<2>(start));
}
end -= 1;
}
2018-09-24 12:48:42 +08:00
// Re-delimit the subproblem in case some decoupling occurred.
2020-03-21 19:16:46 +08:00
let sub = Self::delimit_subproblem(
&mut diagonal,
&mut off_diagonal,
&mut u,
&mut v_t,
2021-06-18 15:45:37 +08:00
bi_matrix.is_upper_diagonal(),
2020-03-21 19:16:46 +08:00
end,
eps,
);
start = sub.0;
2018-02-02 19:26:35 +08:00
end = sub.1;
niter += 1;
if niter == max_niter {
return None;
}
}
diagonal *= m_amax;
// Ensure all singular value are non-negative.
2018-02-02 19:26:35 +08:00
for i in 0..dim {
2019-03-18 18:23:19 +08:00
let sval = diagonal[i];
2021-04-11 17:00:38 +08:00
if sval < T::RealField::zero() {
diagonal[i] = -sval;
if let Some(ref mut u) = u {
u.column_mut(i).neg_mut();
}
}
}
Some(Self {
2019-03-18 18:23:19 +08:00
u,
v_t,
singular_values: diagonal,
2018-02-02 19:26:35 +08:00
})
}
/*
2021-04-11 17:00:38 +08:00
fn display_bidiag(b: &Bidiagonal<T, R, C>, begin: usize, end: usize) {
for i in begin .. end {
for k in begin .. i {
print!(" ");
}
println!("{} {}", b.diagonal[i], b.off_diagonal[i]);
}
for k in begin .. end {
print!(" ");
}
println!("{}", b.diagonal[end]);
}
*/
2018-02-02 19:26:35 +08:00
fn delimit_subproblem(
2021-04-11 17:00:38 +08:00
diagonal: &mut OVector<T::RealField, DimMinimum<R, C>>,
off_diagonal: &mut OVector<T::RealField, DimDiff<DimMinimum<R, C>, U1>>,
u: &mut Option<OMatrix<T, R, DimMinimum<R, C>>>,
v_t: &mut Option<OMatrix<T, DimMinimum<R, C>, C>>,
2019-03-18 18:23:19 +08:00
is_upper_diagonal: bool,
2018-02-02 19:26:35 +08:00
end: usize,
2021-04-11 17:00:38 +08:00
eps: T::RealField,
2020-04-06 00:49:48 +08:00
) -> (usize, usize) {
let mut n = end;
while n > 0 {
let m = n - 1;
2019-03-18 18:23:19 +08:00
if off_diagonal[m].is_zero()
|| off_diagonal[m].norm1() <= eps * (diagonal[n].norm1() + diagonal[m].norm1())
2018-02-02 19:26:35 +08:00
{
2021-04-11 17:00:38 +08:00
off_diagonal[m] = T::RealField::zero();
} else if diagonal[m].norm1() <= eps {
2021-04-11 17:00:38 +08:00
diagonal[m] = T::RealField::zero();
2020-03-21 19:16:46 +08:00
Self::cancel_horizontal_off_diagonal_elt(
diagonal,
off_diagonal,
u,
v_t,
is_upper_diagonal,
m,
m + 1,
);
if m != 0 {
2020-03-21 19:16:46 +08:00
Self::cancel_vertical_off_diagonal_elt(
diagonal,
off_diagonal,
u,
v_t,
is_upper_diagonal,
m - 1,
);
}
} else if diagonal[n].norm1() <= eps {
2021-04-11 17:00:38 +08:00
diagonal[n] = T::RealField::zero();
2020-03-21 19:16:46 +08:00
Self::cancel_vertical_off_diagonal_elt(
diagonal,
off_diagonal,
u,
v_t,
is_upper_diagonal,
m,
);
2018-02-02 19:26:35 +08:00
} else {
break;
}
n -= 1;
}
if n == 0 {
return (0, 0);
}
let mut new_start = n - 1;
while new_start > 0 {
let m = new_start - 1;
if off_diagonal[m].norm1() <= eps * (diagonal[new_start].norm1() + diagonal[m].norm1())
2018-02-02 19:26:35 +08:00
{
2021-04-11 17:00:38 +08:00
off_diagonal[m] = T::RealField::zero();
break;
}
2020-11-15 23:57:49 +08:00
// TODO: write a test that enters this case.
else if diagonal[m].norm1() <= eps {
2021-04-11 17:00:38 +08:00
diagonal[m] = T::RealField::zero();
2020-03-21 19:16:46 +08:00
Self::cancel_horizontal_off_diagonal_elt(
diagonal,
off_diagonal,
u,
v_t,
is_upper_diagonal,
m,
n,
);
if m != 0 {
2020-03-21 19:16:46 +08:00
Self::cancel_vertical_off_diagonal_elt(
diagonal,
off_diagonal,
u,
v_t,
is_upper_diagonal,
m - 1,
);
}
break;
}
new_start -= 1;
}
(new_start, n)
}
// Cancels the i-th off-diagonal element using givens rotations.
2018-02-02 19:26:35 +08:00
fn cancel_horizontal_off_diagonal_elt(
2021-04-11 17:00:38 +08:00
diagonal: &mut OVector<T::RealField, DimMinimum<R, C>>,
off_diagonal: &mut OVector<T::RealField, DimDiff<DimMinimum<R, C>, U1>>,
u: &mut Option<OMatrix<T, R, DimMinimum<R, C>>>,
v_t: &mut Option<OMatrix<T, DimMinimum<R, C>, C>>,
2019-03-18 18:23:19 +08:00
is_upper_diagonal: bool,
2018-02-02 19:26:35 +08:00
i: usize,
end: usize,
2020-04-06 00:49:48 +08:00
) {
2019-03-18 18:23:19 +08:00
let mut v = Vector2::new(off_diagonal[i], diagonal[i + 1]);
2021-04-11 17:00:38 +08:00
off_diagonal[i] = T::RealField::zero();
2018-02-02 19:26:35 +08:00
for k in i..end {
2019-03-18 18:23:19 +08:00
if let Some((rot, norm)) = GivensRotation::cancel_x(&v) {
2021-04-11 17:00:38 +08:00
let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s()));
2019-03-18 18:23:19 +08:00
diagonal[k + 1] = norm;
2019-03-18 18:23:19 +08:00
if is_upper_diagonal {
if let Some(ref mut u) = *u {
2018-02-02 19:26:35 +08:00
rot.inverse()
2021-04-11 17:00:38 +08:00
.rotate_rows(&mut u.fixed_columns_with_step_mut::<2>(i, k - i));
}
2018-02-02 19:26:35 +08:00
} else if let Some(ref mut v_t) = *v_t {
2021-04-11 17:00:38 +08:00
rot.rotate(&mut v_t.fixed_rows_with_step_mut::<2>(i, k - i));
}
if k + 1 != end {
v.x = -rot.s().real() * off_diagonal[k + 1];
2019-03-18 18:23:19 +08:00
v.y = diagonal[k + 2];
off_diagonal[k + 1] *= rot.c();
}
2018-02-02 19:26:35 +08:00
} else {
break;
}
}
}
// Cancels the i-th off-diagonal element using givens rotations.
2018-02-02 19:26:35 +08:00
fn cancel_vertical_off_diagonal_elt(
2021-04-11 17:00:38 +08:00
diagonal: &mut OVector<T::RealField, DimMinimum<R, C>>,
off_diagonal: &mut OVector<T::RealField, DimDiff<DimMinimum<R, C>, U1>>,
u: &mut Option<OMatrix<T, R, DimMinimum<R, C>>>,
v_t: &mut Option<OMatrix<T, DimMinimum<R, C>, C>>,
2019-03-18 18:23:19 +08:00
is_upper_diagonal: bool,
2018-02-02 19:26:35 +08:00
i: usize,
2020-04-06 00:49:48 +08:00
) {
2019-03-18 18:23:19 +08:00
let mut v = Vector2::new(diagonal[i], off_diagonal[i]);
2021-04-11 17:00:38 +08:00
off_diagonal[i] = T::RealField::zero();
2018-02-02 19:26:35 +08:00
for k in (0..i + 1).rev() {
2019-03-18 18:23:19 +08:00
if let Some((rot, norm)) = GivensRotation::cancel_y(&v) {
2021-04-11 17:00:38 +08:00
let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s()));
2019-03-18 18:23:19 +08:00
diagonal[k] = norm;
2019-03-18 18:23:19 +08:00
if is_upper_diagonal {
if let Some(ref mut v_t) = *v_t {
2021-04-11 17:00:38 +08:00
rot.rotate(&mut v_t.fixed_rows_with_step_mut::<2>(k, i - k));
}
2018-02-02 19:26:35 +08:00
} else if let Some(ref mut u) = *u {
rot.inverse()
2021-04-11 17:00:38 +08:00
.rotate_rows(&mut u.fixed_columns_with_step_mut::<2>(k, i - k));
}
if k > 0 {
2019-03-18 18:23:19 +08:00
v.x = diagonal[k - 1];
v.y = rot.s().real() * off_diagonal[k - 1];
off_diagonal[k - 1] *= rot.c();
}
2018-02-02 19:26:35 +08:00
} else {
break;
}
}
}
/// Computes the rank of the decomposed matrix, i.e., the number of singular values greater
/// than `eps`.
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn rank(&self, eps: T::RealField) -> usize {
2018-02-02 19:26:35 +08:00
assert!(
2021-04-11 17:00:38 +08:00
eps >= T::RealField::zero(),
2018-02-02 19:26:35 +08:00
"SVD rank: the epsilon must be non-negative."
);
self.singular_values.iter().filter(|e| **e > eps).count()
}
/// Rebuild the original matrix.
///
/// This is useful if some of the singular values have been manually modified.
/// Returns `Err` if the right- and left- singular vectors have not been
/// computed at construction-time.
2021-04-11 17:00:38 +08:00
pub fn recompose(self) -> Result<OMatrix<T, R, C>, &'static str> {
match (self.u, self.v_t) {
2018-10-10 04:21:00 +08:00
(Some(mut u), Some(v_t)) => {
for i in 0..self.singular_values.len() {
let val = self.singular_values[i];
u.column_mut(i).scale_mut(val);
}
Ok(u * v_t)
}
(None, None) => Err("SVD recomposition: U and V^t have not been computed."),
(None, _) => Err("SVD recomposition: U has not been computed."),
2020-03-21 19:16:46 +08:00
(_, None) => Err("SVD recomposition: V^t has not been computed."),
}
}
/// Computes the pseudo-inverse of the decomposed matrix.
///
/// Any singular value smaller than `eps` is assumed to be zero.
/// Returns `Err` if the right- and left- singular vectors have not
/// been computed at construction-time.
2021-04-11 17:00:38 +08:00
pub fn pseudo_inverse(mut self, eps: T::RealField) -> Result<OMatrix<T, C, R>, &'static str>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, C, R>,
2020-04-06 00:49:48 +08:00
{
2021-04-11 17:00:38 +08:00
if eps < T::RealField::zero() {
Err("SVD pseudo inverse: the epsilon must be non-negative.")
2020-03-21 19:16:46 +08:00
} else {
for i in 0..self.singular_values.len() {
let val = self.singular_values[i];
if val > eps {
2021-04-11 17:00:38 +08:00
self.singular_values[i] = T::RealField::one() / val;
} else {
2021-04-11 17:00:38 +08:00
self.singular_values[i] = T::RealField::zero();
}
}
self.recompose().map(|m| m.adjoint())
}
}
/// Solves the system `self * x = b` where `self` is the decomposed matrix and `x` the unknown.
///
/// Any singular value smaller than `eps` is assumed to be zero.
/// Returns `Err` if the singular vectors `U` and `V` have not been computed.
2020-11-15 23:57:49 +08:00
// TODO: make this more generic wrt the storage types and the dimensions for `b`.
2018-02-02 19:26:35 +08:00
pub fn solve<R2: Dim, C2: Dim, S2>(
&self,
2021-04-11 17:00:38 +08:00
b: &Matrix<T, R2, C2, S2>,
eps: T::RealField,
) -> Result<OMatrix<T, C, C2>, &'static str>
2018-02-02 19:26:35 +08:00
where
2021-04-11 17:00:38 +08:00
S2: Storage<T, R2, C2>,
DefaultAllocator: Allocator<T, C, C2> + Allocator<T, DimMinimum<R, C>, C2>,
2018-02-02 19:26:35 +08:00
ShapeConstraint: SameNumberOfRows<R, R2>,
{
2021-04-11 17:00:38 +08:00
if eps < T::RealField::zero() {
Err("SVD solve: the epsilon must be non-negative.")
2020-03-21 19:16:46 +08:00
} else {
match (&self.u, &self.v_t) {
(Some(u), Some(v_t)) => {
let mut ut_b = u.ad_mul(b);
for j in 0..ut_b.ncols() {
let mut col = ut_b.column_mut(j);
for i in 0..self.singular_values.len() {
let val = self.singular_values[i];
if val > eps {
col[i] = col[i].unscale(val);
} else {
2021-04-11 17:00:38 +08:00
col[i] = T::zero();
}
}
}
Ok(v_t.ad_mul(&ut_b))
}
(None, None) => Err("SVD solve: U and V^t have not been computed."),
(None, _) => Err("SVD solve: U has not been computed."),
2020-03-21 19:16:46 +08:00
(_, None) => Err("SVD solve: V^t has not been computed."),
}
}
}
}
2021-04-11 17:00:38 +08:00
impl<T: ComplexField, R: DimMin<C>, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S>
2018-02-02 19:26:35 +08:00
where
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, R, C>
+ Allocator<T, C>
+ Allocator<T, R>
+ Allocator<T, DimDiff<DimMinimum<R, C>, U1>>
+ Allocator<T, DimMinimum<R, C>, C>
+ Allocator<T, R, DimMinimum<R, C>>
+ Allocator<T, DimMinimum<R, C>>
+ Allocator<T::RealField, DimMinimum<R, C>>
+ Allocator<T::RealField, DimDiff<DimMinimum<R, C>, U1>>,
2018-02-02 19:26:35 +08:00
{
/// Computes the singular values of this matrix.
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn singular_values(&self) -> OVector<T::RealField, DimMinimum<R, C>> {
SVD::new(self.clone_owned(), false, false).singular_values
}
/// Computes the rank of this matrix.
///
2017-10-27 12:13:35 +08:00
/// All singular values below `eps` are considered equal to 0.
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn rank(&self, eps: T::RealField) -> usize {
let svd = SVD::new(self.clone_owned(), false, false);
svd.rank(eps)
}
/// Computes the pseudo-inverse of this matrix.
///
2017-10-27 12:13:35 +08:00
/// All singular values below `eps` are considered equal to 0.
2021-04-11 17:00:38 +08:00
pub fn pseudo_inverse(self, eps: T::RealField) -> Result<OMatrix<T, C, R>, &'static str>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, C, R>,
2020-04-06 00:49:48 +08:00
{
SVD::new(self.clone_owned(), true, true).pseudo_inverse(eps)
}
}
// Explicit formulae inspired from the paper "Computing the Singular Values of 2-by-2 Complex
// Matrices", Sanzheng Qiao and Xiaohong Wang.
// http://www.cas.mcmaster.ca/sqrl/papers/sqrl5.pdf
2021-04-11 17:00:38 +08:00
fn compute_2x2_uptrig_svd<T: RealField>(
m11: T,
m12: T,
m22: T,
compute_u: bool,
compute_v: bool,
2020-03-21 19:16:46 +08:00
) -> (
2021-04-11 17:00:38 +08:00
Option<GivensRotation<T>>,
Vector2<T>,
Option<GivensRotation<T>>,
2020-04-06 00:49:48 +08:00
) {
2021-04-11 17:00:38 +08:00
let two: T::RealField = crate::convert(2.0f64);
let half: T::RealField = crate::convert(0.5f64);
let denom = (m11 + m22).hypot(m12) + (m11 - m22).hypot(m12);
// NOTE: v1 is the singular value that is the closest to m22.
// This prevents cancellation issues when constructing the vector `csv` below. If we chose
// otherwise, we would have v1 ~= m11 when m12 is small. This would cause catastrophic
// cancellation on `v1 * v1 - m11 * m11` below.
let mut v1 = m11 * m22 * two / denom;
let mut v2 = half * denom;
let mut u = None;
let mut v_t = None;
if compute_u || compute_v {
let (csv, sgn_v) = GivensRotation::new(m11 * m12, v1 * v1 - m11 * m11);
v1 *= sgn_v;
v2 *= sgn_v;
if compute_v {
v_t = Some(csv);
}
if compute_u {
let cu = (m11.scale(csv.c()) + m12 * csv.s()) / v1;
let su = (m22 * csv.s()) / v1;
let (csu, sgn_u) = GivensRotation::new(cu, su);
v1 *= sgn_u;
v2 *= sgn_u;
u = Some(csu);
}
}
(u, Vector2::new(v1, v2), v_t)
2020-03-21 19:16:46 +08:00
}