use crate::linalg::{Inverse, Matmul};
use crate::prelude::{Scalar, ShapeError, TensorError, TensorExpr, TensorResult};
use crate::tensor::{self, TensorBase};
use acme::prelude::UnaryOp;
use num::traits::{Num, NumAssign};
fn inverse_impl<T>(tensor: &TensorBase<T>) -> TensorResult<TensorBase<T>>
where
T: Copy + NumAssign + PartialOrd,
{
let op = TensorExpr::unary(tensor.clone(), UnaryOp::Inv);
let n = tensor.nrows();
if !tensor.is_square() {
return Err(ShapeError::NotSquare.into()); }
let eye = TensorBase::eye(n);
let mut aug = TensorBase::zeros((n, 2 * n));
for i in 0..n {
for j in 0..n {
aug[[i, j]] = tensor[[i, j]];
}
for j in n..(2 * n) {
aug[[i, j]] = eye[[i, j - n]];
}
}
for i in 0..n {
let pivot = aug[[i, i]];
if pivot == T::zero() {
return Err(TensorError::Singular); }
for j in 0..(2 * n) {
aug[[i, j]] = aug[[i, j]] / pivot;
}
for j in 0..n {
if i != j {
let am = aug.clone();
let factor = aug[[j, i]];
for k in 0..(2 * n) {
aug[[j, k]] -= factor * am[[i, k]];
}
}
}
}
let mut inv = tensor.zeros_like().with_op(op.into());
for i in 0..n {
for j in 0..n {
inv[[i, j]] = aug[[i, j + n]];
}
}
Ok(inv.to_owned())
}
impl<T> TensorBase<T>
where
T: Copy,
{
pub fn diag(&self) -> Self {
let n = self.nrows();
Self::from_shape_iter(self.shape().diag(), (0..n).map(|i| self[vec![i; n]]))
}
pub fn inv(&self) -> TensorResult<Self>
where
T: NumAssign + PartialOrd,
{
inverse_impl(self)
}
pub fn trace(&self) -> TensorResult<T>
where
T: Num,
{
if !self.is_square() {
return Err(ShapeError::NotSquare.into());
}
let n = self.nrows();
let trace = (0..n).fold(T::zero(), |acc, i| acc + self[[i, i]]);
Ok(trace)
}
}
impl<T> Inverse for TensorBase<T>
where
T: Copy + Num + NumAssign + PartialOrd,
{
type Output = TensorResult<Self>;
fn inv(&self) -> Self::Output {
inverse_impl(self)
}
}
impl<T> Matmul<TensorBase<T>> for TensorBase<T>
where
T: Scalar,
{
type Output = Self;
fn matmul(&self, other: &Self) -> Self {
let sc = |m: usize, n: usize| m * self.ncols() + n;
let oc = |m: usize, n: usize| m * other.ncols() + n;
let shape = self.shape().matmul_shape(&other.shape()).unwrap();
let mut result = vec![T::zero(); shape.size()];
for i in 0..self.nrows() {
for j in 0..other.ncols() {
for k in 0..self.ncols() {
result[oc(i, j)] += self.data[sc(i, k)] * other.data[oc(k, j)];
}
}
}
let op = TensorExpr::matmul(self.clone(), other.clone());
tensor::from_vec_with_op(false, op, shape, result)
}
}