#![allow(non_snake_case)]
use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OMatrix, RealField};
use super::rcond;
pub struct UDU<N: RealField> {
pub zero: N,
pub one: N,
pub minus_one: N,
}
impl<N: Copy + RealField> UDU<N> {
pub fn new() -> UDU<N> {
UDU {
zero: N::zero(),
one: N::one(),
minus_one: N::one().neg(),
}
}
pub fn UdUrcond<R: Dim, C: Dim>(UD: &OMatrix<N, R, C>) -> N
where
DefaultAllocator: Allocator<R, C>,
{
rcond::rcond_symmetric(&UD)
}
pub fn UCrcond<R: Dim, C: Dim>(&self, UC: &OMatrix<N, R, C>) -> N
where
DefaultAllocator: Allocator<R, C>,
{
assert_eq!(UC.nrows(), UC.nrows());
let rcond = rcond::rcond_symmetric(&UC);
if rcond < self.zero {
-(rcond * rcond)
} else {
rcond * rcond
}
}
pub fn UdUfactor<R: Dim, C: Dim>(&self, M: &mut OMatrix<N, R, C>, n: usize) -> N
where
DefaultAllocator: Allocator<R, C>,
{
for j in (0..n).rev() {
let mut d = M[(j, j)];
if d > self.zero {
for i in (0..=j).rev() {
let mut e = M[(i, j)];
for k in j + 1..n {
e -= M[(i, k)] * M[(k, k)] * M[(j, k)];
}
if i == j {
d = e;
M[(i, j)] = e; } else {
M[(i, j)] = e / d;
}
}
} else if d == self.zero {
for k in j + 1..n {
if M[(j, k)] != self.zero {
return self.minus_one;
}
}
} else {
return self.minus_one;
}
}
UDU::UdUrcond(M)
}
pub fn UCfactor_n<R: Dim, C: Dim>(&self, M: &mut OMatrix<N, R, C>, n: usize) -> N
where
DefaultAllocator: Allocator<R, C>,
{
M.fill_lower_triangle(self.zero, 1);
for j in (0..n).rev() {
let mut d = M[(j, j)];
if d > self.zero {
d = d.sqrt();
M[(j, j)] = d;
d = self.one / d;
for i in 0..j {
let e = d * M[(i, j)];
M[(i, j)] = e;
for k in 0..=i {
let t = e * M[(k, j)];
M[(k, i)] -= t;
}
}
} else if d == self.zero {
for i in 0..j {
if M[(i, j)] != self.zero {
return self.minus_one;
}
}
} else {
return self.minus_one;
}
}
self.UCrcond(M)
}
pub fn UdUinverse<R: Dim, C: Dim>(&self, UD: &mut OMatrix<N, R, C>) -> Result<(), ()>
where
DefaultAllocator: Allocator<R, C>,
{
let n = UD.nrows();
assert_eq!(n, UD.ncols());
if n > 1 {
for i in (0..n - 1).rev() {
for j in (i + 1..n).rev() {
let mut UDij = -UD[(i, j)];
for k in i + 1..j {
UDij -= UD[(i, k)] * UD[(k, j)];
}
UD[(i, j)] = UDij;
}
}
}
let mut singular = false;
for i in 0..n {
let UDii = UD[(i, i)];
if UDii != self.zero {
UD[(i, i)] = self.one / UDii;
} else {
singular = true;
}
}
if singular {
Err(())
} else {
Ok(())
}
}
pub fn UTinverse<R: Dim, C: Dim>(&self, U: &mut OMatrix<N, R, C>) -> Result<(), ()>
where
DefaultAllocator: Allocator<R, C>,
{
let n = U.nrows();
assert_eq!(n, U.ncols());
let mut singular = false;
for i in (0..n).rev() {
let mut d = U[(i, i)];
if d == self.zero {
singular = true;
break;
}
d = self.one / d;
U[(i, i)] = d;
for j in (i + 1..n).rev() {
let mut e = self.zero;
for k in i + 1..=j {
e -= U[(i, k)] * U[(k, j)];
}
U[(i, j)] = e * d;
}
}
if singular {
Err(())
} else {
Ok(())
}
}
pub fn UdUrecompose_transpose<R: Dim, C: Dim>(M: &mut OMatrix<N, R, C>)
where
DefaultAllocator: Allocator<R, C>,
{
let n = M.nrows();
assert_eq!(n, M.ncols());
for i in (0..n).rev() {
for j in 0..i {
M[(i, j)] = M[(j, i)] * M[(j, j)];
}
for j in (i..n).rev() {
if j > i {
let mii = M[(i, i)];
M[(i, j)] *= mii;
}
for k in 0..i {
let t = M[(i, k)] * M[(k, j)];
M[(i, j)] += t; }
M[(j, i)] = M[(i, j)];
}
}
}
pub fn UdUrecompose<R: Dim, C: Dim>(M: &mut OMatrix<N, R, C>)
where
DefaultAllocator: Allocator<R, C>,
{
let n = M.nrows();
assert_eq!(n, M.ncols());
for i in 0..n {
for j in i + 1..n {
M[(j, i)] = M[(i, j)] * M[(j, j)];
}
for j in 0..=i {
if j > i {
let mii = M[(i, i)];
M[(i, j)] *= mii;
}
for k in i + 1..n {
let t = M[(i, k)] * M[(k, j)];
M[(i, j)] += t; }
M[(j, i)] = M[(i, j)];
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use nalgebra::matrix;
#[test]
fn test_factor_inverse() {
let M = matrix![2., 1.; 0., 5000.];
let udu = UDU::new();
let rows = M.nrows();
let mut MI = M.clone();
let rcond = udu.UdUfactor(&mut MI, rows);
approx::assert_relative_eq!(rcond, 0.0004, max_relative = 0.001);
udu.UdUinverse(&mut MI).unwrap();
UDU::UdUrecompose_transpose(&mut MI);
let I = &MI * &M;
approx::assert_relative_eq!(I.determinant(), 1., max_relative = 0.001);
}
}