conspire/math/tensor/
mod.rs

1pub mod test;
2
3pub mod list;
4pub mod rank_0;
5pub mod rank_1;
6pub mod rank_2;
7pub mod rank_3;
8pub mod rank_4;
9pub mod tuple;
10pub mod vec;
11
12use super::{SquareMatrix, Vector};
13use crate::defeat_message;
14use rank_0::TensorRank0;
15use std::{
16    fmt::{self, Debug, Display, Formatter},
17    iter::Sum,
18    ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign},
19};
20
21/// A scalar.
22pub type Scalar = TensorRank0;
23
24/// Possible errors for tensors.
25#[derive(PartialEq)]
26pub enum TensorError {
27    NotPositiveDefinite,
28}
29
30impl Debug for TensorError {
31    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
32        let error = match self {
33            Self::NotPositiveDefinite => "\x1b[1;91mResult is not positive definite.".to_string(),
34        };
35        write!(f, "\n{error}\n\x1b[0;2;31m{}\x1b[0m\n", defeat_message())
36    }
37}
38
39impl Display for TensorError {
40    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
41        let error = match self {
42            Self::NotPositiveDefinite => "\x1b[1;91mResult is not positive definite.".to_string(),
43        };
44        write!(f, "{error}\x1b[0m")
45    }
46}
47
48/// Common methods for solutions.
49pub trait Solution
50where
51    Self: From<Vector> + Tensor,
52{
53    /// Decrements the solution from another vector.
54    fn decrement_from(&mut self, other: &Vector);
55    /// Decrements the solution chained with a vector from another vector.
56    fn decrement_from_chained(&mut self, other: &mut Vector, vector: Vector);
57    /// Decrements the solution from another vector on retained entries.
58    fn decrement_from_retained(&mut self, _retained: &[bool], _other: &Vector) {
59        unimplemented!()
60    }
61}
62
63/// Common methods for Jacobians.
64pub trait Jacobian
65where
66    Self:
67        From<Vector> + Tensor + Sub<Vector, Output = Self> + for<'a> Sub<&'a Vector, Output = Self>,
68{
69    /// Fills the Jacobian into a vector.
70    fn fill_into(self, vector: &mut Vector);
71    /// Fills the Jacobian chained with a vector into another vector.
72    fn fill_into_chained(self, other: Vector, vector: &mut Vector);
73    /// Return only the retained indices.
74    fn retain_from(self, _retained: &[bool]) -> Vector {
75        unimplemented!()
76    }
77    /// Zero out the specified indices.
78    fn zero_out(&mut self, _indices: &[usize]) {
79        unimplemented!()
80    }
81}
82
83/// Common methods for Hessians.
84pub trait Hessian
85where
86    Self: Tensor,
87{
88    /// Fills the Hessian into a square matrix.
89    fn fill_into(self, square_matrix: &mut SquareMatrix);
90    /// Return only the retained indices.
91    fn retain_from(self, _retained: &[bool]) -> SquareMatrix {
92        unimplemented!()
93    }
94}
95
96/// Common methods for rank-2 tensors.
97pub trait Rank2
98where
99    Self: Sized,
100{
101    /// The type that is the transpose of the tensor.
102    type Transpose;
103    /// Returns the deviatoric component of the rank-2 tensor.
104    fn deviatoric(&self) -> Self;
105    /// Returns the deviatoric component and trace of the rank-2 tensor.
106    fn deviatoric_and_trace(&self) -> (Self, TensorRank0);
107    /// Checks whether the tensor is a diagonal tensor.
108    fn is_diagonal(&self) -> bool;
109    /// Checks whether the tensor is the identity tensor.
110    fn is_identity(&self) -> bool;
111    /// Checks whether the tensor is a symmetric tensor.
112    fn is_symmetric(&self) -> bool;
113    /// Returns the second invariant of the rank-2 tensor.
114    fn second_invariant(&self) -> TensorRank0 {
115        0.5 * (self.trace().powi(2) - self.squared_trace())
116    }
117    /// Returns the trace of the rank-2 tensor squared.
118    fn squared_trace(&self) -> TensorRank0;
119    /// Returns the trace of the rank-2 tensor.
120    fn trace(&self) -> TensorRank0;
121    /// Returns the transpose of the rank-2 tensor.
122    fn transpose(&self) -> Self::Transpose;
123}
124
125/// Common methods for tensors.
126#[allow(clippy::len_without_is_empty)]
127pub trait Tensor
128where
129    for<'a> Self: Sized
130        + Add<Self, Output = Self>
131        + Add<&'a Self, Output = Self>
132        + AddAssign
133        + AddAssign<&'a Self>
134        + Clone
135        + Debug
136        + Default
137        + Display
138        + Div<TensorRank0, Output = Self>
139        + Div<&'a TensorRank0, Output = Self>
140        + DivAssign<TensorRank0>
141        + DivAssign<&'a TensorRank0>
142        + Mul<TensorRank0, Output = Self>
143        + Mul<&'a TensorRank0, Output = Self>
144        + MulAssign<TensorRank0>
145        + MulAssign<&'a TensorRank0>
146        + Sub<Self, Output = Self>
147        + Sub<&'a Self, Output = Self>
148        + SubAssign
149        + SubAssign<&'a Self>
150        + Sum,
151    Self::Item: Tensor,
152{
153    /// The type of item encountered when iterating over the tensor.
154    type Item;
155    /// Returns number of different entries given absolute and relative tolerances.
156    fn error_count(&self, other: &Self, tol_abs: &Scalar, tol_rel: &Scalar) -> Option<usize> {
157        let error_count = self
158            .iter()
159            .zip(other.iter())
160            .filter_map(|(self_entry, other_entry)| {
161                self_entry.error_count(other_entry, tol_abs, tol_rel)
162            })
163            .sum();
164        if error_count > 0 {
165            Some(error_count)
166        } else {
167            None
168        }
169    }
170    /// Returns the full contraction with another tensor.
171    fn full_contraction(&self, tensor: &Self) -> TensorRank0 {
172        self.iter()
173            .zip(tensor.iter())
174            .map(|(self_entry, tensor_entry)| self_entry.full_contraction(tensor_entry))
175            .sum()
176    }
177    /// Checks whether the tensor is the zero tensor.
178    fn is_zero(&self) -> bool {
179        self.iter().filter(|entry| !entry.is_zero()).count() == 0
180    }
181    /// Returns an iterator.
182    ///
183    /// The iterator yields all items from start to end. [Read more](https://doc.rust-lang.org/std/iter/)
184    fn iter(&self) -> impl Iterator<Item = &Self::Item>;
185    /// Returns an iterator that allows modifying each value.
186    ///
187    /// The iterator yields all items from start to end. [Read more](https://doc.rust-lang.org/std/iter/)
188    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item>;
189    /// Returns the number of elements, also referred to as the ‘length’.
190    fn len(&self) -> usize;
191    /// Returns the tensor norm.
192    fn norm(&self) -> TensorRank0 {
193        self.norm_squared().sqrt()
194    }
195    /// Returns the infinity norm.
196    fn norm_inf(&self) -> TensorRank0 {
197        self.iter()
198            .fold(0.0, |acc, entry| entry.norm_inf().max(acc))
199    }
200    /// Returns the tensor norm squared.
201    fn norm_squared(&self) -> TensorRank0 {
202        self.full_contraction(self)
203    }
204    /// Normalizes the tensor.
205    fn normalize(&mut self) {
206        *self /= self.norm()
207    }
208    /// Returns the tensor normalized.
209    fn normalized(self) -> Self {
210        let norm = self.norm();
211        self / norm
212    }
213    /// Returns the total number of entries.
214    fn size(&self) -> usize;
215    /// Returns the positive difference of the two tensors.
216    fn sub_abs(&self, other: &Self) -> Self {
217        let mut difference = self.clone();
218        difference
219            .iter_mut()
220            .zip(self.iter().zip(other.iter()))
221            .for_each(|(entry, (self_entry, other_entry))| {
222                *entry = self_entry.sub_abs(other_entry)
223            });
224        difference
225    }
226    /// Returns the relative difference of the two tensors.
227    fn sub_rel(&self, other: &Self) -> Self {
228        let mut difference = self.clone();
229        difference
230            .iter_mut()
231            .zip(self.iter().zip(other.iter()))
232            .for_each(|(entry, (self_entry, other_entry))| {
233                *entry = self_entry.sub_rel(other_entry)
234            });
235        difference
236    }
237}
238
239/// Common methods for tensors derived from arrays.
240pub trait TensorArray {
241    /// The type of array corresponding to the tensor.
242    type Array;
243    /// The type of item encountered when iterating over the tensor.
244    type Item;
245    /// Returns the tensor as an array.
246    fn as_array(&self) -> Self::Array;
247    /// Returns the identity tensor.
248    fn identity() -> Self;
249    /// Returns a tensor given an array.
250    fn new(array: Self::Array) -> Self;
251    /// Returns the zero tensor.
252    fn zero() -> Self;
253}
254
255/// Common methods for tensors derived from Vec.
256pub trait TensorVec
257where
258    Self: FromIterator<Self::Item> + Index<usize, Output = Self::Item> + IndexMut<usize>,
259{
260    /// The type of element encountered when iterating over the tensor.
261    type Item;
262    /// Moves all the elements of other into self, leaving other empty.
263    fn append(&mut self, other: &mut Self);
264    /// Returns the total number of elements the vector can hold without reallocating.
265    fn capacity(&self) -> usize;
266    /// Returns `true` if the vector contains no elements.
267    fn is_empty(&self) -> bool;
268    /// Constructs a new, empty Vec, not allocating until elements are pushed onto it.
269    fn new() -> Self;
270    /// Appends an element to the back of the Vec.
271    fn push(&mut self, item: Self::Item);
272    /// Removes an element from the Vec and returns it, shifting elements to the left.
273    fn remove(&mut self, _index: usize) -> Self::Item;
274    /// Retains only the elements specified by the predicate.
275    fn retain<F>(&mut self, f: F)
276    where
277        F: FnMut(&Self::Item) -> bool;
278    /// Removes an element from the Vec and returns it, replacing it with the last element.
279    fn swap_remove(&mut self, _index: usize) -> Self::Item;
280}