pub struct Matrix<const ROWS: usize, const COLS: usize, T = f64> { /* private fields */ }Expand description
A ROWS×COLS matrix stored inline on the stack in row-major order.
use multicalc::linear_algebra::{Matrix, Vector};
let a = Matrix::new([[1.0, 2.0], [3.0, 4.0]]);
let b = Matrix::new([[5.0, 6.0], [7.0, 8.0]]);
assert_eq!(a[(0, 1)], 2.0);
assert_eq!((a + b).into_array(), [[6.0, 8.0], [10.0, 12.0]]);
assert_eq!((b - a).into_array(), [[4.0, 4.0], [4.0, 4.0]]);
assert_eq!((-a).into_array(), [[-1.0, -2.0], [-3.0, -4.0]]);
assert_eq!((a * 2.0).into_array(), [[2.0, 4.0], [6.0, 8.0]]);
assert_eq!((a * b).into_array(), [[19.0, 22.0], [43.0, 50.0]]);
assert_eq!(a * Vector::new([1.0, 1.0]), Vector::new([3.0, 7.0]));Implementations§
Source§impl<const N: usize, T: Numeric> Matrix<N, N, T>
impl<const N: usize, T: Numeric> Matrix<N, N, T>
Sourcepub fn cholesky(self) -> Result<Cholesky<N, T>, CalcError>
pub fn cholesky(self) -> Result<Cholesky<N, T>, CalcError>
Factorizes self as L·Lᵀ by the Cholesky–Banachiewicz algorithm.
Only the lower triangle is read; self is assumed symmetric. Returns
CalcError::NotPositiveDefinite if a diagonal radicand is not strictly positive — the
matrix is not positive definite — rather than taking a root of it.
use multicalc::linear_algebra::Matrix;
let a = Matrix::<3, 3>::new([[4.0, 12.0, -16.0], [12.0, 37.0, -43.0], [-16.0, -43.0, 98.0]]);
let l = a.cholesky().unwrap().l();
// L·Lᵀ == A.
let prod = l * l.transpose();
for r in 0..3 {
for c in 0..3 {
assert!((prod[(r, c)] - a[(r, c)]).abs() < 1e-12);
}
}Source§impl<const N: usize, T: Numeric> Matrix<N, N, T>
impl<const N: usize, T: Numeric> Matrix<N, N, T>
Sourcepub fn lu(self) -> Result<Lu<N, T>, CalcError>
pub fn lu(self) -> Result<Lu<N, T>, CalcError>
Factorizes self by Doolittle LU with partial pivoting.
Returns CalcError::SingularMatrix if a pivot column is entirely zero — the largest
available pivot is zero — rather than dividing by it.
use multicalc::linear_algebra::Matrix;
let a = Matrix::<3, 3>::new([[2.0, 1.0, 1.0], [4.0, 3.0, 3.0], [8.0, 7.0, 9.0]]);
let f = a.lu().unwrap();
// P·A == L·U.
let (l, u, perm) = (f.l(), f.u(), f.permutation());
let pa = Matrix::<3, 3>::from_fn(|i, c| a[(perm[i], c)]);
let prod = l * u;
for i in 0..3 {
for c in 0..3 {
assert!((pa[(i, c)] - prod[(i, c)]).abs() < 1e-12);
}
}Sourcepub fn solve(self, b: Vector<N, T>) -> Result<Vector<N, T>, CalcError>
pub fn solve(self, b: Vector<N, T>) -> Result<Vector<N, T>, CalcError>
Solves A·x = b for x, factorizing self by LU.
A one-call convenience over Matrix::lu followed by Lu::solve. Returns
CalcError::SingularMatrix if self is singular. To solve several right-hand sides,
factor once with Matrix::lu and reuse the result. For a symmetric positive-definite
matrix, Matrix::cholesky is faster.
use multicalc::linear_algebra::{Matrix, Vector};
let a = Matrix::<3, 3>::new([[2.0, 1.0, 1.0], [4.0, 3.0, 3.0], [8.0, 7.0, 9.0]]);
let x = a.solve(Vector::new([7.0, 19.0, 49.0])).unwrap();
assert!((x[0] - 1.0).abs() < 1e-12);
assert!((x[1] - 2.0).abs() < 1e-12);
assert!((x[2] - 3.0).abs() < 1e-12);Source§impl<const ROWS: usize, const COLS: usize, T> Matrix<ROWS, COLS, T>
impl<const ROWS: usize, const COLS: usize, T> Matrix<ROWS, COLS, T>
Sourcepub const fn new(data: [[T; COLS]; ROWS]) -> Self
pub const fn new(data: [[T; COLS]; ROWS]) -> Self
Wraps a row-major array of rows into a matrix.
Sourcepub fn from_fn(f: impl FnMut(usize, usize) -> T) -> Self
pub fn from_fn(f: impl FnMut(usize, usize) -> T) -> Self
Builds a matrix by calling f with each (row, column) index.
use multicalc::linear_algebra::Matrix;
let m = Matrix::<2, 2>::from_fn(|r, c| (r * 2 + c) as f64);
assert_eq!(m.into_array(), [[0.0, 1.0], [2.0, 3.0]]);Sourcepub const fn as_slice_rows(&self) -> &[[T; COLS]; ROWS]
pub const fn as_slice_rows(&self) -> &[[T; COLS]; ROWS]
Borrows the rows.
use multicalc::linear_algebra::Matrix;
assert_eq!(Matrix::new([[1.0, 2.0]]).as_slice_rows(), &[[1.0, 2.0]]);Sourcepub fn into_array(self) -> [[T; COLS]; ROWS]
pub fn into_array(self) -> [[T; COLS]; ROWS]
Consumes the matrix, returning its rows.
Source§impl<const ROWS: usize, const COLS: usize, T: Copy> Matrix<ROWS, COLS, T>
impl<const ROWS: usize, const COLS: usize, T: Copy> Matrix<ROWS, COLS, T>
Sourcepub fn try_from_row_slice(slice: &[T]) -> Option<Self>
pub fn try_from_row_slice(slice: &[T]) -> Option<Self>
Builds a matrix from a row-major slice, or None if slice.len() is not ROWS * COLS.
use multicalc::linear_algebra::Matrix;
assert!(Matrix::<2, 2>::try_from_row_slice(&[1.0, 2.0, 3.0, 4.0]).is_some());
assert!(Matrix::<2, 2>::try_from_row_slice(&[1.0, 2.0, 3.0]).is_none());Source§impl<const ROWS: usize, const COLS: usize, T: Numeric> Matrix<ROWS, COLS, T>
impl<const ROWS: usize, const COLS: usize, T: Numeric> Matrix<ROWS, COLS, T>
Sourcepub fn zeros() -> Self
pub fn zeros() -> Self
The zero matrix.
use multicalc::linear_algebra::Matrix;
let m: Matrix<2, 3> = Matrix::zeros();
assert_eq!(m.into_array(), [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]);Source§impl<T: Numeric> Matrix<2, 2, T>
impl<T: Numeric> Matrix<2, 2, T>
Sourcepub fn determinant(self) -> T
pub fn determinant(self) -> T
The determinant m[(0,0)]*m[(1,1)] - m[(0,1)]*m[(1,0)].
use multicalc::linear_algebra::Matrix;
assert_eq!(Matrix::new([[1.0, 2.0], [3.0, 4.0]]).determinant(), -2.0);Sourcepub fn inverse(self) -> Result<Self, CalcError>
pub fn inverse(self) -> Result<Self, CalcError>
The inverse, or CalcError::SingularMatrix if the matrix is singular
(determinant() == 0).
use multicalc::linear_algebra::Matrix;
let m: Matrix<2, 2> = Matrix::new([[4.0, 7.0], [2.0, 6.0]]);
let p = m * m.inverse().unwrap();
assert!((p[(0, 0)] - 1.0).abs() < 1e-12 && (p[(1, 1)] - 1.0).abs() < 1e-12);
assert!(Matrix::<2, 2>::new([[1.0, 2.0], [2.0, 4.0]]).inverse().is_err());Source§impl<T: Numeric> Matrix<3, 3, T>
impl<T: Numeric> Matrix<3, 3, T>
Sourcepub fn determinant(self) -> T
pub fn determinant(self) -> T
The determinant, by cofactor expansion along the first row.
use multicalc::linear_algebra::Matrix;
let i: Matrix<3, 3> = Matrix::identity();
assert_eq!(i.determinant(), 1.0);Sourcepub fn inverse(self) -> Result<Self, CalcError>
pub fn inverse(self) -> Result<Self, CalcError>
The inverse, or CalcError::SingularMatrix if the matrix is singular
(determinant() == 0).
use multicalc::linear_algebra::Matrix;
let i: Matrix<3, 3> = Matrix::identity();
assert_eq!(i.inverse().unwrap().into_array(), i.into_array());Source§impl<T: Numeric> Matrix<4, 4, T>
impl<T: Numeric> Matrix<4, 4, T>
Sourcepub fn determinant(self) -> T
pub fn determinant(self) -> T
The determinant, as the Laplace expansion along the first two rows.
use multicalc::linear_algebra::Matrix;
let m = Matrix::<4, 4>::new([
[2.0, 1.0, 1.0, 1.0],
[0.0, 3.0, 1.0, 1.0],
[0.0, 0.0, 4.0, 1.0],
[0.0, 0.0, 0.0, 5.0],
]);
assert_eq!(m.determinant(), 120.0);Sourcepub fn inverse(self) -> Result<Self, CalcError>
pub fn inverse(self) -> Result<Self, CalcError>
The inverse, or CalcError::SingularMatrix if the matrix is singular
(determinant() == 0).
Built from the adjugate, the transpose of the cofactor matrix, scaled by 1/det.
use multicalc::linear_algebra::Matrix;
let m = Matrix::<4, 4>::new([
[2.0, 1.0, 1.0, 1.0],
[0.0, 3.0, 1.0, 1.0],
[0.0, 0.0, 4.0, 1.0],
[0.0, 0.0, 0.0, 5.0],
]);
let p = m * m.inverse().unwrap();
for r in 0..4 {
for c in 0..4 {
let expected = if r == c { 1.0 } else { 0.0 };
assert!((p[(r, c)] - expected).abs() < 1e-12);
}
}Source§impl<const M: usize, const N: usize, T: Numeric> Matrix<M, N, T>
impl<const M: usize, const N: usize, T: Numeric> Matrix<M, N, T>
Sourcepub fn svd(self) -> Result<Svd<M, N, T>, CalcError>
pub fn svd(self) -> Result<Svd<M, N, T>, CalcError>
Decomposes self as U · diag(σ) · Vᵀ by one-sided Jacobi (thin form, M ≥ N).
U has orthonormal columns, the σ are non-negative and descending, and V has orthonormal
columns. Returns CalcError::Underdetermined for a wide matrix (M < N) — transpose it
first — or CalcError::NonFiniteValue if any entry is not finite.
use multicalc::linear_algebra::Matrix;
let a = Matrix::<3, 2>::new([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]);
let svd = a.svd().unwrap();
let (u, s, v) = (svd.u(), svd.singular_values(), svd.v());
// U · diag(σ) · Vᵀ == A.
for r in 0..3 {
for c in 0..2 {
let mut acc = 0.0;
for k in 0..2 {
acc += u[(r, k)] * s[k] * v[(c, k)];
}
assert!((acc - a[(r, c)]).abs() < 1e-12);
}
}A wide matrix (M < N) has no thin form here; take the SVD of its transpose, whose singular
values are the same. Its pseudo-inverse then follows from A⁺ = ((Aᵀ)⁺)ᵀ, which
Matrix::pseudo_inverse applies for any shape.
use multicalc::linear_algebra::Matrix;
// For a wide matrix, decompose its transpose.
let a = Matrix::<2, 3>::new([[1.0, 0.0, 2.0], [0.0, 1.0, 1.0]]);
let at = a.transpose();
let svd = at.svd().unwrap();
let (u, s, v) = (svd.u(), svd.singular_values(), svd.v());
for r in 0..3 {
for c in 0..2 {
let mut acc = 0.0;
for k in 0..2 {
acc += u[(r, k)] * s[k] * v[(c, k)];
}
assert!((acc - at[(r, c)]).abs() < 1e-12);
}
}Sourcepub fn pseudo_inverse(self) -> Result<Matrix<N, M, T>, CalcError>
pub fn pseudo_inverse(self) -> Result<Matrix<N, M, T>, CalcError>
The Moore–Penrose pseudo-inverse of self, for any shape.
Tall or square inputs go straight through Matrix::svd; a wide input (M < N) is handled
as ((Aᵀ)⁺)ᵀ. Returns CalcError::NonFiniteValue if any entry is not finite.
use multicalc::linear_algebra::Matrix;
// A wide matrix, handled through the transpose route.
let a = Matrix::<2, 3>::new([[1.0, 0.0, 2.0], [0.0, 1.0, 1.0]]);
let pinv = a.pseudo_inverse().unwrap();
let recon = a * pinv * a; // A·A⁺·A == A
for r in 0..2 {
for c in 0..3 {
assert!((recon[(r, c)] - a[(r, c)]).abs() < 1e-12);
}
}Trait Implementations§
Source§impl<const ROWS: usize, const COLS: usize, T: Numeric> AddAssign for Matrix<ROWS, COLS, T>
impl<const ROWS: usize, const COLS: usize, T: Numeric> AddAssign for Matrix<ROWS, COLS, T>
Source§fn add_assign(&mut self, rhs: Self)
fn add_assign(&mut self, rhs: Self)
+= operation. Read more