#[cfg(test)]
mod test;
use crate::math::{Factor, Reference};
use crate::units::{Dimensionless, UnitInv};
use super::{Quantity, Rank2, Tensor, TensorArray, TensorRank0, TensorRank2, relabel};
use crate::ABS_TOL;
type LuFactors<const D: usize, I, J, U> = (
TensorRank2<D, I, Factor, U>,
TensorRank2<D, Factor, J, U>,
Vec<usize>,
);
type Inverse<U> = <U as UnitInv>::Output;
impl<const D: usize, I, J, U> TensorRank2<D, I, J, U> {
pub fn determinant(&self) -> TensorRank0 {
self.canonical().determinant_core()
}
pub fn inverse(&self) -> TensorRank2<D, J, I, Inverse<U>>
where
U: UnitInv,
{
relabel(self.canonical().inverse_core())
}
pub fn inverse_and_determinant(&self) -> (TensorRank2<D, J, I, Inverse<U>>, TensorRank0)
where
U: UnitInv,
{
let (inverse, determinant) = self.canonical().inverse_and_determinant_core();
(relabel(inverse), determinant)
}
pub fn inverse_transpose(&self) -> TensorRank2<D, I, J, Inverse<U>>
where
U: UnitInv,
{
relabel(self.canonical().inverse_transpose_core())
}
pub fn inverse_transpose_and_determinant(
&self,
) -> (TensorRank2<D, I, J, Inverse<U>>, TensorRank0)
where
U: UnitInv,
{
let (inverse_transpose, determinant) =
self.canonical().inverse_transpose_and_determinant_core();
(relabel(inverse_transpose), determinant)
}
pub fn lu_decomposition(&self) -> LuFactors<D, I, J, U> {
let (tensor_l, tensor_u, p) = self.canonical().lu_decomposition_core();
(relabel(tensor_l), relabel(tensor_u), p)
}
pub fn lu_decomposition_inverse(&self) -> LuFactors<D, I, J, U> {
let (tensor_l, tensor_u, p) = self.canonical().lu_decomposition_inverse_core();
(relabel(tensor_l), relabel(tensor_u), p)
}
}
impl<const D: usize> TensorRank2<D, Reference, Reference, Dimensionless> {
fn determinant_core(&self) -> TensorRank0 {
if D == 2 {
(self[0][0] * self[1][1] - self[0][1] * self[1][0]).value()
} else if D == 3 {
let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
(self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20).value()
} else if D == 4 {
let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
(s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0).value()
} else {
let (_, u, p) = self.lu_decomposition_core();
let num_swaps = p.iter().enumerate().filter(|(i, p_i)| p_i != &i).count();
u.into_iter()
.enumerate()
.map(|(i, u_i)| u_i[i].value())
.product::<TensorRank0>()
* if num_swaps % 2 == 0 { 1.0 } else { -1.0 }
}
}
fn inverse_core(&self) -> Self {
if D == 2 {
let mut adjugate = Self::zero();
adjugate[0][0] = self[1][1];
adjugate[0][1] = -self[0][1];
adjugate[1][0] = -self[1][0];
adjugate[1][1] = self[0][0];
adjugate / self.determinant_core()
} else if D == 3 {
let mut adjugate = Self::zero();
let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
adjugate[0][0] = c_00;
adjugate[0][1] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
adjugate[0][2] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
adjugate[1][0] = c_10;
adjugate[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
adjugate[1][2] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
adjugate[2][0] = c_20;
adjugate[2][1] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
adjugate[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
adjugate / (self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20)
} else if D == 4 {
let mut adjugate = Self::zero();
let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
adjugate[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
adjugate[0][1] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
adjugate[0][2] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
adjugate[0][3] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
adjugate[1][0] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
adjugate[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
adjugate[1][2] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
adjugate[1][3] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
adjugate[2][0] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
adjugate[2][1] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
adjugate[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
adjugate[2][3] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
adjugate[3][0] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
adjugate[3][1] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
adjugate[3][2] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
adjugate[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
adjugate / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0)
} else {
let (l_inverse, u_inverse, p) = self.lu_decomposition_inverse_core();
let mut q = [0; D];
p.into_iter().enumerate().for_each(|(i, p_i)| q[p_i] = i);
u_inverse
.into_iter()
.map(|u_inverse_i| {
q.iter()
.map(|&q_j| {
u_inverse_i
.iter()
.zip(l_inverse.iter())
.map(|(u_inverse_ik, l_inverse_k)| u_inverse_ik * l_inverse_k[q_j])
.sum::<Quantity>()
})
.collect()
})
.collect()
}
}
fn inverse_and_determinant_core(&self) -> (Self, TensorRank0) {
if D == 2 {
let mut adjugate = Self::zero();
adjugate[0][0] = self[1][1];
adjugate[0][1] = -self[0][1];
adjugate[1][0] = -self[1][0];
adjugate[1][1] = self[0][0];
let determinant = self.determinant_core();
(adjugate / determinant, determinant)
} else if D == 3 {
let mut adjugate = Self::zero();
let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
let determinant = (self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20).value();
adjugate[0][0] = c_00;
adjugate[0][1] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
adjugate[0][2] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
adjugate[1][0] = c_10;
adjugate[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
adjugate[1][2] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
adjugate[2][0] = c_20;
adjugate[2][1] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
adjugate[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
(adjugate / determinant, determinant)
} else if D == 4 {
let mut adjugate = Self::zero();
let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
let determinant = (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0).value();
adjugate[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
adjugate[0][1] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
adjugate[0][2] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
adjugate[0][3] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
adjugate[1][0] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
adjugate[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
adjugate[1][2] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
adjugate[1][3] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
adjugate[2][0] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
adjugate[2][1] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
adjugate[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
adjugate[2][3] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
adjugate[3][0] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
adjugate[3][1] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
adjugate[3][2] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
adjugate[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
(adjugate / determinant, determinant)
} else {
(self.inverse_core(), self.determinant_core())
}
}
fn inverse_transpose_core(&self) -> Self {
if D == 2 {
let mut adjugate_transpose = Self::zero();
adjugate_transpose[0][0] = self[1][1];
adjugate_transpose[0][1] = -self[1][0];
adjugate_transpose[1][0] = -self[0][1];
adjugate_transpose[1][1] = self[0][0];
adjugate_transpose / self.determinant_core()
} else if D == 3 {
let mut adjugate_transpose = Self::zero();
let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
adjugate_transpose[0][0] = c_00;
adjugate_transpose[1][0] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
adjugate_transpose[2][0] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
adjugate_transpose[0][1] = c_10;
adjugate_transpose[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
adjugate_transpose[2][1] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
adjugate_transpose[0][2] = c_20;
adjugate_transpose[1][2] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
adjugate_transpose[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
adjugate_transpose / (self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20)
} else if D == 4 {
let mut adjugate_transpose = Self::zero();
let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
adjugate_transpose[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
adjugate_transpose[1][0] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
adjugate_transpose[2][0] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
adjugate_transpose[3][0] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
adjugate_transpose[0][1] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
adjugate_transpose[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
adjugate_transpose[2][1] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
adjugate_transpose[3][1] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
adjugate_transpose[0][2] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
adjugate_transpose[1][2] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
adjugate_transpose[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
adjugate_transpose[3][2] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
adjugate_transpose[0][3] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
adjugate_transpose[1][3] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
adjugate_transpose[2][3] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
adjugate_transpose[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
adjugate_transpose / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0)
} else {
self.inverse_core().transpose()
}
}
fn inverse_transpose_and_determinant_core(&self) -> (Self, TensorRank0) {
if D == 2 {
let mut adjugate_transpose = Self::zero();
adjugate_transpose[0][0] = self[1][1];
adjugate_transpose[0][1] = -self[1][0];
adjugate_transpose[1][0] = -self[0][1];
adjugate_transpose[1][1] = self[0][0];
let determinant = self.determinant_core();
(adjugate_transpose / determinant, determinant)
} else if D == 3 {
let mut adjugate_transpose = Self::zero();
let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
let determinant = (self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20).value();
adjugate_transpose[0][0] = c_00;
adjugate_transpose[1][0] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
adjugate_transpose[2][0] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
adjugate_transpose[0][1] = c_10;
adjugate_transpose[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
adjugate_transpose[2][1] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
adjugate_transpose[0][2] = c_20;
adjugate_transpose[1][2] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
adjugate_transpose[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
(adjugate_transpose / determinant, determinant)
} else if D == 4 {
let mut adjugate_transpose = Self::zero();
let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
let determinant = (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0).value();
adjugate_transpose[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
adjugate_transpose[1][0] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
adjugate_transpose[2][0] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
adjugate_transpose[3][0] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
adjugate_transpose[0][1] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
adjugate_transpose[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
adjugate_transpose[2][1] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
adjugate_transpose[3][1] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
adjugate_transpose[0][2] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
adjugate_transpose[1][2] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
adjugate_transpose[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
adjugate_transpose[3][2] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
adjugate_transpose[0][3] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
adjugate_transpose[1][3] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
adjugate_transpose[2][3] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
adjugate_transpose[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
(adjugate_transpose / determinant, determinant)
} else {
(self.inverse_transpose_core(), self.determinant_core())
}
}
fn lu_decomposition_core(&self) -> (Self, Self, Vec<usize>) {
let n = D;
let mut p: Vec<usize> = (0..n).collect();
let mut factor;
let mut lu = self.clone();
let mut max_row;
let mut max_val;
let mut pivot;
for i in 0..n {
max_row = i;
max_val = lu[max_row][i].abs();
for k in i + 1..n {
if lu[k][i].abs() > max_val {
max_row = k;
max_val = lu[max_row][i].abs();
}
}
if max_row != i {
lu.0.swap(i, max_row);
p.swap(i, max_row);
}
pivot = lu[i][i];
if pivot.abs() < ABS_TOL {
panic!("LU decomposition failed (zero pivot).")
}
for j in i + 1..n {
if lu[j][i] != 0.0 {
lu[j][i] = lu[j][i] / pivot;
factor = lu[j][i];
for k in i + 1..n {
let update = factor * lu[i][k];
lu[j][k] -= update;
}
}
}
}
let mut l = TensorRank2::identity();
for i in 0..D {
for j in 0..i {
l[i][j] = lu[i][j]
}
}
let mut u = TensorRank2::zero();
for i in 0..D {
for j in i..D {
u[i][j] = lu[i][j]
}
}
(l, u, p)
}
fn lu_decomposition_inverse_core(&self) -> (Self, Self, Vec<usize>) {
let (mut tensor_l, mut tensor_u, p) = self.lu_decomposition_core();
let mut sum: Quantity;
for i in 0..D {
tensor_l[i][i] = 1.0 / tensor_l[i][i];
for j in 0..i {
sum = Quantity::new(0.0);
for k in j..i {
sum += tensor_l[i][k] * tensor_l[k][j];
}
tensor_l[i][j] = -sum * tensor_l[i][i];
}
}
for i in 0..D {
tensor_u[i][i] = 1.0 / tensor_u[i][i];
for j in 0..i {
sum = Quantity::new(0.0);
for k in j..i {
sum += tensor_u[j][k] * tensor_u[k][i];
}
tensor_u[j][i] = -sum * tensor_u[i][i];
}
}
(tensor_l, tensor_u, p)
}
}