use nalgebra::DMatrix;
use crate::minimum::error::MinimumError;
use crate::user_covariance::MnUserCovariance;
pub fn squeeze_matrix(mat: &DMatrix<f64>, n: usize) -> DMatrix<f64> {
let dim = mat.nrows();
assert!(n < dim, "index {n} out of range for {dim}x{dim} matrix");
let new_dim = dim - 1;
let mut result = DMatrix::zeros(new_dim, new_dim);
let mut ri = 0;
for i in 0..dim {
if i == n {
continue;
}
let mut rj = 0;
for j in 0..dim {
if j == n {
continue;
}
result[(ri, rj)] = mat[(i, j)];
rj += 1;
}
ri += 1;
}
result
}
pub fn squeeze_user_covariance(cov: &MnUserCovariance, n: usize) -> MnUserCovariance {
let nrow = cov.nrow();
assert!(
n < nrow,
"index {n} out of range for {nrow}x{nrow} covariance"
);
let mut mat = DMatrix::zeros(nrow, nrow);
for i in 0..nrow {
for j in 0..nrow {
mat[(i, j)] = cov.get(i, j);
}
}
let hessian = match mat.clone().try_inverse() {
Some(h) => h,
None => {
return make_diagonal_user_cov(cov, n);
}
};
let squeezed_h = squeeze_matrix(&hessian, n);
let squeezed_cov = match squeezed_h.try_inverse() {
Some(c) => c,
None => {
return make_diagonal_user_cov(cov, n);
}
};
let new_dim = nrow - 1;
let mut result = MnUserCovariance::new(new_dim);
for i in 0..new_dim {
for j in i..new_dim {
result.set(i, j, squeezed_cov[(i, j)]);
}
}
result
}
pub fn squeeze_error(err: &MinimumError, n: usize) -> MinimumError {
let mat = err.matrix();
let dim = mat.nrows();
assert!(
n < dim,
"index {n} out of range for {dim}x{dim} error matrix"
);
let hessian = match mat.clone().try_inverse() {
Some(h) => h,
None => {
let squeezed = squeeze_matrix(mat, n);
return MinimumError::new(squeezed, err.dcovar());
}
};
let squeezed_h = squeeze_matrix(&hessian, n);
match squeezed_h.try_inverse() {
Some(cov) => MinimumError::new(cov, err.dcovar()),
None => {
let new_dim = dim - 1;
let mut diag = DMatrix::zeros(new_dim, new_dim);
let mut ri = 0;
for i in 0..dim {
if i == n {
continue;
}
diag[(ri, ri)] = mat[(i, i)];
ri += 1;
}
MinimumError::new(diag, err.dcovar())
}
}
}
fn make_diagonal_user_cov(cov: &MnUserCovariance, skip: usize) -> MnUserCovariance {
let new_dim = cov.nrow() - 1;
let mut result = MnUserCovariance::new(new_dim);
let mut ri = 0;
for i in 0..cov.nrow() {
if i == skip {
continue;
}
result.set(ri, ri, cov.get(i, i));
ri += 1;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn squeeze_3x3_to_2x2() {
let mat = DMatrix::from_row_slice(3, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
let squeezed = squeeze_matrix(&mat, 1);
assert_eq!(squeezed.nrows(), 2);
assert!((squeezed[(0, 0)] - 1.0).abs() < 1e-15);
assert!((squeezed[(0, 1)] - 3.0).abs() < 1e-15);
assert!((squeezed[(1, 0)] - 7.0).abs() < 1e-15);
assert!((squeezed[(1, 1)] - 9.0).abs() < 1e-15);
}
#[test]
fn squeeze_first_element() {
let mat = DMatrix::from_row_slice(3, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
let squeezed = squeeze_matrix(&mat, 0);
assert_eq!(squeezed.nrows(), 2);
assert!((squeezed[(0, 0)] - 5.0).abs() < 1e-15);
assert!((squeezed[(0, 1)] - 6.0).abs() < 1e-15);
assert!((squeezed[(1, 0)] - 8.0).abs() < 1e-15);
assert!((squeezed[(1, 1)] - 9.0).abs() < 1e-15);
}
#[test]
fn squeeze_error_preserves_structure() {
let mut mat = DMatrix::zeros(3, 3);
mat[(0, 0)] = 2.0;
mat[(1, 1)] = 3.0;
mat[(2, 2)] = 4.0;
mat[(0, 1)] = 0.5;
mat[(1, 0)] = 0.5;
let err = MinimumError::new(mat, 0.0);
let squeezed = squeeze_error(&err, 1);
assert_eq!(squeezed.matrix().nrows(), 2);
}
}