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}