use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use super::vector::is_row_vector;
use crate::{
matrix::{
Matrix,
math::{is_square_matrix, row_eliminate, row_reduce},
vector::{is_column_vector, layer_product, normalize, project_to},
},
number::traits::{fractional::Fractional, number::Number, realfloat::RealFloat},
};
pub fn points_2d<F>((row_lb, row_ub): (usize, usize), (col_lb, col_ub): (usize, usize), criteria: F) -> Vec<(usize, usize)>
where
F: Fn(usize, usize) -> bool,
{
if row_lb > row_ub || col_lb > col_ub {
Vec::new()
} else {
let mut all_points = Vec::with_capacity((row_ub - row_lb + 1) * (col_ub - col_lb + 1));
for row in row_lb..=row_ub {
for col in col_lb..=col_ub {
if criteria(row, col) {
all_points.push((row, col));
}
}
}
all_points.shrink_to_fit();
all_points
}
}
pub fn trace<N>(m: &Matrix<N>) -> N
where
N: Number,
{
m.get_diagonal()
.linear_iter()
.cloned()
.fold(N::zero(), |acc, e| acc + e.clone())
}
pub fn apply<N, M, F>(m: &Matrix<N>, f: F) -> Matrix<M>
where
N: Sync + Clone,
M: Send + Sync,
F: Fn(N) -> M + Sync,
{
let inner = m.inner.par_iter().map(|e| f(e.clone())).collect();
Matrix {
inner,
row: m.row,
column: m.column,
}
}
pub fn transpose<N>(m: &Matrix<N>) -> Matrix<N>
where
N: Clone,
{
let mut inner = Vec::with_capacity(m.column * m.row);
for row_index in 1..=m.column {
for column_index in 1..=m.row {
inner.push(m[(column_index, row_index)].clone());
}
}
Matrix {
inner,
row: m.column,
column: m.row,
}
}
pub fn project_matrix<N>(to: &Matrix<N>) -> Matrix<N>
where
N: RealFloat,
{
assert!(
is_column_vector(to),
concat!(
"Error[matrix::utils::project_matrix]: ",
"A column vector is required."
)
);
let normalized = normalize(to);
layer_product(&normalized, &transpose(&normalized))
}
pub fn rank<N>(m: &Matrix<N>) -> usize
where
N: Fractional,
{
let (_, _, _, reduced) = row_reduce(m);
(1..=m.row)
.map(|row_index| {
reduced
.get_row(row_index)
.expect(&format!(
concat!(
"Error[matrix::utils::rank]: ",
"Failed to retrieve the {}-th row of the matrix."
),
row_index
))
.inner
.par_iter()
.any(|e| !e.is_zero())
})
.filter(|&p| p)
.count()
}
pub fn null_space<N>(m: &Matrix<N>) -> Vec<Matrix<N>>
where
N: Fractional,
{
let refined = row_eliminate(m);
let mut row_flags = vec![0; m.column];
for row in 1..=m.row {
for column in row..=m.column {
if !refined[(row, column)].is_zero() {
row_flags[column - 1] = row;
break;
}
}
}
let mut space = Vec::new();
if row_flags.contains(&0) {
for column in 1..=m.column {
if row_flags[column - 1] == 0 {
let mut base = Matrix::<N>::defaults(m.column, 1);
for check in 1..=m.column {
if row_flags[check - 1] != 0 {
let val = refined[(row_flags[check - 1], column)].clone();
base[(check, 1)] = if val.is_zero() { val } else { -val };
} else if column == check {
base[(check, 1)] = N::one();
}
}
space.push(base);
}
}
}
space
}
pub fn column_space<N>(m: &Matrix<N>) -> Vec<Matrix<N>>
where
N: Fractional,
{
let refined = row_eliminate(m);
let mut cols = Vec::new();
for row in 1..=m.row {
for column in row..=m.column {
if !refined[(row, column)].is_zero() {
cols.push(column);
break;
}
}
}
cols.iter()
.map(|&c| {
apply(
&m.get_column(c).expect(&format!(
concat!(
"Error[matrix::utils::column_space]: ",
"Failed to retrieve the {}-th column of the matrix."
),
c,
)),
|e: &N| e.clone(),
)
})
.collect::<Vec<Matrix<N>>>()
}
pub fn row_space<N>(m: &Matrix<N>) -> Vec<Matrix<N>>
where
N: Fractional,
{
column_space(&transpose(m))
}
pub fn horizontal_concat<N>(m1: &Matrix<N>, m2: &Matrix<N>) -> Matrix<N>
where
N: Clone,
{
assert_eq!(
m1.row, m2.row,
concat!(
"Error[matrix::utils::horizontal_concat]: ",
"The number of rows in m1 and m2 must be the same."
)
);
let mut inner = Vec::with_capacity(m1.row * (m1.column + m2.column));
for row_index in 1..=m1.row {
for column_index in 1..=(m1.column + m2.column) {
inner.push(if column_index <= m1.column {
m1[(row_index, column_index)].clone()
} else {
m2[(row_index, column_index - m1.column)].clone()
});
}
}
Matrix {
inner,
row: m2.row,
column: m1.column + m2.column,
}
}
pub fn vertical_concat<N>(m1: &Matrix<N>, m2: &Matrix<N>) -> Matrix<N>
where
N: Clone,
{
assert_eq!(
m1.column, m2.column,
concat!(
"Error[matrix::utils::horizontal_concat]: ",
"The number of columns in m1 and m2 must be the same."
)
);
let mut inner = Vec::with_capacity((m1.row + m2.row) * m1.column);
for row_index in 1..=(m1.row + m2.row) {
for column_index in 1..=m1.column {
inner.push(if row_index <= m1.row {
m1[(row_index, column_index)].clone()
} else {
m2[(row_index - m1.row, column_index)].clone()
});
}
}
Matrix {
inner,
row: m1.row + m2.row,
column: m2.column,
}
}
pub fn from_columns<N>(row: usize, column: usize, columns: &[Matrix<N>]) -> Option<Matrix<N>>
where
N: Clone + Sync,
{
if columns
.par_iter()
.all(|v| is_column_vector(v) && v.row == row)
{
let mut inner = Vec::with_capacity(row * column);
for row_index in 1..=row {
columns
.iter()
.take(column)
.for_each(|e| inner.push(e[(row_index, 1)].clone()));
}
Some(Matrix { inner, row, column })
} else {
eprintln!(concat!("Error[matrix::utils::from_columns]: ", ""));
None
}
}
pub fn from_rows<N>(row: usize, column: usize, rows: &[Matrix<N>]) -> Option<Matrix<N>>
where
N: Clone + Sync,
{
if rows
.par_iter()
.all(|v| is_row_vector(v) && v.column == column)
{
let mut inner = Vec::with_capacity(row * column);
rows.iter().take(row).for_each(|e| {
for column_index in 1..=column {
inner.push(e[(1, column_index)].clone());
}
});
Some(Matrix { inner, row, column })
} else {
eprintln!(concat!("Error[matrix::utils::from_rows]: ", ""));
None
}
}
pub fn gram_schmidt_process<N>(basis: &Matrix<N>) -> Matrix<N>
where
N: RealFloat,
{
let mut orthonormal_basis = Matrix::defaults(basis.row, basis.column);
for k in 1..=basis.column {
let mut uk = apply(
&basis.get_column(k).expect(concat!(
"Error[matrix::utils::gram_schmidt_process]: ",
"Failed to retrieve the column vector of basis."
)),
|e: &N| e.clone(),
);
for j in 1..k {
let uj = apply(
&orthonormal_basis.get_column(j).expect(concat!(
"Error[matrix::utils::gram_schmidt_process]: ",
"Failed to retrieve the column vector of orthonormal_basis."
)),
|e: &N| e.clone(),
);
uk = uk.clone() - project_to(&uk, &uj);
}
uk = normalize(&uk);
for row in 1..=basis.row {
orthonormal_basis[(row, k)] = uk[(row, 1)].clone();
}
}
orthonormal_basis
}
pub fn givens_rotation_matrix<N>(m: &Matrix<N>, row: usize, column: usize) -> Matrix<N>
where
N: RealFloat,
{
let mut givens = Matrix::<N>::eyes(m.row, m.row);
let e1 = m[(row, column)].clone();
let e2 = m[(row - 1, column)].clone();
let r = (e1.clone() * e1.clone() + e2.clone() * e2.clone()).square_root();
let sin_theta = e1 / r.clone();
let cos_theta = e2 / r;
givens[(row, row)] = cos_theta.clone();
givens[(row - 1, row - 1)] = cos_theta;
givens[(row, row - 1)] = -sin_theta.clone();
givens[(row - 1, row)] = sin_theta;
givens
}
pub fn hessenberg_decomposition<N>(m: &Matrix<N>) -> (Matrix<N>, Matrix<N>)
where
N: RealFloat,
{
assert!(
is_square_matrix(m),
concat!(
"Error[matrix::utils::hessenberg_decomposition]: ",
"Only square matrices can undergo the Heisenberg decomposition."
)
);
if m.row < 3 {
(Matrix::eyes(m.row, m.column), m.clone())
} else {
let mut hessen = m.clone();
let mut p = Matrix::eyes(m.row, m.column);
for column in 1..=m.column {
for row in (column + 1..m.edge()).rev() {
if !hessen[(row + 1, column)].is_zero() {
let pi = transpose(&givens_rotation_matrix(&hessen, row + 1, column));
hessen = transpose(&pi) * hessen * pi.clone();
p = p * pi;
}
}
}
(p, hessen)
}
}