use crate::core::Expression;
use crate::matrices::types::*;
use crate::matrices::unified::Matrix;
use crate::simplify::Simplify;
impl Matrix {
pub fn cholesky_decomposition(&self) -> Option<CholeskyDecomposition> {
let (rows, cols) = self.dimensions();
if rows != cols || !self.is_symmetric() {
return None;
}
match self {
Matrix::Identity(data) => Some(CholeskyDecomposition {
l: Matrix::identity(data.size),
}),
Matrix::Scalar(data) => {
let sqrt_c = Expression::pow(data.scalar_value.clone(), Expression::rational(1, 2));
Some(CholeskyDecomposition {
l: Matrix::scalar(data.size, sqrt_c),
})
}
Matrix::Diagonal(data) => {
let sqrt_elements: Vec<Expression> = data
.diagonal_elements
.iter()
.map(|elem| Expression::pow(elem.clone(), Expression::rational(1, 2)))
.collect();
Some(CholeskyDecomposition {
l: Matrix::diagonal(sqrt_elements),
})
}
_ => {
self.general_cholesky()
}
}
}
fn general_cholesky(&self) -> Option<CholeskyDecomposition> {
let (n, _) = self.dimensions();
let mut l_elements = vec![vec![Expression::integer(0); n]; n];
for i in 0..n {
for j in 0..=i {
if i == j {
let sum =
l_elements[i]
.iter()
.take(i)
.fold(Expression::integer(0), |acc, l_ik| {
Expression::add(vec![
acc,
Expression::pow(l_ik.clone(), Expression::integer(2)),
])
.simplify()
});
let diagonal_val = Expression::add(vec![
self.get_element(i, i),
Expression::mul(vec![Expression::integer(-1), sum]),
])
.simplify();
l_elements[i][i] = Expression::pow(diagonal_val, Expression::rational(1, 2));
} else {
let sum = l_elements[i].iter().zip(l_elements[j].iter()).take(j).fold(
Expression::integer(0),
|acc, (l_ik, l_jk)| {
Expression::add(vec![
acc,
Expression::mul(vec![l_ik.clone(), l_jk.clone()]),
])
.simplify()
},
);
let numerator = Expression::add(vec![
self.get_element(i, j),
Expression::mul(vec![Expression::integer(-1), sum]),
])
.simplify();
l_elements[i][j] = Expression::mul(vec![
numerator,
Expression::pow(l_elements[j][j].clone(), Expression::integer(-1)),
])
.simplify();
}
}
}
Some(CholeskyDecomposition {
l: Matrix::dense(l_elements),
})
}
pub fn is_positive_definite_cholesky(&self) -> bool {
if !self.is_symmetric() {
return false;
}
match self {
Matrix::Identity(_) => true,
Matrix::Scalar(data) => {
!data.scalar_value.is_zero() && data.scalar_value != Expression::integer(-1)
}
Matrix::Diagonal(data) => {
data.diagonal_elements.iter().all(|elem| !elem.is_zero())
}
_ => {
self.cholesky_decomposition().is_some()
}
}
}
}