numeris/traits.rs
1use core::fmt::Debug;
2use num_traits::{Float, Num, One, Zero};
3
4#[cfg(feature = "complex")]
5use num_complex::Complex;
6
7/// Trait for types that can be used as matrix elements.
8///
9/// Blanket-implemented for all types satisfying the bounds.
10/// Covers `f32`, `f64`, and all integer types.
11///
12/// # Example
13///
14/// ```
15/// use numeris::Matrix;
16///
17/// // Works with any Scalar type — f64, i32, etc.
18/// let m_float = Matrix::new([[1.0_f64, 2.0], [3.0, 4.0]]);
19/// let m_int = Matrix::new([[1_i32, 2], [3, 4]]);
20/// assert_eq!(m_float.trace(), 5.0);
21/// assert_eq!(m_int.trace(), 5);
22/// ```
23pub trait Scalar: Copy + PartialEq + Debug + Zero + One + Num + 'static {}
24
25impl<T: Copy + PartialEq + Debug + Zero + One + Num + 'static> Scalar for T {}
26
27/// Trait for floating-point matrix elements.
28///
29/// Required by operations that need `sqrt`, `sin`, `abs`, etc.
30/// (decompositions, norms, trigonometric functions).
31/// Implies `LinalgScalar<Real = Self>` since real floats are their own real type.
32///
33/// Use this for inherently-real operations like quaternions and ordered comparisons.
34/// For decompositions and norms that also work with complex numbers, use [`LinalgScalar`].
35pub trait FloatScalar: Scalar + Float + LinalgScalar<Real = Self> {}
36
37impl<T: Scalar + Float + LinalgScalar<Real = T>> FloatScalar for T {}
38
39/// Trait for matrix elements that support linear algebra operations.
40///
41/// Covers both real floats (`f32`, `f64`) and complex numbers (`Complex<f32>`,
42/// `Complex<f64>`). Use this instead of `FloatScalar` in decompositions and norms.
43///
44/// `FloatScalar` remains for inherently-real operations (quaternions, ordered comparisons).
45pub trait LinalgScalar: Scalar {
46 /// The real component type (`Self` for reals, `T` for `Complex<T>`).
47 type Real: FloatScalar;
48
49 /// Absolute value / modulus: `|z|` for complex, `.abs()` for real.
50 fn modulus(self) -> Self::Real;
51
52 /// Complex conjugate (identity for reals).
53 fn conj(self) -> Self;
54
55 /// Real part.
56 fn re(self) -> Self::Real;
57
58 /// Square root.
59 fn lsqrt(self) -> Self;
60
61 /// Natural logarithm.
62 fn lln(self) -> Self;
63
64 /// Machine epsilon of the underlying real type.
65 fn lepsilon() -> Self::Real;
66
67 /// Promote a real value into `Self`.
68 fn from_real(r: Self::Real) -> Self;
69}
70
71/// Concrete impls for real floats — trivial delegation.
72macro_rules! impl_linalg_scalar_real {
73 ($($t:ty),*) => {
74 $(
75 impl LinalgScalar for $t {
76 type Real = $t;
77
78 #[inline] fn modulus(self) -> $t { Float::abs(self) }
79 #[inline] fn conj(self) -> $t { self }
80 #[inline] fn re(self) -> $t { self }
81 #[inline] fn lsqrt(self) -> $t { Float::sqrt(self) }
82 #[inline] fn lln(self) -> $t { Float::ln(self) }
83 #[inline] fn lepsilon() -> $t { <$t as Float>::epsilon() }
84 #[inline] fn from_real(r: $t) -> $t { r }
85 }
86 )*
87 };
88}
89
90impl_linalg_scalar_real!(f32, f64);
91
92#[cfg(feature = "complex")]
93impl<T: FloatScalar> LinalgScalar for Complex<T> {
94 type Real = T;
95
96 #[inline]
97 fn modulus(self) -> T {
98 self.norm()
99 }
100
101 #[inline]
102 fn conj(self) -> Self {
103 Complex::conj(&self)
104 }
105
106 #[inline]
107 fn re(self) -> T {
108 self.re
109 }
110
111 #[inline]
112 fn lsqrt(self) -> Self {
113 self.sqrt()
114 }
115
116 #[inline]
117 fn lln(self) -> Self {
118 self.ln()
119 }
120
121 #[inline]
122 fn lepsilon() -> T {
123 T::epsilon()
124 }
125
126 #[inline]
127 fn from_real(r: T) -> Self {
128 Complex::new(r, T::zero())
129 }
130}
131
132/// Read-only access to a matrix-like type.
133///
134/// This trait allows algorithms to operate generically over
135/// both fixed-size `Matrix` and future `DynMatrix` types.
136///
137/// # Example
138///
139/// ```
140/// use numeris::{Matrix, MatrixRef};
141///
142/// fn trace<T: numeris::Scalar>(m: &impl MatrixRef<T>) -> T {
143/// let mut sum = T::zero();
144/// let n = m.nrows().min(m.ncols());
145/// for i in 0..n {
146/// sum = sum + *m.get(i, i);
147/// }
148/// sum
149/// }
150///
151/// let m = Matrix::new([[1.0, 2.0], [3.0, 4.0]]);
152/// assert_eq!(trace(&m), 5.0);
153/// ```
154pub trait MatrixRef<T> {
155 /// Number of rows.
156 fn nrows(&self) -> usize;
157 /// Number of columns.
158 fn ncols(&self) -> usize;
159 /// Reference to the element at `(row, col)`.
160 fn get(&self, row: usize, col: usize) -> &T;
161
162 /// Contiguous slice of column `col` from row `row_start` to end.
163 ///
164 /// Returns `&[T]` of length `nrows - row_start`. Available because
165 /// both `Matrix` and `DynMatrix` use column-major contiguous storage.
166 fn col_as_slice(&self, col: usize, row_start: usize) -> &[T];
167}
168
169/// Mutable access to a matrix-like type.
170///
171/// Extends [`MatrixRef`] with mutable element access, enabling
172/// in-place algorithms (Cholesky, LU, etc.) to work generically.
173///
174/// # Example
175///
176/// ```
177/// use numeris::{Matrix, MatrixMut};
178///
179/// fn set_diag<T: numeris::Scalar>(m: &mut impl MatrixMut<T>, val: T) {
180/// let n = m.nrows().min(m.ncols());
181/// for i in 0..n {
182/// *m.get_mut(i, i) = val;
183/// }
184/// }
185///
186/// let mut m: Matrix<f64, 2, 2> = Matrix::zeros();
187/// set_diag(&mut m, 7.0);
188/// assert_eq!(m[(0, 0)], 7.0);
189/// ```
190pub trait MatrixMut<T>: MatrixRef<T> {
191 /// Mutable reference to the element at `(row, col)`.
192 fn get_mut(&mut self, row: usize, col: usize) -> &mut T;
193
194 /// Mutable contiguous slice of column `col` from row `row_start` to end.
195 fn col_as_mut_slice(&mut self, col: usize, row_start: usize) -> &mut [T];
196}