mod basic;
mod blas;
mod cholesky;
mod eigen;
mod element;
mod exact;
mod functions;
mod gemm;
mod hermitian;
mod householder;
mod lu;
mod ops;
mod properties;
mod qr;
mod schur;
mod svd;
mod tridiagonal;
pub use cholesky::Cholesky;
pub use eigen::{Eigen, HermitianEigen};
pub use element::{Element, Scalar};
pub use lu::Lu;
pub use properties::Norm;
pub use qr::Qr;
pub use schur::Schur;
pub use svd::Svd;
use core::fmt;
use core::ops::{Index, IndexMut};
#[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct Matrix<T> {
rows: usize,
cols: usize,
data: Vec<T>,
}
#[track_caller]
fn size(rows: usize, cols: usize) -> usize {
rows.checked_mul(cols)
.expect("the matrix has more elements than a vector can hold")
}
impl<T> Matrix<T> {
#[track_caller]
pub fn from_column_major(rows: usize, cols: usize, data: Vec<T>) -> Self {
assert_eq!(
Some(data.len()),
rows.checked_mul(cols),
"the data doesn't hold rows × cols elements"
);
Matrix { rows, cols, data }
}
#[track_caller]
pub fn from_row_major(rows: usize, cols: usize, data: Vec<T>) -> Self {
Matrix::from_column_major(cols, rows, data).into_transpose()
}
#[track_caller]
pub fn from_fn(rows: usize, cols: usize, mut f: impl FnMut(usize, usize) -> T) -> Self {
let mut data = Vec::with_capacity(size(rows, cols));
for j in 0..cols {
data.extend((0..rows).map(|i| f(i, j)));
}
Matrix { rows, cols, data }
}
#[inline]
pub fn rows(&self) -> usize {
self.rows
}
#[inline]
pub fn cols(&self) -> usize {
self.cols
}
#[inline]
pub fn shape(&self) -> (usize, usize) {
(self.rows, self.cols)
}
#[inline]
pub fn is_square(&self) -> bool {
self.rows == self.cols
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline]
pub fn get(&self, i: usize, j: usize) -> Option<&T> {
if i < self.rows && j < self.cols {
Some(&self.data[i + j * self.rows])
} else {
None
}
}
#[inline]
pub fn get_mut(&mut self, i: usize, j: usize) -> Option<&mut T> {
if i < self.rows && j < self.cols {
Some(&mut self.data[i + j * self.rows])
} else {
None
}
}
#[inline]
#[track_caller]
pub fn column(&self, j: usize) -> &[T] {
assert!(j < self.cols, "the column is outside the matrix");
&self.data[j * self.rows..(j + 1) * self.rows]
}
#[inline]
#[track_caller]
pub fn column_mut(&mut self, j: usize) -> &mut [T] {
assert!(j < self.cols, "the column is outside the matrix");
&mut self.data[j * self.rows..(j + 1) * self.rows]
}
#[track_caller]
pub fn row(&self, i: usize) -> impl ExactSizeIterator<Item = &T> + '_ {
assert!(i < self.rows, "the row is outside the matrix");
(0..self.cols).map(move |j| &self.data[i + j * self.rows])
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.data
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data
}
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.data
}
pub fn map<U>(&self, f: impl FnMut(&T) -> U) -> Matrix<U> {
Matrix {
rows: self.rows,
cols: self.cols,
data: self.data.iter().map(f).collect(),
}
}
fn into_transpose(self) -> Self {
let (rows, cols) = (self.rows, self.cols);
if rows <= 1 || cols <= 1 {
return Matrix {
rows: cols,
cols: rows,
data: self.data,
};
}
let mut slots: Vec<Option<T>> = self.data.into_iter().map(Some).collect();
Matrix::from_fn(cols, rows, |i, j| slots[j + i * rows].take().unwrap())
}
}
impl<T: Clone> Matrix<T> {
#[track_caller]
pub fn from_rows<R: AsRef<[T]>>(rows: &[R]) -> Self {
let cols = rows.first().map_or(0, |r| r.as_ref().len());
assert!(
rows.iter().all(|r| r.as_ref().len() == cols),
"the rows are not all of the same length"
);
Matrix::from_fn(rows.len(), cols, |i, j| rows[i].as_ref()[j].clone())
}
#[track_caller]
pub fn from_columns<C: AsRef<[T]>>(columns: &[C]) -> Self {
let rows = columns.first().map_or(0, |c| c.as_ref().len());
assert!(
columns.iter().all(|c| c.as_ref().len() == rows),
"the columns are not all of the same length"
);
let data = columns
.iter()
.flat_map(|c| c.as_ref().iter().cloned())
.collect();
Matrix {
rows,
cols: columns.len(),
data,
}
}
pub fn diagonal(&self) -> Vec<T> {
(0..self.rows.min(self.cols))
.map(|i| self.data[i + i * self.rows].clone())
.collect()
}
}
impl<T: Element> Matrix<T> {
#[track_caller]
pub fn zeros(rows: usize, cols: usize) -> Self {
Matrix {
rows,
cols,
data: vec![T::zero(); size(rows, cols)],
}
}
pub fn identity(n: usize) -> Self {
Matrix::from_diagonal(&vec![T::one(); n])
}
pub fn from_diagonal(diagonal: &[T]) -> Self {
let n = diagonal.len();
let mut a = Matrix::zeros(n, n);
for (i, d) in diagonal.iter().enumerate() {
a.data[i + i * n] = d.clone();
}
a
}
}
impl<T> Index<(usize, usize)> for Matrix<T> {
type Output = T;
#[inline]
#[track_caller]
fn index(&self, (i, j): (usize, usize)) -> &T {
assert!(
i < self.rows && j < self.cols,
"the index is outside the matrix"
);
&self.data[i + j * self.rows]
}
}
impl<T> IndexMut<(usize, usize)> for Matrix<T> {
#[inline]
#[track_caller]
fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut T {
assert!(
i < self.rows && j < self.cols,
"the index is outside the matrix"
);
&mut self.data[i + j * self.rows]
}
}
impl<T: fmt::Display> fmt::Display for Matrix<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text: Vec<String> = self
.data
.iter()
.map(|x| match f.precision() {
Some(p) => format!("{x:.p$}"),
None => format!("{x}"),
})
.collect();
let widths: Vec<usize> = (0..self.cols)
.map(|j| {
let column = &text[j * self.rows..(j + 1) * self.rows];
column.iter().map(|s| s.chars().count()).max().unwrap_or(0)
})
.collect();
for i in 0..self.rows {
if i > 0 {
writeln!(f)?;
}
for (j, width) in widths.iter().enumerate() {
let gap = if j == 0 { "" } else { " " };
write!(f, "{gap}{:>width$}", text[i + j * self.rows])?;
}
}
Ok(())
}
}
impl<T: fmt::Debug> fmt::Debug for Matrix<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries((0..self.rows).map(|i| DebugRow(self, i)))
.finish()
}
}
struct DebugRow<'a, T>(&'a Matrix<T>, usize);
impl<T: fmt::Debug> fmt::Debug for DebugRow<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.row(self.1)).finish()
}
}