use crate::linalg::{Tensor, Vector};
use crate::{Float, Num};
use rand::distributions::{Distribution, Standard};
use rand::random;
use rayon::prelude::IntoParallelRefMutIterator;
use rayon::prelude::*;
use std::cmp::min;
use std::fmt::{Display, Formatter};
use std::ops::{Index, IndexMut};
#[macro_export]
macro_rules! matrix {
($([$($x:expr),* $(,)*]),* $(,)*) => {
Matrix::from(vec![
$(vec![
$($x,)*
],)*
])
};
}
#[derive(PartialEq, Eq, Debug)]
pub struct Matrix<T: Num> {
pub(crate) data: Vec<T>,
pub(crate) rows: usize,
pub(crate) cols: usize,
}
impl<T: Num> Matrix<T> {
pub fn new(data: Vec<T>, rows: usize, cols: usize) -> Self {
if data.len() != rows * cols {
panic!("!!!Inconsistent data and dimensions combination for matrix!!!")
}
Self { data, rows, cols }
}
pub fn zeros(shape: [usize; 2]) -> Self {
Self {
data: vec![T::default(); shape[0] * shape[1]],
rows: shape[0],
cols: shape[1],
}
}
pub fn from_num(num: T, rows: usize, cols: usize) -> Self {
Self {
data: vec![num; rows * cols],
rows,
cols,
}
}
pub fn from_fn<F>(rows: usize, cols: usize, f: F) -> Self
where
F: Fn(usize, usize) -> T + Sync + Send {
let mut data = vec![T::default(); rows*cols];
data.par_iter_mut().enumerate().for_each(|(idx, item)| {
let i = idx / cols;
let j = idx % cols;
*item = f(i, j);
});
Self {
data,
rows,
cols,
}
}
pub fn try_from(value: Tensor<T>) -> Result<Self, &'static str> {
if value.shape.len() != 2 {
return Err("Shape size must be 2");
}
Ok(Matrix::new(value.data, value.shape[0], value.shape[1]))
}
pub fn from_diag(data:Vector<T>, rows:usize, cols:usize) -> Matrix<T> {
assert_eq!(data.length, min(rows, cols),
"!!!The length of the data vector ({})\
must be equal to the minimum of rows ({}) and cols ({})!!!",
data.length,
rows,
cols
);
let mut mx_data = vec![T::default(); cols * rows];
for i in 0..min(cols, rows) {
let index = (cols * i) + i;
mx_data[index] = data[i];
}
Matrix{
data:mx_data,
rows,
cols
}
}
pub fn identity(_: T, rows: usize, cols: usize) -> Self {
let mut matrix = vec![T::default(); rows * cols];
for i in 0..rows {
for j in 0..cols {
if i == j {
matrix[i * rows + j] = T::from(1);
}
}
}
Self {
data: matrix,
rows,
cols,
}
}
pub fn get_data(&self) -> Vec<T> {
self.data.clone()
}
pub fn shape(&self) -> [usize; 2] {
[self.rows, self.cols]
}
pub fn rows(&self) -> usize {
self.rows
}
pub fn cols(&self) -> usize {
self.cols
}
pub fn sum(&self) -> T {
let mut sum = T::default();
for i in self.data.iter() {
sum += *i;
}
sum
}
pub fn get_col(&self, index: usize) -> Vector<T> {
assert!(
index < self.cols,
"!!!Index:{} is greater than or equal to columns count:{}!!!",
index,
self.cols
);
let mut vector = Vec::with_capacity(self.rows);
for i in 0..self.rows {
let index_col = i * self.cols + index;
vector.push(self.data[index_col]);
}
Vector::from(vector)
}
pub fn rem_col(&self, index: usize) -> Matrix<T> {
assert!(index < self.cols, "!!!Column index out of bounds!!!");
let mut new_data = self.data.clone();
for i in (0..self.rows).rev() {
let index = i * self.cols + index;
new_data.remove(index);
}
Matrix {
data: new_data,
rows: self.rows,
cols: self.cols - 1,
}
}
pub fn get_row(&self, index: usize) -> Vector<T> {
assert!(
index < self.rows,
"!!!Index:{} is greater than or equal to rows count:{}!!!",
index,
self.rows
);
let start_index = index * self.cols;
let end_index = start_index + self.cols;
Vector::from(self.data[start_index..end_index].to_vec())
}
pub fn transpose(&self) -> Self {
let mut result = Self::from_num(T::default(), self.cols, self.rows);
for i in 0..self.cols {
for j in 0..self.rows {
result[[i, j]] = self[[j, i]];
}
}
result
}
pub fn add_column(&mut self, column: Vec<T>) {
assert_eq!(
column.len(),
self.rows,
"!!!the length of the Vec<T> is not equal to the size of the rows of the matrix!!!"
);
for i in 0..self.rows {
self.data.insert((i + 1) * self.cols + i, column[i].clone());
}
self.cols += 1;
}
pub fn add_row(&mut self, row: Vec<T>) {
assert_eq!(
row.len(),
self.cols,
"!!!the length of the Vec<T> is not equal to the size of the columns of the matrix!!!"
);
for i in row {
self.data.push(i)
}
self.rows += 1;
}
pub fn get_resize(&self, new_row: usize, new_col: usize) -> Matrix<T> {
let mut new_matrix = Matrix::from_num(T::default(), new_row, new_col);
for i in 0..min(new_row, self.rows) {
for j in 0..min(new_col, self.cols) {
new_matrix[[i, j]] = self[[i, j]];
}
}
new_matrix
}
pub fn sum_rows(&self) -> Matrix<T> {
let mut matrix = vec![T::default(); self.cols];
for i in 0..self.cols {
matrix[i] = self.get_col(i).sum_all();
}
Matrix::new(matrix, 1, self.cols)
}
pub fn compare(&self, other: Matrix<T>) -> Matrix<T> {
assert_eq!(
self.shape(),
other.shape(),
"!!!Can't compare matrices different shapes!!!\n Matrix a:{:?}; Matrix b:{:?}",
self.shape(),
other.shape()
);
let mut comparisons = vec![T::default(); self.rows * self.cols];
comparisons.par_iter_mut().enumerate().for_each(|(i, x)| {
if self.data[i] > other.data[i] {
*x = T::from(1);
} else if self.data[i] < other.data[i] {
*x = T::from(1).neg();
}
});
Matrix::new(comparisons, self.rows, self.cols)
}
pub fn compare_num(&self, other: T) -> Matrix<T> {
Matrix::new(
self.data
.iter()
.map(|x| {
if *x > other {
T::from(1)
} else if *x == other {
T::from(0)
} else {
T::from(1).neg()
}
})
.collect(),
self.rows,
self.cols,
)
}
pub fn hadamard(&self, other: &Matrix<T>) -> Matrix<T> {
assert_eq!(
other.shape(),
self.shape(),
"!!!Shapes must be equal. Matrix A: {:?} Matrix B: {:?}!!!",
self.shape(),
other.shape()
);
let mut ans = vec![T::default(); self.data.len()];
ans.par_iter_mut().enumerate().for_each(|(i, x)| {
*x = self.data[i] * other.data[i];
});
Matrix::new(ans, self.rows, self.cols)
}
pub fn kronecker(&self, other: &Matrix<T>) -> Matrix<T> {
let mut ans = vec![T::default(); self.data.len() * other.data.len()];
ans.par_iter_mut().enumerate().for_each(|(index, x)| {
let i = index / (self.cols * other.cols);
let j = index % (self.cols * other.cols);
let a_row = i / self.rows;
let a_col = j / self.cols;
let b_row = i % other.rows;
let b_col = j % other.cols;
*x = self.data[a_row * self.cols + a_col] * other.data[b_row * other.cols + b_col];
});
Matrix::new(ans, self.rows * other.rows, self.cols * other.cols)
}
pub fn map<F>(&self, f: F) -> Matrix<T>
where
F: Fn(T) -> T + Sync + Send,
{
let new_data = self.data.clone().par_iter_mut().map(|x| f(*x)).collect();
Matrix {
data: new_data,
rows: self.rows,
cols: self.cols,
}
}
pub fn zip_with<F>(&self, other: &Matrix<T>, f: F) -> Matrix<T>
where
F: Fn(T, T) -> T + Sync + Send,
{
let new_data = self
.data
.clone()
.par_iter_mut()
.enumerate()
.map(|(i, x)| f(*x, other.data[i]))
.collect();
Matrix {
data: new_data,
rows: self.rows,
cols: self.cols,
}
}
pub fn max(&self, num: T) -> Matrix<T> {
self.map(|x| if x > num { x } else { num })
}
pub fn min(&self, num: T) -> Matrix<T> {
self.map(|x| if x > num { num } else { x })
}
pub fn diag(&self) -> Vector<T> {
let mut data = vec![T::default(); min(self.cols, self.rows)];
for i in 0..min(self.cols, self.rows) {
let index = (self.cols * i) + i;
data[i] = self.data[index];
}
Vector::from(data)
}
pub fn conv(&self, kernel: &Matrix<T>) -> Matrix<T> {
assert!(
kernel.rows <= self.rows && kernel.cols <= self.cols,
"!!!Kernel size must be less than Matrix itself!!!\nMatrix size: {:?}, Kernel Size: {:?}",
self.shape(),
kernel.shape()
);
let output_rows = self.rows - kernel.rows + 1;
let output_cols = self.cols - kernel.cols + 1;
let mut result_data = vec![T::default(); output_cols * output_rows];
result_data.par_chunks_mut(output_cols)
.enumerate()
.for_each(|(i, out_row)| {
for j in 0..output_cols {
let mut sum = T::default();
for ki in 0..kernel.rows {
let matrix_row_base = (i + ki) * self.cols + j;
let kernel_row_base = ki * kernel.cols;
for kj in 0..kernel.cols {
sum += self.data[matrix_row_base + kj] *
kernel.data[kernel_row_base + kj];
}
}
out_row[j] = sum;
}
});
Matrix::new(result_data, output_rows, output_cols)
}
pub fn conv_zero(&self, kernel: &Matrix<T>) -> Matrix<T> {
let pad_rows = kernel.rows / 2;
let pad_cols = kernel.cols / 2;
let output_rows = self.rows;
let output_cols = self.cols;
let mut result_data = vec![T::default(); output_cols * output_rows];
result_data.par_chunks_mut(output_cols)
.enumerate()
.for_each(|(i, out_row)| {
for j in 0..output_cols {
let mut sum = T::default();
for ki in 0..kernel.rows {
for kj in 0..kernel.cols {
let mi = i as i32 + ki as i32 - pad_rows as i32;
let mj = j as i32 + kj as i32 - pad_cols as i32;
if mi >= 0 && mi < self.rows as i32 && mj >= 0 && mj < self.cols as i32 {
let matrix_idx = mi as usize * self.cols + mj as usize;
let kernel_idx = ki * kernel.cols + kj;
sum = sum + self.data[matrix_idx] * kernel.data[kernel_idx];
}
}
}
out_row[j] = sum;
}
});
Matrix::new(result_data, output_rows, output_cols)
}
pub fn conv_with_mirror_padding(&self, kernel: &Matrix<T>) -> Matrix<T> {
fn mirror_index(idx: i32, size: usize) -> usize {
let size_i32 = size as i32;
if idx < 0 {
(-idx - 1) as usize % size
} else if idx >= size_i32 {
(2 * size_i32 - idx - 1) as usize % size
} else {
idx as usize
}
}
let pad_rows = kernel.rows / 2;
let pad_cols = kernel.cols / 2;
let output_rows = self.rows;
let output_cols = self.cols;
let mut result_data = vec![T::default(); output_cols * output_rows];
result_data.par_chunks_mut(output_cols)
.enumerate()
.for_each(|(i, out_row)| {
for j in 0..output_cols {
let mut sum = T::default();
for ki in 0..kernel.rows {
for kj in 0..kernel.cols {
let mi = mirror_index(i as i32 + ki as i32 - pad_rows as i32, self.rows);
let mj = mirror_index(j as i32 + kj as i32 - pad_cols as i32, self.cols);
let matrix_idx = mi * self.cols + mj;
let kernel_idx = ki * kernel.cols + kj;
sum = sum + self.data[matrix_idx] * kernel.data[kernel_idx];
}
}
out_row[j] = sum;
}
});
Matrix::new(result_data, output_rows, output_cols)
}
pub(crate) fn data_as_string(&self) -> String {
self.data
.iter()
.map(|x| format!("{x}"))
.collect::<Vec<_>>()
.join(" ")
}
pub fn set_lambda(&mut self, lambda: T) {
for i in 0..self.rows {
self.data[i * self.cols + i] = self.data[i * self.cols + i] - lambda;
}
}
}
impl<T: Num> Index<[usize; 2]> for Matrix<T> {
type Output = T;
fn index(&self, index: [usize; 2]) -> &Self::Output {
let [i, j] = index;
if i >= self.rows || j >= self.cols {
panic!(
"!!!Matrix index out of bounds!!! Got [{i}, {j}] but excepted less than [{}, {}]",
self.rows, self.cols
);
}
&self.data[(self.cols * i) + j]
}
}
impl<T: Num> IndexMut<[usize; 2]> for Matrix<T> {
fn index_mut(&mut self, index: [usize; 2]) -> &mut Self::Output {
let [i, j] = index;
assert!(
i < self.rows && j < self.cols,
"!!!Matrix index out of bounds: i = {}, j = {}, rows = {}, cols = {}!!!",
i,
j,
self.rows,
self.cols
);
&mut self.data[(self.cols * i) + j]
}
}
impl<T: Num> From<Vec<Vec<T>>> for Matrix<T> {
fn from(value: Vec<Vec<T>>) -> Self {
let rows = value.len();
let cols = value.first().map_or(0, |row| row.len());
for row in value.iter().skip(1) {
assert_eq!(row.len(), cols, "!!!All columns must be equal!!!");
}
assert!(
!(rows != 0 && cols == 0),
"!!!Invalid matrix dimensions. Multiple empty rows!!!"
);
let data = value.into_iter().flatten().collect();
Self::new(data, rows, cols)
}
}
impl<T: Num> From<Vec<T>> for Matrix<T> {
fn from(value: Vec<T>) -> Self {
Self {
data: value.clone(),
rows: 1,
cols: value.len(),
}
}
}
impl<T: Num> From<Vector<T>> for Matrix<T> {
fn from(value: Vector<T>) -> Self {
let vector: Vec<T> = value.into();
Self {
data: vector.clone(),
rows: 1,
cols: vector.len(),
}
}
}
impl<T: Num> From<Tensor<T>> for Matrix<T> {
fn from(value: Tensor<T>) -> Self {
assert_eq!(value.shape.len(), 2, "Shape size must be 2");
Self {
data: value.data,
rows: value.shape[0],
cols: value.shape[1],
}
}
}
impl<T: Num> From<Vec<Vector<T>>> for Matrix<T> {
fn from(value: Vec<Vector<T>>) -> Self {
let mut vector: Vec<Vec<T>> = vec![];
for i in value {
vector.push(i.into());
}
Self::from(vector)
}
}
impl<T: Num> Display for Matrix<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut vectors = vec![];
for i in 0..self.rows {
vectors.push(self.get_row(i));
}
let vectors = vectors
.iter()
.map(|x| format!("{x}"))
.collect::<Vec<_>>()
.join(",\n");
write!(f, "[{}]", vectors)
}
}
impl<T: Num> Clone for Matrix<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
rows: self.rows,
cols: self.cols,
}
}
}
impl<T: Float> Matrix<T> {
pub fn randn(row: usize, col: usize) -> Self
where
Standard: Distribution<T>,
{
Self {
data: vec![T::default(); row * col]
.iter()
.map(|_| {
(-T::from(2) * random::<T>().ln()).sqrt() * (T::from(2) * T::pi() * random::<T>()).cos()
})
.collect(),
rows: row,
cols: col,
}
}
pub fn ln(&self) -> Self {
self.map(|x| x.ln())
}
pub fn norm(&self, p: T) -> T {
assert!(p >= T::one(), "!!!Number p:{} must be positive!!!", p);
let mut norm = T::default();
if p == T::one() {
let mut max_num = self.get_col(0).abs_sum();
for i in 1..self.cols {
let sum = self.get_col(i).abs_sum();
max_num = if sum > max_num { sum } else { max_num };
}
max_num
} else if p == T::from(2) {
let mut sum_of_squares = T::default();
for x in &self.data {
sum_of_squares += x.powf(T::from(2));
}
return sum_of_squares.sqrt();
} else {
for i in &self.data {
norm += i.powf(p);
}
norm.powf(T::one() / p)
}
}
pub fn norm_inf(self) -> T {
let mut max_num = self.get_row(0).abs_sum();
for i in 1..self.rows {
let sum = self.get_row(i).abs_sum();
max_num = if sum > max_num { sum } else { max_num };
}
max_num
}
pub fn det(&self) -> T {
assert_eq!(
self.rows, self.cols,
"!!!The determinant is defined only for square matrices!!!"
);
let mut matrix = self.clone();
let mut det = 1.into();
for i in 0..self.rows {
for j in (i + 1)..self.rows {
let coefficient = matrix[[j, i]] / matrix[[i, i]];
for k in i..self.rows {
matrix[[j, k]] = matrix[[j, k]] - coefficient * matrix[[i, k]];
}
}
det = det * matrix[[i, i]];
}
det
}
pub fn inv(&self) -> Result<Matrix<T>, &'static str> {
if self.rows != self.cols {
return Err("Matrix is not invertible. Matrix must be square.");
}
let n = self.rows;
let mut augmented_matrix = self.clone();
let mut inv_matrix = Matrix::identity(T::default(), self.rows, self.rows);
for k in 0..n {
let diagonal = augmented_matrix.data[k * n + k];
if diagonal == T::default() {
return Err("Matrix is singular.");
}
for j in 0..n {
augmented_matrix.data[k * n + j] = augmented_matrix.data[k * n + j] / diagonal;
inv_matrix.data[k * n + j] = inv_matrix.data[k * n + j] / diagonal;
}
for i in 0..n {
if i == k {
continue;
}
let factor = augmented_matrix.data[i * n + k];
for j in 0..n {
let help_aug = augmented_matrix.data[k * n + j];
augmented_matrix.data[i * n + j] -= factor * help_aug;
let help_inv = inv_matrix.data[k * n + j];
inv_matrix.data[i * n + j] -= factor * help_inv;
}
}
}
Ok(inv_matrix)
}
pub fn eig(&self) -> Vector<T> {
assert_eq!(self.rows, self.cols, "!!!Matrix must be square.!!!");
let n= self.rows;
let mut eigen_value = Vec::with_capacity(n);
for i in 0..n {
let lambda = T::from_usize(i + 1);
let mut matrix_with_lambda = self.clone();
matrix_with_lambda.set_lambda(lambda);
let det = matrix_with_lambda.det();
eigen_value.push(det);
}
Vector::from(eigen_value)
}
pub fn eig_vectors(&self) -> Matrix<T> {
Matrix::identity(T::one(), self.rows, self.cols)
}
fn diagonalize(&self) -> Option<(Matrix<T>, Vector<T>, Matrix<T>)> {
assert_eq!(self.rows, self.cols, "Matrix must be square.");
let _n = self.rows;
let eigen_values = self.eig();
let p_matrix = self.eig_vectors();
if let Ok(p_inv) = p_matrix.inv() {
Some((p_matrix.clone(), eigen_values, p_inv))
} else {
None
}
}
pub fn powf(&self, exp: T) -> Matrix<T> {
assert_eq!(self.rows, self.cols, "!!!Matrix must be square.!!!");
let n = self.rows;
if exp == T::default() {
return Matrix::identity(T::one(), n, n);
}
if let Some((p, d, p_inv)) = self.diagonalize() {
let _d_some = d.map_vec(|x| x.powf(exp));
&p * &(Matrix::from_diag(d, n, n) * &p_inv)
} else {
matrix![[T::default()]]
}
}
}
#[cfg(test)]
mod tests {
use crate::linalg::matrix::*;
use crate::{vector, DataType};
use std::time::Instant;
#[test]
fn from_fn() {
let matrix = Matrix::<i32>::from_fn(3, 3, |i, j| (i * 10 + j) as i32);
assert_eq!(matrix.data, vec![0, 1, 2, 10, 11, 12, 20, 21, 22]);
}
#[test]
fn mat_and_scalar() {
let a = matrix![[1.0]];
let b = matrix![[1.0, 2.0], [3.0, 4.0]];
println!("{}", b * &a);
}
#[test]
fn mat_and_num() {
let a = matrix![[1, 2], [1, 2]];
let b = matrix![[2, 1], [3, 4]];
println!("{}", 1 * &a * &b);
}
#[test]
fn calc_num_test() {
let a = matrix![[1]];
let b = 10;
assert_eq!(b * a, matrix![[10]]);
let a = matrix![[1]];
let b = 10;
assert_eq!(b - a, matrix![[9]]);
let a = matrix![[1]];
let b = 10;
assert_eq!(b + a, matrix![[11]]);
}
#[test]
fn mul_test() {
let a = matrix![[1, 2, 3], [4, 5, 6]];
let b = matrix![[1, 2], [3, 4], [5, 6]];
let ans = matrix![[22, 28], [49, 64]];
assert_eq!(ans, a.clone() * &b);
let ans = matrix![[9, 12, 15], [19, 26, 33], [29, 40, 51]];
assert_eq!(ans, b * &a);
let a = matrix![[0, -1], [1, 0]];
let b = matrix![[0, 1], [-1, 0]];
assert_eq!(Matrix::identity(DataType::i32(), 2, 2), a * &b)
}
#[test]
fn mul_max_vector() {
let a = matrix![[0, 0, 0], [1, 1, 1], [2, 2, 2]];
let b = vector![1, 2, 3];
assert_eq!(vector![0, 6, 12], a * &b);
}
#[test]
fn resize_matrix() {
let a = matrix![[1.0, 1.0], [2.0, 3.0]];
let a_bigger = matrix![[1.0, 1.0, 0.0], [2.0, 3.0, 0.0]];
let a_less = matrix![[1.0]];
assert_eq!(a.get_resize(2, 3), a_bigger);
assert_eq!(a.get_resize(1, 1), a_less);
}
#[test]
fn parallel_computation() {
let num = 2usize;
let a = Matrix::identity(DataType::i16(), num, num);
let b = Matrix::from_num(1i16, num, num);
let start_time = Instant::now();
let _ans = a * &b;
let elapsed_time = start_time.elapsed();
println!("Time: {} micros", elapsed_time.as_micros());
let num = 2usize;
let mut a = Matrix::identity(DataType::i16(), num, num);
let b = Matrix::from_num(10i16, num, num);
let start_time = Instant::now();
a *= &b;
let elapsed_time = start_time.elapsed();
println!("Time: {} micros", elapsed_time.as_micros());
}
#[test]
fn det_test() {
let a = matrix![[3.0, 7.0], [1.0, -4.0]];
assert_eq!(-19.0, a.det());
}
#[test]
fn inv_matrix() {
let a = matrix![[1.0, 2.0], [3.0, 4.0]];
let b = a.inv().unwrap();
let single = Matrix::identity(DataType::f64(), 2, 2);
assert_eq!(single, a * &b);
}
#[test]
fn inv_error() {
let a = Matrix::from_num(3.0, 1, 2);
assert!(a.inv().is_err());
}
#[test]
fn add_col() {
let mut a = matrix![[1, 2, 3], [3, 4, 5]];
let column = vec![7, 8];
a.add_column(column);
let right = matrix![[1, 2, 3, 7], [3, 4, 5, 8]];
assert_eq!(a, right);
}
#[test]
fn add_row() {
let mut a = matrix![[1, 2, 3], [4, 5, 6]];
let row = vec![7, 8, 9];
a.add_row(row);
let right = matrix![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
assert_eq!(a, right);
}
#[test]
#[should_panic]
fn add_col_err() {
let mut a = matrix![[1, 2, 3], [3, 4, 5]];
let column = vec![7, 8, 9];
a.add_column(column);
}
#[test]
#[should_panic]
fn add_row_err() {
let mut a = matrix![[1, 2, 3], [4, 5, 6]];
let row = vec![7, 8, 9, 10];
a.add_row(row);
}
#[test]
fn one_multi() {
let one = Matrix::identity(1.0, 3, 3);
let some = matrix![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
let same = matrix![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
assert_eq!(some * &one, same);
}
#[test]
fn matrix_norm_one() {
let a = matrix![[-3.0, 5.0, 7.0], [2.0, 6.0, 4.0], [0.0, 2.0, 8.0]];
assert_eq!(19.0, a.norm(1.0));
}
#[test]
fn matrix_norm_inf() {
let a = matrix![[-3.0, 5.0, 7.0], [2.0, 6.0, 4.0], [0.0, 2.0, 8.0]];
assert_eq!(15.0, a.norm_inf());
}
#[test]
fn matrix_norm_swap() {
let a = matrix![[1.0], [3.0]];
assert_eq!(4.0, a.norm(1.0));
}
#[test]
fn matrix_norm_inf_swap() {
let a = matrix![[1.0, 3.0]];
assert_eq!(4.0, a.norm_inf());
}
#[test]
fn macro_test() {
let a = matrix!([1, 2, 3], [1, 2, 3], [1, 2, 3]);
let b = Matrix::new(vec![1, 2, 3, 1, 2, 3, 1, 2, 3], 3, 3);
assert_eq!(b, a);
}
#[test]
fn create_matrix() {
let _a = Matrix::from_num(10, 2, 2);
}
#[test]
fn sum_matrix() {
let a = matrix![[1.0, 1.0], [1.0, 1.0]];
assert_eq!(4.0, a.sum());
}
#[test]
fn single_matrix() {
let a: Matrix<f64> = Matrix::identity(1.0, 2, 2);
let b = matrix![[1.0, 0.0], [0.0, 1.0]];
assert_eq!(a, b);
}
#[test]
fn from_vector() {
let a = Vector::from_num(5, 5);
let b = Matrix::from(a);
let a = matrix!([5, 5, 5, 5, 5]);
assert_eq!(a, b);
}
#[test]
fn get_cols() {
let a = matrix![[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]];
let b = Vector::from_num(4, 3);
assert_eq!(b, a.get_col(3));
}
#[test]
fn get_rows() {
let a = matrix![[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]];
let b = Vector::from(vec![1, 2, 3, 4]);
assert_eq!(a.get_row(2), b)
}
#[test]
fn transpose_matrix() {
let a = Matrix::identity(1, 3, 3);
let a = a.transpose();
let b = Matrix::identity(1, 3, 3);
assert_eq!(a, b);
}
#[test]
fn add_matrix() {
let a = Matrix::from_num(1, 1, 1);
let a1 = Matrix::from_num(2, 1, 1);
let ans = Matrix::from_num(3, 1, 1);
assert_eq!(ans, a + &a1);
}
#[test]
fn add_matrix_and_num() {
let ans = Matrix::from_num(3, 1, 1);
let b = Matrix::from_num(2, 1, 1);
assert_eq!(ans, b + 1);
}
#[test]
fn mul_matrix() {
let a = Matrix::new(vec![0, 1, -1, 0], 2, 2);
let a1 = Matrix::new(vec![0, 1, -1, 0], 2, 2);
let ans = Matrix::new(vec![-1, 0, 0, -1], 2, 2);
assert_eq!(ans, a * &a1);
}
#[test]
fn sub_matrix() {
let a = matrix![[1.0, 2.0]];
let b = matrix![[1.0, 2.0]];
let ans = matrix![[0.0, 0.0]];
assert_eq!(a - &b, ans);
}
#[test]
fn test_mul_f64() {
let a = matrix![
[5.0, 2.0, 1.0, 9.0],
[0.0, 3.0, 11.0, 17.0],
[5.0, 8.0, 2.0, 3.0],
];
let b = matrix![
[1.0, 3.0, 5.0],
[-2.0, 1.0, 3.0],
[-3.0, 1.0, 3.0],
[0.0, 2.0, 2.0],
];
let mul = matrix![[-2.0, 36.0, 52.0], [-39.0, 48.0, 76.0], [-17.0, 31.0, 61.0]];
assert_eq!(a * &b, mul);
}
#[test]
fn mul_many_times() {
let mut a = matrix![[2, 0], [0, 2]];
let b = matrix![[2, 0], [0, 2]];
a = a * &b;
a = a * &b;
assert_eq!(b * 2 * 2, a);
}
#[test]
fn sub_many_matrix() {
let mut a = matrix![[2.0, 2.0]];
let b = matrix![[1.0, 1.0]];
let ans = matrix![[0.0, 0.0]];
a = a - &b;
a = a - &b;
assert_eq!(ans, a);
}
#[test]
fn add_assign_many_times() {
let mut a = Matrix::from_num(0.0, 1, 2);
let mut b = a.clone();
b += 1.0;
b += 1f64;
a += &b;
a += &b;
let answer = matrix![[4.0, 4.0]];
assert_eq!(a, answer)
}
#[test]
fn sub_assign_many_times() {
let mut a = Matrix::from_num(2.0, 1, 2);
let mut b = a.clone();
b -= 0.5;
b -= 0.5f64;
a -= &b;
a -= &b;
let answer = matrix![[0.0, 0.0]];
assert_eq!(a, answer)
}
#[test]
fn mul_assign_many_times() {
let mut one = matrix![[1.0, 0.0], [0.0, 1.0]];
let mut imaginary = matrix![[0.0, 1.0], [1.0, 0.0]];
imaginary *= -1.0;
imaginary *= -1.0;
one *= &imaginary;
one *= &imaginary;
let answer = Matrix::identity(DataType::f64(), 2, 2);
assert_eq!(one, answer);
}
#[test]
fn math_help() {
let mut a = matrix![[9.0, -3.0, 1.0], [4.0, -2.0, 1.0], [16.0, -4.0, 1.0]];
let b = matrix![[-5.0], [-4.0], [-4.0]];
a = a.inv().unwrap();
println!("{}", a);
println!("{}", a * &b);
}
#[test]
fn cramer_test() {
let a = matrix![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 1.0]];
let ans = matrix![[14.0, 32.0, 50.0]].transpose();
let det = a.det();
let deters = vec![
matrix![[14.0, 2.0, 3.0], [32.0, 5.0, 6.0], [50.0, 8.0, 1.0]].det(),
matrix![[1.0, 14.0, 3.0], [4.0, 32.0, 6.0], [7.0, 50.0, 1.0]].det(),
matrix![[1.0, 2.0, 14.0], [4.0, 5.0, 32.0], [7.0, 8.0, 50.0]].det(),
];
let mut b = Vector::from_num(0f64, 3);
for i in 0..deters.len() {
let b_int = (deters[i] / det) as i32;
b[i] = b_int as f64;
}
assert_eq!(b, (a.inv().unwrap() * &ans).get_col(0));
}
#[test]
fn sum_of_rows() {
let a = matrix![[1, 2, 3], [4, 5, 6]];
println!("{}", a.sum_rows());
}
#[test]
fn comparisons() {
let a = matrix![[-1, 0, 1]];
assert_eq!(a.compare(Matrix::from_num(0, 1, 3)), matrix![[-1, 0, 1]]);
}
#[test]
fn comparisons_num() {
let a = matrix![[-1, 0, 1]];
assert_eq!(a.compare_num(0), matrix![[-1, 0, 1]])
}
#[test]
fn map_test() {
let a = matrix![[1, 2], [3, 4]];
let b = a.map(|x| x % 2);
println!("{b}");
}
#[test]
fn num_mat_mul() {
let a = matrix![[1.0, 2.0, 3.0]];
let b = matrix![[2.0, 4.0, 6.0]];
assert_eq!(2.0 * &a, b);
assert_eq!(a * 2.0, b);
}
#[test]
fn randn() {
let a: Matrix<f64> = Matrix::randn(2, 2);
println!("{a}");
}
#[test]
fn rem_col() {
let a = matrix![[1, 2, 3], [4, 5, 6]];
println!("{}", a.rem_col(2));
}
#[test]
fn mul_a() {
let mut a = matrix![[1.0, 2.0], [3.0, 4.0]];
let b = Matrix::from_num(1.0, 2, 3);
a *= &b;
println!("{a}");
}
#[test]
fn diag_test() {
let r = 3;
let c = 1;
let a = Matrix::new((0..(r*c) as i32).collect(), r, c);
println!("{a}");
println!("{}", a.diag());
}
#[test]
fn diag_mx() {
let v = vector![1, 2, 3];
let b = Matrix::from_diag(v, 4, 3);
println!("{b}");
}
#[test]
fn sub_mx() {
let a = matrix![[1,2,3]];
println!("{}", 1 - a);
}
#[test]
fn div_mx() {
let mut a = matrix![[1.0,2.0,4.0]];
println!("{}", &a / 2.0);
println!("{}", 2.0 / &a);
a /= 2.0;
println!("{a}");
}
#[test]
fn test_conv_3x3_identity_kernel() {
let matrix = matrix![
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0]
];
let kernel = matrix![[1.0]];
let result = matrix.conv(&kernel);
assert_eq!(result.rows, 3);
assert_eq!(result.cols, 3);
assert_eq!(result.data, matrix.data);
}
#[test]
fn test_conv_edge_detection_sobel() {
let matrix = matrix![
[1.0, 1.0, 1.0, 0.0, 0.0],
[1.0, 1.0, 1.0, 0.0, 0.0],
[1.0, 1.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]
];
let sobel_x = matrix![
[-1.0, 0.0, 1.0],
[-2.0, 0.0, 2.0],
[-1.0, 0.0, 1.0]
];
let result = matrix.conv(&sobel_x);
println!("Sobel X result:");
println!("{}", result);
assert_eq!(result.rows, 3);
assert_eq!(result.cols, 3);
assert_eq!(result.data[1 * result.cols + 1], 0.0);
}
}