use crate::error::{LoadError, LoadErrorKind};
use crate::utils;
use crate::vector;
use csv;
use num::{FromPrimitive, Num};
use rand::distributions::uniform::SampleUniform;
use rand::distributions::{Distribution, Normal, Uniform};
use std::fs::File;
use std::ops;
use std::path::Path;
use std::marker::PhantomData;
#[macro_export]
macro_rules! matrix {
($elem:expr; $shape:expr) => {{
let nrows = $shape[0];
let ncols = $shape[1];
let elements = vec![vec![$elem; ncols]; nrows];
$crate::Matrix::from(elements)
}};
($($x:expr),*) => {{
let elements = vec![vec![$($x),*]];
$crate::Matrix::from(elements)
}};
($($x:expr,)*) => {{
let elements = vec![vec![$($x),*]];
Matrix::from(elements)
}};
($($($x:expr),*;)*) => {{
let elements = vec![$(vec![$($x),*]),*];
Matrix::from(elements)
}};
($($($x:expr),*);*) => {{
let elements = vec![$(vec![$($x),*]),*];
Matrix::from(elements)
}};
}
type RowMatrix<T> = vector::Vector<T>;
#[derive(Debug)]
pub struct Matrix<T> {
nrows: usize,
ncols: usize,
elements: Vec<RowMatrix<T>>,
}
impl<T> Matrix<T> {
pub fn shape(&self) -> [usize; 2] {
[self.nrows, self.ncols]
}
pub fn full(shape: [usize; 2], value: T) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
let nrows = shape[0];
let ncols = shape[1];
let elements = vec![vector![value; ncols]; nrows];
Matrix {
nrows,
ncols,
elements,
}
}
pub fn full_like(m: &Matrix<T>, value: T) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
let nrows = m.nrows;
let ncols = m.ncols;
let elements = vec![vector![value; ncols]; nrows];
Matrix {
nrows,
ncols,
elements,
}
}
pub fn zeros(shape: [usize; 2]) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
Self::full(shape, T::from_i32(0).unwrap())
}
pub fn zeros_like(m: &Matrix<T>) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
Self::full([m.nrows, m.ncols], T::from_i32(0).unwrap())
}
pub fn ones(shape: [usize; 2]) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
Self::full(shape, T::from_i32(1).unwrap())
}
pub fn ones_like(m: &Matrix<T>) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
Self::full([m.nrows, m.ncols], T::from_i32(1).unwrap())
}
pub fn power(&self, exp: usize) -> Matrix<T>
where
T: FromPrimitive + Num + Copy,
{
let elements =
self.elements.iter().map(|row| row.power(exp)).collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
pub fn uniform(shape: [usize; 2], low: T, high: T) -> Matrix<T>
where
T: Num + SampleUniform + Copy,
{
let nrows = shape[0];
let ncols = shape[1];
let mut elements = Vec::with_capacity(nrows);
let uniform_distribution = Uniform::new(low, high);
let mut rng = rand::thread_rng();
for _ in 0..nrows {
let mut cols = Vec::with_capacity(ncols);
for _ in 0..ncols {
cols.push(uniform_distribution.sample(&mut rng));
}
elements.push(RowMatrix::from(cols));
}
Matrix {
nrows,
ncols,
elements,
}
}
pub fn from_csv<P>(file_path: P) -> MatrixLoaderForCSV<T, P>
where
P: AsRef<Path>,
{
MatrixLoaderForCSV {
file_path,
has_headers: false,
phantom: PhantomData
}
}
}
impl Matrix<f64> {
pub fn normal(shape: [usize; 2], mean: f64, std_dev: f64) -> Matrix<f64> {
let nrows = shape[0];
let ncols = shape[1];
let mut elements = Vec::with_capacity(nrows);
let normal_distribution = Normal::new(mean, std_dev);
let mut rng = rand::thread_rng();
for _ in 0..nrows {
let mut cols = Vec::with_capacity(ncols);
for _ in 0..ncols {
cols.push(normal_distribution.sample(&mut rng));
}
elements.push(RowMatrix::from(cols));
}
Matrix {
nrows,
ncols,
elements,
}
}
}
impl<T> From<Vec<Vec<T>>> for Matrix<T>
where
T: Num + Copy,
{
fn from(source: Vec<Vec<T>>) -> Self {
let nrows = source.len();
let ncols = source[0].len();
let ncols_inconsistent = source.iter().any(|v| v.len() != ncols);
if ncols_inconsistent {
panic!("Invalid matrix: the number of columns is inconsistent")
}
let elements = source
.iter()
.map(|v| {
let mut row = Vec::new();
v.iter().for_each(|x| row.push(*x));
RowMatrix::from(row)
})
.collect();
Matrix {
nrows,
ncols,
elements,
}
}
}
impl<T> PartialEq for Matrix<T>
where
T: Num + Copy,
{
fn eq(&self, other: &Matrix<T>) -> bool {
if self.elements != other.elements {
return false;
}
true
}
fn ne(&self, other: &Matrix<T>) -> bool {
if self.elements == other.elements {
return false;
}
true
}
}
impl<T> ops::Index<usize> for Matrix<T> {
type Output = RowMatrix<T>;
fn index(&self, i: usize) -> &RowMatrix<T> {
&self.elements[i]
}
}
impl<T> IntoIterator for Matrix<T> {
type Item = RowMatrix<T>;
type IntoIter = ::std::vec::IntoIter<RowMatrix<T>>;
fn into_iter(self) -> Self::IntoIter {
self.elements.into_iter()
}
}
impl<T> ops::Add<Matrix<T>> for Matrix<T>
where
T: Num + Copy,
{
type Output = Matrix<T>;
fn add(self, other: Matrix<T>) -> Matrix<T> {
if self.shape() != other.shape() {
panic!(
"Matrix addition with invalid shape: {:?} != {:?}",
self.shape(),
other.shape()
);
}
let elements = self
.elements
.iter()
.enumerate()
.map(|(i, row)| {
row.elements
.iter()
.enumerate()
.map(|(j, value)| *value + other[i][j])
.collect()
})
.collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
}
impl<T> ops::Add<T> for Matrix<T>
where
T: Num + Copy,
{
type Output = Matrix<T>;
fn add(self, value: T) -> Matrix<T> {
let elements = self
.elements
.iter()
.map(|row| {
row.elements.iter().map(|elem| *elem + value).collect()
})
.collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
}
macro_rules! impl_add_matrix_for_type {
($t: ty) => {
impl ops::Add<Matrix<$t>> for $t {
type Output = Matrix<$t>;
fn add(self, m: Matrix<$t>) -> Matrix<$t> {
let elements = m
.elements
.iter()
.map(|row| {
row.elements.iter().map(|elem| elem + self).collect()
})
.collect();
Matrix {
nrows: m.nrows,
ncols: m.ncols,
elements,
}
}
}
};
}
impl_add_matrix_for_type!(usize);
impl_add_matrix_for_type!(i8);
impl_add_matrix_for_type!(i16);
impl_add_matrix_for_type!(i32);
impl_add_matrix_for_type!(i64);
impl_add_matrix_for_type!(i128);
impl_add_matrix_for_type!(u8);
impl_add_matrix_for_type!(u16);
impl_add_matrix_for_type!(u32);
impl_add_matrix_for_type!(u64);
impl_add_matrix_for_type!(u128);
impl_add_matrix_for_type!(f32);
impl_add_matrix_for_type!(f64);
impl<T> ops::AddAssign<Matrix<T>> for Matrix<T>
where
T: Num + Copy + ops::AddAssign,
{
fn add_assign(&mut self, other: Matrix<T>) {
if self.shape() != other.shape() {
panic!(
"Matrix addition with invalid length: {:?} != {:?}",
self.shape(),
other.shape()
);
}
self.elements.iter_mut().enumerate().for_each(|(i, row)| {
row.elements
.iter_mut()
.enumerate()
.for_each(|(j, value)| *value += other[i][j])
})
}
}
impl<T> ops::AddAssign<T> for Matrix<T>
where
T: Num + Copy + ops::AddAssign,
{
fn add_assign(&mut self, value: T) {
self.elements.iter_mut().for_each(|row| {
row.elements.iter_mut().for_each(|elem| *elem += value)
})
}
}
impl<T> ops::Sub<Matrix<T>> for Matrix<T>
where
T: Num + Copy,
{
type Output = Matrix<T>;
fn sub(self, other: Matrix<T>) -> Matrix<T> {
if self.shape() != other.shape() {
panic!(
"Matrix substraction with invalid shape: {:?} != {:?}",
self.shape(),
other.shape()
);
}
let elements = self
.elements
.iter()
.enumerate()
.map(|(i, row)| {
row.elements
.iter()
.enumerate()
.map(|(j, value)| *value - other[i][j])
.collect()
})
.collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
}
impl<T> ops::Sub<T> for Matrix<T>
where
T: Num + Copy,
{
type Output = Matrix<T>;
fn sub(self, value: T) -> Matrix<T> {
let elements = self
.elements
.iter()
.map(|row| {
row.elements.iter().map(|elem| *elem - value).collect()
})
.collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
}
macro_rules! impl_sub_matrix_for_type {
($t: ty) => {
impl ops::Sub<Matrix<$t>> for $t {
type Output = Matrix<$t>;
fn sub(self, m: Matrix<$t>) -> Matrix<$t> {
let elements = m
.elements
.iter()
.map(|row| {
row.elements.iter().map(|elem| self - elem).collect()
})
.collect();
Matrix {
nrows: m.nrows,
ncols: m.ncols,
elements,
}
}
}
};
}
impl_sub_matrix_for_type!(usize);
impl_sub_matrix_for_type!(i8);
impl_sub_matrix_for_type!(i16);
impl_sub_matrix_for_type!(i32);
impl_sub_matrix_for_type!(i64);
impl_sub_matrix_for_type!(i128);
impl_sub_matrix_for_type!(u8);
impl_sub_matrix_for_type!(u16);
impl_sub_matrix_for_type!(u32);
impl_sub_matrix_for_type!(u64);
impl_sub_matrix_for_type!(u128);
impl_sub_matrix_for_type!(f32);
impl_sub_matrix_for_type!(f64);
impl<T> ops::SubAssign<Matrix<T>> for Matrix<T>
where
T: Num + Copy + ops::SubAssign,
{
fn sub_assign(&mut self, other: Matrix<T>) {
if self.shape() != other.shape() {
panic!(
"Matrix substraction with invalid length: {:?} != {:?}",
self.shape(),
other.shape()
);
}
self.elements.iter_mut().enumerate().for_each(|(i, row)| {
row.elements
.iter_mut()
.enumerate()
.for_each(|(j, value)| *value -= other[i][j])
})
}
}
impl<T> ops::SubAssign<T> for Matrix<T>
where
T: Num + Copy + ops::SubAssign,
{
fn sub_assign(&mut self, value: T) {
self.elements.iter_mut().for_each(|row| {
row.elements.iter_mut().for_each(|elem| *elem -= value)
})
}
}
impl<T> ops::Mul<Matrix<T>> for Matrix<T>
where
T: Num + Copy,
{
type Output = Matrix<T>;
fn mul(self, other: Matrix<T>) -> Matrix<T> {
if self.shape() != other.shape() {
panic!(
"Matrix multiplication with invalid shape: {:?} != {:?}",
self.shape(),
other.shape()
);
}
let elements = self
.elements
.iter()
.enumerate()
.map(|(i, row)| {
row.elements
.iter()
.enumerate()
.map(|(j, value)| *value * other[i][j])
.collect()
})
.collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
}
impl<T> ops::Mul<T> for Matrix<T>
where
T: Num + Copy,
{
type Output = Matrix<T>;
fn mul(self, value: T) -> Matrix<T> {
let elements = self
.elements
.iter()
.map(|row| {
row.elements.iter().map(|elem| *elem * value).collect()
})
.collect();
Matrix {
nrows: self.nrows,
ncols: self.ncols,
elements,
}
}
}
macro_rules! impl_sub_matrix_for_type {
($t: ty) => {
impl ops::Mul<Matrix<$t>> for $t {
type Output = Matrix<$t>;
fn mul(self, m: Matrix<$t>) -> Matrix<$t> {
let elements = m
.elements
.iter()
.map(|row| {
row.elements.iter().map(|elem| self * elem).collect()
})
.collect();
Matrix {
nrows: m.nrows,
ncols: m.ncols,
elements,
}
}
}
};
}
impl_sub_matrix_for_type!(usize);
impl_sub_matrix_for_type!(i8);
impl_sub_matrix_for_type!(i16);
impl_sub_matrix_for_type!(i32);
impl_sub_matrix_for_type!(i64);
impl_sub_matrix_for_type!(i128);
impl_sub_matrix_for_type!(u8);
impl_sub_matrix_for_type!(u16);
impl_sub_matrix_for_type!(u32);
impl_sub_matrix_for_type!(u64);
impl_sub_matrix_for_type!(u128);
impl_sub_matrix_for_type!(f32);
impl_sub_matrix_for_type!(f64);
impl<T> ops::MulAssign<Matrix<T>> for Matrix<T>
where
T: Num + Copy + ops::MulAssign,
{
fn mul_assign(&mut self, other: Matrix<T>) {
if self.shape() != other.shape() {
panic!(
"Matrix multiplication with invalid length: {:?} != {:?}",
self.shape(),
other.shape()
);
}
self.elements.iter_mut().enumerate().for_each(|(i, row)| {
row.elements
.iter_mut()
.enumerate()
.for_each(|(j, value)| *value *= other[i][j])
})
}
}
impl<T> ops::MulAssign<T> for Matrix<T>
where
T: Num + Copy + ops::MulAssign,
{
fn mul_assign(&mut self, value: T) {
self.elements.iter_mut().for_each(|row| {
row.elements.iter_mut().for_each(|elem| *elem *= value)
})
}
}
#[derive(Debug)]
pub struct MatrixLoaderForCSV<T, P>
where
P: AsRef<Path>,
{
file_path: P,
has_headers: bool,
phantom: PhantomData<T>,
}
impl<T, P> MatrixLoaderForCSV<T, P>
where
P: AsRef<Path>,
{
pub fn has_headers(self, yes: bool) -> MatrixLoaderForCSV<T, P> {
MatrixLoaderForCSV {
file_path: self.file_path,
has_headers: yes,
phantom: PhantomData
}
}
pub fn load(self) -> Result<Matrix<T>, LoadError>
where
T: FromPrimitive + Num + Copy + utils::TypeName,
{
let file = File::open(self.file_path)?;
let mut rdr = csv::ReaderBuilder::new()
.has_headers(self.has_headers)
.from_reader(file);
let mut elements = Vec::new();
for result in rdr.records() {
let record = result?;
let mut rows = Vec::with_capacity(record.len());
for value in record.iter() {
let element = match T::from_str_radix(value.trim(), 10) {
Ok(value) => value,
Err(_err) => {
return Err(LoadError::new(
LoadErrorKind::InvalidElement,
format!(
"{:?} is not valid {}",
value,
T::type_name()
),
));
}
};
rows.push(element);
}
elements.push(rows);
}
if elements.len() == 0 {
return Err(LoadError::new(
LoadErrorKind::Empty,
String::from("Cannot load empty file"),
));
}
Ok(Matrix::from(elements))
}
}