add pseudo inverse for symmetric eigenvalue decomposition
This commit is contained in:
parent
9bc7f8b0d8
commit
c827c7f455
|
@ -8,6 +8,7 @@ use crate::allocator::Allocator;
|
||||||
use crate::base::{DefaultAllocator, Matrix2, OMatrix, OVector, SquareMatrix, Vector2};
|
use crate::base::{DefaultAllocator, Matrix2, OMatrix, OVector, SquareMatrix, Vector2};
|
||||||
use crate::dimension::{Dim, DimDiff, DimSub, U1};
|
use crate::dimension::{Dim, DimDiff, DimSub, U1};
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
|
use crate::RowOVector;
|
||||||
use simba::scalar::ComplexField;
|
use simba::scalar::ComplexField;
|
||||||
|
|
||||||
use crate::linalg::givens::GivensRotation;
|
use crate::linalg::givens::GivensRotation;
|
||||||
|
@ -295,6 +296,71 @@ where
|
||||||
u_t.adjoint_mut();
|
u_t.adjoint_mut();
|
||||||
&self.eigenvectors * u_t
|
&self.eigenvectors * u_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes the pseudo-inverse of this matrix.
|
||||||
|
///
|
||||||
|
/// Calculate a generalized inverse of a complex Hermitian/real symmetric
|
||||||
|
/// matrix using its eigenvalue decomposition and including all eigenvalues
|
||||||
|
/// with 'large' absolute value.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `atol` − absolute threshold term, if `None` provided value is 0.
|
||||||
|
/// * `rtol` − relative threshold term, if `None` provided value is `N * eps` where
|
||||||
|
/// `eps` is the machine precision value of the `T::RealField`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn pseudo_inverse(
|
||||||
|
&self,
|
||||||
|
atol: Option<T::RealField>,
|
||||||
|
rtol: Option<T::RealField>,
|
||||||
|
) -> OMatrix<T, D, D>
|
||||||
|
where
|
||||||
|
DefaultAllocator: Allocator<usize, D>,
|
||||||
|
DefaultAllocator: Allocator<T, U1, D>,
|
||||||
|
{
|
||||||
|
let u = &self.eigenvectors;
|
||||||
|
let s = &self.eigenvalues;
|
||||||
|
let max_s = s.camax();
|
||||||
|
let atol = atol.unwrap_or(T::RealField::zero());
|
||||||
|
let rtol =
|
||||||
|
rtol.unwrap_or(T::RealField::default_epsilon() * crate::convert(u.ncols() as f64));
|
||||||
|
assert!(
|
||||||
|
rtol >= T::RealField::zero() && atol >= T::RealField::zero(),
|
||||||
|
"atol and rtol values must be positive.",
|
||||||
|
);
|
||||||
|
let val = atol + max_s * rtol;
|
||||||
|
let mut above_cutoff = OVector::<usize, D>::zeros_generic(u.shape_generic().0, U1);
|
||||||
|
let mut r_take = 0;
|
||||||
|
for i in 0..s.len() {
|
||||||
|
if s[i].clone().abs() > val {
|
||||||
|
above_cutoff[r_take] = i;
|
||||||
|
r_take += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let psigma_diag = RowOVector::<T, D>::from_fn_generic(U1, u.shape_generic().0, |_, j| {
|
||||||
|
if j < r_take {
|
||||||
|
T::from_real(s[above_cutoff[j]].clone().recip())
|
||||||
|
} else {
|
||||||
|
T::zero()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let u = OMatrix::<T, D, D>::from_fn_generic(
|
||||||
|
u.shape_generic().0,
|
||||||
|
u.shape_generic().1,
|
||||||
|
|i, j| {
|
||||||
|
if j < r_take {
|
||||||
|
u[(i, above_cutoff[j])].clone()
|
||||||
|
} else {
|
||||||
|
T::zero()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let mut up = u.clone();
|
||||||
|
for i in 0..u.nrows() {
|
||||||
|
up.row_mut(i).component_mul_assign(&psigma_diag);
|
||||||
|
}
|
||||||
|
up * u.conjugate().transpose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the wilkinson shift, i.e., the 2x2 symmetric matrix eigenvalue to its tailing
|
/// Computes the wilkinson shift, i.e., the 2x2 symmetric matrix eigenvalue to its tailing
|
||||||
|
|
|
@ -62,6 +62,23 @@ mod proptest_tests {
|
||||||
|
|
||||||
prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5))
|
prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn symmetric_eigen_pseudo_inverse(m in dmatrix_($scalar)) {
|
||||||
|
let eig = m.clone().symmetric_eigen();
|
||||||
|
let pinv = eig.pseudo_inverse(None, None);
|
||||||
|
prop_assert!(relative_eq!(
|
||||||
|
m,
|
||||||
|
&m*&pinv*&m,
|
||||||
|
epsilon = 1.0e-5
|
||||||
|
));
|
||||||
|
prop_assert!(relative_eq!(
|
||||||
|
pinv,
|
||||||
|
&pinv*m*&pinv,
|
||||||
|
epsilon = 1.0e-5
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue