Start fixing SVD.
This commit is contained in:
parent
010c009cff
commit
e4748c69ce
|
@ -61,10 +61,10 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies the reflection to the rows of `rhs`.
|
/// Applies the reflection to the rows of `lhs`.
|
||||||
pub fn reflect_rows<R2: Dim, C2: Dim, S2, S3>(
|
pub fn reflect_rows<R2: Dim, C2: Dim, S2, S3>(
|
||||||
&self,
|
&self,
|
||||||
rhs: &mut Matrix<N, R2, C2, S2>,
|
lhs: &mut Matrix<N, R2, C2, S2>,
|
||||||
work: &mut Vector<N, R2, S3>,
|
work: &mut Vector<N, R2, S3>,
|
||||||
) where
|
) where
|
||||||
S2: StorageMut<N, R2, C2>,
|
S2: StorageMut<N, R2, C2>,
|
||||||
|
@ -72,13 +72,13 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
|
||||||
ShapeConstraint: DimEq<C2, D> + AreMultipliable<R2, C2, D, U1>,
|
ShapeConstraint: DimEq<C2, D> + AreMultipliable<R2, C2, D, U1>,
|
||||||
DefaultAllocator: Allocator<N, D>
|
DefaultAllocator: Allocator<N, D>
|
||||||
{
|
{
|
||||||
rhs.mul_to(&self.axis, work);
|
lhs.mul_to(&self.axis, work);
|
||||||
|
|
||||||
if !self.bias.is_zero() {
|
if !self.bias.is_zero() {
|
||||||
work.add_scalar_mut(-self.bias);
|
work.add_scalar_mut(-self.bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
let m_two: N = ::convert(-2.0f64);
|
let m_two: N = ::convert(-2.0f64);
|
||||||
rhs.ger(m_two, &work, &self.axis.conjugate(), N::one());
|
lhs.ger(m_two, &work, &self.axis.conjugate(), N::one());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,9 @@ where
|
||||||
// contiguous. This prevents some useless copies.
|
// contiguous. This prevents some useless copies.
|
||||||
uv: MatrixMN<N, R, C>,
|
uv: MatrixMN<N, R, C>,
|
||||||
/// The diagonal elements of the decomposed matrix.
|
/// The diagonal elements of the decomposed matrix.
|
||||||
pub diagonal: VectorN<N, DimMinimum<R, C>>,
|
diagonal: VectorN<N, DimMinimum<R, C>>,
|
||||||
/// The off-diagonal elements of the decomposed matrix.
|
/// The off-diagonal elements of the decomposed matrix.
|
||||||
pub off_diagonal: VectorN<N, DimDiff<DimMinimum<R, C>, U1>>,
|
off_diagonal: VectorN<N, DimDiff<DimMinimum<R, C>, U1>>,
|
||||||
upper_diagonal: bool,
|
upper_diagonal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +143,9 @@ where
|
||||||
|
|
||||||
Bidiagonal {
|
Bidiagonal {
|
||||||
uv: matrix,
|
uv: matrix,
|
||||||
diagonal: diagonal,
|
diagonal,
|
||||||
off_diagonal: off_diagonal,
|
off_diagonal,
|
||||||
upper_diagonal: upper_diagonal,
|
upper_diagonal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ where
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the orthogonal matrix `V` of this `U * D * V` decomposition.
|
/// Computes the orthogonal matrix `V_t` of this `U * D * V_t` decomposition.
|
||||||
pub fn v_t(&self) -> MatrixMN<N, DimMinimum<R, C>, C>
|
pub fn v_t(&self) -> MatrixMN<N, DimMinimum<R, C>, C>
|
||||||
where DefaultAllocator: Allocator<N, DimMinimum<R, C>, C> {
|
where DefaultAllocator: Allocator<N, DimMinimum<R, C>, C> {
|
||||||
let (nrows, ncols) = self.uv.data.shape();
|
let (nrows, ncols) = self.uv.data.shape();
|
||||||
|
@ -258,13 +258,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The diagonal part of this decomposed matrix.
|
/// The diagonal part of this decomposed matrix.
|
||||||
pub fn diagonal(&self) -> &VectorN<N, DimMinimum<R, C>> {
|
pub fn diagonal(&self) -> VectorN<N, DimMinimum<R, C>> {
|
||||||
&self.diagonal
|
self.diagonal.map(|e| N::from_real(e.modulus()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The off-diagonal part of this decomposed matrix.
|
/// The off-diagonal part of this decomposed matrix.
|
||||||
pub fn off_diagonal(&self) -> &VectorN<N, DimDiff<DimMinimum<R, C>, U1>> {
|
pub fn off_diagonal(&self) -> VectorN<N, DimDiff<DimMinimum<R, C>, U1>> {
|
||||||
&self.off_diagonal
|
self.off_diagonal.map(|e| N::from_real(e.modulus()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Construction of givens rotations.
|
//! Construction of givens rotations.
|
||||||
|
|
||||||
use alga::general::{Complex, Real};
|
use alga::general::{Complex, Real};
|
||||||
use num::Zero;
|
use num::{Zero, One};
|
||||||
use num_complex::Complex as NumComplex;
|
use num_complex::Complex as NumComplex;
|
||||||
|
|
||||||
use base::dimension::{Dim, U2};
|
use base::dimension::{Dim, U2};
|
||||||
|
@ -12,60 +12,37 @@ use base::{Vector, Matrix};
|
||||||
use geometry::UnitComplex;
|
use geometry::UnitComplex;
|
||||||
|
|
||||||
/// A Givens rotation.
|
/// A Givens rotation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct GivensRotation<N: Complex> {
|
pub struct GivensRotation<N: Complex> {
|
||||||
// FIXME: c should be a `N::Real`.
|
c: N::Real,
|
||||||
c: N,
|
|
||||||
s: N
|
s: N
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: remove this
|
|
||||||
/// Computes the rotation `R` required such that the `y` component of `R * v` is zero.
|
|
||||||
///
|
|
||||||
/// Returns `None` if no rotation is needed (i.e. if `v.y == 0`). Otherwise, this returns the norm
|
|
||||||
/// of `v` and the rotation `r` such that `R * v = [ |v|, 0.0 ]^t` where `|v|` is the norm of `v`.
|
|
||||||
pub fn cancel_y<N: Real, S: Storage<N, U2>>(v: &Vector<N, U2, S>) -> Option<(UnitComplex<N>, N)> {
|
|
||||||
if !v[1].is_zero() {
|
|
||||||
let c = NumComplex::new(v[0], -v[1]);
|
|
||||||
Some(UnitComplex::from_complex_and_get(c))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: remove this
|
|
||||||
/// Computes the rotation `R` required such that the `x` component of `R * v` is zero.
|
|
||||||
///
|
|
||||||
/// Returns `None` if no rotation is needed (i.e. if `v.x == 0`). Otherwise, this returns the norm
|
|
||||||
/// of `v` and the rotation `r` such that `R * v = [ 0.0, |v| ]^t` where `|v|` is the norm of `v`.
|
|
||||||
pub fn cancel_x<N: Real, S: Storage<N, U2>>(v: &Vector<N, U2, S>) -> Option<(UnitComplex<N>, N)> {
|
|
||||||
if !v[0].is_zero() {
|
|
||||||
let c = NumComplex::new(v[1], v[0]);
|
|
||||||
Some(UnitComplex::from_complex_and_get(c))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Matrix = UnitComplex * Matrix
|
// Matrix = UnitComplex * Matrix
|
||||||
impl<N: Complex> GivensRotation<N> {
|
impl<N: Complex> GivensRotation<N> {
|
||||||
|
/// The Givents rotation that does nothing.
|
||||||
|
pub fn identity() -> Self {
|
||||||
|
Self {
|
||||||
|
c: N::Real::one(),
|
||||||
|
s: N::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initializes a Givens rotation from its non-normalized cosine an sine components.
|
/// Initializes a Givens rotation from its non-normalized cosine an sine components.
|
||||||
pub fn new(c: N, s: N) -> Self {
|
pub fn new(c: N, s: N) -> (Self, N) {
|
||||||
let res = Self::try_new(c, s, N::Real::zero()).unwrap();
|
Self::try_new(c, s, N::Real::zero()).unwrap()
|
||||||
println!("The rot: {:?}", res);
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes a Givens rotation form its non-normalized cosine an sine components.
|
/// Initializes a Givens rotation form its non-normalized cosine an sine components.
|
||||||
pub fn try_new(c: N, s: N, eps: N::Real) -> Option<Self> {
|
pub fn try_new(c: N, s: N, eps: N::Real) -> Option<(Self, N)> {
|
||||||
let (mod0, sign0) = c.to_exp();
|
let (mod0, sign0) = c.to_exp();
|
||||||
let denom = (mod0 * mod0 + s.modulus_squared()).sqrt();
|
let denom = (mod0 * mod0 + s.modulus_squared()).sqrt();
|
||||||
|
|
||||||
if denom > eps {
|
if denom > eps {
|
||||||
let c = N::from_real(mod0 / denom);
|
let norm = sign0.scale(denom);
|
||||||
let s = s / sign0.scale(denom);
|
let c = mod0 / denom;
|
||||||
Some(Self { c, s })
|
let s = s / norm;
|
||||||
|
Some((Self { c, s }, norm))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -79,8 +56,8 @@ impl<N: Complex> GivensRotation<N> {
|
||||||
if !v[1].is_zero() {
|
if !v[1].is_zero() {
|
||||||
let (mod0, sign0) = v[0].to_exp();
|
let (mod0, sign0) = v[0].to_exp();
|
||||||
let denom = (mod0 * mod0 + v[1].modulus_squared()).sqrt();
|
let denom = (mod0 * mod0 + v[1].modulus_squared()).sqrt();
|
||||||
let c = N::from_real(mod0 / denom);
|
let c = mod0 / denom;
|
||||||
let s = (sign0 * v[1].conjugate()).unscale(-denom);
|
let s = -v[1] / sign0.scale(denom);
|
||||||
let r = sign0.scale(denom);
|
let r = sign0.scale(denom);
|
||||||
Some((Self { c, s }, r))
|
Some((Self { c, s }, r))
|
||||||
} else {
|
} else {
|
||||||
|
@ -94,11 +71,11 @@ impl<N: Complex> GivensRotation<N> {
|
||||||
/// of `v` and the rotation `r` such that `R * v = [ 0.0, |v| ]^t` where `|v|` is the norm of `v`.
|
/// of `v` and the rotation `r` such that `R * v = [ 0.0, |v| ]^t` where `|v|` is the norm of `v`.
|
||||||
pub fn cancel_x<S: Storage<N, U2>>(v: &Vector<N, U2, S>) -> Option<(Self, N)> {
|
pub fn cancel_x<S: Storage<N, U2>>(v: &Vector<N, U2, S>) -> Option<(Self, N)> {
|
||||||
if !v[0].is_zero() {
|
if !v[0].is_zero() {
|
||||||
let (mod0, sign0) = v[0].to_exp();
|
let (mod1, sign1) = v[1].to_exp();
|
||||||
let denom = (mod0 * mod0 + v[1].modulus_squared()).sqrt();
|
let denom = (mod1 * mod1 + v[0].modulus_squared()).sqrt();
|
||||||
let c = N::from_real(mod0 / denom);
|
let c = mod1 / denom;
|
||||||
let s = (sign0 * v[1].conjugate()).unscale(denom);
|
let s = (v[0].conjugate() * sign1).unscale(denom);
|
||||||
let r = sign0.scale(denom);
|
let r = sign1.scale(denom);
|
||||||
Some((Self { c, s }, r))
|
Some((Self { c, s }, r))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -106,7 +83,7 @@ impl<N: Complex> GivensRotation<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The cos part of this roration.
|
/// The cos part of this roration.
|
||||||
pub fn c(&self) -> N {
|
pub fn c(&self) -> N::Real {
|
||||||
self.c
|
self.c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +117,8 @@ impl<N: Complex> GivensRotation<N> {
|
||||||
let a = *rhs.get_unchecked((0, j));
|
let a = *rhs.get_unchecked((0, j));
|
||||||
let b = *rhs.get_unchecked((1, j));
|
let b = *rhs.get_unchecked((1, j));
|
||||||
|
|
||||||
*rhs.get_unchecked_mut((0, j)) = c * a + -s.conjugate() * b;
|
*rhs.get_unchecked_mut((0, j)) = a.scale(c) - s.conjugate() * b;
|
||||||
*rhs.get_unchecked_mut((1, j)) = s * a + c * b;
|
*rhs.get_unchecked_mut((1, j)) = s * a + b.scale(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,8 +144,8 @@ impl<N: Complex> GivensRotation<N> {
|
||||||
let a = *lhs.get_unchecked((j, 0));
|
let a = *lhs.get_unchecked((j, 0));
|
||||||
let b = *lhs.get_unchecked((j, 1));
|
let b = *lhs.get_unchecked((j, 1));
|
||||||
|
|
||||||
*lhs.get_unchecked_mut((j, 0)) = c * a + s * b;
|
*lhs.get_unchecked_mut((j, 0)) = a.scale(c) + s * b;
|
||||||
*lhs.get_unchecked_mut((j, 1)) = -s.conjugate() * a + c * b;
|
*lhs.get_unchecked_mut((j, 1)) = -s.conjugate() * a + b.scale(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,20 +23,20 @@ pub fn reflection_axis_mut<N: Complex, D: Dim, S: StorageMut<N, D>>(
|
||||||
let reflection_norm = reflection_sq_norm.sqrt();
|
let reflection_norm = reflection_sq_norm.sqrt();
|
||||||
|
|
||||||
let factor;
|
let factor;
|
||||||
let scaled_norm;
|
let signed_norm;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let (modulus, exp) = column.vget_unchecked(0).to_exp();
|
let (modulus, sign) = column.vget_unchecked(0).to_exp();
|
||||||
scaled_norm = exp.scale(reflection_norm);
|
signed_norm = sign.scale(reflection_norm);
|
||||||
factor = (reflection_sq_norm + modulus * reflection_norm) * ::convert(2.0);
|
factor = (reflection_sq_norm + modulus * reflection_norm) * ::convert(2.0);
|
||||||
*column.vget_unchecked_mut(0) += scaled_norm;
|
*column.vget_unchecked_mut(0) += signed_norm;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !factor.is_zero() {
|
if !factor.is_zero() {
|
||||||
column.unscale_mut(factor.sqrt());
|
column.unscale_mut(factor.sqrt());
|
||||||
(-scaled_norm, true)
|
(-signed_norm, true)
|
||||||
} else {
|
} else {
|
||||||
(-scaled_norm, false)
|
(-signed_norm, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ pub fn clear_column_unchecked<N: Complex, R: Dim, C: Dim>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uses an hoseholder reflection to zero out the `irow`-th row, ending before the `shift + 1`-th
|
/// Uses an householder reflection to zero out the `irow`-th row, ending before the `shift + 1`-th
|
||||||
/// superdiagonal element.
|
/// superdiagonal element.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn clear_row_unchecked<N: Complex, R: Dim, C: Dim>(
|
pub fn clear_row_unchecked<N: Complex, R: Dim, C: Dim>(
|
||||||
|
@ -85,6 +85,7 @@ pub fn clear_row_unchecked<N: Complex, R: Dim, C: Dim>(
|
||||||
axis.tr_copy_from(&top.columns_range(irow + shift..));
|
axis.tr_copy_from(&top.columns_range(irow + shift..));
|
||||||
|
|
||||||
let (reflection_norm, not_zero) = reflection_axis_mut(&mut axis);
|
let (reflection_norm, not_zero) = reflection_axis_mut(&mut axis);
|
||||||
|
axis.conjugate_mut(); // So that reflect_rows actually cancels the first row.
|
||||||
*diag_elt = reflection_norm;
|
*diag_elt = reflection_norm;
|
||||||
|
|
||||||
if not_zero {
|
if not_zero {
|
||||||
|
|
|
@ -30,6 +30,6 @@ pub use self::lu::*;
|
||||||
pub use self::permutation_sequence::*;
|
pub use self::permutation_sequence::*;
|
||||||
pub use self::qr::*;
|
pub use self::qr::*;
|
||||||
pub use self::schur::*;
|
pub use self::schur::*;
|
||||||
pub use self::svd::*;
|
//pub use self::svd::*;
|
||||||
pub use self::symmetric_eigen::*;
|
pub use self::symmetric_eigen::*;
|
||||||
pub use self::symmetric_tridiagonal::*;
|
pub use self::symmetric_tridiagonal::*;
|
||||||
|
|
|
@ -417,10 +417,11 @@ where
|
||||||
if compute_q {
|
if compute_q {
|
||||||
// XXX: we have to build the matrix manually because
|
// XXX: we have to build the matrix manually because
|
||||||
// rot.to_rotation_matrix().unwrap() causes an ICE.
|
// rot.to_rotation_matrix().unwrap() causes an ICE.
|
||||||
|
let c = N::from_real(rot.c());
|
||||||
q = Some(MatrixN::from_column_slice_generic(
|
q = Some(MatrixN::from_column_slice_generic(
|
||||||
dim,
|
dim,
|
||||||
dim,
|
dim,
|
||||||
&[rot.c(), rot.s(), -rot.s().conjugate(), rot.c()],
|
&[c, rot.s(), -rot.s().conjugate(), c],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,9 +480,9 @@ fn compute_2x2_basis<N: Complex, S: Storage<N, U2, U2>>(
|
||||||
// This is necessary for numerical stability of the normalization of the complex
|
// This is necessary for numerical stability of the normalization of the complex
|
||||||
// number.
|
// number.
|
||||||
if x1.modulus() > x2.modulus() {
|
if x1.modulus() > x2.modulus() {
|
||||||
Some(GivensRotation::new(x1, h10))
|
Some(GivensRotation::new(x1, h10).0)
|
||||||
} else {
|
} else {
|
||||||
Some(GivensRotation::new(x2, h10))
|
Some(GivensRotation::new(x2, h10).0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
#[cfg(feature = "serde-serialize")]
|
#[cfg(feature = "serde-serialize")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use num_complex::Complex;
|
use num_complex::Complex as NumComplex;
|
||||||
|
use num::Zero;
|
||||||
use std::ops::MulAssign;
|
use std::ops::MulAssign;
|
||||||
|
use approx::AbsDiffEq;
|
||||||
|
|
||||||
use alga::general::Real;
|
use alga::general::Complex;
|
||||||
use allocator::Allocator;
|
use allocator::Allocator;
|
||||||
use base::{DefaultAllocator, Matrix, Matrix2x3, MatrixMN, Vector2, VectorN};
|
use base::{DefaultAllocator, Matrix, Matrix2x3, MatrixMN, Vector2, VectorN};
|
||||||
use constraint::{SameNumberOfRows, ShapeConstraint};
|
use constraint::{SameNumberOfRows, ShapeConstraint};
|
||||||
use dimension::{Dim, DimDiff, DimMin, DimMinimum, DimSub, U1, U2};
|
use dimension::{Dim, DimDiff, DimMin, DimMinimum, DimSub, U1, U2};
|
||||||
use storage::Storage;
|
use storage::Storage;
|
||||||
|
|
||||||
use geometry::UnitComplex;
|
|
||||||
use linalg::givens;
|
use linalg::givens;
|
||||||
use linalg::symmetric_eigen;
|
use linalg::symmetric_eigen;
|
||||||
use linalg::Bidiagonal;
|
use linalg::Bidiagonal;
|
||||||
|
use linalg::givens::GivensRotation;
|
||||||
|
|
||||||
/// Singular Value Decomposition of a general matrix.
|
/// Singular Value Decomposition of a general matrix.
|
||||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde-serialize",
|
feature = "serde-serialize",
|
||||||
serde(bound(
|
serde(bound(
|
||||||
serialize = "DefaultAllocator: Allocator<N, R, C> +
|
serialize = "DefaultAllocator: Allocator<N, R, C> +
|
||||||
Allocator<N, DimMinimum<R, C>> +
|
Allocator<N, DimMinimum<R, C>> +
|
||||||
Allocator<N, DimMinimum<R, C>, C> +
|
Allocator<N, DimMinimum<R, C>, C> +
|
||||||
Allocator<N, R, DimMinimum<R, C>>,
|
Allocator<N, R, DimMinimum<R, C>>,
|
||||||
|
@ -33,7 +35,7 @@ use linalg::Bidiagonal;
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde-serialize",
|
feature = "serde-serialize",
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "DefaultAllocator: Allocator<N, R, C> +
|
deserialize = "DefaultAllocator: Allocator<N, R, C> +
|
||||||
Allocator<N, DimMinimum<R, C>> +
|
Allocator<N, DimMinimum<R, C>> +
|
||||||
Allocator<N, DimMinimum<R, C>, C> +
|
Allocator<N, DimMinimum<R, C>, C> +
|
||||||
Allocator<N, R, DimMinimum<R, C>>,
|
Allocator<N, R, DimMinimum<R, C>>,
|
||||||
|
@ -43,7 +45,7 @@ use linalg::Bidiagonal;
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SVD<N: Real, R: DimMin<C>, C: Dim>
|
pub struct SVD<N: Complex, R: DimMin<C>, C: Dim>
|
||||||
where DefaultAllocator: Allocator<N, DimMinimum<R, C>, C>
|
where DefaultAllocator: Allocator<N, DimMinimum<R, C>, C>
|
||||||
+ Allocator<N, R, DimMinimum<R, C>>
|
+ Allocator<N, R, DimMinimum<R, C>>
|
||||||
+ Allocator<N, DimMinimum<R, C>>
|
+ Allocator<N, DimMinimum<R, C>>
|
||||||
|
@ -52,11 +54,13 @@ where DefaultAllocator: Allocator<N, DimMinimum<R, C>, C>
|
||||||
pub u: Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
pub u: Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
||||||
/// The right-singular vectors `V^t` of this SVD.
|
/// The right-singular vectors `V^t` of this SVD.
|
||||||
pub v_t: Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
pub v_t: Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
||||||
|
// FIXME: this should a vector of N::Real
|
||||||
|
// because singular values are necessarily real.
|
||||||
/// The singular values of this SVD.
|
/// The singular values of this SVD.
|
||||||
pub singular_values: VectorN<N, DimMinimum<R, C>>,
|
pub singular_values: VectorN<N, DimMinimum<R, C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Real, R: DimMin<C>, C: Dim> Copy for SVD<N, R, C>
|
impl<N: Complex, R: DimMin<C>, C: Dim> Copy for SVD<N, R, C>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, DimMinimum<R, C>, C>
|
DefaultAllocator: Allocator<N, DimMinimum<R, C>, C>
|
||||||
+ Allocator<N, R, DimMinimum<R, C>>
|
+ Allocator<N, R, DimMinimum<R, C>>
|
||||||
|
@ -66,7 +70,7 @@ where
|
||||||
VectorN<N, DimMinimum<R, C>>: Copy,
|
VectorN<N, DimMinimum<R, C>>: Copy,
|
||||||
{}
|
{}
|
||||||
|
|
||||||
impl<N: Real, R: DimMin<C>, C: Dim> SVD<N, R, C>
|
impl<N: Complex, R: DimMin<C>, C: Dim> SVD<N, R, C>
|
||||||
where
|
where
|
||||||
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
|
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
|
||||||
DefaultAllocator: Allocator<N, R, C>
|
DefaultAllocator: Allocator<N, R, C>
|
||||||
|
@ -79,7 +83,7 @@ where
|
||||||
{
|
{
|
||||||
/// Computes the Singular Value Decomposition of `matrix` using implicit shift.
|
/// Computes the Singular Value Decomposition of `matrix` using implicit shift.
|
||||||
pub fn new(matrix: MatrixMN<N, R, C>, compute_u: bool, compute_v: bool) -> Self {
|
pub fn new(matrix: MatrixMN<N, R, C>, compute_u: bool, compute_v: bool) -> Self {
|
||||||
Self::try_new(matrix, compute_u, compute_v, N::default_epsilon(), 0).unwrap()
|
Self::try_new(matrix, compute_u, compute_v, N::Real::default_epsilon(), 0).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift.
|
/// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift.
|
||||||
|
@ -96,7 +100,7 @@ where
|
||||||
mut matrix: MatrixMN<N, R, C>,
|
mut matrix: MatrixMN<N, R, C>,
|
||||||
compute_u: bool,
|
compute_u: bool,
|
||||||
compute_v: bool,
|
compute_v: bool,
|
||||||
eps: N,
|
eps: N::Real,
|
||||||
max_niter: usize,
|
max_niter: usize,
|
||||||
) -> Option<Self>
|
) -> Option<Self>
|
||||||
{
|
{
|
||||||
|
@ -108,18 +112,20 @@ where
|
||||||
let min_nrows_ncols = nrows.min(ncols);
|
let min_nrows_ncols = nrows.min(ncols);
|
||||||
let dim = min_nrows_ncols.value();
|
let dim = min_nrows_ncols.value();
|
||||||
|
|
||||||
let m_amax = matrix.amax();
|
let m_amax = matrix.camax();
|
||||||
|
|
||||||
if !m_amax.is_zero() {
|
if !m_amax.is_zero() {
|
||||||
matrix /= m_amax;
|
matrix.unscale_mut(m_amax);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut b = Bidiagonal::new(matrix);
|
let mut b = Bidiagonal::new(matrix);
|
||||||
let mut u = if compute_u { Some(b.u()) } else { None };
|
let mut u = if compute_u { Some(b.u()) } else { None };
|
||||||
let mut v_t = if compute_v { Some(b.v_t()) } else { None };
|
let mut v_t = if compute_v { Some(b.v_t()) } else { None };
|
||||||
|
let mut diagonal = b.diagonal();
|
||||||
|
let mut off_diagonal = b.off_diagonal();
|
||||||
|
|
||||||
let mut niter = 0;
|
let mut niter = 0;
|
||||||
let (mut start, mut end) = Self::delimit_subproblem(&mut b, &mut u, &mut v_t, dim - 1, eps);
|
let (mut start, mut end) = Self::delimit_subproblem(&mut diagonal, &mut off_diagonal, &mut u, &mut v_t, b.is_upper_diagonal(), dim - 1, eps);
|
||||||
|
|
||||||
while end != start {
|
while end != start {
|
||||||
let subdim = end - start + 1;
|
let subdim = end - start + 1;
|
||||||
|
@ -131,19 +137,19 @@ where
|
||||||
|
|
||||||
let mut vec;
|
let mut vec;
|
||||||
{
|
{
|
||||||
let dm = b.diagonal[m];
|
let dm = diagonal[m];
|
||||||
let dn = b.diagonal[n];
|
let dn = diagonal[n];
|
||||||
let fm = b.off_diagonal[m];
|
let fm = off_diagonal[m];
|
||||||
|
|
||||||
let tmm = dm * dm + b.off_diagonal[m - 1] * b.off_diagonal[m - 1];
|
let tmm = dm * dm + off_diagonal[m - 1] * off_diagonal[m - 1];
|
||||||
let tmn = dm * fm;
|
let tmn = dm * fm;
|
||||||
let tnn = dn * dn + fm * fm;
|
let tnn = dn * dn + fm * fm;
|
||||||
|
|
||||||
let shift = symmetric_eigen::wilkinson_shift(tmm, tnn, tmn);
|
let shift = symmetric_eigen::wilkinson_shift(tmm, tnn, tmn);
|
||||||
|
|
||||||
vec = Vector2::new(
|
vec = Vector2::new(
|
||||||
b.diagonal[start] * b.diagonal[start] - shift,
|
diagonal[start] * diagonal[start] - shift,
|
||||||
b.diagonal[start] * b.off_diagonal[start],
|
diagonal[start] * off_diagonal[start],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,31 +157,31 @@ where
|
||||||
let m12 = if k == n - 1 {
|
let m12 = if k == n - 1 {
|
||||||
N::zero()
|
N::zero()
|
||||||
} else {
|
} else {
|
||||||
b.off_diagonal[k + 1]
|
off_diagonal[k + 1]
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut subm = Matrix2x3::new(
|
let mut subm = Matrix2x3::new(
|
||||||
b.diagonal[k],
|
diagonal[k],
|
||||||
b.off_diagonal[k],
|
off_diagonal[k],
|
||||||
N::zero(),
|
N::zero(),
|
||||||
N::zero(),
|
N::zero(),
|
||||||
b.diagonal[k + 1],
|
diagonal[k + 1],
|
||||||
m12,
|
m12,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some((rot1, norm1)) = givens::cancel_y(&vec) {
|
if let Some((rot1, norm1)) = GivensRotation::cancel_y(&vec) {
|
||||||
rot1.conjugate()
|
rot1.inverse()
|
||||||
.rotate_rows(&mut subm.fixed_columns_mut::<U2>(0));
|
.rotate_rows(&mut subm.fixed_columns_mut::<U2>(0));
|
||||||
|
|
||||||
if k > start {
|
if k > start {
|
||||||
// This is not the first iteration.
|
// This is not the first iteration.
|
||||||
b.off_diagonal[k - 1] = norm1;
|
off_diagonal[k - 1] = norm1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let v = Vector2::new(subm[(0, 0)], subm[(1, 0)]);
|
let v = Vector2::new(subm[(0, 0)], subm[(1, 0)]);
|
||||||
// FIXME: does the case `v.y == 0` ever happen?
|
// FIXME: does the case `v.y == 0` ever happen?
|
||||||
let (rot2, norm2) =
|
let (rot2, norm2) =
|
||||||
givens::cancel_y(&v).unwrap_or((UnitComplex::identity(), subm[(0, 0)]));
|
GivensRotation::cancel_y(&v).unwrap_or((GivensRotation::identity(), subm[(0, 0)]));
|
||||||
rot2.rotate(&mut subm.fixed_columns_mut::<U2>(1));
|
rot2.rotate(&mut subm.fixed_columns_mut::<U2>(1));
|
||||||
subm[(0, 0)] = norm2;
|
subm[(0, 0)] = norm2;
|
||||||
|
|
||||||
|
@ -197,12 +203,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.diagonal[k + 0] = subm[(0, 0)];
|
diagonal[k + 0] = subm[(0, 0)];
|
||||||
b.diagonal[k + 1] = subm[(1, 1)];
|
diagonal[k + 1] = subm[(1, 1)];
|
||||||
b.off_diagonal[k + 0] = subm[(0, 1)];
|
off_diagonal[k + 0] = subm[(0, 1)];
|
||||||
|
|
||||||
if k != n - 1 {
|
if k != n - 1 {
|
||||||
b.off_diagonal[k + 1] = subm[(1, 2)];
|
off_diagonal[k + 1] = subm[(1, 2)];
|
||||||
}
|
}
|
||||||
|
|
||||||
vec.x = subm[(0, 1)];
|
vec.x = subm[(0, 1)];
|
||||||
|
@ -214,16 +220,16 @@ where
|
||||||
} else if subdim == 2 {
|
} else if subdim == 2 {
|
||||||
// Solve the remaining 2x2 subproblem.
|
// Solve the remaining 2x2 subproblem.
|
||||||
let (u2, s, v2) = Self::compute_2x2_uptrig_svd(
|
let (u2, s, v2) = Self::compute_2x2_uptrig_svd(
|
||||||
b.diagonal[start],
|
diagonal[start],
|
||||||
b.off_diagonal[start],
|
off_diagonal[start],
|
||||||
b.diagonal[start + 1],
|
diagonal[start + 1],
|
||||||
compute_u && b.is_upper_diagonal() || compute_v && !b.is_upper_diagonal(),
|
compute_u && b.is_upper_diagonal() || compute_v && !b.is_upper_diagonal(),
|
||||||
compute_v && b.is_upper_diagonal() || compute_u && !b.is_upper_diagonal(),
|
compute_v && b.is_upper_diagonal() || compute_u && !b.is_upper_diagonal(),
|
||||||
);
|
);
|
||||||
|
|
||||||
b.diagonal[start + 0] = s[0];
|
diagonal[start + 0] = s[0];
|
||||||
b.diagonal[start + 1] = s[1];
|
diagonal[start + 1] = s[1];
|
||||||
b.off_diagonal[start] = N::zero();
|
off_diagonal[start] = N::zero();
|
||||||
|
|
||||||
if let Some(ref mut u) = u {
|
if let Some(ref mut u) = u {
|
||||||
let rot = if b.is_upper_diagonal() {
|
let rot = if b.is_upper_diagonal() {
|
||||||
|
@ -247,7 +253,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-delimit the subproblem in case some decoupling occurred.
|
// Re-delimit the subproblem in case some decoupling occurred.
|
||||||
let sub = Self::delimit_subproblem(&mut b, &mut u, &mut v_t, end, eps);
|
let sub = Self::delimit_subproblem(&mut diagonal, &mut off_diagonal, &mut u, &mut v_t, b.is_upper_diagonal(), end, eps);
|
||||||
start = sub.0;
|
start = sub.0;
|
||||||
end = sub.1;
|
end = sub.1;
|
||||||
|
|
||||||
|
@ -257,24 +263,25 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.diagonal *= m_amax;
|
diagonal.scale_mut(m_amax);
|
||||||
|
|
||||||
// Ensure all singular value are non-negative.
|
// Ensure all singular value are non-negative.
|
||||||
for i in 0..dim {
|
for i in 0..dim {
|
||||||
let sval = b.diagonal[i];
|
let sval = diagonal[i];
|
||||||
if sval < N::zero() {
|
let (modulus, sign) = sval.to_exp();
|
||||||
b.diagonal[i] = -sval;
|
if modulus != N::Real::zero() {
|
||||||
|
diagonal[i] = N::from_real(modulus);
|
||||||
|
|
||||||
if let Some(ref mut u) = u {
|
if let Some(ref mut u) = u {
|
||||||
u.column_mut(i).neg_mut();
|
u.column_mut(i).mul_assign(sign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
u: u,
|
u,
|
||||||
v_t: v_t,
|
v_t,
|
||||||
singular_values: b.diagonal,
|
singular_values: diagonal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,10 +294,10 @@ where
|
||||||
m22: N,
|
m22: N,
|
||||||
compute_u: bool,
|
compute_u: bool,
|
||||||
compute_v: bool,
|
compute_v: bool,
|
||||||
) -> (Option<UnitComplex<N>>, Vector2<N>, Option<UnitComplex<N>>)
|
) -> (Option<GivensRotation<N>>, Vector2<N>, Option<GivensRotation<N>>)
|
||||||
{
|
{
|
||||||
let two: N = ::convert(2.0f64);
|
let two: N::Real = ::convert(2.0f64);
|
||||||
let half: N = ::convert(0.5f64);
|
let half: N::Real = ::convert(0.5f64);
|
||||||
|
|
||||||
let denom = (m11 + m22).hypot(m12) + (m11 - m22).hypot(m12);
|
let denom = (m11 + m22).hypot(m12) + (m11 - m22).hypot(m12);
|
||||||
|
|
||||||
|
@ -298,24 +305,29 @@ where
|
||||||
// This prevents cancellation issues when constructing the vector `csv` below. If we chose
|
// 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
|
// otherwise, we would have v1 ~= m11 when m12 is small. This would cause catastrophic
|
||||||
// cancellation on `v1 * v1 - m11 * m11` below.
|
// cancellation on `v1 * v1 - m11 * m11` below.
|
||||||
let v1 = two * m11 * m22 / denom;
|
let mut v1 = (m11 * m22).scale(two / denom);
|
||||||
let v2 = half * denom;
|
let mut v2 = N::from_real(half * denom);
|
||||||
|
|
||||||
let mut u = None;
|
let mut u = None;
|
||||||
let mut v_t = None;
|
let mut v_t = None;
|
||||||
|
|
||||||
if compute_u || compute_v {
|
if compute_u || compute_v {
|
||||||
let csv = Vector2::new(m11 * m12, v1 * v1 - m11 * m11).normalize();
|
let (csv, sgn_v) = GivensRotation::new(m11 * m12, v1 * v1 - m11 * m11);
|
||||||
|
v1 *= sgn_v;
|
||||||
|
v2 *= sgn_v;
|
||||||
|
|
||||||
if compute_v {
|
if compute_v {
|
||||||
v_t = Some(UnitComplex::new_unchecked(Complex::new(csv.x, csv.y)));
|
v_t = Some(csv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if compute_u {
|
if compute_u {
|
||||||
let cu = (m11 * csv.x + m12 * csv.y) / v1;
|
let cu = (m11.scale(csv.c()) + m12 * csv.s()) / v1;
|
||||||
let su = (m22 * csv.y) / v1;
|
let su = (m22 * csv.s()) / v1;
|
||||||
|
let (csu, sgn_u) = GivensRotation::new(cu, su);
|
||||||
|
|
||||||
u = Some(UnitComplex::new_unchecked(Complex::new(cu, su)));
|
v1 *= sgn_u;
|
||||||
|
v2 *= sgn_u;
|
||||||
|
u = Some(csu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,11 +350,13 @@ where
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn delimit_subproblem(
|
fn delimit_subproblem(
|
||||||
b: &mut Bidiagonal<N, R, C>,
|
diagonal: &mut VectorN<N, DimMinimum<R, C>>,
|
||||||
|
off_diagonal: &mut VectorN<N, DimDiff<DimMinimum<R, C>, U1>>,
|
||||||
u: &mut Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
u: &mut Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
||||||
v_t: &mut Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
v_t: &mut Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
||||||
|
is_upper_diagonal: bool,
|
||||||
end: usize,
|
end: usize,
|
||||||
eps: N,
|
eps: N::Real,
|
||||||
) -> (usize, usize)
|
) -> (usize, usize)
|
||||||
{
|
{
|
||||||
let mut n = end;
|
let mut n = end;
|
||||||
|
@ -350,20 +364,20 @@ where
|
||||||
while n > 0 {
|
while n > 0 {
|
||||||
let m = n - 1;
|
let m = n - 1;
|
||||||
|
|
||||||
if b.off_diagonal[m].is_zero()
|
if off_diagonal[m].is_zero()
|
||||||
|| b.off_diagonal[m].abs() <= eps * (b.diagonal[n].abs() + b.diagonal[m].abs())
|
|| off_diagonal[m].modulus() <= eps * (diagonal[n].modulus() + diagonal[m].modulus())
|
||||||
{
|
{
|
||||||
b.off_diagonal[m] = N::zero();
|
off_diagonal[m] = N::zero();
|
||||||
} else if b.diagonal[m].abs() <= eps {
|
} else if diagonal[m].modulus() <= eps {
|
||||||
b.diagonal[m] = N::zero();
|
diagonal[m] = N::zero();
|
||||||
Self::cancel_horizontal_off_diagonal_elt(b, u, v_t, m, m + 1);
|
Self::cancel_horizontal_off_diagonal_elt(diagonal, off_diagonal, u, v_t, is_upper_diagonal, m, m + 1);
|
||||||
|
|
||||||
if m != 0 {
|
if m != 0 {
|
||||||
Self::cancel_vertical_off_diagonal_elt(b, u, v_t, m - 1);
|
Self::cancel_vertical_off_diagonal_elt(diagonal, off_diagonal, u, v_t, is_upper_diagonal, m - 1);
|
||||||
}
|
}
|
||||||
} else if b.diagonal[n].abs() <= eps {
|
} else if diagonal[n].modulus() <= eps {
|
||||||
b.diagonal[n] = N::zero();
|
diagonal[n] = N::zero();
|
||||||
Self::cancel_vertical_off_diagonal_elt(b, u, v_t, m);
|
Self::cancel_vertical_off_diagonal_elt(diagonal, off_diagonal, u, v_t, is_upper_diagonal, m);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -379,18 +393,18 @@ where
|
||||||
while new_start > 0 {
|
while new_start > 0 {
|
||||||
let m = new_start - 1;
|
let m = new_start - 1;
|
||||||
|
|
||||||
if b.off_diagonal[m].abs() <= eps * (b.diagonal[new_start].abs() + b.diagonal[m].abs())
|
if off_diagonal[m].modulus() <= eps * (diagonal[new_start].modulus() + diagonal[m].modulus())
|
||||||
{
|
{
|
||||||
b.off_diagonal[m] = N::zero();
|
off_diagonal[m] = N::zero();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// FIXME: write a test that enters this case.
|
// FIXME: write a test that enters this case.
|
||||||
else if b.diagonal[m].abs() <= eps {
|
else if diagonal[m].modulus() <= eps {
|
||||||
b.diagonal[m] = N::zero();
|
diagonal[m] = N::zero();
|
||||||
Self::cancel_horizontal_off_diagonal_elt(b, u, v_t, m, n);
|
Self::cancel_horizontal_off_diagonal_elt(diagonal, off_diagonal, u, v_t, is_upper_diagonal, m, n);
|
||||||
|
|
||||||
if m != 0 {
|
if m != 0 {
|
||||||
Self::cancel_vertical_off_diagonal_elt(b, u, v_t, m - 1);
|
Self::cancel_vertical_off_diagonal_elt(diagonal, off_diagonal, u, v_t, is_upper_diagonal, m - 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -403,21 +417,23 @@ where
|
||||||
|
|
||||||
// Cancels the i-th off-diagonal element using givens rotations.
|
// Cancels the i-th off-diagonal element using givens rotations.
|
||||||
fn cancel_horizontal_off_diagonal_elt(
|
fn cancel_horizontal_off_diagonal_elt(
|
||||||
b: &mut Bidiagonal<N, R, C>,
|
diagonal: &mut VectorN<N, DimMinimum<R, C>>,
|
||||||
|
off_diagonal: &mut VectorN<N, DimDiff<DimMinimum<R, C>, U1>>,
|
||||||
u: &mut Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
u: &mut Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
||||||
v_t: &mut Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
v_t: &mut Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
||||||
|
is_upper_diagonal: bool,
|
||||||
i: usize,
|
i: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let mut v = Vector2::new(b.off_diagonal[i], b.diagonal[i + 1]);
|
let mut v = Vector2::new(off_diagonal[i], diagonal[i + 1]);
|
||||||
b.off_diagonal[i] = N::zero();
|
off_diagonal[i] = N::zero();
|
||||||
|
|
||||||
for k in i..end {
|
for k in i..end {
|
||||||
if let Some((rot, norm)) = givens::cancel_x(&v) {
|
if let Some((rot, norm)) = GivensRotation::cancel_x(&v) {
|
||||||
b.diagonal[k + 1] = norm;
|
diagonal[k + 1] = norm;
|
||||||
|
|
||||||
if b.is_upper_diagonal() {
|
if is_upper_diagonal {
|
||||||
if let Some(ref mut u) = *u {
|
if let Some(ref mut u) = *u {
|
||||||
rot.inverse()
|
rot.inverse()
|
||||||
.rotate_rows(&mut u.fixed_columns_with_step_mut::<U2>(i, k - i));
|
.rotate_rows(&mut u.fixed_columns_with_step_mut::<U2>(i, k - i));
|
||||||
|
@ -427,9 +443,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if k + 1 != end {
|
if k + 1 != end {
|
||||||
v.x = -rot.sin_angle() * b.off_diagonal[k + 1];
|
v.x = -rot.s() * off_diagonal[k + 1];
|
||||||
v.y = b.diagonal[k + 2];
|
v.y = diagonal[k + 2];
|
||||||
b.off_diagonal[k + 1] *= rot.cos_angle();
|
off_diagonal[k + 1] = off_diagonal[k + 1].scale(rot.c());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -439,20 +455,22 @@ where
|
||||||
|
|
||||||
// Cancels the i-th off-diagonal element using givens rotations.
|
// Cancels the i-th off-diagonal element using givens rotations.
|
||||||
fn cancel_vertical_off_diagonal_elt(
|
fn cancel_vertical_off_diagonal_elt(
|
||||||
b: &mut Bidiagonal<N, R, C>,
|
diagonal: &mut VectorN<N, DimMinimum<R, C>>,
|
||||||
|
off_diagonal: &mut VectorN<N, DimDiff<DimMinimum<R, C>, U1>>,
|
||||||
u: &mut Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
u: &mut Option<MatrixMN<N, R, DimMinimum<R, C>>>,
|
||||||
v_t: &mut Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
v_t: &mut Option<MatrixMN<N, DimMinimum<R, C>, C>>,
|
||||||
|
is_upper_diagonal: bool,
|
||||||
i: usize,
|
i: usize,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let mut v = Vector2::new(b.diagonal[i], b.off_diagonal[i]);
|
let mut v = Vector2::new(diagonal[i], off_diagonal[i]);
|
||||||
b.off_diagonal[i] = N::zero();
|
off_diagonal[i] = N::zero();
|
||||||
|
|
||||||
for k in (0..i + 1).rev() {
|
for k in (0..i + 1).rev() {
|
||||||
if let Some((rot, norm)) = givens::cancel_y(&v) {
|
if let Some((rot, norm)) = GivensRotation::cancel_y(&v) {
|
||||||
b.diagonal[k] = norm;
|
diagonal[k] = norm;
|
||||||
|
|
||||||
if b.is_upper_diagonal() {
|
if is_upper_diagonal {
|
||||||
if let Some(ref mut v_t) = *v_t {
|
if let Some(ref mut v_t) = *v_t {
|
||||||
rot.rotate(&mut v_t.fixed_rows_with_step_mut::<U2>(k, i - k));
|
rot.rotate(&mut v_t.fixed_rows_with_step_mut::<U2>(k, i - k));
|
||||||
}
|
}
|
||||||
|
@ -462,9 +480,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if k > 0 {
|
if k > 0 {
|
||||||
v.x = b.diagonal[k - 1];
|
v.x = diagonal[k - 1];
|
||||||
v.y = rot.sin_angle() * b.off_diagonal[k - 1];
|
v.y = rot.s() * off_diagonal[k - 1];
|
||||||
b.off_diagonal[k - 1] *= rot.cos_angle();
|
off_diagonal[k - 1] = off_diagonal[k - 1].scale(rot.c());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -474,12 +492,12 @@ where
|
||||||
|
|
||||||
/// Computes the rank of the decomposed matrix, i.e., the number of singular values greater
|
/// Computes the rank of the decomposed matrix, i.e., the number of singular values greater
|
||||||
/// than `eps`.
|
/// than `eps`.
|
||||||
pub fn rank(&self, eps: N) -> usize {
|
pub fn rank(&self, eps: N::Real) -> usize {
|
||||||
assert!(
|
assert!(
|
||||||
eps >= N::zero(),
|
eps >= N::Real::zero(),
|
||||||
"SVD rank: the epsilon must be non-negative."
|
"SVD rank: the epsilon must be non-negative."
|
||||||
);
|
);
|
||||||
self.singular_values.iter().filter(|e| **e > eps).count()
|
self.singular_values.iter().filter(|e| e.asum() > eps).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rebuild the original matrix.
|
/// Rebuild the original matrix.
|
||||||
|
@ -507,18 +525,18 @@ where
|
||||||
/// Any singular value smaller than `eps` is assumed to be zero.
|
/// Any singular value smaller than `eps` is assumed to be zero.
|
||||||
/// Returns `Err` if the right- and left- singular vectors have not
|
/// Returns `Err` if the right- and left- singular vectors have not
|
||||||
/// been computed at construction-time.
|
/// been computed at construction-time.
|
||||||
pub fn pseudo_inverse(mut self, eps: N) -> Result<MatrixMN<N, C, R>, &'static str>
|
pub fn pseudo_inverse(mut self, eps: N::Real) -> Result<MatrixMN<N, C, R>, &'static str>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, C, R>,
|
DefaultAllocator: Allocator<N, C, R>,
|
||||||
{
|
{
|
||||||
if eps < N::zero() {
|
if eps < N::Real::zero() {
|
||||||
Err("SVD pseudo inverse: the epsilon must be non-negative.")
|
Err("SVD pseudo inverse: the epsilon must be non-negative.")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for i in 0..self.singular_values.len() {
|
for i in 0..self.singular_values.len() {
|
||||||
let val = self.singular_values[i];
|
let val = self.singular_values[i];
|
||||||
|
|
||||||
if val > eps {
|
if val.asum() > eps {
|
||||||
self.singular_values[i] = N::one() / val;
|
self.singular_values[i] = N::one() / val;
|
||||||
} else {
|
} else {
|
||||||
self.singular_values[i] = N::zero();
|
self.singular_values[i] = N::zero();
|
||||||
|
@ -537,14 +555,14 @@ where
|
||||||
pub fn solve<R2: Dim, C2: Dim, S2>(
|
pub fn solve<R2: Dim, C2: Dim, S2>(
|
||||||
&self,
|
&self,
|
||||||
b: &Matrix<N, R2, C2, S2>,
|
b: &Matrix<N, R2, C2, S2>,
|
||||||
eps: N,
|
eps: N::Real,
|
||||||
) -> Result<MatrixMN<N, C, C2>, &'static str>
|
) -> Result<MatrixMN<N, C, C2>, &'static str>
|
||||||
where
|
where
|
||||||
S2: Storage<N, R2, C2>,
|
S2: Storage<N, R2, C2>,
|
||||||
DefaultAllocator: Allocator<N, C, C2> + Allocator<N, DimMinimum<R, C>, C2>,
|
DefaultAllocator: Allocator<N, C, C2> + Allocator<N, DimMinimum<R, C>, C2>,
|
||||||
ShapeConstraint: SameNumberOfRows<R, R2>,
|
ShapeConstraint: SameNumberOfRows<R, R2>,
|
||||||
{
|
{
|
||||||
if eps < N::zero() {
|
if eps < N::Real::zero() {
|
||||||
Err("SVD solve: the epsilon must be non-negative.")
|
Err("SVD solve: the epsilon must be non-negative.")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -557,7 +575,7 @@ where
|
||||||
|
|
||||||
for i in 0..self.singular_values.len() {
|
for i in 0..self.singular_values.len() {
|
||||||
let val = self.singular_values[i];
|
let val = self.singular_values[i];
|
||||||
if val > eps {
|
if val.asum() > eps {
|
||||||
col[i] /= val;
|
col[i] /= val;
|
||||||
} else {
|
} else {
|
||||||
col[i] = N::zero();
|
col[i] = N::zero();
|
||||||
|
@ -575,7 +593,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Real, R: DimMin<C>, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S>
|
impl<N: Complex, R: DimMin<C>, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S>
|
||||||
where
|
where
|
||||||
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
|
DimMinimum<R, C>: DimSub<U1>, // for Bidiagonal.
|
||||||
DefaultAllocator: Allocator<N, R, C>
|
DefaultAllocator: Allocator<N, R, C>
|
||||||
|
@ -605,7 +623,7 @@ where
|
||||||
self,
|
self,
|
||||||
compute_u: bool,
|
compute_u: bool,
|
||||||
compute_v: bool,
|
compute_v: bool,
|
||||||
eps: N,
|
eps: N::Real,
|
||||||
max_niter: usize,
|
max_niter: usize,
|
||||||
) -> Option<SVD<N, R, C>>
|
) -> Option<SVD<N, R, C>>
|
||||||
{
|
{
|
||||||
|
@ -620,7 +638,7 @@ where
|
||||||
/// Computes the rank of this matrix.
|
/// Computes the rank of this matrix.
|
||||||
///
|
///
|
||||||
/// All singular values below `eps` are considered equal to 0.
|
/// All singular values below `eps` are considered equal to 0.
|
||||||
pub fn rank(&self, eps: N) -> usize {
|
pub fn rank(&self, eps: N::Real) -> usize {
|
||||||
let svd = SVD::new(self.clone_owned(), false, false);
|
let svd = SVD::new(self.clone_owned(), false, false);
|
||||||
svd.rank(eps)
|
svd.rank(eps)
|
||||||
}
|
}
|
||||||
|
@ -628,7 +646,7 @@ where
|
||||||
/// Computes the pseudo-inverse of this matrix.
|
/// Computes the pseudo-inverse of this matrix.
|
||||||
///
|
///
|
||||||
/// All singular values below `eps` are considered equal to 0.
|
/// All singular values below `eps` are considered equal to 0.
|
||||||
pub fn pseudo_inverse(self, eps: N) -> Result<MatrixMN<N, C, R>, &'static str>
|
pub fn pseudo_inverse(self, eps: N::Real) -> Result<MatrixMN<N, C, R>, &'static str>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, C, R>,
|
DefaultAllocator: Allocator<N, C, R>,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[cfg(feature = "serde-serialize")]
|
#[cfg(feature = "serde-serialize")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use num::Zero;
|
use num::{Zero, One};
|
||||||
use num_complex::Complex as NumComplex;
|
use num_complex::Complex as NumComplex;
|
||||||
use approx::AbsDiffEq;
|
use approx::AbsDiffEq;
|
||||||
use std::ops::MulAssign;
|
use std::ops::MulAssign;
|
||||||
|
@ -119,6 +119,8 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>
|
||||||
q = Some(res.0);
|
q = Some(res.0);
|
||||||
diag = res.1;
|
diag = res.1;
|
||||||
off_diag = res.2;
|
off_diag = res.2;
|
||||||
|
|
||||||
|
println!("Tridiagonalization q: {:.5?}", q);
|
||||||
} else {
|
} else {
|
||||||
let res = SymmetricTridiagonal::new(m).unpack_tridiagonal();
|
let res = SymmetricTridiagonal::new(m).unpack_tridiagonal();
|
||||||
q = None;
|
q = None;
|
||||||
|
@ -150,6 +152,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>
|
||||||
let j = i + 1;
|
let j = i + 1;
|
||||||
|
|
||||||
if let Some((rot, norm)) = GivensRotation::cancel_y(&v) {
|
if let Some((rot, norm)) = GivensRotation::cancel_y(&v) {
|
||||||
|
println!("Canceling: {:.5?} with norm: {:.5?}", rot, norm);
|
||||||
if i > start {
|
if i > start {
|
||||||
// Not the first iteration.
|
// Not the first iteration.
|
||||||
off_diag[i - 1] = norm;
|
off_diag[i - 1] = norm;
|
||||||
|
@ -160,19 +163,33 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>
|
||||||
let mij = off_diag[i];
|
let mij = off_diag[i];
|
||||||
|
|
||||||
let cc = rot.c() * rot.c();
|
let cc = rot.c() * rot.c();
|
||||||
let ss = rot.s() * rot.s().conjugate();
|
let ss = rot.s().modulus_squared(); // rot.s() * rot.s().conjugate()
|
||||||
let cs = rot.c() * rot.s();
|
let cs = rot.s().scale(rot.c());
|
||||||
|
|
||||||
let b = cs * mij.conjugate() + cs.conjugate() * mij;
|
// b = cs * mij.conjugate() + cs.conjugate() * mij
|
||||||
|
let b = N::from_real((cs * mij.conjugate()).real() * ::convert(2.0));
|
||||||
|
|
||||||
diag[i] = (cc * mii + ss * mjj) - b;
|
diag[i] = (mii.scale(cc) + mjj.scale(ss)) - b;
|
||||||
diag[j] = (ss * mii + cc * mjj) + b;
|
diag[j] = (mii.scale(ss) + mjj.scale(cc)) + b;
|
||||||
off_diag[i] = cs * (mii - mjj) + mij * cc - mij.conjugate() * rot.s() * rot.s();
|
off_diag[i] = cs * (mii - mjj) + mij.scale(cc) - mij.conjugate() * rot.s() * rot.s();
|
||||||
|
|
||||||
|
let mut mat = Matrix2::new(
|
||||||
|
mii, mij.conjugate(),
|
||||||
|
mij, mjj);
|
||||||
|
println!("The mat before rotate: {:.5}", mat);
|
||||||
|
println!("The v before rotate: {:.5?}", v);
|
||||||
|
rot.rotate(&mut mat);
|
||||||
|
rot.inverse().rotate_rows(&mut mat);
|
||||||
|
let mut v2 = v.clone();
|
||||||
|
rot.rotate(&mut v2);
|
||||||
|
println!("The v: {:.5?}", v2);
|
||||||
|
println!("The mat: {:.5}", mat);
|
||||||
|
println!("Its components: {:.5}, {:.5}, {:.5}", diag[i], diag[j], off_diag[i]);
|
||||||
|
|
||||||
if i != n - 1 {
|
if i != n - 1 {
|
||||||
v.x = off_diag[i];
|
v.x = off_diag[i];
|
||||||
v.y = -rot.s() * off_diag[i + 1];
|
v.y = -rot.s() * off_diag[i + 1];
|
||||||
off_diag[i + 1] *= rot.c();
|
off_diag[i + 1] = off_diag[i + 1].scale(rot.c());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut q) = q {
|
if let Some(ref mut q) = q {
|
||||||
|
@ -197,12 +214,12 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>
|
||||||
diag[start + 0] = eigvals[0];
|
diag[start + 0] = eigvals[0];
|
||||||
diag[start + 1] = eigvals[1];
|
diag[start + 1] = eigvals[1];
|
||||||
|
|
||||||
println!("Eigvals: {:?}", eigvals);
|
println!("Eigvals: {:.5?}", eigvals);
|
||||||
println!("m: {}", m);
|
println!("m: {:.5}", m);
|
||||||
println!("Curr q: {:?}", q);
|
println!("Curr q: {:.5?}", q);
|
||||||
|
|
||||||
if let Some(ref mut q) = q {
|
if let Some(ref mut q) = q {
|
||||||
if let Some(rot) = GivensRotation::try_new(basis.x, basis.y, eps) {
|
if let Some((rot, _)) = GivensRotation::try_new(basis.x, basis.y, eps) {
|
||||||
rot.rotate_rows(&mut q.fixed_columns_mut::<U2>(start));
|
rot.rotate_rows(&mut q.fixed_columns_mut::<U2>(start));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimDiff<D, U1>>
|
||||||
m.ger_symm(-N::one(), &p, &axis.conjugate(), N::one());
|
m.ger_symm(-N::one(), &p, &axis.conjugate(), N::one());
|
||||||
m.ger_symm(-N::one(), &axis, &p.conjugate(), N::one());
|
m.ger_symm(-N::one(), &axis, &p.conjugate(), N::one());
|
||||||
m.ger_symm(dot * ::convert(2.0), &axis, &axis.conjugate(), N::one());
|
m.ger_symm(dot * ::convert(2.0), &axis, &axis.conjugate(), N::one());
|
||||||
|
println!("The m: {}", m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ quickcheck! {
|
||||||
relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)
|
relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn bidiagonal_static_square(m: Matrix4<RandComplex<f64>>) -> bool {
|
fn bidiagonal_static_square(m: Matrix4<RandComplex<f64>>) -> bool {
|
||||||
let m = m.map(|e| e.0);
|
let m = m.map(|e| e.0);
|
||||||
let bidiagonal = m.bidiagonalize();
|
let bidiagonal = m.bidiagonalize();
|
||||||
|
|
|
@ -9,10 +9,9 @@ mod quickcheck_tests {
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
/*
|
|
||||||
fn symmetric_eigen(n: usize) -> bool {
|
fn symmetric_eigen(n: usize) -> bool {
|
||||||
let n = cmp::max(1, cmp::min(n, 10));
|
let n = cmp::max(1, cmp::min(n, 10));
|
||||||
let m = DMatrix::<RandComplex<f64>>::new_random(n, n).map(|e| e.0);
|
let m = DMatrix::<RandComplex<f64>>::new_random(n, n).map(|e| e.0).hermitian_part();
|
||||||
let eig = m.clone().symmetric_eigen();
|
let eig = m.clone().symmetric_eigen();
|
||||||
let recomp = eig.recompose();
|
let recomp = eig.recompose();
|
||||||
|
|
||||||
|
@ -23,7 +22,7 @@ mod quickcheck_tests {
|
||||||
|
|
||||||
fn symmetric_eigen_singular(n: usize) -> bool {
|
fn symmetric_eigen_singular(n: usize) -> bool {
|
||||||
let n = cmp::max(1, cmp::min(n, 10));
|
let n = cmp::max(1, cmp::min(n, 10));
|
||||||
let mut m = DMatrix::<RandComplex<f64>>::new_random(n, n).map(|e| e.0);
|
let mut m = DMatrix::<RandComplex<f64>>::new_random(n, n).map(|e| e.0).hermitian_part();
|
||||||
m.row_mut(n / 2).fill(na::zero());
|
m.row_mut(n / 2).fill(na::zero());
|
||||||
m.column_mut(n / 2).fill(na::zero());
|
m.column_mut(n / 2).fill(na::zero());
|
||||||
let eig = m.clone().symmetric_eigen();
|
let eig = m.clone().symmetric_eigen();
|
||||||
|
@ -35,7 +34,7 @@ mod quickcheck_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symmetric_eigen_static_square_4x4(m: Matrix4<RandComplex<f64>>) -> bool {
|
fn symmetric_eigen_static_square_4x4(m: Matrix4<RandComplex<f64>>) -> bool {
|
||||||
let m = m.map(|e| e.0);
|
let m = m.map(|e| e.0).hermitian_part();
|
||||||
let eig = m.symmetric_eigen();
|
let eig = m.symmetric_eigen();
|
||||||
let recomp = eig.recompose();
|
let recomp = eig.recompose();
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ mod quickcheck_tests {
|
||||||
|
|
||||||
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)
|
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
fn symmetric_eigen_static_square_3x3(m: Matrix3<RandComplex<f64>>) -> bool {
|
fn symmetric_eigen_static_square_3x3(m: Matrix3<RandComplex<f64>>) -> bool {
|
||||||
let m = m.map(|e| e.0).hermitian_part();
|
let m = m.map(|e| e.0).hermitian_part();
|
||||||
|
@ -57,7 +55,6 @@ mod quickcheck_tests {
|
||||||
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)
|
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
fn symmetric_eigen_static_square_2x2(m: Matrix2<RandComplex<f64>>) -> bool {
|
fn symmetric_eigen_static_square_2x2(m: Matrix2<RandComplex<f64>>) -> bool {
|
||||||
let m = m.map(|e| e.0).hermitian_part();
|
let m = m.map(|e| e.0).hermitian_part();
|
||||||
let eig = m.symmetric_eigen();
|
let eig = m.symmetric_eigen();
|
||||||
|
@ -68,11 +65,9 @@ mod quickcheck_tests {
|
||||||
|
|
||||||
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)
|
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Test proposed on the issue #176 of rulinalg.
|
// Test proposed on the issue #176 of rulinalg.
|
||||||
#[test]
|
#[test]
|
||||||
fn symmetric_eigen_singular_24x24() {
|
fn symmetric_eigen_singular_24x24() {
|
||||||
|
@ -115,7 +110,7 @@ fn symmetric_eigen_singular_24x24() {
|
||||||
recomp.lower_triangle(),
|
recomp.lower_triangle(),
|
||||||
epsilon = 1.0e-5
|
epsilon = 1.0e-5
|
||||||
));
|
));
|
||||||
}*/
|
}
|
||||||
|
|
||||||
// #[cfg(feature = "arbitrary")]
|
// #[cfg(feature = "arbitrary")]
|
||||||
// quickcheck! {
|
// quickcheck! {
|
||||||
|
|
|
@ -7,8 +7,11 @@ mod quickcheck_tests {
|
||||||
DMatrix, DVector, Matrix2, Matrix2x5, Matrix3, Matrix3x5, Matrix4, Matrix5x2, Matrix5x3,
|
DMatrix, DVector, Matrix2, Matrix2x5, Matrix3, Matrix3x5, Matrix4, Matrix5x2, Matrix5x3,
|
||||||
};
|
};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use core::helper::{RandScalar, RandComplex};
|
||||||
|
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
|
/*
|
||||||
fn svd(m: DMatrix<f64>) -> bool {
|
fn svd(m: DMatrix<f64>) -> bool {
|
||||||
if m.len() > 0 {
|
if m.len() > 0 {
|
||||||
let svd = m.clone().svd(true, true);
|
let svd = m.clone().svd(true, true);
|
||||||
|
@ -68,6 +71,7 @@ mod quickcheck_tests {
|
||||||
relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)
|
relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn svd_static_square(m: Matrix4<f64>) -> bool {
|
fn svd_static_square(m: Matrix4<f64>) -> bool {
|
||||||
let svd = m.svd(true, true);
|
let svd = m.svd(true, true);
|
||||||
let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
|
let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
|
||||||
|
@ -78,18 +82,27 @@ mod quickcheck_tests {
|
||||||
u.is_orthogonal(1.0e-5) &&
|
u.is_orthogonal(1.0e-5) &&
|
||||||
v_t.is_orthogonal(1.0e-5)
|
v_t.is_orthogonal(1.0e-5)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
fn svd_static_square_2x2(m: Matrix2<f64>) -> bool {
|
|
||||||
|
fn svd_static_square_2x2(m: Matrix2<RandComplex<f64>>) -> bool {
|
||||||
|
let m = m.map(|e| e.0);
|
||||||
let svd = m.svd(true, true);
|
let svd = m.svd(true, true);
|
||||||
let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
|
let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
|
||||||
let ds = Matrix2::from_diagonal(&s);
|
let ds = Matrix2::from_diagonal(&s);
|
||||||
|
|
||||||
s.iter().all(|e| *e >= 0.0) &&
|
println!("u, s, v_t: {}{}{}", u, s, v_t);
|
||||||
|
println!("m: {}", m);
|
||||||
|
println!("recomp: {}", u * ds * v_t);
|
||||||
|
println!("uu_t, vv_t: {}{}", u * u.conjugate_transpose(), v_t.conjugate_transpose() * v_t);
|
||||||
|
|
||||||
|
s.iter().all(|e| e.re >= 0.0) &&
|
||||||
relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5) &&
|
relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5) &&
|
||||||
u.is_orthogonal(1.0e-5) &&
|
u.is_orthogonal(1.0e-5) &&
|
||||||
v_t.is_orthogonal(1.0e-5)
|
v_t.is_orthogonal(1.0e-5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn svd_pseudo_inverse(m: DMatrix<f64>) -> bool {
|
fn svd_pseudo_inverse(m: DMatrix<f64>) -> bool {
|
||||||
if m.len() > 0 {
|
if m.len() > 0 {
|
||||||
let svd = m.clone().svd(true, true);
|
let svd = m.clone().svd(true, true);
|
||||||
|
@ -140,9 +153,11 @@ mod quickcheck_tests {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Test proposed on the issue #176 of rulinalg.
|
// Test proposed on the issue #176 of rulinalg.
|
||||||
#[test]
|
#[test]
|
||||||
fn svd_singular() {
|
fn svd_singular() {
|
||||||
|
@ -342,3 +357,5 @@ fn svd_err() {
|
||||||
assert_eq!(Err("SVD recomposition: U and V^t have not been computed."), svd.clone().recompose());
|
assert_eq!(Err("SVD recomposition: U and V^t have not been computed."), svd.clone().recompose());
|
||||||
assert_eq!(Err("SVD pseudo inverse: the epsilon must be non-negative."), svd.clone().pseudo_inverse(-1.0));
|
assert_eq!(Err("SVD pseudo inverse: the epsilon must be non-negative."), svd.clone().pseudo_inverse(-1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
|
@ -15,6 +15,20 @@ quickcheck! {
|
||||||
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)
|
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn symm_tridiagonal_singular(n: usize) -> bool {
|
||||||
|
let n = cmp::max(1, cmp::min(n, 4));
|
||||||
|
let mut m = DMatrix::<RandComplex<f64>>::new_random(n, n).map(|e| e.0).hermitian_part();
|
||||||
|
m.row_mut(n / 2).fill(na::zero());
|
||||||
|
m.column_mut(n / 2).fill(na::zero());
|
||||||
|
let tri = m.clone().symmetric_tridiagonalize();
|
||||||
|
println!("Tri: {:?}", tri);
|
||||||
|
let recomp = tri.recompose();
|
||||||
|
println!("Recomp: {:?}", recomp);
|
||||||
|
|
||||||
|
|
||||||
|
relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)
|
||||||
|
}
|
||||||
|
|
||||||
fn symm_tridiagonal_static_square(m: Matrix4<RandComplex<f64>>) -> bool {
|
fn symm_tridiagonal_static_square(m: Matrix4<RandComplex<f64>>) -> bool {
|
||||||
let m = m.map(|e| e.0).hermitian_part();
|
let m = m.map(|e| e.0).hermitian_part();
|
||||||
let tri = m.symmetric_tridiagonalize();
|
let tri = m.symmetric_tridiagonalize();
|
||||||
|
|
Loading…
Reference in New Issue