1#[cfg(test)]
2mod test;
3
4pub(super) mod configuration;
5pub(super) mod list;
6pub(super) mod norm;
7pub(super) mod quantity;
8pub(super) mod rank_0;
9pub(super) mod rank_1;
10pub(super) mod rank_2;
11pub(super) mod rank_3;
12pub(super) mod rank_4;
13pub(super) mod tuple;
14pub(super) mod vec;
15
16pub use configuration::{
17 Auxiliary, Configuration, Current, Factor, Flattened, Intermediate, Projection, Reference,
18};
19pub use norm::Norm;
20pub use quantity::{Is, Quantity};
21
22use super::{SquareMatrix, Vector};
23use crate::math::{Style, StyledError, styled_error};
24use crate::units::{Dimensionless, Time, UnitMul};
25use rank_0::{
26 TensorRank0,
27 list::{TensorRank0List, vec::TensorRank0ListVec},
28};
29use std::{
30 fmt::{Debug, Display},
31 iter::Sum,
32 ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign},
33};
34
35pub type Scalar = TensorRank0;
37
38pub type Scalars = Vector;
40
41pub type ScalarList<const N: usize> = TensorRank0List<N>;
43
44pub type ScalarListVec<const N: usize> = TensorRank0ListVec<N>;
46
47#[derive(PartialEq)]
49pub enum TensorError {
50 NotPositiveDefinite,
51 SquareRootDidNotConverge,
52 SymmetricMatrixComplexEigenvalues,
53}
54
55impl StyledError for TensorError {
56 fn message(&self, style: &Style) -> String {
57 let h = style.headline;
58 match self {
59 Self::NotPositiveDefinite => format!("{h}Result is not positive definite."),
60 Self::SquareRootDidNotConverge => {
61 format!("{h}Matrix square root iteration did not converge.")
62 }
63 Self::SymmetricMatrixComplexEigenvalues => {
64 format!("{h}Symmetric matrix produced complex eigenvalues")
65 }
66 }
67 }
68}
69
70styled_error!(TensorError);
71
72pub trait Differentiable<T = Time>
83where
84 Self: Tensor,
85{
86 type Derivative: Tensor;
88}
89
90pub type Derivative<Y, T = Time> = <Y as Differentiable<T>>::Derivative;
96
97pub type Square<U> = <U as UnitMul<U>>::Output;
104
105pub trait ContractWith<Rhs> {
112 type Output;
114 fn contract_with(&self, rhs: &Rhs) -> Self::Output;
116}
117
118pub trait Erase {
124 type Erased: Tensor;
126 fn erase(&self) -> &Self::Erased;
128}
129
130impl Erase for TensorRank0 {
131 type Erased = Self;
132 fn erase(&self) -> &Self {
133 self
134 }
135}
136
137pub trait Solution
139where
140 Self: From<Vector> + Tensor,
141{
142 fn decrement_from(&mut self, other: &Vector);
144 fn decrement_from_chained(&mut self, other: &mut Vector, vector: &Vector);
146 fn decrement_from_retained(&mut self, _retained: &[bool], _other: &Vector) {
148 unimplemented!()
149 }
150}
151
152pub trait Jacobian
154where
155 Self:
156 From<Vector> + Tensor + Sub<Vector, Output = Self> + for<'a> Sub<&'a Vector, Output = Self>,
157{
158 fn fill_into(&self, vector: &mut Vector);
160 fn fill_into_chained(self, other: Vector, vector: &mut Vector);
162 fn retain_from(self, _retained: &[bool]) -> Vector {
164 unimplemented!()
165 }
166 fn zero_out(&mut self, _indices: &[usize]) {
168 unimplemented!()
169 }
170}
171
172pub trait Hessian
174where
175 Self: Tensor,
176{
177 fn entry(&self, row: usize, column: usize) -> Scalar;
179 fn fill_into(self, square_matrix: &mut SquareMatrix);
181 fn quadratic_form(&self, _vector: &Vector) -> Scalar {
187 unimplemented!()
188 }
189 fn retain_from(self, _retained: &[bool]) -> SquareMatrix {
191 unimplemented!()
192 }
193}
194
195pub trait HessianBlock {
197 fn entry(&self, row: usize, column: usize) -> TensorRank0;
199 fn height(&self) -> usize;
201 fn width(&self) -> usize;
203 fn fill_into_block<M>(&self, matrix: &mut M, row: usize, column: usize)
205 where
206 M: IndexMut<usize, Output = Vector>;
207}
208
209pub struct Transposed<H>(pub H);
211
212impl<H: HessianBlock> HessianBlock for Transposed<H> {
213 fn entry(&self, row: usize, column: usize) -> TensorRank0 {
214 self.0.entry(column, row)
215 }
216 fn height(&self) -> usize {
217 self.0.width()
218 }
219 fn width(&self) -> usize {
220 self.0.height()
221 }
222 fn fill_into_block<M>(&self, matrix: &mut M, row: usize, column: usize)
223 where
224 M: IndexMut<usize, Output = Vector>,
225 {
226 (0..self.0.height()).for_each(|i| {
227 (0..self.0.width()).for_each(|j| matrix[row + j][column + i] = self.0.entry(i, j))
228 })
229 }
230}
231
232pub trait HessianAccumulate<const D: usize, I, U = Dimensionless> {
238 fn accumulate(&mut self, a: usize, b: usize, block: rank_2::TensorRank2<D, I, I, U>);
239}
240
241pub trait Rank2
243where
244 Self: Sized + Tensor,
245{
246 type Transpose;
248 fn deviatoric(&self) -> Self;
250 fn deviatoric_and_trace(&self) -> (Self, Quantity<Self::Unit>);
252 fn is_diagonal(&self) -> bool;
254 fn is_identity(&self) -> bool;
256 fn is_symmetric(&self) -> bool;
258 fn second_invariant(&self) -> Quantity<Square<Self::Unit>>
260 where
261 Self::Unit: UnitMul<Self::Unit>,
262 {
263 let trace = self.trace();
264 (trace * trace - self.squared_trace()) * 0.5
265 }
266 fn squared_trace(&self) -> Quantity<Square<Self::Unit>>
268 where
269 Self::Unit: UnitMul<Self::Unit>;
270 fn trace(&self) -> Quantity<Self::Unit>;
272 fn transpose(&self) -> Self::Transpose;
274}
275
276#[expect(clippy::len_without_is_empty)]
278pub trait Tensor
279where
280 for<'a> Self: Sized
281 + Add<Self, Output = Self>
282 + Add<&'a Self, Output = Self>
283 + AddAssign
284 + AddAssign<&'a Self>
285 + Clone
286 + Debug
287 + Default
288 + Display
289 + Div<TensorRank0, Output = Self>
290 + DivAssign<TensorRank0>
292 + DivAssign<&'a TensorRank0>
293 + Mul<TensorRank0, Output = Self>
294 + MulAssign<TensorRank0>
296 + MulAssign<&'a TensorRank0>
297 + Sub<Self, Output = Self>
298 + Sub<&'a Self, Output = Self>
299 + SubAssign
300 + SubAssign<&'a Self>
301 + Sum,
302 Self::Item: Tensor,
303{
304 type Item;
306 type Unit;
308 fn error_count_zero(&self, tol_abs: Scalar, tol_rel: Scalar) -> Option<usize> {
310 let error_count = self
311 .iter()
312 .filter_map(|entry| entry.error_count_zero(tol_abs, tol_rel))
313 .sum();
314 if error_count > 0 {
315 Some(error_count)
316 } else {
317 None
318 }
319 }
320 fn error_count(&self, other: &Self, tol_abs: Scalar, tol_rel: Scalar) -> Option<usize> {
322 let error_count = self
323 .iter()
324 .zip(other.iter())
325 .filter_map(|(self_entry, other_entry)| {
326 self_entry.error_count(other_entry, tol_abs, tol_rel)
327 })
328 .sum();
329 if error_count > 0 {
330 Some(error_count)
331 } else {
332 None
333 }
334 }
335 fn full_contraction(&self, tensor: &Self) -> TensorRank0 {
337 self.iter()
338 .zip(tensor.iter())
339 .map(|(self_entry, tensor_entry)| self_entry.full_contraction(tensor_entry))
340 .fold(0.0, f64::algebraic_add)
341 }
342 fn is_zero(&self) -> bool {
344 self.iter().filter(|entry| !entry.is_zero()).count() == 0
345 }
346 fn iter(&self) -> impl Iterator<Item = &Self::Item>;
350 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item>;
354 fn len(&self) -> usize;
356 fn norm(&self) -> Quantity<Self::Unit> {
358 Quantity::new(self.full_contraction(self).sqrt())
359 }
360 fn norm_inf(&self) -> Quantity<Self::Unit> {
362 Quantity::new(
363 self.iter()
364 .fold(0.0, |acc, entry| entry.norm_inf().value().max(acc)),
365 )
366 }
367 fn norm_l1(&self) -> Quantity<Self::Unit> {
369 Quantity::new(
370 self.iter()
371 .fold(0.0, |acc, entry| acc + entry.norm_l1().value()),
372 )
373 }
374 fn norm_p_sum(&self, p: TensorRank0) -> TensorRank0 {
376 self.iter()
377 .fold(0.0, |acc, entry| acc + entry.norm_p_sum(p))
378 }
379 fn norm_p(&self, p: TensorRank0) -> Quantity<Self::Unit> {
381 Quantity::new(self.norm_p_sum(p).powf(1.0 / p))
382 }
383 fn norm_squared(&self) -> Quantity<Square<Self::Unit>>
385 where
386 Self::Unit: UnitMul<Self::Unit>,
387 {
388 Quantity::new(self.full_contraction(self))
389 }
390 fn normalize(&mut self) {
392 *self /= self.norm().value()
393 }
394 fn size(&self) -> usize;
396 fn sub_abs(&self, other: &Self) -> Self {
398 let mut difference = self.clone();
399 difference
400 .iter_mut()
401 .zip(self.iter().zip(other.iter()))
402 .for_each(|(entry, (self_entry, other_entry))| {
403 *entry = self_entry.sub_abs(other_entry)
404 });
405 difference
406 }
407 fn sub_rel(&self, other: &Self) -> Self {
409 let mut difference = self.clone();
410 difference
411 .iter_mut()
412 .zip(self.iter().zip(other.iter()))
413 .for_each(|(entry, (self_entry, other_entry))| {
414 *entry = self_entry.sub_rel(other_entry)
415 });
416 difference
417 }
418}
419
420pub trait TensorArray {
422 type Array;
424 type Item;
426 fn as_array(&self) -> Self::Array;
428 fn identity() -> Self;
430 fn zero() -> Self;
432}
433
434pub trait TensorVec
436where
437 Self: FromIterator<Self::Item> + Index<usize, Output = Self::Item> + IndexMut<usize>,
438{
439 type Item;
441 fn append(&mut self, other: &mut Self);
443 fn capacity(&self) -> usize;
445 fn is_empty(&self) -> bool;
447 fn new() -> Self;
449 fn push(&mut self, item: Self::Item);
451 fn remove(&mut self, index: usize) -> Self::Item;
453 fn reserve(&mut self, additional: usize);
455 fn retain<F>(&mut self, f: F)
457 where
458 F: FnMut(&Self::Item) -> bool;
459 fn swap_remove(&mut self, index: usize) -> Self::Item;
461 fn with_capacity(capacity: usize) -> Self;
463}