2021-04-12 18:14:16 +08:00
|
|
|
|
#[cfg(feature = "serde-serialize-no-std")]
|
2018-10-22 13:00:10 +08:00
|
|
|
|
use serde::{Deserialize, Serialize};
|
2021-11-27 00:45:42 +08:00
|
|
|
|
use std::any::TypeId;
|
2017-08-14 01:53:04 +08:00
|
|
|
|
|
2020-03-21 19:16:46 +08:00
|
|
|
|
use num::{One, Zero};
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
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-08-03 00:41:46 +08:00
|
|
|
|
use crate::dimension::{Dim, DimDiff, DimMin, DimMinimum, DimSub, U1};
|
|
|
|
|
use crate::storage::Storage;
|
2021-11-27 00:45:42 +08:00
|
|
|
|
use crate::{Matrix2, Matrix3, RawStorage, U2, U3};
|
2020-03-21 19:16:46 +08:00
|
|
|
|
use simba::scalar::{ComplexField, RealField};
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
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;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2017-08-14 01:53:04 +08:00
|
|
|
|
/// Singular Value Decomposition of a general matrix.
|
2021-04-12 18:14:16 +08:00
|
|
|
|
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
|
2018-05-19 23:15:15 +08:00
|
|
|
|
#[cfg_attr(
|
2021-04-12 18:14:16 +08:00
|
|
|
|
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
|
|
|
|
))
|
2018-05-19 23:15:15 +08:00
|
|
|
|
)]
|
|
|
|
|
#[cfg_attr(
|
2021-04-12 18:14:16 +08:00
|
|
|
|
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
|
|
|
|
))
|
2018-05-19 23:15:15 +08:00
|
|
|
|
)]
|
2021-07-30 01:33:45 +08:00
|
|
|
|
#[derive(Clone, Debug)]
|
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
|
|
|
|
{
|
2017-08-03 01:37:44 +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>>>,
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// 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>>,
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// The singular values of this SVD.
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub singular_values: OVector<T::RealField, DimMinimum<R, C>>,
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-03 00:41:46 +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-08-03 00:41:46 +08:00
|
|
|
|
OMatrix<T, R, DimMinimum<R, C>>: Copy,
|
|
|
|
|
OMatrix<T, DimMinimum<R, C>, C>: Copy,
|
|
|
|
|
OVector<T::RealField, DimMinimum<R, C>>: Copy,
|
2021-07-17 17:36:14 +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
|
|
|
|
{
|
2021-12-09 18:52:37 +08:00
|
|
|
|
fn use_special_always_ordered_svd2() -> bool {
|
|
|
|
|
TypeId::of::<OMatrix<T, R, C>>() == TypeId::of::<Matrix2<T::RealField>>()
|
|
|
|
|
&& TypeId::of::<Self>() == TypeId::of::<SVD<T::RealField, U2, U2>>()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn use_special_always_ordered_svd3() -> bool {
|
|
|
|
|
TypeId::of::<OMatrix<T, R, C>>() == TypeId::of::<Matrix3<T::RealField>>()
|
|
|
|
|
&& TypeId::of::<Self>() == TypeId::of::<SVD<T::RealField, U3, U3>>()
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// Computes the Singular Value Decomposition of `matrix` using implicit shift.
|
2021-11-08 17:27:53 +08:00
|
|
|
|
/// The singular values are not guaranteed to be sorted in any particular order.
|
|
|
|
|
/// If a descending order is required, consider using `new` instead.
|
|
|
|
|
pub fn new_unordered(matrix: OMatrix<T, R, C>, compute_u: bool, compute_v: bool) -> Self {
|
2022-03-09 15:13:12 +08:00
|
|
|
|
Self::try_new_unordered(matrix, compute_u, compute_v, crate::convert(1e-15), 0).unwrap()
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-14 01:52:46 +08:00
|
|
|
|
/// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift.
|
2021-11-08 17:27:53 +08:00
|
|
|
|
/// The singular values are not guaranteed to be sorted in any particular order.
|
|
|
|
|
/// If a descending order is required, consider using `try_new` instead.
|
2017-08-03 01:37:44 +08:00
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `compute_u` − set this to `true` to enable the computation of left-singular vectors.
|
2020-04-21 22:23:00 +08:00
|
|
|
|
/// * `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.
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// * `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.
|
2021-11-08 17:27:53 +08:00
|
|
|
|
pub fn try_new_unordered(
|
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."
|
|
|
|
|
);
|
2021-08-03 00:41:46 +08:00
|
|
|
|
let (nrows, ncols) = matrix.shape_generic();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
let min_nrows_ncols = nrows.min(ncols);
|
2021-11-27 00:45:42 +08:00
|
|
|
|
|
2021-12-09 18:52:37 +08:00
|
|
|
|
if Self::use_special_always_ordered_svd2() {
|
2021-11-27 00:45:42 +08:00
|
|
|
|
// SAFETY: the reference transmutes are OK since we checked that the types match exactly.
|
|
|
|
|
let matrix: &Matrix2<T::RealField> = unsafe { std::mem::transmute(&matrix) };
|
2021-12-09 18:52:37 +08:00
|
|
|
|
let result = super::svd2::svd_ordered2(matrix, compute_u, compute_v);
|
2021-11-27 00:45:42 +08:00
|
|
|
|
let typed_result: &Self = unsafe { std::mem::transmute(&result) };
|
|
|
|
|
return Some(typed_result.clone());
|
2021-12-09 18:52:37 +08:00
|
|
|
|
} else if Self::use_special_always_ordered_svd3() {
|
2021-11-27 00:45:42 +08:00
|
|
|
|
// SAFETY: the reference transmutes are OK since we checked that the types match exactly.
|
|
|
|
|
let matrix: &Matrix3<T::RealField> = unsafe { std::mem::transmute(&matrix) };
|
2021-12-09 18:52:37 +08:00
|
|
|
|
let result = super::svd3::svd_ordered3(matrix, compute_u, compute_v, eps, max_niter);
|
2021-11-27 00:45:42 +08:00
|
|
|
|
let typed_result: &Self = unsafe { std::mem::transmute(&result) };
|
|
|
|
|
return Some(typed_result.clone());
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-03 01:37:44 +08:00
|
|
|
|
let dim = min_nrows_ncols.value();
|
|
|
|
|
|
2019-03-18 18:23:19 +08:00
|
|
|
|
let m_amax = matrix.camax();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
if !m_amax.is_zero() {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
matrix.unscale_mut(m_amax.clone());
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
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();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
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,
|
2021-08-04 23:34:25 +08:00
|
|
|
|
eps.clone(),
|
2020-03-21 19:16:46 +08:00
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
while end != start {
|
|
|
|
|
let subdim = end - start + 1;
|
|
|
|
|
|
|
|
|
|
// Solve the subproblem.
|
2021-06-18 15:45:37 +08:00
|
|
|
|
#[allow(clippy::comparison_chain)]
|
2017-08-03 01:37:44 +08:00
|
|
|
|
if subdim > 2 {
|
|
|
|
|
let m = end - 1;
|
|
|
|
|
let n = end;
|
|
|
|
|
|
|
|
|
|
let mut vec;
|
|
|
|
|
{
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let dm = diagonal[m].clone();
|
|
|
|
|
let dn = diagonal[n].clone();
|
|
|
|
|
let fm = off_diagonal[m].clone();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let tmm = dm.clone() * dm.clone()
|
|
|
|
|
+ off_diagonal[m - 1].clone() * off_diagonal[m - 1].clone();
|
|
|
|
|
let tmn = dm * fm.clone();
|
|
|
|
|
let tnn = dn.clone() * dn + fm.clone() * fm;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
let shift = symmetric_eigen::wilkinson_shift(tmm, tnn, tmn);
|
|
|
|
|
|
2018-02-02 19:26:35 +08:00
|
|
|
|
vec = Vector2::new(
|
2021-08-04 23:34:25 +08:00
|
|
|
|
diagonal[start].clone() * diagonal[start].clone() - shift,
|
|
|
|
|
diagonal[start].clone() * off_diagonal[start].clone(),
|
2018-02-02 19:26:35 +08:00
|
|
|
|
);
|
2017-08-03 01:37:44 +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 {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
off_diagonal[k + 1].clone()
|
2018-02-02 19:26:35 +08:00
|
|
|
|
};
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
let mut subm = Matrix2x3::new(
|
2021-08-04 23:34:25 +08:00
|
|
|
|
diagonal[k].clone(),
|
|
|
|
|
off_diagonal[k].clone(),
|
2021-04-11 17:00:38 +08:00
|
|
|
|
T::RealField::zero(),
|
|
|
|
|
T::RealField::zero(),
|
2021-08-04 23:34:25 +08:00
|
|
|
|
diagonal[k + 1].clone(),
|
2018-02-02 19:26:35 +08:00
|
|
|
|
m12,
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
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()));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
if k > start {
|
|
|
|
|
// This is not the first iteration.
|
2019-03-18 18:23:19 +08:00
|
|
|
|
off_diagonal[k - 1] = norm1;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let v = Vector2::new(subm[(0, 0)].clone(), subm[(1, 0)].clone());
|
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)
|
2021-08-04 23:34:25 +08:00
|
|
|
|
.unwrap_or((GivensRotation::identity(), subm[(0, 0)].clone()));
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
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()));
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
diagonal[k] = subm[(0, 0)].clone();
|
|
|
|
|
diagonal[k + 1] = subm[(1, 1)].clone();
|
|
|
|
|
off_diagonal[k] = subm[(0, 1)].clone();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
if k != n - 1 {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
off_diagonal[k + 1] = subm[(1, 2)].clone();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
vec.x = subm[(0, 1)].clone();
|
|
|
|
|
vec.y = subm[(0, 2)].clone();
|
2018-02-02 19:26:35 +08:00
|
|
|
|
} else {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-02 19:26:35 +08:00
|
|
|
|
} else if subdim == 2 {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
// Solve the remaining 2x2 subproblem.
|
2019-03-19 21:22:59 +08:00
|
|
|
|
let (u2, s, v2) = compute_2x2_uptrig_svd(
|
2021-08-04 23:34:25 +08:00
|
|
|
|
diagonal[start].clone(),
|
|
|
|
|
off_diagonal[start].clone(),
|
|
|
|
|
diagonal[start + 1].clone(),
|
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())));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
diagonal[start] = s[0].clone();
|
|
|
|
|
diagonal[start + 1] = s[1].clone();
|
2021-04-11 17:00:38 +08:00
|
|
|
|
off_diagonal[start] = T::RealField::zero();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
if let Some(ref mut u) = u {
|
2021-06-18 15:45:37 +08:00
|
|
|
|
let rot = if bi_matrix.is_upper_diagonal() {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
u2.clone().unwrap()
|
2018-02-02 19:26:35 +08:00
|
|
|
|
} else {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
v2.clone().unwrap()
|
2018-02-02 19:26:35 +08:00
|
|
|
|
};
|
2021-04-11 17:00:38 +08:00
|
|
|
|
rot.rotate_rows(&mut u.fixed_columns_mut::<2>(start));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2021-08-04 23:34:25 +08:00
|
|
|
|
eps.clone(),
|
2020-03-21 19:16:46 +08:00
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
start = sub.0;
|
2018-02-02 19:26:35 +08:00
|
|
|
|
end = sub.1;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
niter += 1;
|
|
|
|
|
if niter == max_niter {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 21:22:59 +08:00
|
|
|
|
diagonal *= m_amax;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
// Ensure all singular value are non-negative.
|
2018-02-02 19:26:35 +08:00
|
|
|
|
for i in 0..dim {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let sval = diagonal[i].clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
2021-04-11 17:00:38 +08:00
|
|
|
|
if sval < T::RealField::zero() {
|
2019-03-19 21:22:59 +08:00
|
|
|
|
diagonal[i] = -sval;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
if let Some(ref mut u) = u {
|
2019-03-19 21:22:59 +08:00
|
|
|
|
u.column_mut(i).neg_mut();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-17 05:29:41 +08:00
|
|
|
|
Some(Self {
|
2019-03-18 18:23:19 +08:00
|
|
|
|
u,
|
|
|
|
|
v_t,
|
|
|
|
|
singular_values: diagonal,
|
2018-02-02 19:26:35 +08:00
|
|
|
|
})
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2021-04-11 17:00:38 +08:00
|
|
|
|
fn display_bidiag(b: &Bidiagonal<T, R, C>, begin: usize, end: usize) {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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) {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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()
|
2021-08-04 23:34:25 +08:00
|
|
|
|
|| off_diagonal[m].clone().norm1()
|
|
|
|
|
<= eps.clone() * (diagonal[n].clone().norm1() + diagonal[m].clone().norm1())
|
2018-02-02 19:26:35 +08:00
|
|
|
|
{
|
2021-04-11 17:00:38 +08:00
|
|
|
|
off_diagonal[m] = T::RealField::zero();
|
2021-08-04 23:34:25 +08:00
|
|
|
|
} else if diagonal[m].clone().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,
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
2021-08-04 23:34:25 +08:00
|
|
|
|
} else if diagonal[n].clone().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 {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n == 0 {
|
|
|
|
|
return (0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut new_start = n - 1;
|
|
|
|
|
while new_start > 0 {
|
|
|
|
|
let m = new_start - 1;
|
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
if off_diagonal[m].clone().norm1()
|
|
|
|
|
<= eps.clone() * (diagonal[new_start].clone().norm1() + diagonal[m].clone().norm1())
|
2018-02-02 19:26:35 +08:00
|
|
|
|
{
|
2021-04-11 17:00:38 +08:00
|
|
|
|
off_diagonal[m] = T::RealField::zero();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2020-11-15 23:57:49 +08:00
|
|
|
|
// TODO: write a test that enters this case.
|
2021-08-04 23:34:25 +08:00
|
|
|
|
else if diagonal[m].clone().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,
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
) {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let mut v = Vector2::new(off_diagonal[i].clone(), diagonal[i + 1].clone());
|
2021-04-11 17:00:38 +08:00
|
|
|
|
off_diagonal[i] = T::RealField::zero();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
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;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2019-03-18 18:23:19 +08:00
|
|
|
|
if is_upper_diagonal {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if k + 1 != end {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
v.x = -rot.s().real() * off_diagonal[k + 1].clone();
|
|
|
|
|
v.y = diagonal[k + 2].clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
off_diagonal[k + 1] *= rot.c();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
2018-02-02 19:26:35 +08:00
|
|
|
|
} else {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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
|
|
|
|
) {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let mut v = Vector2::new(diagonal[i].clone(), off_diagonal[i].clone());
|
2021-04-11 17:00:38 +08:00
|
|
|
|
off_diagonal[i] = T::RealField::zero();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
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;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2019-03-18 18:23:19 +08:00
|
|
|
|
if is_upper_diagonal {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
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));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if k > 0 {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
v.x = diagonal[k - 1].clone();
|
|
|
|
|
v.y = rot.s().real() * off_diagonal[k - 1].clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
off_diagonal[k - 1] *= rot.c();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
2018-02-02 19:26:35 +08:00
|
|
|
|
} else {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the rank of the decomposed matrix, i.e., the number of singular values greater
|
|
|
|
|
/// than `eps`.
|
2021-06-07 22:34:03 +08:00
|
|
|
|
#[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."
|
|
|
|
|
);
|
2019-03-19 21:22:59 +08:00
|
|
|
|
self.singular_values.iter().filter(|e| **e > eps).count()
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Rebuild the original matrix.
|
|
|
|
|
///
|
2018-10-09 05:13:56 +08:00
|
|
|
|
/// 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> {
|
2018-10-09 05:13:56 +08:00
|
|
|
|
match (self.u, self.v_t) {
|
2018-10-10 04:21:00 +08:00
|
|
|
|
(Some(mut u), Some(v_t)) => {
|
2018-10-09 05:13:56 +08:00
|
|
|
|
for i in 0..self.singular_values.len() {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let val = self.singular_values[i].clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
u.column_mut(i).scale_mut(val);
|
2018-10-09 05:13:56 +08:00
|
|
|
|
}
|
|
|
|
|
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."),
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the pseudo-inverse of the decomposed matrix.
|
|
|
|
|
///
|
|
|
|
|
/// Any singular value smaller than `eps` is assumed to be zero.
|
2018-10-09 05:13:56 +08:00
|
|
|
|
/// 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() {
|
2018-10-09 05:13:56 +08:00
|
|
|
|
Err("SVD pseudo inverse: the epsilon must be non-negative.")
|
2020-03-21 19:16:46 +08:00
|
|
|
|
} else {
|
2018-10-09 05:13:56 +08:00
|
|
|
|
for i in 0..self.singular_values.len() {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let val = self.singular_values[i].clone();
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2019-03-19 21:22:59 +08:00
|
|
|
|
if val > eps {
|
2021-04-11 17:00:38 +08:00
|
|
|
|
self.singular_values[i] = T::RealField::one() / val;
|
2018-10-09 05:13:56 +08:00
|
|
|
|
} else {
|
2021-04-11 17:00:38 +08:00
|
|
|
|
self.singular_values[i] = T::RealField::zero();
|
2018-10-09 05:13:56 +08:00
|
|
|
|
}
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 18:48:12 +08:00
|
|
|
|
self.recompose().map(|m| m.adjoint())
|
2018-10-09 05:13:56 +08:00
|
|
|
|
}
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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.
|
2018-10-09 05:13:56 +08:00
|
|
|
|
/// 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() {
|
2018-10-09 05:13:56 +08:00
|
|
|
|
Err("SVD solve: the epsilon must be non-negative.")
|
2020-03-21 19:16:46 +08:00
|
|
|
|
} else {
|
2018-10-09 05:13:56 +08:00
|
|
|
|
match (&self.u, &self.v_t) {
|
|
|
|
|
(Some(u), Some(v_t)) => {
|
2019-03-23 18:48:12 +08:00
|
|
|
|
let mut ut_b = u.ad_mul(b);
|
2018-10-09 05:13:56 +08:00
|
|
|
|
|
|
|
|
|
for j in 0..ut_b.ncols() {
|
|
|
|
|
let mut col = ut_b.column_mut(j);
|
|
|
|
|
|
|
|
|
|
for i in 0..self.singular_values.len() {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let val = self.singular_values[i].clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
if val > eps {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
col[i] = col[i].clone().unscale(val);
|
2018-10-09 05:13:56 +08:00
|
|
|
|
} else {
|
2021-04-11 17:00:38 +08:00
|
|
|
|
col[i] = T::zero();
|
2018-10-09 05:13:56 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2019-03-23 18:48:12 +08:00
|
|
|
|
Ok(v_t.ad_mul(&ut_b))
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
2018-10-09 05:13:56 +08:00
|
|
|
|
(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."),
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-31 05:15:04 +08:00
|
|
|
|
/// converts SVD results to Polar decomposition form of the original Matrix: `A = P' * U`.
|
|
|
|
|
///
|
2021-12-27 10:01:05 +08:00
|
|
|
|
/// The polar decomposition used here is Left Polar Decomposition (or Reverse Polar Decomposition)
|
2021-12-31 05:15:04 +08:00
|
|
|
|
/// Returns None if the singular vectors of the SVD haven't been calculated
|
2021-12-27 10:01:05 +08:00
|
|
|
|
pub fn to_polar(&self) -> Option<(OMatrix<T, R, R>, OMatrix<T, R, C>)>
|
|
|
|
|
where
|
|
|
|
|
DefaultAllocator: Allocator<T, R, C> //result
|
2021-12-22 13:12:27 +08:00
|
|
|
|
+ Allocator<T, DimMinimum<R, C>, R> // adjoint
|
|
|
|
|
+ Allocator<T, DimMinimum<R, C>> // mapped vals
|
2021-12-27 10:01:05 +08:00
|
|
|
|
+ Allocator<T, R, R> // result
|
|
|
|
|
+ Allocator<T, DimMinimum<R, C>, DimMinimum<R, C>>, // square matrix
|
2021-12-22 13:12:27 +08:00
|
|
|
|
{
|
|
|
|
|
match (&self.u, &self.v_t) {
|
2021-12-27 10:01:05 +08:00
|
|
|
|
(Some(u), Some(v_t)) => Some((
|
2021-12-22 13:12:27 +08:00
|
|
|
|
u * OMatrix::from_diagonal(&self.singular_values.map(|e| T::from_real(e)))
|
|
|
|
|
* u.adjoint(),
|
|
|
|
|
u * v_t,
|
|
|
|
|
)),
|
2021-12-27 10:01:05 +08:00
|
|
|
|
_ => None,
|
2021-12-22 13:12:27 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 17:27:53 +08:00
|
|
|
|
impl<T: ComplexField, R: DimMin<C>, C: Dim> SVD<T, R, C>
|
|
|
|
|
where
|
|
|
|
|
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
|
|
|
|
|
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>>
|
|
|
|
|
+ Allocator<(usize, usize), DimMinimum<R, C>> // for sorted singular values
|
|
|
|
|
+ Allocator<(T::RealField, usize), DimMinimum<R, C>>, // for sorted singular values
|
|
|
|
|
{
|
|
|
|
|
/// Computes the Singular Value Decomposition of `matrix` using implicit shift.
|
|
|
|
|
/// The singular values are guaranteed to be sorted in descending order.
|
|
|
|
|
/// If this order is not required consider using `new_unordered`.
|
|
|
|
|
pub fn new(matrix: OMatrix<T, R, C>, compute_u: bool, compute_v: bool) -> Self {
|
|
|
|
|
let mut svd = Self::new_unordered(matrix, compute_u, compute_v);
|
2021-12-09 18:52:37 +08:00
|
|
|
|
|
|
|
|
|
if !Self::use_special_always_ordered_svd3() && !Self::use_special_always_ordered_svd2() {
|
|
|
|
|
svd.sort_by_singular_values();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 17:27:53 +08:00
|
|
|
|
svd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift.
|
|
|
|
|
/// The singular values are guaranteed to be sorted in descending order.
|
|
|
|
|
/// If this order is not required consider using `try_new_unordered`.
|
|
|
|
|
///
|
|
|
|
|
/// # 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.
|
|
|
|
|
/// * `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.
|
|
|
|
|
pub fn try_new(
|
|
|
|
|
matrix: OMatrix<T, R, C>,
|
|
|
|
|
compute_u: bool,
|
|
|
|
|
compute_v: bool,
|
|
|
|
|
eps: T::RealField,
|
|
|
|
|
max_niter: usize,
|
|
|
|
|
) -> Option<Self> {
|
|
|
|
|
Self::try_new_unordered(matrix, compute_u, compute_v, eps, max_niter).map(|mut svd| {
|
2021-12-09 18:52:37 +08:00
|
|
|
|
if !Self::use_special_always_ordered_svd3() && !Self::use_special_always_ordered_svd2()
|
|
|
|
|
{
|
|
|
|
|
svd.sort_by_singular_values();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 17:27:53 +08:00
|
|
|
|
svd
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sort the estimated components of the SVD by its singular values in descending order.
|
|
|
|
|
/// Such an ordering is often implicitly required when the decompositions are used for estimation or fitting purposes.
|
|
|
|
|
/// Using this function is only required if `new_unordered` or `try_new_unorderd` were used and the specific sorting is required afterward.
|
|
|
|
|
pub fn sort_by_singular_values(&mut self) {
|
|
|
|
|
const VALUE_PROCESSED: usize = usize::MAX;
|
|
|
|
|
|
|
|
|
|
// Collect the singular values with their original index, ...
|
|
|
|
|
let mut singular_values = self.singular_values.map_with_location(|r, _, e| (e, r));
|
|
|
|
|
assert_ne!(
|
|
|
|
|
singular_values.data.shape().0.value(),
|
|
|
|
|
VALUE_PROCESSED,
|
|
|
|
|
"Too many singular values"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// ... sort the singular values, ...
|
|
|
|
|
singular_values
|
|
|
|
|
.as_mut_slice()
|
|
|
|
|
.sort_unstable_by(|(a, _), (b, _)| b.partial_cmp(a).expect("Singular value was NaN"));
|
|
|
|
|
|
|
|
|
|
// ... and store them.
|
|
|
|
|
self.singular_values
|
|
|
|
|
.zip_apply(&singular_values, |value, (new_value, _)| {
|
|
|
|
|
value.clone_from(&new_value)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Calculate required permutations given the sorted indices.
|
|
|
|
|
// We need to identify all circles to calculate the required swaps.
|
|
|
|
|
let mut permutations =
|
|
|
|
|
crate::PermutationSequence::identity_generic(singular_values.data.shape().0);
|
|
|
|
|
|
|
|
|
|
for i in 0..singular_values.len() {
|
|
|
|
|
let mut index_1 = i;
|
|
|
|
|
let mut index_2 = singular_values[i].1;
|
|
|
|
|
|
|
|
|
|
// Check whether the value was already visited ...
|
|
|
|
|
while index_2 != VALUE_PROCESSED // ... or a "double swap" must be avoided.
|
|
|
|
|
&& singular_values[index_2].1 != VALUE_PROCESSED
|
|
|
|
|
{
|
|
|
|
|
// Add the permutation ...
|
|
|
|
|
permutations.append_permutation(index_1, index_2);
|
|
|
|
|
// ... and mark the value as visited.
|
|
|
|
|
singular_values[index_1].1 = VALUE_PROCESSED;
|
|
|
|
|
|
|
|
|
|
index_1 = index_2;
|
|
|
|
|
index_2 = singular_values[index_1].1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Permute the optional components
|
|
|
|
|
if let Some(u) = self.u.as_mut() {
|
|
|
|
|
permutations.permute_columns(u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(v_t) = self.v_t.as_mut() {
|
|
|
|
|
permutations.permute_rows(v_t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// Computes the singular values of this matrix.
|
2021-11-08 17:27:53 +08:00
|
|
|
|
/// The singular values are not guaranteed to be sorted in any particular order.
|
|
|
|
|
/// If a descending order is required, consider using `singular_values` instead.
|
2021-06-07 22:34:03 +08:00
|
|
|
|
#[must_use]
|
2021-11-08 17:27:53 +08:00
|
|
|
|
pub fn singular_values_unordered(&self) -> OVector<T::RealField, DimMinimum<R, C>> {
|
|
|
|
|
SVD::new_unordered(self.clone_owned(), false, false).singular_values
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the rank of this matrix.
|
|
|
|
|
///
|
2017-10-27 12:13:35 +08:00
|
|
|
|
/// All singular values below `eps` are considered equal to 0.
|
2021-06-07 22:34:03 +08:00
|
|
|
|
#[must_use]
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn rank(&self, eps: T::RealField) -> usize {
|
2021-11-08 17:27:53 +08:00
|
|
|
|
let svd = SVD::new_unordered(self.clone_owned(), false, false);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
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
|
|
|
|
{
|
2021-11-08 17:27:53 +08:00
|
|
|
|
SVD::new_unordered(self.clone_owned(), true, true).pseudo_inverse(eps)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: ComplexField, R: DimMin<C>, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S>
|
|
|
|
|
where
|
|
|
|
|
DimMinimum<R, C>: DimSub<U1>,
|
|
|
|
|
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>>
|
|
|
|
|
+ Allocator<(usize, usize), DimMinimum<R, C>>
|
|
|
|
|
+ Allocator<(T::RealField, usize), DimMinimum<R, C>>,
|
|
|
|
|
{
|
|
|
|
|
/// Computes the singular values of this matrix.
|
|
|
|
|
/// The singular values are guaranteed to be sorted in descending order.
|
|
|
|
|
/// If this order is not required consider using `singular_values_unordered`.
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn singular_values(&self) -> OVector<T::RealField, DimMinimum<R, C>> {
|
|
|
|
|
SVD::new(self.clone_owned(), false, false).singular_values
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
|
|
|
|
// 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,
|
2019-03-19 21:22:59 +08:00
|
|
|
|
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);
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let denom = (m11.clone() + m22.clone()).hypot(m12.clone())
|
|
|
|
|
+ (m11.clone() - m22.clone()).hypot(m12.clone());
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
|
|
|
|
// 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.
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let mut v1 = m11.clone() * m22.clone() * two / denom.clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
let mut v2 = half * denom;
|
|
|
|
|
|
|
|
|
|
let mut u = None;
|
|
|
|
|
let mut v_t = None;
|
|
|
|
|
|
|
|
|
|
if compute_u || compute_v {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
let (csv, sgn_v) = GivensRotation::new(
|
|
|
|
|
m11.clone() * m12.clone(),
|
|
|
|
|
v1.clone() * v1.clone() - m11.clone() * m11.clone(),
|
|
|
|
|
);
|
|
|
|
|
v1 *= sgn_v.clone();
|
2019-03-19 21:22:59 +08:00
|
|
|
|
v2 *= sgn_v;
|
|
|
|
|
|
|
|
|
|
if compute_v {
|
2021-08-04 23:34:25 +08:00
|
|
|
|
v_t = Some(csv.clone());
|
2019-03-19 21:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-09 15:13:12 +08:00
|
|
|
|
let cu = (m11.scale(csv.c()) + m12 * csv.s()) / v1.clone();
|
|
|
|
|
let su = (m22 * csv.s()) / v1.clone();
|
|
|
|
|
let (csu, sgn_u) = GivensRotation::new(cu, su);
|
|
|
|
|
v1 *= sgn_u.clone();
|
|
|
|
|
v2 *= sgn_u;
|
2019-03-19 21:22:59 +08:00
|
|
|
|
|
2022-03-09 15:13:12 +08:00
|
|
|
|
if compute_u {
|
2019-03-19 21:22:59 +08:00
|
|
|
|
u = Some(csu);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(u, Vector2::new(v1, v2), v_t)
|
2020-03-21 19:16:46 +08:00
|
|
|
|
}
|