single_utilities/traits/
mod.rs

1#[cfg(feature = "convert")]
2use nalgebra::{Dim, Dyn, U1};
3#[cfg(feature = "convert")]
4use ndarray::ShapeBuilder;
5use num_traits::float::FloatCore;
6use num_traits::{Bounded, FromPrimitive, NumCast, One, ToPrimitive, Unsigned, Zero};
7#[cfg(feature = "simd")]
8use simba::{scalar::RealField, simd::SimdRealField};
9use std::fmt::Debug;
10use std::hash::Hash;
11use std::iter::Sum;
12use std::ops::{Add, AddAssign, MulAssign, SubAssign};
13
14/// A trait defining fundamental numeric operations and constraints.
15///
16/// This trait bundles common numeric operations required for mathematical computations,
17/// including zero/one elements, numeric casting, assignment operations, ordering,
18/// bounds checking, and basic arithmetic. Types implementing this trait can be used
19/// in generic numeric algorithms.
20pub trait NumericOps:
21    Zero
22    + One
23    + NumCast
24    + Copy
25    + AddAssign
26    + MulAssign
27    + SubAssign
28    + PartialOrd
29    + Bounded
30    + Add<Output = Self>
31    + Sum
32    + Debug
33    + Default
34{
35}
36impl<
37    T: Zero
38        + One
39        + NumCast
40        + Copy
41        + AddAssign
42        + MulAssign
43        + SubAssign
44        + PartialOrd
45        + Bounded
46        + Add<Output = Self>
47        + Sum
48        + Debug
49        + Default,
50> NumericOps for T
51{
52}
53
54/// A thread-safe version of `NumericOps`.
55///
56/// This trait extends `NumericOps` with `Send + Sync` bounds, making it suitable
57/// for use in concurrent and parallel computations where data needs to be
58/// safely shared across threads.
59pub trait NumericOpsTS: NumericOps + Send + Sync {}
60
61impl<T: NumericOps + Send + Sync> NumericOpsTS for T {}
62
63/// A trait for floating-point numeric operations.
64///
65/// Extends `NumericOps` with floating-point specific operations from the `num_traits`
66/// crate, including floating-point arithmetic, primitive conversions, and core
67/// floating-point functionality. This trait is designed for types that represent
68/// real numbers with decimal precision.
69pub trait FloatOps:
70    NumericOps + num_traits::Float + FromPrimitive + ToPrimitive + FloatCore
71{
72}
73
74impl<T: NumericOps + num_traits::Float + FromPrimitive + ToPrimitive + FloatCore> FloatOps for T {}
75
76/// A thread-safe version of `FloatOps`.
77///
78/// This trait extends `FloatOps` with `Send + Sync` bounds for safe use in
79/// concurrent floating-point computations across multiple threads.
80pub trait FloatOpsTS: FloatOps + Sync + Send {}
81
82impl<T: FloatOps + Send + Sync> FloatOpsTS for T {}
83
84#[cfg(feature = "simd")]
85/// A SIMD-enabled floating-point operations trait.
86///
87/// Extends `FloatOpsTS` with SIMD (Single Instruction, Multiple Data) capabilities
88/// from the `simba` crate. This enables vectorized floating-point operations for
89/// improved performance in mathematical computations.
90///
91/// Only available when the "simd" feature is enabled.
92pub trait FloatOpsTSSimba: FloatOpsTS + SimdRealField + RealField {}
93
94#[cfg(feature = "simd")]
95impl<T: FloatOpsTS + SimdRealField + RealField> FloatOpsTSSimba for T {}
96
97/// A trait for numeric types suitable for normalization operations.
98///
99/// This trait combines floating-point arithmetic with assignment operations,
100/// summation capabilities, and numeric casting. It's specifically designed
101/// for types that need to participate in normalization algorithms where
102/// values are scaled or adjusted relative to some total or maximum.
103pub trait NumericNormalize:
104    num_traits::Float + std::ops::AddAssign + std::iter::Sum + num_traits::NumCast
105{
106}
107
108// Blanket implementation for any type that satisfies the bounds
109impl<T> NumericNormalize for T where
110    T: num_traits::Float + std::ops::AddAssign + std::iter::Sum + num_traits::NumCast
111{
112}
113
114/// A trait for vectors that can be resized and filled with default values.
115///
116/// Provides functionality to clear a vector and resize it to a specific length,
117/// filling all positions with the default value of the element type. This is
118/// useful for initializing or resetting vectors to a known state.
119pub trait ZeroVec {
120    /// Clears the vector and resizes it to the specified length, filling with default values.
121    ///
122    /// # Arguments
123    /// * `len` - The desired length of the vector
124    fn zero_len(&mut self, len: usize);
125}
126
127impl<T: Default + Clone> ZeroVec for Vec<T> {
128    fn zero_len(&mut self, len: usize) {
129        self.clear();
130        self.reserve(len);
131        self.extend(std::iter::repeat_n(T::default(), len));
132    }
133}
134
135/// A trait for unsigned integer types suitable for indexing operations.
136///
137/// This trait combines unsigned integer properties with zero/one elements,
138/// ordering capabilities, and conversion to/from `usize`. It's designed for
139/// types that can be safely used as array or vector indices while providing
140/// mathematical operations and bounds checking.
141pub trait UIndex:
142    Unsigned + Zero + One + Copy + Eq + Ord + PartialOrd + From<usize> + Into<usize> + Bounded + Hash
143{
144}
145
146impl<
147    I: Unsigned
148        + Zero
149        + One
150        + Copy
151        + Eq
152        + Ord
153        + PartialOrd
154        + From<usize>
155        + Into<usize>
156        + Bounded
157        + Hash,
158> UIndex for I
159{
160}
161
162/// A trait for scalar values used in mathematical computations.
163///
164/// This trait defines the minimal requirements for types that can be used as
165/// scalar values in mathematical operations. It requires static lifetime,
166/// cloning capability, equality comparison, and debug formatting. This is
167/// typically used as a constraint for generic mathematical algorithms.
168pub trait Scalar: 'static + Clone + PartialEq + Debug {}
169
170impl<T: 'static + Clone + PartialEq + Debug> Scalar for T {}
171
172#[cfg(feature = "convert")]
173pub trait IntoNalgebra {
174    type Out;
175
176    fn into_nalgebra(self) -> Self::Out;
177}
178
179#[cfg(feature = "convert")]
180pub trait IntoNdarray1 {
181    type Out;
182
183    fn into_ndarray1(self) -> Self::Out;
184}
185
186#[cfg(feature = "convert")]
187pub trait IntoNdarray2 {
188    type Out;
189
190    fn into_ndarray2(self) -> Self::Out;
191}
192
193#[cfg(feature = "convert")]
194impl<'a, N: Scalar, R: Dim, RStride: Dim, CStride: Dim> IntoNdarray1
195    for nalgebra::Matrix<N, R, U1, nalgebra::ViewStorageMut<'a, N, R, U1, RStride, CStride>>
196{
197    type Out = ndarray::ArrayViewMut1<'a, N>;
198
199    fn into_ndarray1(self) -> Self::Out {
200        unsafe {
201            ndarray::ArrayViewMut1::from_shape_ptr(
202                (self.shape().0,).strides((self.strides().0,)),
203                self.as_ptr() as *mut N,
204            )
205        }
206    }
207}
208
209#[cfg(feature = "convert")]
210impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> IntoNdarray2
211    for nalgebra::Matrix<N, R, C, nalgebra::ViewStorage<'a, N, R, C, RStride, CStride>>
212{
213    type Out = ndarray::ArrayView2<'a, N>;
214
215    fn into_ndarray2(self) -> Self::Out {
216        unsafe {
217            ndarray::ArrayView2::from_shape_ptr(self.shape().strides(self.strides()), self.as_ptr())
218        }
219    }
220}
221
222#[cfg(feature = "convert")]
223impl<N: Scalar> IntoNdarray2 for nalgebra::Matrix<N, Dyn, Dyn, nalgebra::VecStorage<N, Dyn, Dyn>>
224where
225    nalgebra::DefaultAllocator:
226        nalgebra::allocator::Allocator<Dyn, Dyn, Buffer<N> = nalgebra::VecStorage<N, Dyn, Dyn>>,
227{
228    type Out = ndarray::Array2<N>;
229
230    fn into_ndarray2(self) -> Self::Out {
231        ndarray::Array2::from_shape_vec(self.shape().strides(self.strides()), self.data.into())
232            .unwrap()
233    }
234}
235
236#[cfg(feature = "convert")]
237impl<N: Scalar> IntoNdarray1 for nalgebra::DVector<N> {
238    type Out = ndarray::Array1<N>;
239
240    fn into_ndarray1(self) -> Self::Out {
241        ndarray::Array1::from_shape_vec((self.shape().0,), self.data.into()).unwrap()
242    }
243}
244
245#[cfg(feature = "convert")]
246impl<T> IntoNalgebra for ndarray::Array1<T>
247where
248    T: nalgebra::Scalar,
249{
250    type Out = nalgebra::DVector<T>;
251    fn into_nalgebra(self) -> Self::Out {
252        let len = Dyn(self.len());
253        // There is no method to give nalgebra the vector directly where it isn't allocated. If you call
254        // from_vec_generic, it simply calls from_iterator_generic which uses Iterator::collect(). Due to this,
255        // the simplest solution is to just pass an iterator over the values. If you come across this because you
256        // have a performance issue, I would recommend creating the owned data using naglebra and borrowing it with
257        // ndarray to perform operations on it instead of the other way around.
258        Self::Out::from_iterator_generic(len, nalgebra::Const::<1>, self.iter().cloned())
259    }
260}
261
262#[cfg(feature = "convert")]
263impl<T> IntoNalgebra for ndarray::Array2<T>
264where
265    T: nalgebra::Scalar,
266{
267    type Out = nalgebra::DMatrix<T>;
268    fn into_nalgebra(self) -> Self::Out {
269        let nrows = Dyn(self.nrows());
270        let ncols = Dyn(self.ncols());
271        Self::Out::from_iterator_generic(nrows, ncols, self.t().iter().cloned())
272    }
273}