use std::{
any::Any,
fmt::Debug,
ops::{Add, Div, Mul, Sub},
};
use nalgebra::DMatrix;
use ndarray::{s, Array1, Array2, ArrayView1};
use crate::{Abs, Max, One, RelativeEq, Zero};
pub(crate) trait Trace<T> {
fn trace(&self) -> T;
}
impl<T> Trace<T> for Array2<T>
where
T: Add<Output = T> + Clone + Zero,
{
fn trace(&self) -> T {
self.diag().fold(T::zero(), |acc, d| acc + d.clone())
}
}
pub(crate) trait ScalarMul<T> {
type Output;
fn smul(self, x: T) -> Self::Output;
}
impl<T> ScalarMul<T> for &Array2<T>
where
T: Clone + Mul<Output = T>,
{
type Output = Array2<T>;
fn smul(self, x: T) -> Self::Output {
self.mapv(|a_ij| a_ij * x.clone())
}
}
impl<T> ScalarMul<T> for Array2<T>
where
T: Clone + Mul<Output = T>,
{
type Output = Self;
fn smul(mut self, x: T) -> Self::Output {
self.mapv_inplace(|a_ij| a_ij * x.clone());
self
}
}
impl<T> ScalarMul<T> for &Array1<T>
where
T: Clone + Mul<Output = T>,
{
type Output = Array1<T>;
fn smul(self, x: T) -> Self::Output {
self.mapv(|v_i| v_i * x.clone())
}
}
impl<T> ScalarMul<T> for Array1<T>
where
T: Clone + Mul<Output = T>,
{
type Output = Self;
fn smul(mut self, x: T) -> Self::Output {
self.mapv_inplace(|v_i| v_i * x.clone());
self
}
}
impl<T> ScalarMul<T> for ArrayView1<'_, T>
where
T: Clone + Mul<Output = T>,
{
type Output = Array1<T>;
fn smul(self, x: T) -> Self::Output {
self.mapv(|v_i| v_i * x.clone())
}
}
pub(crate) trait MatrixMul<Rhs = Self> {
type Output;
fn mmul(self, b: Rhs) -> Self::Output;
}
pub(crate) trait MatrixMulSca<T, Rhs = Self> {
type Output;
fn mmul_scale(self, b: Rhs, c: T) -> Self::Output;
}
impl<T> MatrixMul for &Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
type Output = Array2<T>;
#[allow(clippy::many_single_char_names)]
fn mmul(self, b: Self) -> Self::Output {
let a = self;
let m = a.nrows();
let n = a.ncols();
let p = b.ncols();
debug_assert_eq!(n, b.nrows());
Array2::from_shape_fn((m, p), |(i, j)| {
a.row(i)
.iter()
.zip(b.column(j))
.fold(T::zero(), |acc, (ai, bj)| acc + ai.clone() * bj.clone())
})
}
}
impl<T> MatrixMulSca<T> for &Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
type Output = Array2<T>;
#[allow(clippy::many_single_char_names)]
fn mmul_scale(self, b: &Array2<T>, c: T) -> Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
let a = self;
let m = a.nrows();
let n = a.ncols();
let p = b.ncols();
debug_assert_eq!(n, b.nrows());
Array2::from_shape_fn((m, p), |(i, j)| {
a.row(i)
.iter()
.zip(b.column(j))
.fold(T::zero(), |acc, (ai, bj)| acc + ai.clone() * bj.clone())
* c.clone()
})
}
}
impl<T> MatrixMul<Array2<T>> for &Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
type Output = Array2<T>;
#[allow(clippy::many_single_char_names)]
fn mmul(self, b: Array2<T>) -> Self::Output {
self.mmul(&b)
}
}
impl<T> MatrixMul<&Array1<T>> for &Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
type Output = Array1<T>;
#[allow(clippy::many_single_char_names)]
fn mmul(self, b: &Array1<T>) -> Self::Output {
let a = self;
let m = a.nrows();
let n = a.ncols();
debug_assert_eq!(n, b.len());
Array1::from_shape_fn(m, |i| {
a.row(i)
.iter()
.zip(b)
.fold(T::zero(), |acc, (ai, bj)| acc + ai.clone() * bj.clone())
})
}
}
impl<T> MatrixMul<Array1<T>> for &Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
type Output = Array1<T>;
#[allow(clippy::many_single_char_names)]
fn mmul(self, b: Array1<T>) -> Self::Output {
self.mmul(&b)
}
}
impl<T> MatrixMul<&[T]> for &Array2<T>
where
T: Add<Output = T> + Clone + Mul<Output = T> + Zero,
{
type Output = Array1<T>;
#[allow(clippy::many_single_char_names)]
fn mmul(self, b: &[T]) -> Self::Output {
let a = self;
let m = a.nrows();
let n = a.ncols();
debug_assert_eq!(n, b.len());
Array1::from_shape_fn(m, |i| {
a.row(i)
.iter()
.zip(b)
.fold(T::zero(), |acc, (ai, bj)| acc + ai.clone() * bj.clone())
})
}
}
pub(crate) fn max_vabs<T>(mut v: Array1<T>) -> T
where
T: Abs + Max + Clone,
{
v.mapv_inplace(|x| x.abs());
let mut it = v.iter();
let first = it.next().unwrap().clone();
it.fold(first, |acc, x| acc.max(x))
}
pub(crate) fn relative_eq<T>(a: &Array1<T>, b: &Array1<T>, epsilon: &T) -> bool
where
T: Clone + RelativeEq,
{
a.iter().zip(b).all(|(x, y)| x.relative_eq(y, epsilon))
}
pub(crate) fn identity<T>(n: usize) -> Array2<T>
where
T: Clone + One + Zero,
{
let mut temp = Array2::from_elem((n, n), T::zero());
for d in temp.diag_mut() {
*d = T::one();
}
temp
}
#[derive(Clone, Debug)]
pub(crate) struct Lup<T> {
lu: Array2<T>,
p: Array1<usize>,
}
impl<T> Lup<T>
where
T: Add<Output = T> + Clone + Div<Output = T> + Mul<Output = T> + Sub<Output = T> + Zero,
{
pub(crate) fn solve(&self, b: &Array1<T>) -> Array1<T> {
let n = self.lu.nrows();
let mut y = vec![T::zero(); n];
let mut x = Array1::from_elem(n, T::zero());
self.forward_substitution(n, b, &mut y);
self.backward_substitution(n, &y, &mut x);
x
}
fn forward_substitution(&self, n: usize, b: &Array1<T>, y: &mut [T]) {
y[0] = b[self.p[0]].clone();
for i in 1..n {
y[i] = b[self.p[i]].clone()
- (0..i).fold(T::zero(), |acc, j| {
acc + self.lu[(i, j)].clone() * y[j].clone()
});
}
}
fn backward_substitution(&self, n: usize, y: &[T], x: &mut Array1<T>) {
x[n - 1] = y[n - 1].clone() / self.lu[(n - 1, n - 1)].clone();
for i in (0..(n - 1)).rev() {
x[i] = (y[i].clone()
- ((i + 1)..n).fold(T::zero(), |acc, j| {
acc + self.lu[(i, j)].clone() * x[j].clone()
}))
/ self.lu[(i, i)].clone();
}
}
pub(crate) fn solve_mut(&self, b: &mut Array1<T>) {
let n = self.lu.nrows();
let mut y = vec![T::zero(); n];
self.forward_substitution(n, b, &mut y);
let x = b; self.backward_substitution(n, &y, x);
}
}
pub(crate) fn lup<T>(m: Array2<T>) -> Option<Lup<T>>
where
T: Abs
+ Add<Output = T>
+ Clone
+ Div<Output = T>
+ Mul<Output = T>
+ PartialOrd
+ Sub<Output = T>
+ Zero,
{
let mut a = m;
let n = a.nrows();
let mut pi: Array1<_> = (0_usize..n).collect();
for k in 0_usize..n {
let mut p = T::zero();
let mut kp = 0;
for i in k..n {
let aik = a[(i, k)].abs();
if aik > p {
p = aik;
kp = i;
}
}
if p.is_zero() {
return None;
}
pi.swap(k, kp);
for i in 0..n {
a.swap((k, i), (kp, i));
}
for i in (k + 1)..n {
a[(i, k)] = a[(i, k)].clone() / a[(k, k)].clone();
for j in (k + 1)..n {
a[(i, j)] = a[(i, j)].clone() - a[(i, k)].clone() * a[(k, j)].clone();
}
}
}
Some(Lup { lu: a, p: pi })
}
pub(crate) fn inverse<T>(a: Array2<T>) -> Option<Array2<T>>
where
T: Abs
+ Add<Output = T>
+ Clone
+ Div<Output = T>
+ Mul<Output = T>
+ One
+ PartialOrd
+ Sub<Output = T>
+ Zero,
{
let n = a.nrows();
let lup = lup(a)?;
let mut inv = Array2::from_elem((n, n), T::zero());
for i in 0..n {
let x = lup.solve(&Array1::from_shape_fn(n, |j| {
if j == i {
T::one()
} else {
T::zero()
}
}));
inv.slice_mut(s![.., i]).assign(&x);
}
Some(inv)
}
pub(crate) fn ar_to_dm<T>(ar: &Array2<T>) -> DMatrix<T>
where
T: Any + Copy + Debug + PartialEq,
{
DMatrix::from_iterator(ar.nrows(), ar.ncols(), ar.t().iter().copied())
}
#[cfg(test)]
mod tests {
use ndarray::{arr1, arr2};
use super::*;
#[test]
#[allow(clippy::float_cmp)]
fn matrix_trace() {
let m11 = -13.;
let m22 = 7.;
let m = arr2(&[[m11, 3.], [1., m22]]);
assert_eq!(m11 + m22, m.trace());
}
#[test]
fn scalar_matrix_multiplication_ref() {
let a = arr2(&[[2., 4.], [6., 8.]]);
let x = 4.;
let c = ScalarMul::smul(&a, x);
assert_eq!(c, x * a);
}
#[test]
fn scalar_matrix_multiplication_value() {
let a = arr2(&[[2., 4.], [6., 8.]]);
let x = 4.;
let c = ScalarMul::smul(a.clone(), x);
assert_eq!(c, x * a);
}
#[test]
fn scalar_vector_multiplication() {
let a = arr1(&[2., 4., 6., 8.]);
let x = 4.;
let c = (&a).smul(x);
assert_eq!(c, x * &a);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn matrix_matrix_multiplication() {
let a = arr2(&[[2., 4.], [6., 8.]]);
let b = arr2(&[[2., 0.], [0., 2.]]);
let c = a.mmul(&b);
assert_eq!(c, a.dot(&b));
let d = arr2(&[[-3.], [2.]]);
let e = c.mmul(&d);
assert_eq!(e, c.dot(&d));
}
#[test]
fn matrix_matrix_multiplication_value() {
let a = arr2(&[[2., 4.], [6., 8.]]);
let b = arr2(&[[2., 0.], [0., 2.]]);
let c = a.mmul(b.clone());
assert_eq!(c, a.dot(&b));
}
#[test]
fn matrix_vector_multiplication() {
let a = arr2(&[[2., 4.], [6., 8.]]);
let b = arr1(&[2., -3.]);
let c = a.mmul(&b);
assert_eq!(c, a.dot(&b));
}
#[test]
#[allow(clippy::float_cmp)]
fn maximum_absolute_value() {
let expected = 28.;
let v = arr1(&[2., -expected, 0.32, 8.123]);
assert_eq!(expected, max_vabs(v));
}
#[test]
#[allow(clippy::float_cmp)]
fn identity_matrix() {
let id = identity::<f32>(10);
let res = id.mmul(&id);
assert_eq!(res[(0, 0)], 1.);
assert_eq!(id, res);
}
#[test]
fn lup_decomposition() {
let a = arr2(&[
[2., 0., 2., 0.6],
[3., 3., 4., -2.],
[5., 5., 4., 2.],
[-1., -2., 3.4, -1.],
]);
let lup = lup(a).unwrap();
assert_relative_eq!(5., lup.lu[(0, 0)], max_relative = 1e-10);
assert_relative_eq!(5., lup.lu[(0, 1)], max_relative = 1e-10);
assert_relative_eq!(4., lup.lu[(0, 2)], max_relative = 1e-10);
assert_relative_eq!(2., lup.lu[(0, 3)], max_relative = 1e-10);
assert_relative_eq!(0.4, lup.lu[(1, 0)], max_relative = 1e-10);
assert_relative_eq!(-2., lup.lu[(1, 1)], max_relative = 1e-10);
assert_relative_eq!(0.4, lup.lu[(1, 2)], max_relative = 1e-10);
assert_relative_eq!(-0.2, lup.lu[(1, 3)], max_relative = 1e-10);
assert_relative_eq!(-0.2, lup.lu[(2, 0)], max_relative = 1e-10);
assert_relative_eq!(0.5, lup.lu[(2, 1)], max_relative = 1e-10);
assert_relative_eq!(4., lup.lu[(2, 2)], max_relative = 1e-10);
assert_relative_eq!(-0.5, lup.lu[(2, 3)], max_relative = 1e-10);
assert_relative_eq!(0.6, lup.lu[(3, 0)], max_relative = 1e-10);
assert_relative_eq!(0., lup.lu[(3, 1)], max_relative = 1e-10);
assert_relative_eq!(0.4, lup.lu[(3, 2)], max_relative = 1e-10);
assert_relative_eq!(-3., lup.lu[(3, 3)], max_relative = 1e-10);
assert_eq!(2, lup.p[0]);
assert_eq!(0, lup.p[1]);
assert_eq!(3, lup.p[2]);
assert_eq!(1, lup.p[3]);
}
#[test]
fn system_solve() {
let a = arr2(&[[1., 2., 0.], [3., 4., 4.], [5., 6., 3.]]);
let lup = lup(a).unwrap();
assert_relative_eq!(5., lup.lu[(0, 0)], max_relative = 1e-10);
assert_relative_eq!(6., lup.lu[(0, 1)], max_relative = 1e-10);
assert_relative_eq!(3., lup.lu[(0, 2)], max_relative = 1e-10);
assert_relative_eq!(0.2, lup.lu[(1, 0)], max_relative = 1e-10);
assert_relative_eq!(0.8, lup.lu[(1, 1)], max_relative = 1e-10);
assert_relative_eq!(-0.6, lup.lu[(1, 2)], max_relative = 1e-10);
assert_relative_eq!(0.6, lup.lu[(2, 0)], max_relative = 1e-10);
assert_relative_eq!(0.5, lup.lu[(2, 1)], max_relative = 1e-10);
assert_relative_eq!(2.5, lup.lu[(2, 2)], max_relative = 1e-10);
assert_eq!(2, lup.p[0]);
assert_eq!(0, lup.p[1]);
assert_eq!(1, lup.p[2]);
let x = lup.solve(&arr1(&[3., 7., 8.]));
assert_relative_eq!(-1.4, x[0], max_relative = 1e-10);
assert_relative_eq!(2.2, x[1], max_relative = 1e-10);
assert_relative_eq!(0.6, x[2], max_relative = 1e-10);
}
#[test]
fn matrix_inverse() {
let a = arr2(&[[1., 2.], [2., 3.]]);
let inv = arr2(&[[-3., 2.], [2., -1.]]);
assert_eq!(inv, inverse(a).unwrap());
}
#[test]
fn smul() {}
}