use core::ops::{Index, IndexMut, Neg, Add, Sub, Mul, Div};
use core::ops::{AddAssign, SubAssign, MulAssign, DivAssign};
use std::{fmt, fs::File, io::Write, mem};
pub use crate::traits::{Number, Signed, Zero, One};
pub use crate::complex::Complex;
pub use crate::vector::{Vector, Vec64};
pub struct Matrix<T> {
mat: Vec< Vector<T> >,
rows: usize,
cols: usize,
}
pub type Mat64 = Matrix<f64>;
impl<T> Matrix<T> {
#[inline]
pub fn empty() -> Self {
let row = Vector::<T>::empty();
let mut mat = Vec::new();
mat.push( row );
let rows = 0;
let cols = 0;
Matrix { mat, rows, cols }
}
#[inline]
pub fn create( mat: Vec< Vector<T> > ) -> Self {
let rows = mat.len();
let cols = mat[0].size();
Matrix { mat, rows, cols }
}
#[inline]
pub fn rows(&self) -> usize {
self.rows
}
#[inline]
pub fn cols(&self) -> usize {
self.cols
}
#[inline]
pub fn numel(&self) -> usize {
self.cols * self.rows
}
#[inline]
pub fn clear(&mut self) {
self.mat.clear();
self.rows = 0;
self.cols = 0;
}
}
impl<T: Clone + Number> Matrix<T> {
#[inline]
pub fn new( rows: usize, cols: usize, elem: T ) -> Self {
let row = Vector::<T>::new( cols, elem );
let mut mat = Vec::new();
for _i in 0..rows {
mat.push( row.clone() );
}
Matrix { mat, rows, cols }
}
#[inline]
pub fn get_row(&self, row: usize ) -> Vector<T> {
self.mat[ row ].clone()
}
#[inline]
pub fn get_col(&self, col: usize ) -> Vector<T> {
let mut result = Vector::<T>::new( self.rows, T::zero() );
for i in 0..self.rows {
result[ i ] = self[i][col].clone()
}
result
}
#[inline]
pub fn set_row(&mut self, row: usize, vec: Vector<T> ) {
self[ row ] = vec;
}
#[inline]
pub fn set_col(&mut self, col: usize, vec: Vector<T> ) {
for i in 0..self.rows {
self[i][col] = vec[i].clone();
}
}
#[inline]
pub fn delete_row(&mut self, row: usize ) {
self.mat.remove( row );
self.rows -= 1;
}
#[inline]
pub fn multiply(&self, vec: Vector<T> ) -> Vector<T> {
if vec.size() != self.cols { panic!( "Matrix dimensions do not agree in multiply." ); }
let mut result = Vector::<T>::empty();
for row in 0..self.rows {
result.push( self[row].dot( vec.clone() ) );
}
result
}
#[inline]
pub fn eye( size: usize ) -> Self {
let mut identity = Matrix::<T>::new( size, size, T::zero() );
for i in 0..size {
identity[i][i] = T::one();
}
identity
}
#[inline]
pub fn resize(&mut self, n_rows: usize, n_cols: usize ) {
let temp = self.clone();
*self = Matrix::<T>::new( n_rows, n_cols, T::zero() );
for i in 0..n_rows {
for j in 0..n_cols {
if i < temp.rows() && j < temp.cols() {
self[i][j] = temp[i][j].clone();
}
}
}
}
#[inline]
pub fn transpose_in_place(&mut self) {
if self.rows == self.cols {
for i in 0..self.rows {
for j in i+1..self.cols {
let mut temp = self[i][j].clone();
mem::swap( &mut self[j][i], &mut temp );
self[i][j] = temp;
}
}
} else {
let row = Vector::<T>::new( self.rows, T::zero() );
let mut temp = Vec::new();
for _i in 0..self.cols {
temp.push( row.clone() );
}
for i in 0..self.rows {
for j in 0..self.cols {
temp[j][i] = self[i][j].clone();
}
}
self.mat = temp;
mem::swap( &mut self.rows, &mut self.cols );
}
}
#[inline]
pub fn transpose(&self) -> Matrix<T> {
let mut temp: Matrix<T> = self.clone();
temp.transpose_in_place();
temp
}
#[inline]
pub fn swap_rows(&mut self, row_1: usize, row_2: usize ) {
if self.rows <= row_1 || self.rows <= row_2 {
panic!( "Matrix swap row range error." );
}
let mut temp = self.mat[ row_1 ].clone();
mem::swap( &mut self.mat[ row_2 ], &mut temp );
self.mat[ row_1 ] = temp;
}
#[inline]
pub fn swap_elem(&mut self, row_1: usize, col_1: usize, row_2: usize, col_2: usize ) {
let mut temp = self[row_1][col_1].clone();
mem::swap( &mut self[row_2][col_2], &mut temp );
self[row_1][col_1] = temp;
}
#[inline]
pub fn fill(&mut self, elem: T ) {
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] = elem.clone();
}
}
}
#[inline]
pub fn fill_diag(&mut self, elem: T ) {
let n: usize = if self.cols < self.rows { self.cols } else { self.rows };
for i in 0..n {
self[i][i] = elem.clone();
}
}
#[inline]
pub fn fill_band(&mut self, offset: isize, elem: T ) {
for row in 0..self.rows {
let i = (row as isize) + offset;
if (i as usize) < self.cols && i >= 0 {
self[ row ][ i as usize ] = elem.clone();
}
}
}
#[inline]
pub fn fill_tridiag(&mut self, lower: T, diag: T, upper: T ) {
self.fill_band( -1, lower );
self.fill_diag( diag );
self.fill_band( 1, upper );
}
}
impl<T: Clone + Copy + Number + Signed + std::cmp::PartialOrd> Matrix<T> {
#[inline]
fn max_abs_in_column(&self, col: usize, start_row: usize) -> usize {
let mut max_index: usize = 0;
let mut max = T::zero();
for i in start_row..self.rows {
if max < self[ i ][ col ].abs() {
max = self[ i ][ col ].abs();
max_index = i;
}
}
max_index
}
#[inline]
fn backsolve(&self, x: &mut Vector<T> ) {
let last = self.rows - 1;
x[ last ] = x[ last ]/ self[ last ][ last ];
for n in 2..self.rows+1 {
let k = self.rows - n;
for j in self.rows-n+1..self.rows {
let xj = x[ j ];
x[ k ] -= self[ k ][ j ] * xj;
}
x[ k ] /= self[ k ][ k ];
}
}
#[inline]
fn partial_pivot(&mut self, x: &mut Vector<T>, k: usize ) {
let pivot: usize = self.max_abs_in_column( k, k );
self.swap_rows( pivot, k );
x.swap( pivot, k );
}
#[inline]
fn gauss_with_pivot(&mut self, x: &mut Vector<T> ){
for k in 0..self.rows-1 {
self.partial_pivot( x, k );
for i in k+1..self.rows {
let elem = self[ i ][ k ] / self[ k ][ k ];
for j in k..self.rows {
let kj = self[ k ][ j ];
self[ i ][ j ] -= elem * kj;
}
let xk = x[ k ];
x[ i ] -= elem * xk;
}
}
}
#[inline]
pub fn solve_basic(&mut self, b: Vector<T> ) -> Vector<T> {
if self.rows != b.size() { panic!( "solve_basic error: rows != b.size()" ); }
if self.rows != self.cols() {
panic!( "solve_basic error: matrix is not square" ); }
let mut x: Vector<T> = b.clone();
self.gauss_with_pivot( &mut x );
self.backsolve( &mut x );
x
}
#[inline]
pub fn lu_decomp_in_place(&mut self) -> ( usize, Matrix<T> ) {
if self.rows != self.cols() {
panic!( "lu_decomp_in_place error: matrix is not square" ); }
let mut pivots : usize = 0;
let mut permutation = Matrix::<T>::eye( self.rows );
for i in 0..self.rows() {
let mut max_a = T::zero();
let mut imax = i;
for k in i..self.rows() {
let abs_a = self[ k ][ i ].abs();
if abs_a > max_a {
max_a = abs_a;
imax = k;
}
}
if imax != i {
permutation.swap_rows( i, imax );
self.swap_rows( i, imax );
pivots += 1;
}
for j in i+1..self.rows() {
let ii = self[ i ][ i ];
self[ j ][ i ] /= ii;
for k in i+1..self.rows() {
let ji = self[ j ][ i ];
let ik = self[ i ][ k ];
self[ j ][ k ] -= ji * ik;
}
}
}
( pivots, permutation )
}
#[inline]
pub fn solve_lu(&mut self, b: Vector<T> ) -> Vector<T> {
if self.rows != b.size() { panic!( "solve_LU error: rows != b.size()" ); }
if self.rows != self.cols() {
panic!( "solve_LU error: matrix is not square" ); }
let mut x: Vector<T> = b.clone();
let ( _pivots, permutation ) = self.lu_decomp_in_place();
x = permutation * x;
for i in 0..self.rows() {
for k in 0..i {
let xk = x[ k ];
x[ i ] -= self[ i ][ k ] * xk;
}
}
self.backsolve( &mut x );
x
}
#[inline]
pub fn determinant(&self) -> T {
let mut det = T::one();
let mut temp = self.clone();
let ( pivots, _permutation ) = temp.lu_decomp_in_place();
for i in 0..self.rows() {
det *= temp.mat[ i ][ i ];
}
if pivots % 2 == 0 {
det
} else {
- det
}
}
#[inline]
pub fn inverse(&self) -> Matrix<T> {
if self.rows != self.cols() {
panic!( "inverse error: matrix is not square" ); }
let mut lu: Matrix<T> = self.clone();
let ( _pivots, mut inv ) = lu.lu_decomp_in_place();
for j in 0..self.rows() {
for i in 0..self.rows() {
for k in 0..i {
let inv_kj = inv.mat[ k ][ j ];
inv.mat[ i ][ j ] -= lu.mat[ i ][ k ] * inv_kj;
}
}
for i in (0..self.rows()).rev() {
for k in i+1..self.rows() {
let inv_kj = inv.mat[ k ][ j ];
inv.mat[ i ][ j ] -= lu.mat[ i ][ k ] * inv_kj;
}
inv.mat[ i ][ j ] /= lu.mat[ i ][ i ];
}
}
inv
}
}
impl Matrix<f64> {
#[inline]
pub fn norm_1(&self) -> f64 {
let mut result: f64 = 0.0;
for j in 0..self.cols {
let mut sum: f64 = 0.0;
for i in 0..self.rows {
sum += self[i][j].abs()
}
result = result.max( sum )
}
result
}
#[inline]
pub fn norm_inf(&self) -> f64 {
let mut result: f64 = 0.0;
for i in 0..self.rows {
let mut sum: f64 = 0.0;
for j in 0..self.cols {
sum += self[i][j].abs()
}
result = result.max( sum )
}
result
}
#[inline]
pub fn norm_p(&self, p: f64 ) -> f64 {
let mut sum: f64 = 0.0;
for i in 0..self.rows {
for j in 0..self.cols {
sum += libm::pow( self[i][j].abs(), p );
}
}
libm::pow( sum, 1.0/p )
}
#[inline]
pub fn norm_frob(&self) -> f64 {
self.norm_p( 2.0 )
}
#[inline]
pub fn norm_max(&self) -> f64 {
let mut result: f64 = 0.0;
for i in 0..self.rows {
for j in 0..self.cols {
result = result.max( self[i][j].abs() );
}
}
result
}
#[inline]
pub fn jacobian( point: Vec64, func: &dyn Fn(Vec64) -> Vec64, delta: f64 ) -> Self {
let n = point.size();
let f = func( point.clone() );
let m = f.size();
let mut state = point.clone();
let mut jac = Mat64::new( m, n, 0.0 );
for i in 0..n {
state[i] += delta;
let f_new = func( state.clone() );
state[i] -= delta;
jac.set_col( i, ( f_new - f.clone() ) / delta );
}
jac
}
}
impl<T: Clone> Clone for Matrix<T> {
#[inline]
fn clone(&self) -> Self {
Self::create( self.mat.clone() )
}
}
impl<T> Index<usize> for Matrix<T> {
type Output = Vector<T>;
#[inline]
fn index<'a>(&'a self, index: usize ) -> &'a Vector<T> {
&self.mat[ index ]
}
}
impl<T> IndexMut<usize> for Matrix<T> {
#[inline]
fn index_mut(&mut self, index: usize ) -> &mut Vector<T> {
&mut self.mat[ index ]
}
}
impl<T: Clone + Neg<Output = T>> Neg for Matrix<T> {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
let mut result = self.clone();
for i in 0..result.rows() {
for j in 0..result.cols() {
result[i][j] = -result[i][j].clone();
}
}
result
}
}
impl<T: Clone + Number> Add<Matrix<T>> for Matrix<T> {
type Output = Self;
#[inline]
fn add(self, plus: Self) -> Self::Output {
if self.rows != plus.rows { panic!( "Matrix row dimensions do not agree (+)." ); }
if self.cols != plus.cols { panic!( "Matrix col dimensions do not agree (+)." ); }
let mut result = Matrix::<T>::new( self.rows(), self.cols(), T::zero() );
for i in 0..result.rows() {
for j in 0..result.cols() {
result[i][j] = self[i][j].clone() + plus[i][j].clone();
}
}
result
}
}
impl<T: Clone + Number> Sub<Matrix<T>> for Matrix<T> {
type Output = Self;
#[inline]
fn sub(self, minus: Self) -> Self::Output {
if self.rows != minus.rows { panic!( "Matrix row dimensions do not agree (-)." ); }
if self.cols != minus.cols { panic!( "Matrix col dimensions do not agree (-)." ); }
let mut result = Matrix::<T>::new( self.rows(), self.cols(), T::zero() );
for i in 0..result.rows() {
for j in 0..result.cols() {
result[i][j] = self[i][j].clone() - minus[i][j].clone();
}
}
result
}
}
impl<T: Clone + Number> Mul<T> for Matrix<T> {
type Output = Self;
#[inline]
fn mul(self, scalar: T) -> Self::Output {
let mut result = Matrix::<T>::new( self.rows(), self.cols(), T::zero() );
for i in 0..result.rows() {
for j in 0..result.cols() {
result[i][j] = self[i][j].clone() * scalar.clone();
}
}
result
}
}
impl Mul<Matrix<f64>> for f64 {
type Output = Matrix<f64>;
#[inline]
fn mul(self, matrix: Matrix<f64>) -> Self::Output {
let mut result = Matrix::<f64>::new( matrix.rows(), matrix.cols(), 0.0 );
for i in 0..result.rows() {
for j in 0..result.cols() {
result[i][j] = matrix[i][j].clone() * self.clone();
}
}
result
}
}
impl<T: Clone + Number> Div<T> for Matrix<T> {
type Output = Self;
fn div(self, scalar: T) -> Self::Output {
let mut result = Matrix::<T>::new( self.rows(), self.cols(), T::zero() );
for i in 0..result.rows() {
for j in 0..result.cols() {
result[i][j] = self[i][j].clone() / scalar.clone();
}
}
result
}
}
impl<T: Clone + Number> AddAssign for Matrix<T> {
fn add_assign(&mut self, rhs: Self) {
if self.rows != rhs.rows { panic!( "Matrix row dimensions do not agree (+=)." ); }
if self.cols != rhs.cols { panic!( "Matrix col dimensions do not agree (+=)." ); }
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] += rhs[i][j].clone();
}
}
}
}
impl<T: Clone + Number> SubAssign for Matrix<T> {
fn sub_assign(&mut self, rhs: Self) {
if self.rows != rhs.rows { panic!( "Matrix row dimensions do not agree (-=)." ); }
if self.cols != rhs.cols { panic!( "Matrix col dimensions do not agree (-=)." ); }
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] -= rhs[i][j].clone();
}
}
}
}
impl<T: Clone + Number> MulAssign<T> for Matrix<T> {
fn mul_assign(&mut self, rhs: T) {
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] *= rhs.clone();
}
}
}
}
impl<T: Clone + Number> DivAssign<T> for Matrix<T> {
fn div_assign(&mut self, rhs: T) {
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] /= rhs.clone();
}
}
}
}
impl<T: Clone + Number> AddAssign<T> for Matrix<T> {
fn add_assign(&mut self, rhs: T) {
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] += rhs.clone();
}
}
}
}
impl<T: Clone + Number> SubAssign<T> for Matrix<T> {
fn sub_assign(&mut self, rhs: T) {
for i in 0..self.rows {
for j in 0..self.cols {
self[i][j] -= rhs.clone();
}
}
}
}
impl<T: Clone + Number> Mul<Matrix<T>> for Matrix<T> {
type Output = Self;
#[inline]
fn mul(self, mul: Self) -> Self::Output {
if self.cols != mul.rows { panic!( "Matrix dimensions do not agree (*)." ); }
let mut result = Matrix::<T>::new( self.rows(), mul.cols(), T::zero() );
for col in 0..mul.cols() {
result.set_col( col, self.multiply( mul.get_col( col ) ) );
}
result
}
}
impl<T: Clone + Number> Mul<Vector<T>> for Matrix<T> {
type Output = Vector<T>;
#[inline]
fn mul(self, vec: Vector<T> ) -> Vector<T> {
self.multiply( vec )
}
}
impl<T> fmt::Debug for Matrix<T> where
T: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for i in 0..self.rows-1 {
writeln!(f, "\t{:?}", self.mat[i] ).unwrap();
}
write!(f, "\t{:?}", self.mat[self.rows-1] )
}
}
impl<T> fmt::Display for Matrix<T> where
T: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for i in 0..self.rows-1 {
writeln!(f, "\t{:?}", self.mat[i] ).unwrap();
}
write!(f, "\t{:?}", self.mat[self.rows-1] )
}
}
impl<T: fmt::Display> Matrix<T> {
#[inline]
pub fn output(&self, filename: &str) {
let mut f = File::create(filename).expect("Unable to create file");
for i in 0..self.rows {
for j in 0..self.cols {
write!(f, "\t{}", self[i][j] ).unwrap();
}
writeln!(f, "").unwrap();
}
}
}