use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign}; use std::ptr::swap; use crate::{Float, Matrix, Numeric, Primitive, View, ViewMut}; #[cfg(feature = "angular")] use crate::Angular; use crate::types::matrix::{compare, ComparisonResult, MatrixMut, multiply}; /// Struct representing a dense matrix. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct GenericMatrix { pub data: [[T; COLUMNS]; ROWS], } impl Matrix for GenericMatrix { fn get(&self, row: usize, col: usize) -> T { self.data[row][col] } } impl MatrixMut for GenericMatrix { fn set(&mut self, row: usize, col: usize, value: T) { self.data[row][col] = value; } } /// Special kind of `GenericMatrix` where both dimensions are equal. pub type SquareMatrix = GenericMatrix; /// Special kind of `GenericMatrix` with just one column. pub type ColumnVector = GenericMatrix; /// Special kind of `GenericMatrix` with just one row. pub type RowVector = GenericMatrix; /// A macro for easier creation of row vectors. /// /// # Example /// /// ```rust /// # use lineal::{RowVector, rvec}; /// let a = rvec![1.0, 2.0, 3.0]; /// // is the same as /// let b = RowVector { /// data: [[1.0, 2.0, 3.0]], /// }; /// ``` #[macro_export] macro_rules! rvec { ($($value:expr),* $(,)?) => { { RowVector { data: [[$( $value, )*]], } } } } /// A macro for easier creation of column vectors. /// /// # Example /// /// ```rust /// # use lineal::{ColumnVector, cvec}; /// let a = cvec![1.0, 2.0, 3.0]; /// // is the same as /// let b = ColumnVector { /// data: [[1.0], [2.0], [3.0]], /// }; /// ``` #[macro_export] macro_rules! cvec { ($($value:expr),* $(,)?) => { { ColumnVector { data: [$( [$value], )*], } } } } impl Default for GenericMatrix { /// Create a matrix with all cells being zero. fn default() -> Self { let zero = T::whole(0); Self { data: [[zero; COLUMNS]; ROWS], } } } impl GenericMatrix { /// Create a matrix with all cells being zero. pub fn new() -> Self { Self::default() } /// Create a view for a part of the matrix. /// /// # Example /// /// ```rust /// # use lineal::GenericMatrix; /// let mat = GenericMatrix::from([ /// [1, 2, 3, 4], /// [5, 6, 7, 8], /// [9, 10, 11, 12], /// ]); /// // 3 and 2 are the size of the view, 0 and 1 are the starting indices of the view. /// let view = mat.view::<3, 2>(0, 1); /// assert_eq!( /// view, /// GenericMatrix::from([ /// [2, 3], /// [6, 7], /// [10, 11], /// ]) /// ); /// ``` pub fn view(&self, base_row: usize, base_col: usize) -> View { View::new(self, (base_row, base_col)) } /// Create a mutable view for a part of the matrix. /// /// # Example /// /// ```rust /// # use lineal::{GenericMatrix, MatrixMut}; /// let mut mat = GenericMatrix::from([ /// [1, 2, 3, 4], /// [5, 6, 7, 8], /// [9, 10, 11, 12], /// ]); /// { /// // 3 and 2 are the size of the view, 0 and 1 are the starting indices of the view. /// let mut view = mat.view_mut::<3, 2>(0, 1); /// view.set(1, 0, 23); /// view.set(1, 1, 42); /// assert_eq!( /// view, /// GenericMatrix::from([ /// [2, 3], /// [23, 42], /// [10, 11], /// ]) /// ); /// } /// assert_eq!( /// mat, /// GenericMatrix::from([ /// [1, 2, 3, 4], /// [5, 23, 42, 8], /// [9, 10, 11, 12], /// ]) /// ); /// ``` pub fn view_mut(&mut self, base_row: usize, base_col: usize) -> ViewMut { ViewMut::new(self, (base_row, base_col)) } /// Create a transpose of the input matrix. /// /// # Example /// /// ```rust /// # use lineal::{ColumnVector, RowVector, cvec, rvec}; /// let a = cvec![1, 2, 3]; /// let b = rvec![1, 2, 3]; /// assert_eq!(a.transposed(), b); /// ``` pub fn transposed(&self) -> GenericMatrix { let mut result = GenericMatrix::default(); for r in 0..ROWS { for c in 0..COLUMNS { result.data[c][r] = self.data[r][c]; } } result } } impl SquareMatrix { /// Create a square identity matrix. /// /// In an identity matrix the main diagonal is filled with ones, /// while the rest if the cells are zero. /// /// # Example /// /// ```rust /// # use lineal::{SquareMatrix}; /// let a: SquareMatrix = SquareMatrix::identity(); /// // is the same as /// let b: SquareMatrix = SquareMatrix { /// data: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], /// }; /// ``` pub fn identity() -> Self { let mut mat = Self::default(); let one = T::whole(1); for i in 0..DIMENSION { mat.data[i][i] = one; } mat } /// Create a square matrix with its main diagonal filled with the given values. /// /// # Example /// /// ```rust /// # use lineal::{SquareMatrix}; /// let a = SquareMatrix::diagonal(&[1, 2, 3]); /// // is the same as /// let b = SquareMatrix { /// data: [ /// [1, 0, 0], /// [0, 2, 0], /// [0, 0, 3], /// ], /// }; /// ``` pub fn diagonal(values: &[T; DIMENSION]) -> Self { let mut mat = Self::default(); for i in 0..DIMENSION { mat.data[i][i] = values[i]; } mat } } impl SquareMatrix { /// Apply an LUP decomposition to the matrix. /// /// # Safety /// /// This function is unsafe, because even when the result is false, /// the matrix may already have been modified. pub unsafe fn lup_decompose(&mut self, pivot: &mut Vec, tolerance: T) -> bool { pivot.clear(); pivot.reserve(DIMENSION + 1); for i in 0..(DIMENSION + 1) { pivot.push(i); } let zero = T::whole(0); for i in 0..DIMENSION { let mut max_a = zero; let mut max_i = i; for k in i..DIMENSION { let abs_a = self[(k, i)].abs(); if abs_a > max_a { max_a = abs_a; max_i = k; } } if max_a <= tolerance { return false; } if max_i != i { pivot.swap(i, max_i); let p_a: *mut [T; DIMENSION] = (&mut self.data[i]) as *mut [T; DIMENSION]; let p_a_max: *mut [T; DIMENSION] = (&mut self.data[max_i]) as *mut [T; DIMENSION]; swap(p_a, p_a_max); pivot[DIMENSION] += 1; } for j in (i + 1)..DIMENSION { let divisor = self[(i, i)]; self[(j, i)] /= divisor; for k in (i + 1)..DIMENSION { let a = self[(j, i)]; let b = self[(i, k)]; self[(j, k)] -= a * b; } } } true } /// Calculate the determinant of the input matrix. pub fn determinant(mut self, tolerance: T) -> Option { let mut pivot = Vec::new(); if !unsafe { self.lup_decompose(&mut pivot, tolerance) } { return None; } debug_assert_eq!(pivot.len(), DIMENSION + 1); let mut result = self[(0, 0)]; for i in 1..DIMENSION { result *= self[(i, i)]; } if (pivot[DIMENSION] - DIMENSION) % 2 == 0 { Some(result) } else { Some(-result) } } /// Solve the equation `self` * `x` = `b` for `x`. pub fn solve(mut self, b: &ColumnVector, tolerance: T) -> Option> { let mut pivot = Vec::new(); if !unsafe { self.lup_decompose(&mut pivot, tolerance) } { return None; } debug_assert_eq!(pivot.len(), DIMENSION + 1); let mut result = RowVector::new(); for i in 0..DIMENSION { result[(0, i)] = b[(pivot[i], 0)]; for k in 0..i { let factor = result[(0, k)]; result[(0, i)] -= self[(i, k)] * factor; } } for i in (0..DIMENSION).rev() { for k in (i + 1)..DIMENSION { let factor = result[(0, k)]; result[(0, i)] -= self[(i, k)] * factor; } result[(0, i)] /= self[(i, i)]; } Some(result) } /// Calculate the inverse of the input matrix. pub fn inverted(mut self, tolerance: T) -> Option { let mut pivot = Vec::new(); if !unsafe { self.lup_decompose(&mut pivot, tolerance) } { return None; } debug_assert_eq!(pivot.len(), DIMENSION + 1); let mut result = Self::new(); let zero = T::whole(0); let one = T::whole(1); for j in 0..DIMENSION { for i in 0..DIMENSION { result[(i, j)] = if pivot[i] == j { one } else { zero }; for k in 0..i { let factor = result[(k, j)]; result[(i, j)] -= self[(i, k)] * factor; } } for i in (0..DIMENSION).rev() { for k in (i + 1)..DIMENSION { let factor = result[(k, j)]; result[(i, j)] -= self[(i, k)] * factor; } result[(i, j)] /= self[(i, i)]; } } Some(result) } } impl From<[[T; COLUMNS]; ROWS]> for GenericMatrix { fn from(data: [[T; COLUMNS]; ROWS]) -> Self { Self { data, } } } impl Index<(usize, usize)> for GenericMatrix { type Output = T; fn index(&self, index: (usize, usize)) -> &Self::Output { let (row, column) = index; &self.data[row][column] } } impl IndexMut<(usize, usize)> for GenericMatrix { fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output { let (row, column) = index; &mut self.data[row][column] } } impl, const ROWS: usize, const COLUMNS: usize> Neg for GenericMatrix { type Output = Self; fn neg(self) -> Self::Output { let mut result = Self::default(); for r in 0..ROWS { for c in 0..COLUMNS { result.data[r][c] = -self.data[r][c]; } } result } } impl> Add for GenericMatrix { type Output = Self; fn add(mut self, rhs: RHS) -> Self::Output { self += rhs; self } } impl> AddAssign for GenericMatrix { fn add_assign(&mut self, rhs: RHS) { for r in 0..ROWS { for c in 0..COLUMNS { self.data[r][c] += rhs.get(r, c); } } } } impl> Sub for GenericMatrix { type Output = Self; fn sub(mut self, rhs: RHS) -> Self::Output { self -= rhs; self } } impl> SubAssign for GenericMatrix { fn sub_assign(&mut self, rhs: RHS) { for r in 0..ROWS { for c in 0..COLUMNS { self.data[r][c] -= rhs.get(r, c); } } } } impl Mul for GenericMatrix { type Output = Self; fn mul(mut self, rhs: T) -> Self::Output { self *= rhs; self } } impl MulAssign for GenericMatrix { fn mul_assign(&mut self, rhs: T) { for r in 0..ROWS { for c in 0..COLUMNS { self.data[r][c] *= rhs; } } } } impl Div for GenericMatrix { type Output = Self; fn div(mut self, rhs: T) -> Self::Output { self /= rhs; self } } impl DivAssign for GenericMatrix { fn div_assign(&mut self, rhs: T) { for r in 0..ROWS { for c in 0..COLUMNS { self.data[r][c] /= rhs; } } } } impl Div> for ColumnVector { type Output = RowVector; fn div(self, rhs: SquareMatrix) -> Self::Output { rhs.solve(&self, T::value(1e-9)).unwrap() } } impl Mul> for GenericMatrix { type Output = GenericMatrix; fn mul(self, rhs: GenericMatrix) -> Self::Output { let mut result = Self::Output::default(); multiply(&mut result, &self, &rhs); result } } /// Apply a transformation matrix to a column vector. impl Mul> for SquareMatrix { type Output = RowVector; fn mul(self, rhs: ColumnVector) -> Self::Output { let x_old = rhs[(0, 0)]; let y_old = rhs[(1, 0)]; let z_old = rhs[(2, 0)]; rvec![ self[(0,0)] * x_old + self[(0, 1)] * y_old + self[(0, 2)] * z_old + self[(0, 3)], self[(1,0)] * x_old + self[(1, 1)] * y_old + self[(1, 2)] * z_old + self[(1, 3)], self[(2,0)] * x_old + self[(2, 1)] * y_old + self[(2, 2)] * z_old + self[(2, 3)], ] } } impl SquareMatrix { /// Create a 3D translation matrix. pub fn translation(x: T, y: T, z: T) -> Self { let mut result = SquareMatrix::identity(); result[(0, 3)] = x; result[(1, 3)] = y; result[(2, 3)] = z; result } /// Create a 3D scale matrix. pub fn scale(x: T, y: T, z: T) -> Self { let one = T::whole(1); SquareMatrix::diagonal(&[x, y, z, one]) } fn rotation_impl(angle: T, x: T, y: T, z: T) -> Self { let zero = T::whole(0); let one = T::whole(1); let c = angle.cos(); let s = angle.sin(); let omc = one - c; Self::from([ [x * x * omc + c, x * y * omc - z * s, x * z * omc + y * s, zero], [y * x * omc + z * s, y * y * omc + c, y * z * omc - x * s, zero], [x * z * omc - y * s, y * z * omc + x * s, z * z * omc + c, zero], [zero, zero, zero, one], ]) } /// Create a 3D rotation matrix. /// /// `angle` is expected to be in radians. #[cfg(not(feature = "angular"))] pub fn rotation(angle: T, x: T, y: T, z: T) -> Self { Self::rotation_impl(angle, x, y, z) } /// Create a 3D rotation matrix. #[cfg(feature = "angular")] pub fn rotation>(angle: A, x: T, y: T, z: T) -> Self { Self::rotation_impl(angle.radiant(), x, y, z) } } impl> PartialEq for GenericMatrix { fn eq(&self, other: &RHS) -> bool { compare(self, other, T::epsilon()) == ComparisonResult::Equal } fn ne(&self, other: &RHS) -> bool { compare(self, other, T::epsilon()) == ComparisonResult::NotEqual } } #[cfg(test)] mod tests { use crate::{ColumnVector, Complex, cplx, GenericMatrix, Matrix, MatrixMut, RowVector, SquareMatrix}; #[test] fn identity_matrix() { let mat: SquareMatrix = SquareMatrix::identity(); let expected = SquareMatrix::from([ [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ]); assert_eq!(mat, expected); } #[test] fn diagonal_matrix() { let mat: SquareMatrix = SquareMatrix::diagonal(&[1.0, 2.0, 3.0]); let expected = SquareMatrix { data: [[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 3.0]], }; assert_eq!(mat, expected); } #[test] #[should_panic = "index out of bounds"] fn out_of_bounds_access() { let mat: SquareMatrix = SquareMatrix::identity(); mat[(1, 0)]; } #[test] fn transposing_matrix() { let mat = GenericMatrix { data: [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], }.transposed(); let expected = GenericMatrix { data: [[1.0, 3.0, 5.0], [2.0, 4.0, 6.0]], }; assert_eq!(mat, expected); } #[test] fn negating_matrix() { let mat = -GenericMatrix { data: [[1, -1], [2, -2]], }; let expected = GenericMatrix { data: [[-1, 1], [-2, 2]], }; assert_eq!(mat, expected); } #[test] fn dot_product_of_vectors() { let a = rvec![1, 3, -5]; let b = cvec![4, -2, -1]; let product = a * b; assert_eq!(product[(0, 0)], 3); } #[test] fn matrix_product_of_vectors() { let a = cvec![1, 3, -5]; let b = rvec![4, -2, -1]; let product = a * b; let expected = SquareMatrix { data: [ [4, -2, -1], [12, -6, -3], [-20, 10, 5], ], }; assert_eq!(product, expected); } #[test] fn complex_matrix_multiplication() { let a = SquareMatrix { data: [ [cplx!(2.0, 1.0), cplx!(i = 5.0,)], [cplx!(3.0), cplx!(3.0, -4.0)], ], }; let b = SquareMatrix { data: [ [cplx!(1.0, -1.0), cplx!(4.0, 2.0)], [cplx!(1.0, -6.0), cplx!(3.0)], ], }; let mat = a * b; let expected = SquareMatrix::from([ [cplx!(33.0, 4.0), cplx!(6.0, 23.0)], [cplx!(-18.0, -25.0), cplx!(21.0, -6.0)], ]); assert_eq!(mat, expected); } #[test] fn invert_matrix() { let tolerance = 1e-9; let mat = SquareMatrix::from([ [2.0, 1.0], [6.0, 4.0], ]); let inverted = mat.inverted(tolerance).unwrap(); let expected = SquareMatrix::from([ [2.0, -0.5], [-3.0, 1.0], ]); assert_eq!(inverted, expected); assert_eq!(inverted * mat, SquareMatrix::identity()); } #[test] #[should_panic = "index out of bounds"] fn out_of_range_get() { let mat = SquareMatrix::from([[1]]); mat.get(1, 1); } #[test] #[should_panic = "index out of bounds"] fn out_of_range_set() { let mut mat = SquareMatrix::from([[1]]); mat.set(1, 1, 2); } }