Skip to main content

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}