Skip to main content

a/math/matrix/
mod.rs

1//! Matrices: a [`Matrix<T>`] is a rectangular array of elements of the type `T`, which is `f32`,
2//! `f64`, [`Complex<f32>`](crate::math::complex::Complex),
3//! [`Complex<f64>`](crate::math::complex::Complex) or [`Number`](crate::math::numbers::Number).
4
5mod basic;
6mod blas;
7mod cholesky;
8mod eigen;
9mod element;
10mod exact;
11mod functions;
12mod gemm;
13mod hermitian;
14mod householder;
15mod lu;
16mod ops;
17mod properties;
18mod qr;
19mod schur;
20mod svd;
21mod tridiagonal;
22
23pub use cholesky::Cholesky;
24pub use eigen::{Eigen, HermitianEigen};
25pub use element::{Element, Scalar};
26pub use lu::Lu;
27pub use properties::Norm;
28pub use qr::Qr;
29pub use schur::Schur;
30pub use svd::Svd;
31
32use core::fmt;
33use core::ops::{Index, IndexMut};
34
35/// A matrix of `rows × cols` elements of the type `T`, held column after column in one vector.
36///
37/// # Examples
38///
39/// ```
40/// use a::math::matrix::Matrix;
41///
42/// let a = Matrix::from_rows(&[[1.0, 2.0], [3.0, 4.0]]);
43/// let b = Matrix::identity(2);
44/// assert_eq!(&a * &b, a);
45/// assert_eq!(a[(1, 0)], 3.0);
46/// assert_eq!(a.transpose(), Matrix::from_rows(&[[1.0, 3.0], [2.0, 4.0]]));
47/// ```
48#[derive(Clone, PartialEq, Eq, Hash, Default)]
49pub struct Matrix<T> {
50    /// The number of rows.
51    rows: usize,
52    /// The number of columns.
53    cols: usize,
54    /// The elements, column after column.
55    data: Vec<T>,
56}
57
58/// The number of elements of a `rows × cols` matrix, which a release build would otherwise let
59/// wrap around and leave a matrix of nonempty dimensions with no elements.
60///
61/// # Panics
62///
63/// Panics if it overflows `usize`.
64#[track_caller]
65fn size(rows: usize, cols: usize) -> usize {
66    rows.checked_mul(cols)
67        .expect("the matrix has more elements than a vector can hold")
68}
69
70impl<T> Matrix<T> {
71    /// The matrix of `rows × cols` elements given column after column.
72    ///
73    /// # Panics
74    ///
75    /// Panics if `data` doesn't hold `rows · cols` elements.
76    #[track_caller]
77    pub fn from_column_major(rows: usize, cols: usize, data: Vec<T>) -> Self {
78        assert_eq!(
79            Some(data.len()),
80            rows.checked_mul(cols),
81            "the data doesn't hold rows × cols elements"
82        );
83        Matrix { rows, cols, data }
84    }
85
86    /// The matrix of `rows × cols` elements given row after row.
87    ///
88    /// # Panics
89    ///
90    /// Panics if `data` doesn't hold `rows · cols` elements.
91    #[track_caller]
92    pub fn from_row_major(rows: usize, cols: usize, data: Vec<T>) -> Self {
93        Matrix::from_column_major(cols, rows, data).into_transpose()
94    }
95
96    /// The matrix whose element in row `i` and column `j` is `f(i, j)`, computed column after
97    /// column.
98    ///
99    /// # Panics
100    ///
101    /// Panics if `rows · cols` overflows `usize`.
102    #[track_caller]
103    pub fn from_fn(rows: usize, cols: usize, mut f: impl FnMut(usize, usize) -> T) -> Self {
104        let mut data = Vec::with_capacity(size(rows, cols));
105        for j in 0..cols {
106            // A column at a time, from a range whose length is known, so that the vector is not
107            // checked for room at each element.
108            data.extend((0..rows).map(|i| f(i, j)));
109        }
110        Matrix { rows, cols, data }
111    }
112
113    /// The number of rows.
114    #[inline]
115    pub fn rows(&self) -> usize {
116        self.rows
117    }
118
119    /// The number of columns.
120    #[inline]
121    pub fn cols(&self) -> usize {
122        self.cols
123    }
124
125    /// `(rows, cols)`.
126    #[inline]
127    pub fn shape(&self) -> (usize, usize) {
128        (self.rows, self.cols)
129    }
130
131    /// Whether it has as many rows as columns.
132    #[inline]
133    pub fn is_square(&self) -> bool {
134        self.rows == self.cols
135    }
136
137    /// Whether it has no elements.
138    #[inline]
139    pub fn is_empty(&self) -> bool {
140        self.data.is_empty()
141    }
142
143    /// The element in row `i` and column `j`, or `None` outside the matrix.
144    #[inline]
145    pub fn get(&self, i: usize, j: usize) -> Option<&T> {
146        if i < self.rows && j < self.cols {
147            Some(&self.data[i + j * self.rows])
148        } else {
149            None
150        }
151    }
152
153    /// The element in row `i` and column `j`, to change, or `None` outside the matrix.
154    #[inline]
155    pub fn get_mut(&mut self, i: usize, j: usize) -> Option<&mut T> {
156        if i < self.rows && j < self.cols {
157            Some(&mut self.data[i + j * self.rows])
158        } else {
159            None
160        }
161    }
162
163    /// Column `j`.
164    ///
165    /// # Panics
166    ///
167    /// Panics if `j` is not below the number of columns.
168    #[inline]
169    #[track_caller]
170    pub fn column(&self, j: usize) -> &[T] {
171        assert!(j < self.cols, "the column is outside the matrix");
172        &self.data[j * self.rows..(j + 1) * self.rows]
173    }
174
175    /// Column `j`, to change.
176    ///
177    /// # Panics
178    ///
179    /// Panics if `j` is not below the number of columns.
180    #[inline]
181    #[track_caller]
182    pub fn column_mut(&mut self, j: usize) -> &mut [T] {
183        assert!(j < self.cols, "the column is outside the matrix");
184        &mut self.data[j * self.rows..(j + 1) * self.rows]
185    }
186
187    /// The elements of row `i`, from the left.
188    ///
189    /// # Panics
190    ///
191    /// Panics if `i` is not below the number of rows.
192    #[track_caller]
193    pub fn row(&self, i: usize) -> impl ExactSizeIterator<Item = &T> + '_ {
194        assert!(i < self.rows, "the row is outside the matrix");
195        (0..self.cols).map(move |j| &self.data[i + j * self.rows])
196    }
197
198    /// The elements, column after column.
199    #[inline]
200    pub fn as_slice(&self) -> &[T] {
201        &self.data
202    }
203
204    /// The elements, column after column, to change.
205    #[inline]
206    pub fn as_mut_slice(&mut self) -> &mut [T] {
207        &mut self.data
208    }
209
210    /// The vector of the elements, column after column.
211    #[inline]
212    pub fn into_vec(self) -> Vec<T> {
213        self.data
214    }
215
216    /// The matrix of `f` of each element.
217    pub fn map<U>(&self, f: impl FnMut(&T) -> U) -> Matrix<U> {
218        Matrix {
219            rows: self.rows,
220            cols: self.cols,
221            data: self.data.iter().map(f).collect(),
222        }
223    }
224
225    /// The transpose, moving the elements.
226    fn into_transpose(self) -> Self {
227        let (rows, cols) = (self.rows, self.cols);
228        if rows <= 1 || cols <= 1 {
229            return Matrix {
230                rows: cols,
231                cols: rows,
232                data: self.data,
233            };
234        }
235        let mut slots: Vec<Option<T>> = self.data.into_iter().map(Some).collect();
236        Matrix::from_fn(cols, rows, |i, j| slots[j + i * rows].take().unwrap())
237    }
238}
239
240impl<T: Clone> Matrix<T> {
241    /// The matrix with the given rows.
242    ///
243    /// # Panics
244    ///
245    /// Panics if the rows are not all of the same length.
246    #[track_caller]
247    pub fn from_rows<R: AsRef<[T]>>(rows: &[R]) -> Self {
248        let cols = rows.first().map_or(0, |r| r.as_ref().len());
249        assert!(
250            rows.iter().all(|r| r.as_ref().len() == cols),
251            "the rows are not all of the same length"
252        );
253        Matrix::from_fn(rows.len(), cols, |i, j| rows[i].as_ref()[j].clone())
254    }
255
256    /// The matrix with the given columns.
257    ///
258    /// # Panics
259    ///
260    /// Panics if the columns are not all of the same length.
261    #[track_caller]
262    pub fn from_columns<C: AsRef<[T]>>(columns: &[C]) -> Self {
263        let rows = columns.first().map_or(0, |c| c.as_ref().len());
264        assert!(
265            columns.iter().all(|c| c.as_ref().len() == rows),
266            "the columns are not all of the same length"
267        );
268        let data = columns
269            .iter()
270            .flat_map(|c| c.as_ref().iter().cloned())
271            .collect();
272        Matrix {
273            rows,
274            cols: columns.len(),
275            data,
276        }
277    }
278
279    /// The elements of the main diagonal, from the top left.
280    pub fn diagonal(&self) -> Vec<T> {
281        (0..self.rows.min(self.cols))
282            .map(|i| self.data[i + i * self.rows].clone())
283            .collect()
284    }
285}
286
287impl<T: Element> Matrix<T> {
288    /// The `rows × cols` matrix of zeros.
289    ///
290    /// # Panics
291    ///
292    /// Panics if `rows · cols` overflows `usize`.
293    #[track_caller]
294    pub fn zeros(rows: usize, cols: usize) -> Self {
295        // Filled at once, rather than element by element: for floats the allocator's zeroed
296        // pages, which took 1.4 µs for `256 × 256` against 69 by `from_fn`.
297        Matrix {
298            rows,
299            cols,
300            data: vec![T::zero(); size(rows, cols)],
301        }
302    }
303
304    /// The `n × n` identity matrix.
305    pub fn identity(n: usize) -> Self {
306        Matrix::from_diagonal(&vec![T::one(); n])
307    }
308
309    /// The square matrix with `diagonal` on its main diagonal and zeros elsewhere.
310    pub fn from_diagonal(diagonal: &[T]) -> Self {
311        let n = diagonal.len();
312        let mut a = Matrix::zeros(n, n);
313        for (i, d) in diagonal.iter().enumerate() {
314            a.data[i + i * n] = d.clone();
315        }
316        a
317    }
318}
319
320impl<T> Index<(usize, usize)> for Matrix<T> {
321    type Output = T;
322
323    /// The element in row `i` and column `j`.
324    ///
325    /// # Panics
326    ///
327    /// Panics outside the matrix.
328    #[inline]
329    #[track_caller]
330    fn index(&self, (i, j): (usize, usize)) -> &T {
331        assert!(
332            i < self.rows && j < self.cols,
333            "the index is outside the matrix"
334        );
335        &self.data[i + j * self.rows]
336    }
337}
338
339impl<T> IndexMut<(usize, usize)> for Matrix<T> {
340    /// The element in row `i` and column `j`, to change.
341    ///
342    /// # Panics
343    ///
344    /// Panics outside the matrix.
345    #[inline]
346    #[track_caller]
347    fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut T {
348        assert!(
349            i < self.rows && j < self.cols,
350            "the index is outside the matrix"
351        );
352        &mut self.data[i + j * self.rows]
353    }
354}
355
356impl<T: fmt::Display> fmt::Display for Matrix<T> {
357    /// Writes the rows on lines of their own, each element as `Display` writes it with the
358    /// formatter's precision, right-aligned in its column, the columns two spaces apart.
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        let text: Vec<String> = self
361            .data
362            .iter()
363            .map(|x| match f.precision() {
364                Some(p) => format!("{x:.p$}"),
365                None => format!("{x}"),
366            })
367            .collect();
368        let widths: Vec<usize> = (0..self.cols)
369            .map(|j| {
370                let column = &text[j * self.rows..(j + 1) * self.rows];
371                column.iter().map(|s| s.chars().count()).max().unwrap_or(0)
372            })
373            .collect();
374        for i in 0..self.rows {
375            if i > 0 {
376                writeln!(f)?;
377            }
378            for (j, width) in widths.iter().enumerate() {
379                let gap = if j == 0 { "" } else { "  " };
380                write!(f, "{gap}{:>width$}", text[i + j * self.rows])?;
381            }
382        }
383        Ok(())
384    }
385}
386
387impl<T: fmt::Debug> fmt::Debug for Matrix<T> {
388    /// Writes the rows as lists: `[[1, 2], [3, 4]]`.
389    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        f.debug_list()
391            .entries((0..self.rows).map(|i| DebugRow(self, i)))
392            .finish()
393    }
394}
395
396/// A row of a matrix for [`fmt::Debug`].
397struct DebugRow<'a, T>(&'a Matrix<T>, usize);
398
399impl<T: fmt::Debug> fmt::Debug for DebugRow<'_, T> {
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        f.debug_list().entries(self.0.row(self.1)).finish()
402    }
403}