use rand::distr::{Distribution, Uniform, uniform::SampleUniform};
use rayon::iter::{
IndexedParallelIterator,
IntoParallelIterator,
IntoParallelRefIterator,
ParallelIterator,
};
use crate::{
matrix::{utils::apply, vector::layer_product},
number::traits::{floating::Floating, fractional::Fractional, number::Number},
};
#[derive(Clone)]
pub struct Matrix<N> {
pub(crate) inner: Vec<N>,
pub row: usize,
pub column: usize,
}
impl<N> Matrix<N> {
pub fn of(row: usize, column: usize, data: &[N]) -> Option<Self>
where
N: Clone,
{
if data.len() < row * column {
eprintln!(
concat!(
"Error[Matrix::of]: ",
"The data length ({}) does not meet the required number ",
"of elements ({}) for the matrix."
),
data.len(),
row * column
);
None
} else {
Some(Self {
inner: Vec::from(&data[..(row * column)]),
row,
column,
})
}
}
pub fn defaults(row: usize, column: usize) -> Self
where
N: Clone + Default,
{
Self {
inner: vec![N::default(); row * column],
row,
column,
}
}
pub fn eyes(row: usize, column: usize) -> Self
where
N: Number,
{
let mut id_mat = Self::defaults(row, column);
for index in 1..=id_mat.edge() {
id_mat[(index, index)] = N::one();
}
id_mat
}
pub fn fills(row: usize, column: usize, data: N) -> Self
where
N: Clone,
{
Self {
inner: vec![data; row * column],
row,
column,
}
}
pub fn diagonal(row: usize, column: usize, data: &[N]) -> Option<Self>
where
N: Clone + Default,
{
let length = row.min(column);
if data.len() < length {
eprintln!(
concat!(
"Error[Matrix::diagonal]: ",
"The data length ({}) does not meet the required number ",
"of elements ({}) for the matrix."
),
data.len(),
length
);
None
} else {
let mut diag = Self::defaults(row, column);
for index in 1..=length {
diag[(index, index)] = data[index - 1].clone();
}
Some(diag)
}
}
pub fn vandermonde(row: usize, column: usize, data: &[N]) -> Option<Self>
where
N: Floating,
{
if data.len() < row {
eprintln!(
concat!(
"Error[Matrix::vandermonde]: ",
"Row ({}) should not exceed the length of the data ({})."
),
row,
data.len()
);
None
} else {
Some(Self {
inner: data
.iter()
.map(|e| {
(0..column)
.map(|p| {
e.clone().power(N::from_str(&format!("{:?}", p)).expect(
"Error[Matrix::vandermonde]: Failed to convert from usize.",
))
})
.collect::<Vec<N>>()
})
.flatten()
.collect::<Vec<N>>(),
row,
column,
})
}
}
pub fn p_change(row: usize, column: usize, from: usize, to: usize) -> Self
where
N: Number,
{
let mut p_mat = Self::eyes(row, column);
p_mat[(from, from)] = N::zero();
p_mat[(to, to)] = N::zero();
p_mat[(from, to)] = N::one();
p_mat[(to, from)] = N::one();
p_mat
}
pub fn p_muls(row: usize, column: usize, with: usize, scalar: N) -> Self
where
N: Number,
{
let mut p_mat = Self::eyes(row, column);
p_mat[(with, with)] = scalar;
p_mat
}
pub fn p_add(row: usize, column: usize, from: usize, to: usize, scalar: N) -> Self
where
N: Number,
{
let mut p_mat = Matrix::eyes(row, column);
p_mat[(to, from)] = scalar;
p_mat
}
pub fn rand(row: usize, column: usize, lb: N, ub: N) -> Self
where
N: Number + SampleUniform,
{
let range = Uniform::new(&lb, &ub).expect(&format!(
concat!(
"Error[Matrix::rand]: ",
"Failed to generate valid range for ({}, {})."
),
lb, ub
));
let mut rng = rand::rng();
let inner = range.sample_iter(&mut rng).take(row * column).collect();
Self { inner, row, column }
}
pub(crate) fn position_to_index(
column: usize,
row_index: usize,
column_index: usize,
) -> usize {
(row_index - 1) * column + column_index - 1
}
pub fn shape(&self) -> (usize, usize) { (self.row, self.column) }
pub const fn edge(&self) -> usize {
if self.row > self.column {
self.column
} else {
self.row
}
}
pub fn linear_iter(&self) -> std::slice::Iter<'_, N> { self.inner.iter() }
pub fn get(&self, row_index: usize, column_index: usize) -> Option<&N> {
if row_index == 0
|| column_index == 0
|| row_index > self.row
|| column_index > self.column
{
eprintln!(
"Error[Matrix::get]: Index ({}, {}) is out of bounds.",
row_index, column_index
);
None
} else {
let position = Self::position_to_index(self.column, row_index, column_index);
self.linear_iter().nth(position)
}
}
pub fn set(&mut self, row_index: usize, column_index: usize, value: N) {
if row_index == 0
|| column_index == 0
|| row_index > self.row
|| column_index > self.column
{
eprintln!(
"Error[Matrix::set]: Index ({}, {}) is out of bounds.",
row_index, column_index
);
} else {
let position = Self::position_to_index(self.column, row_index, column_index);
self.inner[position] = value;
}
}
pub fn get_row(&self, row_index: usize) -> Option<Matrix<&N>> {
if row_index == 0 || row_index > self.row {
eprintln!(
"Error[Matrix::get_row]: Index ({}) is out of bounds.",
row_index
);
None
} else {
let mut inner = Vec::with_capacity(self.column);
for column_index in 1..=self.column {
inner.push(&self[(row_index, column_index)]);
}
Some(Matrix {
inner,
row: 1,
column: self.column,
})
}
}
pub fn get_column(&self, column_index: usize) -> Option<Matrix<&N>> {
if column_index == 0 || column_index > self.column {
eprintln!(
"Error[Matrix::get_column]: Index ({}) is out of bounds.",
column_index
);
None
} else {
let mut inner = Vec::with_capacity(self.row);
for row_index in 1..=self.row {
inner.push(&self[(row_index, column_index)]);
}
Some(Matrix {
inner,
row: self.row,
column: 1,
})
}
}
pub fn get_diagonal(&self) -> Matrix<&N> {
let length = self.edge();
let mut inner = Vec::with_capacity(length);
for index in 1..=length {
inner.push(&self[(index, index)]);
}
return Matrix {
inner,
row: self.row,
column: 1,
};
}
pub fn submatrix(&self, row: usize, column: usize) -> Self
where
N: Clone,
{
assert!(
self.row >= 2 && self.column >= 2,
concat!(
"Error[Matrix::submatrix]: ",
"The matrix is too small to have any submatrices."
)
);
let mut inner = Vec::with_capacity((self.row) * (self.column));
for row_index in 1..=self.row {
for column_index in 1..=self.column {
if row_index == row || column_index == column {
continue;
} else {
inner.push(self[(row_index, column_index)].clone());
}
}
}
Matrix {
inner,
row: self.row - 1,
column: self.column - 1,
}
}
}
impl<N> std::cmp::PartialEq<Matrix<N>> for Matrix<N>
where
N: std::cmp::PartialEq + std::marker::Sync,
{
fn eq(&self, other: &Self) -> bool {
self.shape() == other.shape()
&& self
.inner
.par_iter()
.zip(other.inner.par_iter())
.all(|(e1, e2)| e1 == e2)
}
}
impl<N> std::ops::Neg for Matrix<N>
where
N: Number,
{
type Output = Self;
fn neg(self) -> Self::Output {
let inner = self.inner.par_iter().map(|e1| -e1.clone()).collect();
Self {
inner,
row: self.row,
column: self.column,
}
}
}
impl<N> std::ops::Add<N> for Matrix<N>
where
N: Number,
{
type Output = Self;
fn add(self, rhs: N) -> Self::Output {
let inner = self
.inner
.par_iter()
.map(|e| e.clone() + rhs.clone())
.collect();
Self {
inner,
row: self.row,
column: self.column,
}
}
}
impl<N> std::ops::Sub<N> for Matrix<N>
where
N: Number,
{
type Output = Self;
fn sub(self, rhs: N) -> Self::Output {
let inner = self
.inner
.par_iter()
.map(|e| e.clone() - rhs.clone())
.collect();
Self {
inner,
row: self.row,
column: self.column,
}
}
}
impl<N> std::ops::Mul<N> for Matrix<N>
where
N: Number,
{
type Output = Self;
fn mul(self, rhs: N) -> Self::Output {
let inner = self
.inner
.par_iter()
.map(|e| e.clone() * rhs.clone())
.collect();
Self {
inner,
row: self.row,
column: self.column,
}
}
}
impl<N> std::ops::Div<N> for Matrix<N>
where
N: Fractional,
{
type Output = Self;
fn div(self, rhs: N) -> Self::Output {
let inner = self
.inner
.par_iter()
.map(|e| e.clone() / rhs.clone())
.collect();
Self {
inner,
row: self.row,
column: self.column,
}
}
}
impl<N> std::ops::Add for Matrix<N>
where
N: Number,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let inner = self
.inner
.par_iter()
.zip(rhs.inner.par_iter())
.map(|(e1, e2)| e1.clone() + e2.clone())
.collect();
Self {
inner,
row: self.row,
column: rhs.column,
}
}
}
impl<N> std::ops::Sub for Matrix<N>
where
N: Number,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let inner = self
.inner
.par_iter()
.zip(rhs.inner.par_iter())
.map(|(e1, e2)| e1.clone() - e2.clone())
.collect();
Self {
inner,
row: self.row,
column: rhs.column,
}
}
}
impl<N> std::ops::Mul<Matrix<N>> for Matrix<N>
where
N: Number,
{
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
assert_eq!(
self.column, rhs.row,
concat!(
"Error[Matrix::mul]: ",
"In matrix multiplication, the number of columns ({}) in the first matrix ",
"must be equal to the number of rows ({}) in the second matrix."
),
self.column, rhs.row
);
(1..=self.column)
.into_par_iter()
.map(|k_index| {
layer_product(
&apply(
&self.get_column(k_index).expect(&format!(
concat!(
"Error[Matrix::mul]: ",
"k_index ({}) should be a valid index for self."
),
k_index
)),
|e| e.clone(),
), &apply(
&rhs.get_row(k_index).expect(&format!(
concat!(
"Error[Matrix::mul]: ",
"k_index ({}) should be a valid index for rhs."
),
k_index
)),
|e| e.clone(),
), )
})
.reduce(|| Matrix::defaults(self.row, rhs.column), |a, b| a + b)
}
}
impl<N> std::ops::Index<(usize, usize)> for Matrix<N> {
type Output = N;
fn index(&self, index: (usize, usize)) -> &Self::Output {
self.get(index.0, index.1)
.expect("Error[Matrix::index]: Index is out of bounds.")
}
}
impl<N> std::ops::IndexMut<(usize, usize)> for Matrix<N> {
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
&mut self.inner[Self::position_to_index(self.column, index.0, index.1)]
}
}
impl<N> std::fmt::Display for Matrix<N>
where
N: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for row_index in 1..=self.row {
write!(f, "[")?;
for column_index in 1..self.column {
write!(f, "{}, ", self[(row_index, column_index)])?;
}
writeln!(f, "{}]", self[(row_index, self.column)])?;
}
Ok(())
}
}
impl<N> std::fmt::Debug for Matrix<N>
where
N: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for row_index in 1..=self.row {
write!(f, "[")?;
for column_index in 1..self.column {
write!(f, "{:?}, ", self[(row_index, column_index)])?;
}
writeln!(f, "{:?}]", self[(row_index, self.column)])?;
}
Ok(())
}
}