use crate::error::Error;
use crate::error::Result;
use crate::number::Fractional;
use crate::number::Number;
use crate::utils::is_upper_triangle_matrix;
use crate::vector::times_v;
use crate::vector::VectorC;
use crate::vector::VectorR;
#[cfg(feature = "rayon_mat")]
use rayon::iter::{
IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator,
};
#[cfg(feature = "rand_mat")]
use rand::{
distributions::{Distribution, Standard},
Rng,
};
#[cfg(feature = "serde_mat")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_mat", derive(Serialize, Deserialize))]
pub struct Matrix<T, const ROW: usize, const COL: usize> {
pub(crate) inner: Vec<T>,
}
impl<T, const ROW: usize, const COL: usize> Matrix<T, ROW, COL> {
pub fn create(data: Vec<T>) -> Result<Self> {
if data.len() < ROW * COL {
Err(Error::IncompatibleSizeError((ROW, COL), data.len()))
} else {
Ok(Matrix::<T, ROW, COL> { inner: data })
}
}
pub fn zeros() -> Result<Self>
where
T: Clone + Default,
{
Self::create(vec![T::default(); ROW * COL])
}
pub fn dimensions(&self) -> (usize, usize) {
(ROW, COL)
}
pub(crate) const fn to_inner_index(row: usize, col: usize) -> usize {
(row - 1) * COL + col - 1
}
pub fn get_element(&self, row: usize, col: usize) -> Result<&T> {
match self.inner.get(Self::to_inner_index(row, col)) {
Some(element) => Ok(element),
None => Err(Error::OutOfBoundary(row, col)),
}
}
pub fn set_element(&mut self, row: usize, col: usize, replace: T) -> Result<()> {
match self.inner.get_mut(Self::to_inner_index(row, col)) {
Some(element) => {
*element = replace;
Ok(())
}
None => Err(Error::OutOfBoundary(row, col)),
}
}
pub fn get_row(&self, row: usize) -> Result<VectorR<&T, COL>> {
if row > ROW {
Err(Error::OutOfBoundary(row, 1))
} else {
let mut nth_row = Vec::with_capacity(COL);
for c in 1..=COL {
nth_row.push(self.get_element(row, c)?);
}
VectorR::<&T, COL>::create(nth_row)
}
}
pub fn get_col(&self, col: usize) -> Result<VectorC<&T, ROW>> {
if col > COL {
Err(Error::OutOfBoundary(1, col))
} else {
let mut nth_col = Vec::with_capacity(COL);
for r in 1..=ROW {
nth_col.push(self.get_element(r, col)?);
}
VectorC::<&T, ROW>::create(nth_col)
}
}
pub const fn get_edge() -> usize {
if ROW > COL {
COL
} else {
ROW
}
}
pub fn diag(data: Vec<T>) -> Result<Self>
where
T: Clone + Default,
{
if data.len() < Self::get_edge() {
Err(Error::IncompatibleSizeError((ROW, COL), data.len()))
} else {
let mut diag_mat = Self::zeros()?;
for index in 1..=Self::get_edge() {
match data.get(index - 1) {
Some(v) => diag_mat.set_element(index, index, v.to_owned()),
None => Err(Error::IncompatibleSizeError((ROW, COL), data.len())),
}?;
}
Ok(diag_mat)
}
}
pub fn get_diag(&self) -> Result<VectorC<&T, { Self::get_edge() }>> {
let edge: usize = Self::get_edge();
let mut diag = Vec::with_capacity(edge);
for i in 1..=edge {
diag.push(self.get_element(i, i)?);
}
VectorC::<&T, { Self::get_edge() }>::create(diag)
}
pub fn transpose(&self) -> Result<Matrix<T, COL, ROW>>
where
T: Clone,
{
let mut transposed = Vec::with_capacity(ROW * COL);
for c in 1..=COL {
for r in 1..=ROW {
transposed.push(self.get_element(r, c)?.to_owned());
}
}
Matrix::<T, COL, ROW>::create(transposed)
}
pub fn map<N, F>(&self, f: &mut F) -> Result<Matrix<N, ROW, COL>>
where
T: Clone + std::marker::Send + std::marker::Sync,
N: std::marker::Send + std::marker::Sync,
F: Fn(T) -> N + std::marker::Sync + std::marker::Send,
{
#[cfg(feature = "rayon_mat")]
let mapped = self.inner.par_iter().map(|e| f(e.to_owned())).collect();
#[cfg(not(feature = "rayon_mat"))]
let mapped = self.inner.iter().map(|e| f(e.to_owned())).collect();
Matrix::<N, ROW, COL>::create(mapped)
}
}
impl<T, const ROW: usize, const COL: usize> Matrix<T, ROW, COL>
where
T: Number,
{
pub fn eyes() -> Result<Self> {
let mut mat = Self::zeros()?;
for i in 1..=Self::get_edge() {
mat.set_element(i, i, T::one())?;
}
Ok(mat)
}
#[cfg(feature = "rand_mat")]
pub fn rand() -> Result<Self>
where
T: Number,
Standard: Distribution<T>,
{
#[cfg(feature = "rayon_mat")]
let data = (1..=ROW * COL)
.into_par_iter()
.map(|_| rand::thread_rng().gen())
.collect();
#[cfg(not(feature = "rayon_mat"))]
let data = (1..=ROW * COL).map(|_| rand::thread_rng().gen()).collect();
Self::create(data)
}
pub fn p_change(i: usize, j: usize) -> Result<Matrix<T, ROW, ROW>> {
let mut mat = Matrix::<T, ROW, ROW>::eyes()?;
mat.set_element(i, i, T::zero())?;
mat.set_element(j, j, T::zero())?;
mat.set_element(i, j, T::one())?;
mat.set_element(j, i, T::one())?;
Ok(mat)
}
pub fn p_muls(i: usize, k: T) -> Result<Matrix<T, ROW, ROW>> {
let mut mat = Matrix::<T, ROW, ROW>::eyes()?;
mat.set_element(i, i, k)?;
Ok(mat)
}
pub fn p_add(i: usize, j: usize, k: T) -> Result<Matrix<T, ROW, ROW>> {
let mut mat = Matrix::<T, ROW, ROW>::eyes()?;
mat.set_element(j, i, k)?;
Ok(mat)
}
pub fn plus(self, rhs: Self) -> Result<Self> {
#[cfg(feature = "rayon_mat")]
let sum = self
.inner
.par_iter()
.zip(rhs.inner.par_iter())
.map(|(a, b)| a.to_owned() + b.to_owned())
.collect::<Vec<_>>();
#[cfg(not(feature = "rayon_mat"))]
let sum = self
.inner
.iter()
.zip(rhs.inner.iter())
.map(|(a, b)| a.to_owned() + b.to_owned())
.collect::<Vec<_>>();
Self::create(sum)
}
pub fn adds(self, scalar: T) -> Result<Self> {
self.map(&mut |a| scalar.to_owned() + a)
}
pub fn times<const SIDE: usize>(
self,
rhs: Matrix<T, COL, SIDE>,
) -> Result<Matrix<T, ROW, SIDE>> {
let mut product = Matrix::<T, ROW, SIDE>::create(vec![T::zero(); ROW * SIDE])?;
for c in 1..=COL {
product = product
.plus(times_v(
self.get_col(c)?.map(&mut |e| e.to_owned())?,
rhs.get_row(c)?.map(&mut |e| e.to_owned())?,
)?)?
.to_owned();
}
Ok(product)
}
pub fn muls(self, scalar: T) -> Result<Self> {
self.map(&mut |a| scalar.to_owned() * a)
}
pub fn divs(self, scalar: T) -> Result<Self> {
let mut quotient = self.clone();
for index in 0..ROW * COL {
match quotient.inner.get_mut(index) {
Some(m) => match m.to_owned().ndiv(scalar.to_owned()) {
Ok(v) => {
*m = v;
Ok(())
}
Err(e) => Err(e),
},
None => Err(Error::Message(format!(
"read index {} out of boundary",
index
))),
}?;
}
Ok(quotient)
}
pub fn subtract(self, rhs: Self) -> Result<Self> {
self.plus(rhs.map(&mut |e| -e)?)
}
pub fn conjugate_transpose(&self) -> Result<Matrix<T, COL, ROW>>
where
T: Clone,
{
let mut transposed = Vec::with_capacity(ROW * COL);
for c in 1..=COL {
for r in 1..=ROW {
transposed.push(self.get_element(r, c)?.to_owned().conjugate());
}
}
Matrix::<T, COL, ROW>::create(transposed)
}
pub fn trace(&self) -> Result<T>
where
[(); Self::get_edge()]:,
{
#[cfg(feature = "rayon_mat")]
let tr = if ROW == 1 || COL == 1 {
self.inner
.par_iter()
.fold(|| T::zero(), |acc: T, e: &T| acc + e.to_owned())
.sum()
} else {
self.get_diag()?
.inner
.par_iter()
.cloned()
.fold(|| T::zero(), |acc: T, e: &T| acc + e.to_owned())
.sum()
};
#[cfg(not(feature = "rayon_mat"))]
let tr = if ROW == 1 || COL == 1 {
self.inner
.iter()
.fold(T::zero(), |acc: T, e: &T| acc + e.to_owned())
} else {
self.get_diag()?
.inner
.iter()
.cloned()
.fold(T::zero(), |acc: T, e: &T| acc + e.to_owned())
};
Ok(tr)
}
pub fn rank(&self) -> Result<usize> {
let reduced = self.row_eliminate()?.0;
#[cfg(feature = "rayon_mat")]
let count = (1..=ROW)
.into_par_iter()
.map(|i| Ok(reduced.get_row(i)?.inner))
.map(|v| -> Result<bool> { Ok(v?.par_iter().cloned().all(|e| e.is_zero())) })
.filter(|b| b == &Ok(false))
.count();
#[cfg(not(feature = "rayon_mat"))]
let count = (1..=ROW)
.map(|i| Ok(reduced.get_row(i)?.inner))
.map(|v| -> Result<bool> { Ok(v?.iter().cloned().all(|e| e.is_zero())) })
.filter(|b| b == &Ok(false))
.count();
Ok(count)
}
pub fn submatrix(&self, row: usize, col: usize) -> Result<Matrix<T, { ROW - 1 }, { COL - 1 }>> {
let mut submat = Matrix::zeros()?;
let mut row_index = 1;
for r in (1..=ROW).filter(|e| e != &row) {
let mut col_index = 1;
for c in (1..=COL).filter(|e| e != &col) {
submat.set_element(row_index, col_index, self.get_element(r, c)?.to_owned())?;
col_index = col_index + 1;
}
row_index = row_index + 1;
}
Ok(submat)
}
pub fn adjugate(&self) -> Result<Matrix<T, COL, ROW>>
where
[(); ROW - 1]:,
[(); COL - 1]:,
{
if ROW != COL {
Err(Error::IncompatibleShape((ROW, ROW), (ROW, COL)))
} else {
let mut mat = Self::zeros()?;
for row in 1..=ROW {
for col in 1..=COL {
mat.set_element(
row,
col,
self.submatrix(row, col)?.determinant()?
* if (row + col) & 1 == 1 {
-T::one()
} else {
T::one()
},
)?;
}
}
mat.transpose()
}
}
pub fn row_eliminate(&self) -> Result<(Self, Matrix<T, ROW, ROW>, T)> {
let mut reduced = self.to_owned();
let mut p_all = Matrix::<T, ROW, ROW>::eyes()?;
let mut lambda = T::one();
if !(is_upper_triangle_matrix(&reduced)? || ROW < 2) {
let mut next: usize = 0;
for index in 1..=(ROW - 1) {
if index + next > COL {
break;
}
'check_pivot: while reduced.get_element(index, index + next)?.is_zero() {
for above in (index + 1)..=ROW {
if !reduced.get_element(above, index + next)?.is_zero() {
let p_change = Matrix::<T, ROW, ROW>::p_change(index, above)?;
p_all = p_change.to_owned().times(p_all)?;
reduced = p_change.times(reduced)?;
lambda = -lambda;
break 'check_pivot;
}
}
if index + next < COL {
next = next + 1;
}
}
let value = reduced.to_owned();
let pivot = value.get_element(index, index + next)?;
for above in (index + 1)..=ROW {
let above_pivot = reduced.get_element(above, index + next)?;
if !above_pivot.is_zero() {
let factor = above_pivot.to_owned().ndiv(pivot.to_owned())?;
let p_add = Matrix::<T, ROW, ROW>::p_add(index, above, -factor)?;
p_all = p_add.to_owned().times(p_all)?;
reduced = p_add.times(reduced)?;
}
}
}
}
Ok((reduced, p_all, lambda))
}
pub fn determinant(&self) -> Result<T> {
if ROW != COL {
Err(Error::IncompatibleShape((ROW, ROW), (ROW, COL)))
} else {
let (reduced, _, lambda) = self.row_eliminate()?;
(1..=ROW)
.map(|index| reduced.get_element(index, index))
.fold(Ok(T::one()), |acc, e| {
e.and_then(|ev| acc.map(|v| v * ev.to_owned()))
})
.map(|e| e * lambda) }
}
pub fn row_reduce(&self) -> Result<(Self, Matrix<T, ROW, ROW>)> {
let eliminates = self.row_eliminate()?;
let mut reduced = eliminates.0;
let mut p_all = eliminates.1;
let mut col = 0;
let mut index = 1;
while index <= Self::get_edge() && index + col <= COL {
if reduced.get_element(index, index + col)?.is_zero() {
col += 1;
} else {
let p_smul = Matrix::<T, ROW, ROW>::p_muls(
index,
T::one().ndiv(reduced.get_element(index, index + col)?.to_owned())?,
)?;
p_all = p_smul.to_owned().times(p_all)?;
reduced = p_smul.times(reduced)?;
for j in 1..index {
if !reduced.get_element(j, index + col)?.is_zero() {
let p = reduced
.get_element(j, index + col)?
.to_owned()
.ndiv(reduced.get_element(index, index + col)?.to_owned())?;
let p_add = Matrix::<T, ROW, ROW>::p_add(index, j, -p)?;
p_all = p_add.to_owned().times(p_all)?;
reduced = p_add.times(reduced)?;
}
}
index += 1;
}
}
Ok((reduced, p_all))
}
pub fn inverse(&self) -> Result<Matrix<T, ROW, ROW>> {
Ok(self.row_reduce()?.1)
}
}
impl<T, const ROW: usize, const COL: usize> Matrix<T, ROW, COL>
where
T: Fractional,
{
pub fn equal(&self, rhs: &Self) -> bool {
#[cfg(feature = "rayon_mat")]
let check = self
.inner
.par_iter()
.take(ROW * COL)
.zip(rhs.inner.par_iter())
.all(|(e1, e2)| (e1.to_owned() - e2.to_owned()).is_zero());
#[cfg(not(feature = "rayon_mat"))]
let check = self
.inner
.iter()
.take(ROW * COL)
.zip(rhs.inner.iter())
.all(|(e1, e2)| (e1.to_owned() - e2.to_owned()).is_zero());
check
}
}
impl<T, const ROW: usize, const COL: usize> std::fmt::Display for Matrix<T, ROW, COL>
where
T: std::fmt::Display + Clone + std::marker::Send + std::marker::Sync,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", "\u{007b}")?;
for r in 1..=ROW {
write!(f, "{}", "\u{007b}")?;
for c in 1..=COL {
if let Ok(element) = self.get_element(r, c) {
write!(f, "{}", element)?;
}
if c == COL {
write!(f, "{}", "\u{007d}")?;
} else {
write!(f, ", ")?;
}
}
if r != ROW {
write!(f, ", ")?;
}
}
write!(f, "{}", "\u{007d}")
}
}
impl<T, const ROW: usize, const COL: usize> std::cmp::PartialEq for Matrix<T, ROW, COL>
where
T: std::cmp::PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}