linalg_traits/matrix/
matrix_trait.rs

1use crate::scalar::Scalar;
2use crate::vector::vector_trait::Vector;
3use std::fmt::Debug;
4use std::borrow::Cow;
5use std::ops::{Index, IndexMut};
6
7/// Trait defining a generic matrix type.
8///
9/// # Note
10///
11/// In addition to the methods defined by this trait, this trait also forces that the implementor
12/// also support indexing ([`Index`]) and mutable indexing ([`IndexMut`]) by a 2-element tuple of
13/// [`usize`]s (1st element defining the row index, 2nd element defining the column index).
14///
15/// # Using [`Matrix`] as a trait bound
16///
17/// Say I want to write a function that is generic over all matrices of [`f64`], e.g. I want it to
18/// be compatible both with [`ndarray::Array2<f64>`] and with [`nalgebra::DVector<f64>`]. I can
19/// define this function as
20///
21/// ```ignore
22/// fn my_function<M: Matrix<f64>>(input_vector: &M) -> M { ... }
23/// ```
24///
25/// Since the [`Matrix`] trait is generic over types that implement the [`Scalar`] trait, any
26/// function that is generic over [`Matrix`]es can also be made generic over the type of their
27/// elements. In this case, if we want `my_function` to be compatible with vectors of any scalar
28/// type (i.e. types that implement the [`Scalar`] trait), and not just vectors of [`f64`]s, we can
29/// include an additional generic parameter `S`.
30///
31/// ```ignore
32/// fn my_function<S: Scalar, V: Vector<S>>(input_vector: &V) -> V { ... }
33/// ```
34///
35/// ## Warning
36///
37/// When working with arrays from [`ndarray`], elements of the array must also implement the
38/// following traits in addition to the [`Scalar`] trait:
39///
40/// * [`ndarray::ScalarOperand`]
41/// * [`ndarray::LinalgScalar`]
42///
43/// For example, consider the case where we define the struct `CustomType` and implement the
44/// [`Scalar`] trait for `CustomType`. If we want to be able to pass an
45/// [`ndarray::Array2<CustomType>`] into `my_function` from the example above, then we must also
46/// implement the [`ndarray::ScalarOperand`] and [`ndarray::LinalgScalar`] traits for `CustomType`.
47pub trait Matrix<S: Scalar>:
48    Index<(usize, usize), Output = S>       // Indexing via square brackets.
49    + IndexMut<(usize, usize), Output = S>  // Index-assignment via square brackets.
50    + Clone                                 // Copying (compatible with dynamically-sized types).
51    + Debug                                 // Debug printing.
52    + PartialEq                             // Equality comparisons.
53{
54    // -----------------
55    // Associated types.
56    // -----------------
57
58    /// Length-`N` Vector type implementing the [`crate::Vector`] trait that is compatible with this
59    /// matrix type. An instance of this matrix type with shape `(M, N)` can be multiplied from the
60    /// right by an instance of this vector type with length `N`, resulting in an instance of this
61    /// vector type with length `M` (mathematically representing a column vector).
62    type VectorN: Vector<S>;
63
64    /// Length-`M` Vector type implementing the [`crate::Vector`] trait that is compatible with this
65    /// matrix type. An instance of this matrix type with shape `(M, N)` can be multiplied from the
66    /// left by an instance of this vector type with length `M`, resulting in an instance of this
67    /// vector type with length `N` (mathematically representing a row vector).
68    type VectorM: Vector<S>;
69
70    // -------------------------------
71    // Default method implementations.
72    // -------------------------------
73
74    /// Create a length-`N` vector that is compatible with this `M x N` matrix.
75    /// 
76    /// # Returns
77    /// 
78    /// Length-`N` vector that is compatible with this `M x N` matrix.
79    /// 
80    /// # Examples
81    /// 
82    /// ## Creating a statically-sized vector compatible with a statically-sized matrix
83    /// 
84    /// ```
85    /// use linalg_traits::Matrix;
86    /// use nalgebra::{SMatrix, SVector};
87    /// 
88    /// // Create a statically-sized 3x2 matrix.
89    /// let mat: SMatrix<f64, 3, 2> = SMatrix::new_with_shape(3, 2);
90    /// 
91    /// // Create a statically-sized length-2 vector.
92    /// let vec: SVector<f64, 2> = mat.new_vector_n();
93    /// assert_eq!(vec.len(), 2);
94    /// ```
95    /// 
96    /// ## Creating a dynamically-sized matrix compatible with a dynamically-sized vector
97    /// 
98    /// ```
99    /// use linalg_traits::Matrix;
100    /// use nalgebra::{DMatrix, DVector};
101    /// 
102    /// // Create a dynamically-sized 3x2 matrix.
103    /// let mat: DMatrix<f64> = DMatrix::new_with_shape(3, 2);
104    /// 
105    /// // Create a dynamically-sized length-2 vector.
106    /// let vec: DVector<f64> = mat.new_vector_n();
107    /// assert_eq!(vec.len(), 2);
108    /// ```
109    fn new_vector_n(&self) -> Self::VectorN {
110        let (_, n) = self.shape();
111        Self::VectorN::new_with_length(n)
112    }
113
114    /// Create a length-`M` vector that is compatible with this `M x N` matrix.
115    /// 
116    /// # Returns
117    /// 
118    /// Length-`M` vector that is compatible with this `M x N` matrix.
119    /// 
120    /// # Examples
121    /// 
122    /// ## Creating a statically-sized vector compatible with a statically-sized matrix
123    /// 
124    /// ```
125    /// use linalg_traits::Matrix;
126    /// use nalgebra::{SMatrix, SVector};
127    /// 
128    /// // Create a statically-sized 3x2 matrix.
129    /// let mat: SMatrix<f64, 3, 2> = SMatrix::new_with_shape(3, 2);
130    /// 
131    /// // Create a statically-sized length-3 vector.
132    /// let vec: SVector<f64, 3> = mat.new_vector_m();
133    /// assert_eq!(vec.len(), 3);
134    /// ```
135    /// 
136    /// ## Creating a dynamically-sized matrix compatible with a dynamically-sized vector
137    /// 
138    /// ```
139    /// use linalg_traits::Matrix;
140    /// use nalgebra::{DMatrix, DVector};
141    /// 
142    /// // Create a dynamically-sized 3x2 matrix.
143    /// let mat: DMatrix<f64> = DMatrix::new_with_shape(3, 2);
144    /// 
145    /// // Create a dynamically-sized length-3 vector.
146    /// let vec: DVector<f64> = mat.new_vector_m();
147    /// assert_eq!(vec.len(), 3);
148    /// ```
149    fn new_vector_m(&self) -> Self::VectorM {
150        let (m,_) = self.shape();
151        Self::VectorM::new_with_length(m)
152    }
153
154    /// Assert that this matrix and another matrix have the same shape. 
155    /// 
156    /// # Arguments
157    /// 
158    /// * `other` - The other matrix whose shape we are comparing with this matrix.
159    /// 
160    /// # Panics
161    /// 
162    /// * If the shape of the other matrix is not equal to the shape of this matrix.
163    fn assert_same_shape(&self, other: &Self) {
164        assert_eq!(
165            self.shape(),
166            other.shape(), 
167            "Matrices have incompatible shapes.",
168        );
169    }
170
171    /// Return a slice view of the matrix's elements in row-major order.
172    ///
173    /// # Returns
174    ///
175    /// A slice of the matrix's elements in row-major order.
176    /// 
177    /// # Note
178    /// 
179    /// The slice is returned as a `Cow<[S]>` instead of a `&[S]`. This is primarily because the
180    /// underlying data will be _either_ in row-major order _or_ in column-major order. If it is in
181    /// column-major order, then we will need to re-order it to be in row-major order. This requires
182    /// building a temporary variable within this method which will be dropped when the method scope
183    /// ends. In such cases, this method will clone the data when returning it in a `Cow<[S]>`.
184    /// 
185    /// In most cases, if the data is in row-major order, then this function will not perform any
186    /// cloning. However, in some cases, even if the underlying data is in row-major order, its data
187    /// may not be contiguous in memory. This would also require cloning the data from a temporary
188    /// variable. See [`Matrix::as_slice`] for more information.
189    fn as_row_slice<'a>(&'a self) -> Cow<'a, [S]>  {
190        if Self::is_row_major() {
191            self.as_slice()
192        } else {
193            let (rows, cols) = self.shape();
194            let mut vec = Vec::<S>::with_capacity(rows * cols);
195            for row in 0..rows {
196                for col in 0..cols {
197                    vec.push(self[(row, col)]);
198                }
199            }
200            Cow::from(vec)
201        }
202    }
203
204    /// Return a slice view of the matrix's elements in column-major order.
205    ///
206    /// # Returns
207    ///
208    /// A slice of the matrix's elements in column-major order.
209    /// 
210    /// # Note
211    /// 
212    /// The slice is returned as a `Cow<[S]>` instead of a `&[S]`. This is primarily because the
213    /// underlying data will be _either_ in row-major order _or_ in column-major order. If it is in
214    /// row-major order, then we will need to re-order it to be in column-major order. This requires
215    /// building a temporary variable within this method which will be dropped when the method scope
216    /// ends. In such cases, this method will clone the data when returning it in a `Cow<[S]>`.
217    /// 
218    /// In most cases, if the data is in column-major order, then this function will not perform any
219    /// cloning. However, in some cases, even if the underlying data is in column-major order, its
220    /// data may not be contiguous in memory. This would also require cloning the data from a
221    /// temporary variable. See [`Matrix::as_slice`] for more information.
222    fn as_col_slice<'a>(&'a self) -> Cow<'a, [S]> {
223        if Self::is_column_major() {
224            self.as_slice()
225        } else {
226            let (rows, cols) = self.shape();
227            let mut vec = Vec::<S>::with_capacity(rows * cols);
228            for col in 0..cols {
229                for row in 0..rows {
230                    vec.push(self[(row, col)]);
231                }
232            }
233            Cow::from(vec)
234        }
235    }
236
237    // -----------------------------
238    // Required method declarations.
239    // -----------------------------
240    
241    /// Determine whether or not the matrix is statically-sized.
242    /// 
243    /// # Returns
244    /// 
245    /// `true` if the matrix is statically-sized, `false` if the matrix is dynamically-sized.
246    fn is_statically_sized() -> bool;
247    
248    /// Determine whether or not the matrix is dynamically-sized.
249    /// 
250    /// # Returns
251    /// 
252    /// `true` if the matrix is dynamically-sized, `false` if the matrix is statically-sized.
253    fn is_dynamically_sized() -> bool;
254    
255    /// Determine whether or not the matrix is row-major.
256    /// 
257    /// # Returns
258    /// 
259    /// `true` if the matrix is row-major, `false` if the matrix is column-major.
260    fn is_row_major() -> bool;
261
262    /// Determine whether or not the matrix is column-major.
263    /// 
264    /// # Returns
265    /// 
266    /// `true` if the matrix is column-major, `false` if the matrix is row-major.
267    fn is_column_major() -> bool;
268
269    /// Create a matrix with the specified size, with each element set to 0.
270    ///
271    /// # Arguments
272    ///
273    /// * `rows` - Number of rows.
274    /// * `cols` - Number of columns.
275    ///
276    /// # Returns
277    ///
278    /// Matrix with the specified size, with each element set to 0.
279    /// 
280    /// # Panics
281    /// 
282    /// * If `rows` does not match the number of rows in the matrix (for statically-sized matrices
283    ///   only).
284    fn new_with_shape(rows: usize, cols: usize) -> Self;
285
286    /// Get the shape of the matrix.
287    ///
288    /// # Returns
289    ///
290    /// * `rows` - Number of rows.
291    /// * `cols` - Number of columns.
292    fn shape(&self) -> (usize, usize);
293
294    /// Create a matrix from a slice of scalars arranged in row-major order.
295    ///
296    /// # Arguments
297    ///
298    /// * `rows` - Number of rows.
299    /// * `cols` - Number of columns.
300    /// * `slice` - The slice of scalar values to initialize the matrix.
301    ///
302    /// # Returns
303    ///
304    /// A matrix containing the elements from the slice.
305    /// 
306    /// # Panics
307    /// 
308    /// * If `rows` does not match the number of rows in the matrix (for statically-sized matrices
309    ///   only).
310    fn from_row_slice(rows: usize, cols: usize, slice: &[S]) -> Self;
311
312    /// Create a matrix from a slice of scalars arranged in column-major order.
313    ///
314    /// # Arguments
315    ///
316    /// * `rows` - Number of rows.
317    /// * `cols` - Number of columns.
318    /// * `slice` - The slice of scalar values to initialize the matrix.
319    ///
320    /// # Returns
321    ///
322    /// A matrix containing the elements from the slice.
323    /// 
324    /// # Panics
325    /// 
326    /// * If `rows` does not match the number of rows in the matrix (for statically-sized matrices
327    ///   only).
328    /// * If the slice length is not compatible with the shape of the matrix (for dynamically-sized
329    ///   matrices only).
330    fn from_col_slice(rows: usize, cols: usize, slice: &[S]) -> Self;
331
332    /// Return a slice view of the matrix's elements.
333    ///
334    /// # Returns
335    ///
336    /// A slice of the matrix's elements.
337    /// 
338    /// # Warning
339    /// 
340    /// The order of the elements depends on whether the matrix is row-major or column-major. This
341    /// can be programmatically determined via the [`Matrix::is_row_major`] and
342    /// [`Matrix::is_column_major`] methods.
343    /// 
344    /// # Note
345    /// 
346    /// The slice is returned as a `Cow<[S]>` instead of a `&[S]`. This is because some matrix
347    /// implementations do NOT store data contiguously; for example, the columns of [`faer::Mat`]
348    /// are generally NOT contiguous in memory.
349    /// 
350    /// When the data is not contiguous in memory, this method will first build a vector where the
351    /// data is contiguous. Since this vector is a temporary variable, we cannot return a reference
352    /// to its data (e.g. a `&[S]`) since it will be dropped when the method scope ends. In these
353    /// cases, this method will clone the data when returning it in a `Cow<[S]>`.
354    /// 
355    /// When the data _is_ contiguous in memory, this method will build the [`Cow`] directly from
356    /// a slice of the data. In this case, the data is borrowed, and no cloning occurs.
357    fn as_slice<'a>(&'a self) -> Cow<'a, [S]>;
358
359    /// Matrix addition (elementwise).
360    /// 
361    /// # Arguments
362    /// 
363    /// * `other` - The other matrix to add to this matrix.
364    /// 
365    /// # Returns
366    /// 
367    /// Sum of this matrix with the other matrix (i.e. `self + other`).
368    /// 
369    /// # Panics
370    /// 
371    /// * If `self` and `other` are dynamically-sized matrices and do not have the same shape.
372    fn add(&self, other: &Self) -> Self;
373
374    /// In-place matrix addition (elementwise) (`self += other`).
375    /// 
376    /// # Arguments
377    /// 
378    /// * `other` - The other matrix to add to this matrix.
379    /// 
380    /// # Panics
381    /// 
382    /// * If `self` and `other` are dynamically-sized matrices and do not have the same shape.
383    fn add_assign(&mut self, other: &Self);
384
385    /// Matrix subtraction (elementwise).
386    /// 
387    /// # Arguments
388    /// 
389    /// * `other` - The other matrix to subtract from this matrix.
390    /// 
391    /// # Returns
392    /// 
393    /// The difference of matrix with the other matrix (i.e. `self - other`).
394    /// 
395    /// # Panics
396    /// 
397    /// * If `self` and `other` are dynamically-sized matrices and do not have the same shape.
398    fn sub(&self, other: &Self) -> Self;
399
400    /// In-place matrix subtraction (elementwise) (`self -= other`).
401    /// 
402    /// # Arguments
403    /// 
404    /// * `other` - The other matrix to subtract from this matrix.
405    /// 
406    /// # Panics
407    /// 
408    /// * If `self` and `other` are dynamically-sized matrices and do not have the same shape.
409    fn sub_assign(&mut self, other: &Self);
410
411    /// Matrix-scalar multiplication.
412    /// 
413    /// # Arguments
414    /// 
415    /// * `scalar` - The scalar to multiply each element of this matrix by.
416    /// 
417    /// # Returns
418    /// 
419    /// Product of this matrix with the scalar (i.e. `self * scalar` or `scalar * self`).
420    fn mul(&self, scalar: S) -> Self;
421
422    /// In-place matrix-scalar multiplication (`self * scalar` or `scalar * self`).
423    /// 
424    /// # Arguments
425    /// 
426    /// * `scalar` - The scalar to multiply each element of this matrix by.
427    fn mul_assign(&mut self, scalar: S);
428
429    /// Matrix-scalar division.
430    /// 
431    /// # Arguments
432    /// 
433    /// * `scalar` - The scalar to divide each element of this matrix by.
434    /// 
435    /// # Returns
436    /// 
437    /// This matrix divided by the scalar (i.e. `self / scalar`).
438    fn div(&self, scalar: S) -> Self;
439
440    /// In-place matrix-scalar division (`self / scalar`).
441    /// 
442    /// # Arguments
443    /// 
444    /// * `scalar` - The scalar to divide each element of this matrix by.
445    fn div_assign(&mut self, scalar: S);
446
447}