use core::marker::PhantomData;
use crate::math::scalar::{Scalar, ScalarCastError};
use crate::math::tensor::rank_n::{Dense, Sparse, Tensor};
use rayon::prelude::*;
use super::matrix_backend_trait::MatrixBackend;
#[derive(Debug, Clone)]
pub struct RankNDense<T: Scalar> {
tensor: Tensor<T, Dense>,
}
#[derive(Debug, Clone)]
pub struct RankNSparse<T: Scalar> {
tensor: Tensor<T, Sparse>,
}
#[derive(Debug, Clone)]
pub struct Matrix<T: Scalar, B: MatrixBackend<T> = RankNDense<T>> {
backend: B,
_scalar: PhantomData<T>,
}
pub type DenseMatrix<T> = Matrix<T, RankNDense<T>>;
pub type SparseMatrix<T> = Matrix<T, RankNSparse<T>>;
impl<T: Scalar, B: MatrixBackend<T>> Matrix<T, B> {
#[inline]
pub(crate) fn from_backend(backend: B) -> Self {
Self {
backend,
_scalar: PhantomData,
}
}
#[inline]
pub fn empty(rows: usize, cols: usize) -> Self {
Self::from_backend(B::empty(rows, cols))
}
#[inline]
pub fn zeros(rows: usize, cols: usize) -> Self {
Self::empty(rows, cols)
}
#[inline]
pub fn rows(&self) -> usize {
self.backend.rows()
}
#[inline]
pub fn cols(&self) -> usize {
self.backend.cols()
}
#[inline]
pub fn shape(&self) -> [usize; 2] {
self.backend.shape()
}
#[inline]
pub fn size(&self) -> usize {
self.backend.size()
}
#[inline]
pub fn get(&self, row: isize, col: isize) -> T {
self.backend.get(row, col)
}
#[inline]
pub fn set(&mut self, row: isize, col: isize, value: T) {
self.backend.set(row, col, value);
}
#[inline]
pub fn fill(&mut self, value: T)
where
T: Copy + Send + Sync,
{
self.backend.fill(value);
}
pub fn print(&self)
where
T: Copy,
{
for row in 0..self.rows() {
for col in 0..self.cols() {
print!("{}\t", self.get(row as isize, col as isize));
}
println!();
}
}
fn to_dense_rank_n_tensor(&self) -> Tensor<T, Dense>
where
T: Copy,
{
Tensor::<T, Dense>::from_fn(&[self.rows(), self.cols()], |idx| self.get(idx[0], idx[1]))
}
fn dense_matrix_from_rank_n(tensor: Tensor<T, Dense>) -> DenseMatrix<T>
where
T: Copy,
{
let rows = tensor.shape()[0];
let cols = tensor.shape()[1];
DenseMatrix::from_fn(rows, cols, |row, col| {
tensor.get(&[row as isize, col as isize])
})
}
fn matrix_from_dense_rank_n_preserving(tensor: &Tensor<T, Dense>) -> Self
where
T: Copy,
{
let rows = tensor.shape()[0];
let cols = tensor.shape()[1];
let entries: Vec<(usize, usize, T)> = (0..rows * cols)
.into_par_iter()
.map(|k| {
let row = k / cols;
let col = k % cols;
(row, col, tensor.get(&[row as isize, col as isize]))
})
.collect();
let mut out = Self::empty(rows, cols);
for (row, col, value) in entries {
out.set(row as isize, col as isize, value);
}
out
}
fn zip_preserving<RhsBackend, F>(&self, rhs: &Matrix<T, RhsBackend>, f: F) -> Self
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
F: Fn(T, T) -> T + Sync + Send,
{
assert_eq!(
self.shape(),
rhs.shape(),
"matrix elementwise operation shape mismatch"
);
let rows = self.rows();
let cols = self.cols();
let entries: Vec<(usize, usize, T)> = (0..rows * cols)
.into_par_iter()
.map(|k| {
let row = k / cols;
let col = k % cols;
let value = f(
self.get(row as isize, col as isize),
rhs.get(row as isize, col as isize),
);
(row, col, value)
})
.collect();
let mut out = Self::empty(rows, cols);
for (row, col, value) in entries {
out.set(row as isize, col as isize, value);
}
out
}
pub fn to_dense_matrix(&self) -> DenseMatrix<T>
where
T: Copy,
{
Self::dense_matrix_from_rank_n(self.to_dense_rank_n_tensor())
}
pub fn add<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> Self
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
self.zip_preserving(rhs, |a, b| a + b)
}
pub fn sub<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> Self
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
self.zip_preserving(rhs, |a, b| a - b)
}
pub fn elem_mul<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> Self
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
self.zip_preserving(rhs, |a, b| a * b)
}
pub fn elem_div<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> Self
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
self.zip_preserving(rhs, |a, b| a / b)
}
pub fn scalar_mul(&self, scalar: T) -> Self
where
T: Copy + Send + Sync,
{
let rows = self.rows();
let cols = self.cols();
let entries: Vec<(usize, usize, T)> = (0..rows * cols)
.into_par_iter()
.map(|k| {
let row = k / cols;
let col = k % cols;
(row, col, self.get(row as isize, col as isize) * scalar)
})
.collect();
let mut out = Self::empty(rows, cols);
for (row, col, value) in entries {
out.set(row as isize, col as isize, value);
}
out
}
pub fn transpose(&self) -> Self
where
T: Copy + Send + Sync,
{
let transposed = self.to_dense_rank_n_tensor().transpose();
Self::matrix_from_dense_rank_n_preserving(&transposed)
}
pub fn hermitian_transpose(&self) -> Self
where
T: Copy + Send + Sync,
{
let transposed = self.to_dense_rank_n_tensor().hermitian_transpose();
Self::matrix_from_dense_rank_n_preserving(&transposed)
}
pub fn matmul<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> Self
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
assert_eq!(
self.cols(),
rhs.rows(),
"matrix multiplication dimension mismatch"
);
let lhs = self.to_dense_rank_n_tensor();
let rhs = rhs.to_dense_rank_n_tensor();
let product = lhs.matmul(&rhs);
Self::matrix_from_dense_rank_n_preserving(&product)
}
pub fn add_to_dense<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> DenseMatrix<T>
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
let lhs = self.to_dense_rank_n_tensor();
let rhs = rhs.to_dense_rank_n_tensor();
Self::dense_matrix_from_rank_n(lhs.zip_with(&rhs, |a, b| a + b))
}
pub fn sub_to_dense<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> DenseMatrix<T>
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
let lhs = self.to_dense_rank_n_tensor();
let rhs = rhs.to_dense_rank_n_tensor();
Self::dense_matrix_from_rank_n(lhs.zip_with(&rhs, |a, b| a - b))
}
pub fn elem_mul_to_dense<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> DenseMatrix<T>
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
let lhs = self.to_dense_rank_n_tensor();
let rhs = rhs.to_dense_rank_n_tensor();
Self::dense_matrix_from_rank_n(lhs.elem_mul(&rhs))
}
pub fn elem_div_to_dense<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> DenseMatrix<T>
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
let lhs = self.to_dense_rank_n_tensor();
let rhs = rhs.to_dense_rank_n_tensor();
Self::dense_matrix_from_rank_n(lhs.elem_div(&rhs))
}
pub fn scalar_mul_to_dense(&self, scalar: T) -> DenseMatrix<T>
where
T: Copy + Send + Sync,
{
Self::dense_matrix_from_rank_n(self.to_dense_rank_n_tensor().scalar_mul(scalar))
}
pub fn transpose_to_dense(&self) -> DenseMatrix<T>
where
T: Copy + Send + Sync,
{
Self::dense_matrix_from_rank_n(self.to_dense_rank_n_tensor().transpose())
}
pub fn hermitian_transpose_to_dense(&self) -> DenseMatrix<T>
where
T: Copy + Send + Sync,
{
Self::dense_matrix_from_rank_n(self.to_dense_rank_n_tensor().hermitian_transpose())
}
pub fn matmul_to_dense<RhsBackend>(&self, rhs: &Matrix<T, RhsBackend>) -> DenseMatrix<T>
where
RhsBackend: MatrixBackend<T>,
T: Copy + Send + Sync,
{
assert_eq!(
self.cols(),
rhs.rows(),
"matrix multiplication dimension mismatch"
);
let lhs = self.to_dense_rank_n_tensor();
let rhs = rhs.to_dense_rank_n_tensor();
Self::dense_matrix_from_rank_n(lhs.matmul(&rhs))
}
pub fn trace(&self) -> T
where
T: Copy,
{
let n = self.rows().min(self.cols());
(0..n).fold(T::zero(), |acc, i| acc + self.get(i as isize, i as isize))
}
}
impl<T: Scalar> Matrix<T, RankNDense<T>> {
#[inline]
pub fn from_vec(rows: usize, cols: usize, data: Vec<T>) -> Self {
assert_eq!(
data.len(),
rows.checked_mul(cols)
.expect("matrix shape product overflow"),
"dense matrix data length mismatch"
);
Self::from_backend(RankNDense {
tensor: Tensor::<T, Dense>::from_vec(&[rows, cols], data),
})
}
pub fn from_fn<F>(rows: usize, cols: usize, mut f: F) -> Self
where
F: FnMut(usize, usize) -> T,
{
let data = (0..rows)
.flat_map(|row| (0..cols).map(move |col| (row, col)))
.map(|(row, col)| f(row, col))
.collect();
Self::from_vec(rows, cols, data)
}
#[inline]
pub fn try_cast_to<U: Scalar>(&self) -> Result<Matrix<U, RankNDense<U>>, ScalarCastError> {
self.backend
.tensor
.try_cast_to::<U>()
.map(|tensor| Matrix::from_backend(RankNDense { tensor }))
}
#[inline]
pub fn cast_to<U: Scalar + Send + Sync>(&self) -> Matrix<U, RankNDense<U>> {
Matrix::from_backend(RankNDense {
tensor: self.backend.tensor.cast_to::<U>(),
})
}
#[inline]
pub fn to_sparse(&self) -> Matrix<T, RankNSparse<T>> {
Matrix::from_backend(RankNSparse {
tensor: self.backend.tensor.to_sparse(),
})
}
}
impl<T: Scalar> Matrix<T, RankNSparse<T>> {
pub fn from_triplets(
rows: usize,
cols: usize,
triplets: impl IntoIterator<Item = (usize, usize, T)>,
) -> Self {
let tensor_triplets = triplets
.into_iter()
.map(|(row, col, value)| (vec![row, col], value));
Self::from_backend(RankNSparse {
tensor: Tensor::<T, Sparse>::from_triplets(vec![rows, cols], tensor_triplets),
})
}
#[inline]
pub fn try_cast_to<U: Scalar>(&self) -> Result<Matrix<U, RankNSparse<U>>, ScalarCastError> {
self.backend
.tensor
.try_cast_to::<U>()
.map(|tensor| Matrix::from_backend(RankNSparse { tensor }))
}
#[inline]
pub fn cast_to<U: Scalar + Send + Sync>(&self) -> Matrix<U, RankNSparse<U>> {
Matrix::from_backend(RankNSparse {
tensor: self.backend.tensor.cast_to::<U>(),
})
}
#[inline]
pub fn to_dense(&self) -> Matrix<T, RankNDense<T>> {
Matrix::from_backend(RankNDense {
tensor: self.backend.tensor.to_dense(),
})
}
#[inline]
pub fn nnz(&self) -> usize {
self.backend.tensor.nnz()
}
}
impl<T: Scalar> MatrixBackend<T> for RankNDense<T> {
#[inline]
fn empty(rows: usize, cols: usize) -> Self {
Self {
tensor: Tensor::<T, Dense>::empty(&[rows, cols]),
}
}
#[inline]
fn rows(&self) -> usize {
self.tensor.shape()[0]
}
#[inline]
fn cols(&self) -> usize {
self.tensor.shape()[1]
}
#[inline]
fn get(&self, row: isize, col: isize) -> T {
self.tensor.get(&[row, col])
}
#[inline]
fn set(&mut self, row: isize, col: isize, value: T) {
self.tensor.set(&[row, col], value);
}
}
impl<T: Scalar> MatrixBackend<T> for RankNSparse<T> {
#[inline]
fn empty(rows: usize, cols: usize) -> Self {
Self {
tensor: Tensor::<T, Sparse>::empty(&[rows, cols]),
}
}
#[inline]
fn rows(&self) -> usize {
self.tensor.shape()[0]
}
#[inline]
fn cols(&self) -> usize {
self.tensor.shape()[1]
}
#[inline]
fn get(&self, row: isize, col: isize) -> T {
self.tensor.get(&[row, col])
}
#[inline]
fn set(&mut self, row: isize, col: isize, value: T) {
self.tensor.set(&[row, col], value);
}
}