use crate::error::Error;
use crate::error::Result;
use crate::matrix::Matrix;
use crate::number::Fractional;
use crate::number::Number;
use crate::vector::l2_norm_c;
use crate::vector::times_d;
use crate::vector::VectorC;
#[cfg(feature = "rayon_mat")]
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
pub fn points<T, R>(
mut f: impl FnMut(usize, usize) -> (T, R),
row: usize,
col: usize,
) -> Vec<(T, R)> {
let mut ps = Vec::with_capacity(row * col);
for r in 1..=row {
for c in 1..=col {
ps.push(f(r, c))
}
}
ps
}
pub fn horizontal_concat<T, const ROW: usize, const COL: usize, const RCOL: usize>(
mat: &Matrix<T, ROW, COL>,
rhs: &Matrix<T, ROW, RCOL>,
) -> Result<Matrix<T, ROW, { COL + RCOL }>>
where
T: Clone + Default + std::marker::Send + std::marker::Sync,
{
let mut hmat = Matrix::zeros()?;
for r in 1..=ROW {
for c1 in 1..=COL {
hmat.set_element(r, c1, mat.get_element(r, c1)?.to_owned())?;
}
for c2 in 1..=RCOL {
hmat.set_element(r, COL + c2, rhs.get_element(r, c2)?.to_owned())?;
}
}
Ok(hmat)
}
pub fn vertical_concat<T, const ROW: usize, const COL: usize, const RROW: usize>(
mat: &Matrix<T, ROW, COL>,
rhs: &Matrix<T, RROW, COL>,
) -> Result<Matrix<T, { ROW + RROW }, COL>>
where
T: Clone + std::marker::Send + std::marker::Sync,
{
Matrix::create([&mat.inner[..], &rhs.inner[..]].concat())
}
pub(crate) fn lower_triangularize<T, const ROW: usize>(
mat: Matrix<T, ROW, ROW>,
) -> Result<(Matrix<T, ROW, ROW>, Matrix<T, ROW, ROW>)>
where
T: Number,
{
let mut reduced = mat.to_owned();
let mut p_all = Matrix::<T, ROW, ROW>::eyes()?;
if !(is_lower_triangle_matrix(&reduced)? || ROW < 2) {
let mut next: usize = 0;
for index in (2..=ROW).rev() {
if index <= next {
break;
}
'check_pivot: while reduced.get_element(index, index - next)?.is_zero() {
for above in (1..=(index - 1)).rev() {
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)?;
break 'check_pivot;
}
}
if index > next {
next = next + 1;
}
}
let value = reduced.to_owned();
let pivot = value.get_element(index, index - next)?;
for over in (1..(index - 1)).rev() {
let over_pivot = reduced.get_element(over, index - next)?;
if !over_pivot.is_zero() {
let factor = over_pivot.to_owned().ndiv(pivot.to_owned())?;
let p_add = Matrix::<T, ROW, ROW>::p_add(index, over, -factor)?;
p_all = p_add.to_owned().times(p_all)?;
reduced = p_add.times(reduced)?;
}
}
}
}
Ok((p_all, reduced))
}
pub fn plu_decomposition<T, const ROW: usize, const COL: usize>(
mat: Matrix<T, ROW, COL>,
) -> Result<(
Matrix<T, ROW, ROW>,
Matrix<T, ROW, ROW>,
Matrix<T, ROW, COL>,
)>
where
T: Number,
{
if mat.determinant()?.is_zero() {
Err(Error::SingularMatrix)
} else {
let eliminates = mat.row_eliminate()?;
let pl = lower_triangularize(eliminates.1.inverse()?)?;
Ok((pl.0, pl.1, eliminates.0))
}
}
pub fn qr_decomposition<T, const ROW: usize, const COL: usize>(
mat: Matrix<T, ROW, COL>,
) -> Result<(Matrix<T, ROW, COL>, Matrix<T, COL, COL>)>
where
T: Fractional,
{
let mut an = Vec::with_capacity(COL);
for col in 1..=COL {
an.push(mat.get_col(col)?);
}
let mut un: Vec<VectorC<T, ROW>> = Vec::with_capacity(COL);
let mut en: Vec<VectorC<T, ROW>> = Vec::with_capacity(COL);
for col in 1..=COL {
let ai = match an.get(col - 1) {
Some(element) => Ok(element),
None => Err(Error::Message(format!(
"read index {} out of boundary",
col
))),
}?
.to_owned();
#[cfg(feature = "rayon_mat")]
let ai = VectorC::<T, ROW>::create(
ai.inner
.par_iter()
.map(|e| e.to_owned().to_owned())
.collect(),
)?;
#[cfg(not(feature = "rayon_mat"))]
let ai =
VectorC::<T, ROW>::create(ai.inner.iter().map(|e| e.to_owned().to_owned()).collect())?;
let mut ui = ai.to_owned();
for index in 1..=(col - 1) {
let ek = match en.get(index - 1) {
Some(element) => Ok(element),
None => Err(Error::Message(format!(
"read index {} out of boundary",
index
))),
}?
.to_owned();
#[cfg(feature = "rayon_mat")]
let ek = VectorC::<T, ROW>::create(
ek.inner
.par_iter()
.map(|e| e.to_owned().to_owned())
.collect(),
)?;
#[cfg(not(feature = "rayon_mat"))]
let ek = VectorC::<T, ROW>::create(
ek.inner.iter().map(|e| e.to_owned().to_owned()).collect(),
)?;
ui = ui.subtract(ek.to_owned().muls(times_d(ai.to_owned(), ek)?)?)?;
}
un.push(ui.to_owned());
let norm = l2_norm_c(ui.to_owned());
let ei = ui.divs(norm)?;
en.push(ei);
}
let mut q = Matrix::<T, ROW, COL>::zeros()?;
let mut r = Matrix::<T, COL, COL>::zeros()?;
for row in 1..=ROW {
for col in 1..=COL {
let ei = match en.get(col - 1) {
Some(element) => Ok(element),
None => Err(Error::Message(format!(
"read index {} out of boundary",
col
))),
}?
.to_owned();
q.set_element(row, col, ei.get_element(row, 1)?.to_owned().to_owned())?;
}
}
for col1 in 1..=COL {
let ei = match en.get(col1 - 1) {
Some(element) => Ok(element),
None => Err(Error::Message(format!(
"read index {} out of boundary",
col1
))),
}?
.to_owned();
#[cfg(feature = "rayon_mat")]
let ei = VectorC::<T, ROW>::create(
ei.inner
.par_iter()
.map(|e| e.to_owned().to_owned())
.collect(),
)?;
#[cfg(not(feature = "rayon_mat"))]
let ei =
VectorC::<T, ROW>::create(ei.inner.iter().map(|e| e.to_owned().to_owned()).collect())?;
for col2 in col1..=COL {
let ai = match an.get(col2 - 1) {
Some(element) => Ok(element),
None => Err(Error::Message(format!(
"read index {} out of boundary",
col2
))),
}?
.to_owned();
#[cfg(feature = "rayon_mat")]
let ai = VectorC::<T, ROW>::create(
ai.inner
.par_iter()
.map(|e| e.to_owned().to_owned())
.collect(),
)?;
#[cfg(not(feature = "rayon_mat"))]
let ai = VectorC::<T, ROW>::create(
ai.inner.iter().map(|e| e.to_owned().to_owned()).collect(),
)?;
r.set_element(col1, col2, times_d(ai, ei.to_owned())?)?;
}
}
Ok((q, r))
}
pub fn eigen_values<T, const ROW: usize>(
mat: Matrix<T, ROW, ROW>,
iter_count: usize,
) -> Result<Vec<T>>
where
T: Fractional,
{
let qr = qr_decomposition(mat.to_owned())?;
let mut a = qr.1.times(qr.0)?;
for _ in 0..iter_count {
let qrn = qr_decomposition(a.to_owned())?;
a = qrn.1.times(qrn.0)?;
}
let mut eigens = Vec::with_capacity(ROW);
for index in 1..=ROW {
eigens.push(a.get_element(index, index)?.to_owned());
}
Ok(eigens)
}
pub fn linear_solve<T, const ROW: usize, const COL: usize, const EDGE: usize>(
mat: Matrix<T, ROW, COL>,
b: Matrix<T, ROW, EDGE>,
) -> Result<Matrix<T, ROW, EDGE>>
where
T: Number,
{
if mat.rank()? > COL {
Err(Error::NoSolution(ROW, COL))
} else {
mat.row_reduce()?.1.times(b)
}
}
pub fn eigen_system() {
todo!()
}
pub fn is_sqaure_matrix<T, const ROW: usize, const COL: usize>(_: &Matrix<T, ROW, COL>) -> bool {
ROW == COL
}
pub fn is_upper_triangle_matrix<T, const ROW: usize, const COL: usize>(
mat: &Matrix<T, ROW, COL>,
) -> Result<bool>
where
T: Number,
{
#[cfg(feature = "rayon_mat")]
let predicate = points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r > c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
#[cfg(not(feature = "rayon_mat"))]
let predicate = points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r > c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
Ok(predicate)
}
pub fn is_lower_triangle_matrix<T, const ROW: usize, const COL: usize>(
mat: &Matrix<T, ROW, COL>,
) -> Result<bool>
where
T: Number,
{
#[cfg(feature = "rayon_mat")]
let predicate = points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r < c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
#[cfg(not(feature = "rayon_mat"))]
let predicate = points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r < c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
Ok(predicate)
}
pub fn is_diagonal_matrix<T, const ROW: usize, const COL: usize>(
mat: &Matrix<T, ROW, COL>,
) -> Result<bool>
where
T: Number,
{
#[cfg(feature = "rayon_mat")]
let predicate = points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r != c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
#[cfg(not(feature = "rayon_mat"))]
let predicate = points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r != c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
Ok(predicate)
}
pub fn is_identity_matrix<T, const ROW: usize, const COL: usize>(
mat: &Matrix<T, ROW, COL>,
) -> Result<bool>
where
T: Number,
{
#[cfg(feature = "rayon_mat")]
let predicate = points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r != c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
})
&& points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r == c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_one())
});
#[cfg(not(feature = "rayon_mat"))]
let predicate = points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r != c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
})
&& points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r == c)
.all(|(r, c)| {
mat.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_one())
});
Ok(predicate)
}
pub fn is_orthogonal_matrix<T, const ROW: usize, const COL: usize>(
mat: &Matrix<T, ROW, COL>,
) -> Result<bool>
where
T: Number,
{
let transposed = mat.transpose()?;
if ROW > COL {
is_identity_matrix::<T, COL, COL>(&transposed.times(mat.to_owned())?)
} else {
is_identity_matrix::<T, ROW, ROW>(&mat.to_owned().times(transposed)?)
}
}
pub fn is_unitary_matrix<T, const ROW: usize, const COL: usize>(
mat: &Matrix<T, ROW, COL>,
) -> Result<bool>
where
T: Number,
{
let transposed = mat.conjugate_transpose()?;
if ROW > COL {
is_identity_matrix::<T, COL, COL>(&transposed.times(mat.to_owned())?)
} else {
is_identity_matrix::<T, ROW, ROW>(&mat.to_owned().times(transposed)?)
}
}