feat: documents matrix methods

This commit is contained in:
Volodymyr Orlov
2020-09-06 18:27:11 -07:00
parent 1e3ed4c924
commit bbe810d164
25 changed files with 587 additions and 245 deletions
+169 -79
View File
@@ -34,6 +34,7 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
}
}
/// Column-major, dense matrix. See [Simple Dense Matrix](../index.html).
#[derive(Debug, Clone)]
pub struct DenseMatrix<T: RealNumber> {
ncols: usize,
@@ -57,7 +58,9 @@ impl<T: RealNumber> fmt::Display for DenseMatrix<T> {
}
impl<T: RealNumber> DenseMatrix<T> {
fn new(nrows: usize, ncols: usize, values: Vec<T>) -> Self {
/// Create new instance of `DenseMatrix` without copying data.
/// `values` should be in column-major order.
pub fn new(nrows: usize, ncols: usize, values: Vec<T>) -> Self {
DenseMatrix {
ncols: ncols,
nrows: nrows,
@@ -65,11 +68,13 @@ impl<T: RealNumber> DenseMatrix<T> {
}
}
pub fn from_array(values: &[&[T]]) -> Self {
DenseMatrix::from_vec(&values.into_iter().map(|row| Vec::from(*row)).collect())
/// New instance of `DenseMatrix` from 2d array.
pub fn from_2d_array(values: &[&[T]]) -> Self {
DenseMatrix::from_2d_vec(&values.into_iter().map(|row| Vec::from(*row)).collect())
}
pub fn from_vec(values: &Vec<Vec<T>>) -> DenseMatrix<T> {
/// New instance of `DenseMatrix` from 2d vector.
pub fn from_2d_vec(values: &Vec<Vec<T>>) -> Self {
let nrows = values.len();
let ncols = values
.first()
@@ -88,11 +93,41 @@ impl<T: RealNumber> DenseMatrix<T> {
m
}
pub fn vector_from_array(values: &[T]) -> Self {
DenseMatrix::vector_from_vec(Vec::from(values))
/// Creates new matrix from an array.
/// * `nrows` - number of rows in new matrix.
/// * `ncols` - number of columns in new matrix.
/// * `values` - values to initialize the matrix.
pub fn from_array(nrows: usize, ncols: usize, values: &[T]) -> Self {
DenseMatrix::from_vec(nrows, ncols, &Vec::from(values))
}
pub fn vector_from_vec(values: Vec<T>) -> Self {
/// Creates new matrix from a vector.
/// * `nrows` - number of rows in new matrix.
/// * `ncols` - number of columns in new matrix.
/// * `values` - values to initialize the matrix.
pub fn from_vec(nrows: usize, ncols: usize, values: &Vec<T>) -> DenseMatrix<T> {
let mut m = DenseMatrix {
ncols: ncols,
nrows: nrows,
values: vec![T::zero(); ncols * nrows],
};
for row in 0..nrows {
for col in 0..ncols {
m.set(row, col, values[col + row * ncols]);
}
}
m
}
/// Creates new row vector (_1xN_ matrix) from an array.
/// * `values` - values to initialize the matrix.
pub fn row_vector_from_array(values: &[T]) -> Self {
DenseMatrix::row_vector_from_vec(Vec::from(values))
}
/// Creates new row vector (_1xN_ matrix) from a vector.
/// * `values` - values to initialize the matrix.
pub fn row_vector_from_vec(values: Vec<T>) -> Self {
DenseMatrix {
ncols: values.len(),
nrows: 1,
@@ -100,18 +135,20 @@ impl<T: RealNumber> DenseMatrix<T> {
}
}
pub fn div_mut(&mut self, b: Self) -> () {
if self.nrows != b.nrows || self.ncols != b.ncols {
panic!("Can't divide matrices of different sizes.");
}
for i in 0..self.values.len() {
self.values[i] = self.values[i] / b.values[i];
}
/// Creates new column vector (_1xN_ matrix) from an array.
/// * `values` - values to initialize the matrix.
pub fn column_vector_from_array(values: &[T]) -> Self {
DenseMatrix::column_vector_from_vec(Vec::from(values))
}
pub fn get_raw_values(&self) -> &Vec<T> {
&self.values
/// Creates new column vector (_1xN_ matrix) from a vector.
/// * `values` - values to initialize the matrix.
pub fn column_vector_from_vec(values: Vec<T>) -> Self {
DenseMatrix {
ncols: 1,
nrows: values.len(),
values: values,
}
}
}
@@ -261,7 +298,15 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
}
fn to_row_vector(self) -> Self::RowVector {
self.to_raw_vector()
let mut v = vec![T::zero(); self.nrows * self.ncols];
for r in 0..self.nrows {
for c in 0..self.ncols {
v[r * self.ncols + c] = self.get(r, c);
}
}
v
}
fn get(&self, row: usize, col: usize) -> T {
@@ -312,23 +357,11 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
return matrix;
}
fn to_raw_vector(&self) -> Vec<T> {
let mut v = vec![T::zero(); self.nrows * self.ncols];
for r in 0..self.nrows {
for c in 0..self.ncols {
v[r * self.ncols + c] = self.get(r, c);
}
}
v
}
fn shape(&self) -> (usize, usize) {
(self.nrows, self.ncols)
}
fn h_stack(&self, other: &Self) -> Self {
fn v_stack(&self, other: &Self) -> Self {
if self.ncols != other.ncols {
panic!("Number of columns in both matrices should be equal");
}
@@ -345,7 +378,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
result
}
fn v_stack(&self, other: &Self) -> Self {
fn h_stack(&self, other: &Self) -> Self {
if self.nrows != other.nrows {
panic!("Number of rows in both matrices should be equal");
}
@@ -362,7 +395,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
result
}
fn dot(&self, other: &Self) -> Self {
fn matmul(&self, other: &Self) -> Self {
if self.ncols != other.nrows {
panic!("Number of rows of A should equal number of columns of B");
}
@@ -382,8 +415,8 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
result
}
fn vector_dot(&self, other: &Self) -> T {
if (self.nrows != 1 || self.nrows != 1) && (other.nrows != 1 || other.ncols != 1) {
fn dot(&self, other: &Self) -> T {
if self.nrows != 1 && other.nrows != 1 {
panic!("A and B should both be 1-dimentional vectors.");
}
if self.nrows * self.ncols != other.nrows * other.ncols {
@@ -666,6 +699,22 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
sum
}
fn max(&self) -> T {
let mut max = T::neg_infinity();
for i in 0..self.values.len() {
max = T::max(max, self.values[i]);
}
max
}
fn min(&self) -> T {
let mut min = T::infinity();
for i in 0..self.values.len() {
min = T::min(min, self.values[i]);
}
min
}
fn softmax_mut(&mut self) {
let max = self
.values
@@ -752,6 +801,32 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
mod tests {
use super::*;
#[test]
fn from_array() {
let vec = [1., 2., 3., 4., 5., 6.];
assert_eq!(
DenseMatrix::from_array(3, 2, &vec),
DenseMatrix::new(3, 2, vec![1., 3., 5., 2., 4., 6.])
);
assert_eq!(
DenseMatrix::from_array(2, 3, &vec),
DenseMatrix::new(2, 3, vec![1., 4., 2., 5., 3., 6.])
);
}
#[test]
fn row_column_vec_from_array() {
let vec = vec![1., 2., 3., 4., 5., 6.];
assert_eq!(
DenseMatrix::row_vector_from_array(&vec),
DenseMatrix::new(1, 6, vec![1., 2., 3., 4., 5., 6.])
);
assert_eq!(
DenseMatrix::column_vector_from_array(&vec),
DenseMatrix::new(6, 1, vec![1., 2., 3., 4., 5., 6.])
);
}
#[test]
fn from_to_row_vec() {
let vec = vec![1., 2., 3.];
@@ -766,59 +841,66 @@ mod tests {
}
#[test]
fn h_stack() {
let a = DenseMatrix::from_array(&[&[1., 2., 3.], &[4., 5., 6.], &[7., 8., 9.]]);
let b = DenseMatrix::from_array(&[&[1., 2., 3.], &[4., 5., 6.]]);
let expected = DenseMatrix::from_array(&[
fn v_stack() {
let a = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.], &[7., 8., 9.]]);
let b = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.]]);
let expected = DenseMatrix::from_2d_array(&[
&[1., 2., 3.],
&[4., 5., 6.],
&[7., 8., 9.],
&[1., 2., 3.],
&[4., 5., 6.],
]);
let result = a.h_stack(&b);
assert_eq!(result, expected);
}
#[test]
fn v_stack() {
let a = DenseMatrix::from_array(&[&[1., 2., 3.], &[4., 5., 6.], &[7., 8., 9.]]);
let b = DenseMatrix::from_array(&[&[1., 2.], &[3., 4.], &[5., 6.]]);
let expected = DenseMatrix::from_array(&[
&[1., 2., 3., 1., 2.],
&[4., 5., 6., 3., 4.],
&[7., 8., 9., 5., 6.],
]);
let result = a.v_stack(&b);
assert_eq!(result, expected);
}
#[test]
fn dot() {
let a = DenseMatrix::from_array(&[&[1., 2., 3.], &[4., 5., 6.]]);
let b = DenseMatrix::from_array(&[&[1., 2.], &[3., 4.], &[5., 6.]]);
let expected = DenseMatrix::from_array(&[&[22., 28.], &[49., 64.]]);
let result = a.dot(&b);
assert_eq!(result, expected);
}
#[test]
fn slice() {
let m = DenseMatrix::from_array(&[
fn h_stack() {
let a = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.], &[7., 8., 9.]]);
let b = DenseMatrix::from_2d_array(&[&[1., 2.], &[3., 4.], &[5., 6.]]);
let expected = DenseMatrix::from_2d_array(&[
&[1., 2., 3., 1., 2.],
&[4., 5., 6., 3., 4.],
&[7., 8., 9., 5., 6.],
]);
let expected = DenseMatrix::from_array(&[&[2., 3.], &[5., 6.]]);
let result = a.h_stack(&b);
assert_eq!(result, expected);
}
#[test]
fn matmul() {
let a = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.]]);
let b = DenseMatrix::from_2d_array(&[&[1., 2.], &[3., 4.], &[5., 6.]]);
let expected = DenseMatrix::from_2d_array(&[&[22., 28.], &[49., 64.]]);
let result = a.matmul(&b);
assert_eq!(result, expected);
}
#[test]
fn dot() {
let a = DenseMatrix::from_array(1, 3, &[1., 2., 3.]);
let b = DenseMatrix::from_array(1, 3, &[4., 5., 6.]);
assert_eq!(a.dot(&b), 32.);
}
#[test]
fn slice() {
let m = DenseMatrix::from_2d_array(&[
&[1., 2., 3., 1., 2.],
&[4., 5., 6., 3., 4.],
&[7., 8., 9., 5., 6.],
]);
let expected = DenseMatrix::from_2d_array(&[&[2., 3.], &[5., 6.]]);
let result = m.slice(0..2, 1..3);
assert_eq!(result, expected);
}
#[test]
fn approximate_eq() {
let m = DenseMatrix::from_array(&[&[2., 3.], &[5., 6.]]);
let m_eq = DenseMatrix::from_array(&[&[2.5, 3.0], &[5., 5.5]]);
let m_neq = DenseMatrix::from_array(&[&[3.0, 3.0], &[5., 6.5]]);
let m = DenseMatrix::from_2d_array(&[&[2., 3.], &[5., 6.]]);
let m_eq = DenseMatrix::from_2d_array(&[&[2.5, 3.0], &[5., 5.5]]);
let m_neq = DenseMatrix::from_2d_array(&[&[3.0, 3.0], &[5., 6.5]]);
assert!(m.approximate_eq(&m_eq, 0.5));
assert!(!m.approximate_eq(&m_neq, 0.5));
}
@@ -835,8 +917,8 @@ mod tests {
#[test]
fn transpose() {
let m = DenseMatrix::from_array(&[&[1.0, 3.0], &[2.0, 4.0]]);
let expected = DenseMatrix::from_array(&[&[1.0, 2.0], &[3.0, 4.0]]);
let m = DenseMatrix::from_2d_array(&[&[1.0, 3.0], &[2.0, 4.0]]);
let expected = DenseMatrix::from_2d_array(&[&[1.0, 2.0], &[3.0, 4.0]]);
let m_transposed = m.transpose();
for c in 0..2 {
for r in 0..2 {
@@ -847,7 +929,7 @@ mod tests {
#[test]
fn reshape() {
let m_orig = DenseMatrix::vector_from_array(&[1., 2., 3., 4., 5., 6.]);
let m_orig = DenseMatrix::row_vector_from_array(&[1., 2., 3., 4., 5., 6.]);
let m_2_by_3 = m_orig.reshape(2, 3);
let m_result = m_2_by_3.reshape(1, 6);
assert_eq!(m_2_by_3.shape(), (2, 3));
@@ -858,7 +940,7 @@ mod tests {
#[test]
fn norm() {
let v = DenseMatrix::vector_from_array(&[3., -2., 6.]);
let v = DenseMatrix::row_vector_from_array(&[3., -2., 6.]);
assert_eq!(v.norm(1.), 11.);
assert_eq!(v.norm(2.), 7.);
assert_eq!(v.norm(std::f64::INFINITY), 6.);
@@ -867,7 +949,7 @@ mod tests {
#[test]
fn softmax_mut() {
let mut prob: DenseMatrix<f64> = DenseMatrix::vector_from_array(&[1., 2., 3.]);
let mut prob: DenseMatrix<f64> = DenseMatrix::row_vector_from_array(&[1., 2., 3.]);
prob.softmax_mut();
assert!((prob.get(0, 0) - 0.09).abs() < 0.01);
assert!((prob.get(0, 1) - 0.24).abs() < 0.01);
@@ -876,21 +958,29 @@ mod tests {
#[test]
fn col_mean() {
let a = DenseMatrix::from_array(&[&[1., 2., 3.], &[4., 5., 6.], &[7., 8., 9.]]);
let a = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.], &[7., 8., 9.]]);
let res = a.column_mean();
assert_eq!(res, vec![4., 5., 6.]);
}
#[test]
fn min_max_sum() {
let a = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.]]);
assert_eq!(21., a.sum());
assert_eq!(1., a.min());
assert_eq!(6., a.max());
}
#[test]
fn eye() {
let a = DenseMatrix::from_array(&[&[1., 0., 0.], &[0., 1., 0.], &[0., 0., 1.]]);
let a = DenseMatrix::from_2d_array(&[&[1., 0., 0.], &[0., 1., 0.], &[0., 0., 1.]]);
let res = DenseMatrix::eye(3);
assert_eq!(res, a);
}
#[test]
fn to_from_json() {
let a = DenseMatrix::from_array(&[&[0.9, 0.4, 0.7], &[0.4, 0.5, 0.3], &[0.7, 0.3, 0.8]]);
let a = DenseMatrix::from_2d_array(&[&[0.9, 0.4, 0.7], &[0.4, 0.5, 0.3], &[0.7, 0.3, 0.8]]);
let deserialized_a: DenseMatrix<f64> =
serde_json::from_str(&serde_json::to_string(&a).unwrap()).unwrap();
assert_eq!(a, deserialized_a);
@@ -898,7 +988,7 @@ mod tests {
#[test]
fn to_from_bincode() {
let a = DenseMatrix::from_array(&[&[0.9, 0.4, 0.7], &[0.4, 0.5, 0.3], &[0.7, 0.3, 0.8]]);
let a = DenseMatrix::from_2d_array(&[&[0.9, 0.4, 0.7], &[0.4, 0.5, 0.3], &[0.7, 0.3, 0.8]]);
let deserialized_a: DenseMatrix<f64> =
bincode::deserialize(&bincode::serialize(&a).unwrap()).unwrap();
assert_eq!(a, deserialized_a);
@@ -906,7 +996,7 @@ mod tests {
#[test]
fn to_string() {
let a = DenseMatrix::from_array(&[&[0.9, 0.4, 0.7], &[0.4, 0.5, 0.3], &[0.7, 0.3, 0.8]]);
let a = DenseMatrix::from_2d_array(&[&[0.9, 0.4, 0.7], &[0.4, 0.5, 0.3], &[0.7, 0.3, 0.8]]);
assert_eq!(
format!("{}", a),
"[[0.9, 0.4, 0.7], [0.4, 0.5, 0.3], [0.7, 0.3, 0.8]]"
@@ -915,14 +1005,14 @@ mod tests {
#[test]
fn cov() {
let a = DenseMatrix::from_array(&[
let a = DenseMatrix::from_2d_array(&[
&[64.0, 580.0, 29.0],
&[66.0, 570.0, 33.0],
&[68.0, 590.0, 37.0],
&[69.0, 660.0, 46.0],
&[73.0, 600.0, 55.0],
]);
let expected = DenseMatrix::from_array(&[
let expected = DenseMatrix::from_2d_array(&[
&[11.5, 50.0, 34.75],
&[50.0, 1250.0, 205.0],
&[34.75, 205.0, 110.0],