concision_core/utils/
tensor.rs

1/*
2    Appellation: tensor <module>
3    Contrib: FL03 <jo3mccain@icloud.com>
4*/
5pub use self::{generators::*, stack::*};
6use ndarray::*;
7use num_traits::{NumAssign, Zero};
8
9/// Creates an n-dimensional array from an iterator of n dimensional arrays.
10pub fn concat_iter<D, T>(axis: usize, iter: impl IntoIterator<Item = Array<T, D>>) -> Array<T, D>
11where
12    D: RemoveAxis,
13    T: Clone,
14{
15    let mut arr = iter.into_iter().collect::<Vec<_>>();
16    let mut out = arr.pop().unwrap();
17    for i in arr {
18        out = concatenate!(Axis(axis), out, i);
19    }
20    out
21}
22
23pub fn inverse<T>(matrix: &Array2<T>) -> Option<Array2<T>>
24where
25    T: Copy + NumAssign + ScalarOperand,
26{
27    let (rows, cols) = matrix.dim();
28
29    if !matrix.is_square() {
30        return None; // Matrix must be square for inversion
31    }
32
33    let identity = Array2::eye(rows);
34
35    // Construct an augmented matrix by concatenating the original matrix with an identity matrix
36    let mut aug = Array2::zeros((rows, 2 * cols));
37    aug.slice_mut(s![.., ..cols]).assign(matrix);
38    aug.slice_mut(s![.., cols..]).assign(&identity);
39
40    // Perform Gaussian elimination to reduce the left half to the identity matrix
41    for i in 0..rows {
42        let pivot = aug[[i, i]];
43
44        if pivot == T::zero() {
45            return None; // Matrix is singular
46        }
47
48        aug.slice_mut(s![i, ..]).mapv_inplace(|x| x / pivot);
49
50        for j in 0..rows {
51            if i != j {
52                let am = aug.clone();
53                let factor = aug[[j, i]];
54                let rhs = am.slice(s![i, ..]);
55                aug.slice_mut(s![j, ..])
56                    .zip_mut_with(&rhs, |x, &y| *x -= y * factor);
57            }
58        }
59    }
60
61    // Extract the inverted matrix from the augmented matrix
62    let inverted = aug.slice(s![.., cols..]);
63
64    Some(inverted.to_owned())
65}
66
67/// Returns the lower triangular portion of a matrix.
68pub fn tril<T>(a: &Array2<T>) -> Array2<T>
69where
70    T: Clone + Zero,
71{
72    let mut out = a.clone();
73    for i in 0..a.shape()[0] {
74        for j in i + 1..a.shape()[1] {
75            out[[i, j]] = T::zero();
76        }
77    }
78    out
79}
80/// Returns the upper triangular portion of a matrix.
81pub fn triu<T>(a: &Array2<T>) -> Array2<T>
82where
83    T: Clone + Zero,
84{
85    let mut out = a.clone();
86    for i in 0..a.shape()[0] {
87        for j in 0..i {
88            out[[i, j]] = T::zero();
89        }
90    }
91    out
92}
93
94pub(crate) mod generators {
95    use ndarray::{Array, Array1, Dimension, IntoDimension, ShapeError};
96    use num_traits::{Float, NumCast};
97
98    pub fn genspace<T: NumCast>(features: usize) -> Array1<T> {
99        Array1::from_iter((0..features).map(|x| T::from(x).unwrap()))
100    }
101
102    pub fn linarr<A, D>(dim: impl Clone + IntoDimension<Dim = D>) -> Result<Array<A, D>, ShapeError>
103    where
104        A: Float,
105        D: Dimension,
106    {
107        let dim = dim.into_dimension();
108        let n = dim.size();
109        Array::linspace(A::zero(), A::from(n - 1).unwrap(), n)
110            .to_shape(dim)
111            .map(|x| x.to_owned())
112    }
113}
114
115pub(crate) mod stack {
116    use ndarray::{Array1, Array2, s};
117    use num_traits::Num;
118    /// Creates a larger array from an iterator of smaller arrays.
119    pub fn stack_iter<T>(iter: impl IntoIterator<Item = Array1<T>>) -> Array2<T>
120    where
121        T: Clone + Num,
122    {
123        let mut iter = iter.into_iter();
124        let first = iter.next().unwrap();
125        let shape = [iter.size_hint().0 + 1, first.len()];
126        let mut res = Array2::<T>::zeros(shape);
127        res.slice_mut(s![0, ..]).assign(&first);
128        for (i, s) in iter.enumerate() {
129            res.slice_mut(s![i + 1, ..]).assign(&s);
130        }
131        res
132    }
133    ///
134    pub fn hstack<T>(iter: impl IntoIterator<Item = Array1<T>>) -> Array2<T>
135    where
136        T: Clone + Num,
137    {
138        let iter = Vec::from_iter(iter);
139        let mut res = Array2::<T>::zeros((iter.first().unwrap().len(), iter.len()));
140        for (i, s) in iter.iter().enumerate() {
141            res.slice_mut(s![.., i]).assign(s);
142        }
143        res
144    }
145    ///
146    pub fn vstack<T>(iter: impl IntoIterator<Item = Array1<T>>) -> Array2<T>
147    where
148        T: Clone + Num,
149    {
150        let iter = Vec::from_iter(iter);
151        let mut res = Array2::<T>::zeros((iter.len(), iter.first().unwrap().len()));
152        for (i, s) in iter.iter().enumerate() {
153            res.slice_mut(s![i, ..]).assign(s);
154        }
155        res
156    }
157}