use crate::matrices::{Matrix, Row, Column};
use crate::numeric::{Numeric, NumericRef};
use crate::numeric::extra::Sqrt;
pub fn inverse<T: Numeric>(matrix: &Matrix<T>) -> Option<Matrix<T>>
where for<'a> &'a T: NumericRef<T> {
if matrix.rows() != matrix.columns() {
return None;
}
if matrix.rows() == 1 {
let element = matrix.scalar();
if element == T::zero() {
return None;
}
return Some(Matrix::unit(T::one() / element));
}
match determinant(matrix) {
Some(det) => {
if det == T::zero() {
return None;
}
let determinant_reciprocal = T::one() / det;
let mut cofactor_matrix = Matrix::empty(T::zero(), matrix.size());
for i in 0..matrix.rows() {
for j in 0..matrix.columns() {
let ij_minor = minor(matrix, i, j)?;
let sign = i8::pow(-1, (i.rem_euclid(2) + j.rem_euclid(2)) as u32);
let sign = if sign == 1 {
T::one()
} else {
T::zero() - T::one()
};
cofactor_matrix.set(i, j, sign * ij_minor);
}
}
cofactor_matrix.transpose_mut();
cofactor_matrix.map_mut(|element| element * determinant_reciprocal.clone());
Some(cofactor_matrix)
},
None => None
}
}
fn minor<T: Numeric>(matrix: &Matrix<T>, i: Row, j: Column) -> Option<T>
where for<'a> &'a T: NumericRef<T> {
minor_mut(&mut matrix.clone(), i, j)
}
fn minor_mut<T: Numeric>(matrix: &mut Matrix<T>, i: Row, j: Column) -> Option<T>
where for<'a> &'a T: NumericRef<T> {
if matrix.rows() == 1 || matrix.columns() == 1 {
return None;
}
if matrix.rows() != matrix.columns() {
return None;
}
matrix.remove_row(i);
matrix.remove_column(j);
determinant(matrix)
}
pub fn determinant<T: Numeric>(matrix: &Matrix<T>) -> Option<T>
where for<'a> &'a T: NumericRef<T> {
if matrix.rows() != matrix.columns() {
return None;
}
let length = matrix.rows();
if length == 0 {
return None;
}
if length == 1 {
return Some(matrix.scalar());
}
let mut sum = T::zero();
with_each_permutation(&mut (0..length).collect(), &mut |permutation, even_swap| {
let signature = if even_swap {
T::one()
} else {
T::zero() - T::one()
};
let mut product = T::one();
for (n, i) in permutation.iter().enumerate() {
let element = matrix.get_reference(n, *i);
product = product * element;
}
sum = sum.clone() + (signature * product);
});
Some(sum)
}
#[allow(dead_code)] fn factorial(n: usize) -> usize {
(1..=n).product()
}
fn heaps_permutations<T: Clone, F>(k: usize, list: &mut Vec<T>, consumer: &mut F)
where F: FnMut(&mut Vec<T>) {
if k == 1 {
consumer(list);
return;
}
for i in 0..k {
heaps_permutations(k - 1, list, consumer);
if i < k - 1 {
if k % 2 == 0 {
list.swap(i, k - 1);
} else {
list.swap(0, k - 1);
}
}
}
}
#[allow(dead_code)] fn generate_permutations<T: Clone>(list: &mut Vec<T>) -> Vec<(Vec<T>, bool)> {
let mut permutations = Vec::with_capacity(factorial(list.len()));
let mut even_swaps = true;
heaps_permutations(list.len(), list, &mut |permuted| {
permutations.push((permuted.clone(), even_swaps));
even_swaps = !even_swaps;
});
permutations
}
fn with_each_permutation<T: Clone, F>(list: &mut Vec<T>, consumer: &mut F)
where F: FnMut(&mut Vec<T>, bool) {
let mut even_swaps = true;
heaps_permutations(list.len(), list, &mut |permuted| {
consumer(permuted, even_swaps);
even_swaps = !even_swaps;
});
}
#[test]
fn test_permutations() {
let mut list = vec![ 1, 2, 3 ];
let permutations = generate_permutations(&mut list);
assert!(permutations.contains(&(vec![1, 2, 3], true)));
assert!(permutations.contains(&(vec![3, 2, 1], false)));
assert!(permutations.contains(&(vec![2, 3, 1], true)));
assert!(permutations.contains(&(vec![1, 3, 2], false)));
assert!(permutations.contains(&(vec![2, 1, 3], false)));
assert!(permutations.contains(&(vec![3, 1, 2], true)));
assert_eq!(permutations.len(), 6);
let mut list = vec![ 1, 2, 3, 4, 5 ];
let permuted = generate_permutations(&mut list);
assert!(permuted.contains(&(vec![1, 2, 3, 4, 5], true)));
assert!(permuted.contains(&(vec![1, 2, 3, 5, 4], false)));
assert!(permuted.contains(&(vec![1, 2, 5, 3, 4], true)));
let mut list = vec![0, 1];
let permuted = generate_permutations(&mut list);
assert!(permuted.contains(&(vec![0, 1], true)));
assert!(permuted.contains(&(vec![1, 0], false)));
assert_eq!(permuted.len(), 2);
}
pub fn covariance_column_features<T: Numeric>(matrix: &Matrix<T>) -> Matrix<T>
where for<'a> &'a T: NumericRef<T> {
let features = matrix.columns();
let samples = T::from_usize(matrix.rows()).expect(
"The maximum value of the matrix type T cannot represent this many samples");
let mut covariance_matrix = Matrix::empty(T::zero(), (features, features));
covariance_matrix.map_mut_with_index(|_, i, j| {
let feature_i_mean: T = matrix.column_iter(i).sum::<T>() / &samples;
let feature_j_mean: T = matrix.column_iter(j).sum::<T>() / &samples;
matrix.column_reference_iter(i)
.map(|x| x - &feature_i_mean)
.zip(matrix.column_reference_iter(j).map(|y| y - &feature_j_mean))
.map(|(x, y)| x * y)
.sum::<T>() / &samples
});
covariance_matrix
}
pub fn covariance_row_features<T: Numeric>(matrix: &Matrix<T>) -> Matrix<T>
where for<'a> &'a T: NumericRef<T> {
let features = matrix.rows();
let samples = T::from_usize(matrix.columns()).expect(
"The maximum value of the matrix type T cannot represent this many samples");
let mut covariance_matrix = Matrix::empty(T::zero(), (features, features));
covariance_matrix.map_mut_with_index(|_, i, j| {
let feature_i_mean: T = matrix.row_iter(i).sum::<T>() / &samples;
let feature_j_mean: T = matrix.row_iter(j).sum::<T>() / &samples;
matrix.row_reference_iter(i)
.map(|x| x - &feature_i_mean)
.zip(matrix.row_reference_iter(j).map(|y| y - &feature_j_mean))
.map(|(x, y)| x * y)
.sum::<T>() / &samples
});
covariance_matrix
}
pub fn cholesky_decomposition<T: Numeric + Sqrt<Output = T>>(matrix: &Matrix<T>) -> Option<Matrix<T>>
where for<'a> &'a T: NumericRef<T> {
if matrix.rows() != matrix.columns() {
return None;
}
let mut lower_triangular = Matrix::empty(T::zero(), matrix.size());
for i in 0..lower_triangular.rows() {
for k in 0..lower_triangular.columns() {
if i == k {
let mut sum = T::zero();
for j in 0..k {
sum = &sum + (lower_triangular.get_reference(k, j)
* lower_triangular.get_reference(k, j));
}
lower_triangular.set(i, k, (matrix.get_reference(i, k) - sum).sqrt());
}
if i > k {
let mut sum = T::zero();
for j in 0..k {
sum = &sum + (lower_triangular.get_reference(i, j)
* lower_triangular.get_reference(k, j));
}
lower_triangular.set(i, k,
(T::one() / lower_triangular.get_reference(k, k)) *
(matrix.get_reference(i, k) - sum));
}
}
}
Some(lower_triangular)
}