465 lines
16 KiB
Rust
465 lines
16 KiB
Rust
//! # Linear Algebra and Matrix Decomposition
|
|
//!
|
|
//! Most machine learning algorithms in SmartCore depend on linear algebra and matrix decomposition methods from this module.
|
|
//!
|
|
//! Traits [`BaseMatrix`](trait.BaseMatrix.html), [`Matrix`](trait.Matrix.html) and [`BaseVector`](trait.BaseVector.html) define
|
|
//! abstract methods that can be implemented for any two-dimensional and one-dimentional arrays (matrix and vector).
|
|
//! Functions from these traits are designed for SmartCore machine learning algorithms and should not be used directly in your code.
|
|
//! If you still want to use functions from `BaseMatrix`, `Matrix` and `BaseVector` please be aware that methods defined in these
|
|
//! traits might change in the future.
|
|
//!
|
|
//! One reason why linear algebra traits are public is to allow for different types of matrices and vectors to be plugged into SmartCore.
|
|
//! Once all methods defined in `BaseMatrix`, `Matrix` and `BaseVector` are implemented for your favourite type of matrix and vector you
|
|
//! should be able to run SmartCore algorithms on it. Please see `nalgebra_bindings` and `ndarray_bindings` modules for an example of how
|
|
//! it is done for other libraries.
|
|
//!
|
|
//! You will also find verious matrix decomposition methods that work for any matrix that extends [`Matrix`](trait.Matrix.html).
|
|
//! For example, to decompose matrix defined as [Vec](https://doc.rust-lang.org/std/vec/struct.Vec.html):
|
|
//!
|
|
//! ```
|
|
//! use smartcore::linalg::naive::dense_matrix::*;
|
|
//! use smartcore::linalg::svd::*;
|
|
//!
|
|
//! let A = DenseMatrix::from_2d_array(&[
|
|
//! &[0.9000, 0.4000, 0.7000],
|
|
//! &[0.4000, 0.5000, 0.3000],
|
|
//! &[0.7000, 0.3000, 0.8000],
|
|
//! ]);
|
|
//!
|
|
//! let svd = A.svd().unwrap();
|
|
//!
|
|
//! let s: Vec<f64> = svd.s;
|
|
//! let v: DenseMatrix<f64> = svd.V;
|
|
//! let u: DenseMatrix<f64> = svd.U;
|
|
//! ```
|
|
|
|
/// The matrix is represented in terms of its eigenvalues and eigenvectors.
|
|
pub mod evd;
|
|
/// Factors a matrix as the product of a lower triangular matrix and an upper triangular matrix.
|
|
pub mod lu;
|
|
/// Dense matrix with column-major order that wraps [Vec](https://doc.rust-lang.org/std/vec/struct.Vec.html).
|
|
pub mod naive;
|
|
/// [nalgebra](https://docs.rs/nalgebra/) bindings.
|
|
#[cfg(feature = "nalgebra-bindings")]
|
|
pub mod nalgebra_bindings;
|
|
/// [ndarray](https://docs.rs/ndarray) bindings.
|
|
#[cfg(feature = "ndarray-bindings")]
|
|
pub mod ndarray_bindings;
|
|
/// QR factorization that factors a matrix into a product of an orthogonal matrix and an upper triangular matrix.
|
|
pub mod qr;
|
|
/// Singular value decomposition.
|
|
pub mod svd;
|
|
|
|
use std::fmt::{Debug, Display};
|
|
use std::marker::PhantomData;
|
|
use std::ops::Range;
|
|
|
|
use crate::math::num::RealNumber;
|
|
use evd::EVDDecomposableMatrix;
|
|
use lu::LUDecomposableMatrix;
|
|
use qr::QRDecomposableMatrix;
|
|
use svd::SVDDecomposableMatrix;
|
|
|
|
/// Column or row vector
|
|
pub trait BaseVector<T: RealNumber>: Clone + Debug {
|
|
/// Get an element of a vector
|
|
/// * `i` - index of an element
|
|
fn get(&self, i: usize) -> T;
|
|
|
|
/// Set an element at `i` to `x`
|
|
/// * `i` - index of an element
|
|
/// * `x` - new value
|
|
fn set(&mut self, i: usize, x: T);
|
|
|
|
/// Get number of elevemnt in the vector
|
|
fn len(&self) -> usize;
|
|
|
|
/// Return a vector with the elements of the one-dimensional array.
|
|
fn to_vec(&self) -> Vec<T>;
|
|
|
|
/// Create new vector with zeros of size `len`.
|
|
fn zeros(len: usize) -> Self;
|
|
|
|
/// Create new vector with ones of size `len`.
|
|
fn ones(len: usize) -> Self;
|
|
|
|
/// Create new vector of size `len` where each element is set to `value`.
|
|
fn fill(len: usize, value: T) -> Self;
|
|
}
|
|
|
|
/// Generic matrix type.
|
|
pub trait BaseMatrix<T: RealNumber>: Clone + Debug {
|
|
/// Row vector that is associated with this matrix type,
|
|
/// e.g. if we have an implementation of sparce matrix
|
|
/// we should have an associated sparce vector type that
|
|
/// represents a row in this matrix.
|
|
type RowVector: BaseVector<T> + Clone + Debug;
|
|
|
|
/// Transforms row vector `vec` into a 1xM matrix.
|
|
fn from_row_vector(vec: Self::RowVector) -> Self;
|
|
|
|
/// Transforms 1-d matrix of 1xM into a row vector.
|
|
fn to_row_vector(self) -> Self::RowVector;
|
|
|
|
/// Get an element of the matrix.
|
|
/// * `row` - row number
|
|
/// * `col` - column number
|
|
fn get(&self, row: usize, col: usize) -> T;
|
|
|
|
/// Get a vector with elements of the `row`'th row
|
|
/// * `row` - row number
|
|
fn get_row_as_vec(&self, row: usize) -> Vec<T>;
|
|
|
|
/// Copies a vector with elements of the `row`'th row into `result`
|
|
/// * `row` - row number
|
|
/// * `result` - receiver for the row
|
|
fn copy_row_as_vec(&self, row: usize, result: &mut Vec<T>);
|
|
|
|
/// Get a vector with elements of the `col`'th column
|
|
/// * `col` - column number
|
|
fn get_col_as_vec(&self, col: usize) -> Vec<T>;
|
|
|
|
/// Copies a vector with elements of the `col`'th column into `result`
|
|
/// * `col` - column number
|
|
/// * `result` - receiver for the col
|
|
fn copy_col_as_vec(&self, col: usize, result: &mut Vec<T>);
|
|
|
|
/// Set an element at `col`, `row` to `x`
|
|
fn set(&mut self, row: usize, col: usize, x: T);
|
|
|
|
/// Create an identity matrix of size `size`
|
|
fn eye(size: usize) -> Self;
|
|
|
|
/// Create new matrix with zeros of size `nrows` by `ncols`.
|
|
fn zeros(nrows: usize, ncols: usize) -> Self;
|
|
|
|
/// Create new matrix with ones of size `nrows` by `ncols`.
|
|
fn ones(nrows: usize, ncols: usize) -> Self;
|
|
|
|
/// Create new matrix of size `nrows` by `ncols` where each element is set to `value`.
|
|
fn fill(nrows: usize, ncols: usize, value: T) -> Self;
|
|
|
|
/// Return the shape of an array.
|
|
fn shape(&self) -> (usize, usize);
|
|
|
|
/// Stack arrays in sequence vertically (row wise).
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// let a = DenseMatrix::from_2d_array(&[&[1., 2., 3.], &[4., 5., 6.]]);
|
|
/// let b = DenseMatrix::from_2d_array(&[&[1., 2.], &[3., 4.]]);
|
|
/// let expected = DenseMatrix::from_2d_array(&[
|
|
/// &[1., 2., 3., 1., 2.],
|
|
/// &[4., 5., 6., 3., 4.]
|
|
/// ]);
|
|
///
|
|
/// assert_eq!(a.h_stack(&b), expected);
|
|
/// ```
|
|
fn h_stack(&self, other: &Self) -> Self;
|
|
|
|
/// Stack arrays in sequence horizontally (column wise).
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// let a = DenseMatrix::from_array(1, 3, &[1., 2., 3.]);
|
|
/// let b = DenseMatrix::from_array(1, 3, &[4., 5., 6.]);
|
|
/// let expected = DenseMatrix::from_2d_array(&[
|
|
/// &[1., 2., 3.],
|
|
/// &[4., 5., 6.]
|
|
/// ]);
|
|
///
|
|
/// assert_eq!(a.v_stack(&b), expected);
|
|
/// ```
|
|
fn v_stack(&self, other: &Self) -> Self;
|
|
|
|
/// Matrix product.
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// let a = DenseMatrix::from_2d_array(&[&[1., 2.], &[3., 4.]]);
|
|
/// let expected = DenseMatrix::from_2d_array(&[
|
|
/// &[7., 10.],
|
|
/// &[15., 22.]
|
|
/// ]);
|
|
///
|
|
/// assert_eq!(a.matmul(&a), expected);
|
|
/// ```
|
|
fn matmul(&self, other: &Self) -> Self;
|
|
|
|
/// Vector dot product
|
|
/// Both matrices should be of size _1xM_
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// 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.);
|
|
/// ```
|
|
fn dot(&self, other: &Self) -> T;
|
|
|
|
/// Return a slice of the matrix.
|
|
/// * `rows` - range of rows to return
|
|
/// * `cols` - range of columns to return
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// let m = DenseMatrix::from_2d_array(&[
|
|
/// &[1., 2., 3., 1.],
|
|
/// &[4., 5., 6., 3.],
|
|
/// &[7., 8., 9., 5.]
|
|
/// ]);
|
|
/// let expected = DenseMatrix::from_2d_array(&[&[2., 3.], &[5., 6.]]);
|
|
/// let result = m.slice(0..2, 1..3);
|
|
/// assert_eq!(result, expected);
|
|
/// ```
|
|
fn slice(&self, rows: Range<usize>, cols: Range<usize>) -> Self;
|
|
|
|
/// Returns True if matrices are element-wise equal within a tolerance `error`.
|
|
fn approximate_eq(&self, other: &Self, error: T) -> bool;
|
|
|
|
/// Add matrices, element-wise, overriding original matrix with result.
|
|
fn add_mut(&mut self, other: &Self) -> &Self;
|
|
|
|
/// Subtract matrices, element-wise, overriding original matrix with result.
|
|
fn sub_mut(&mut self, other: &Self) -> &Self;
|
|
|
|
/// Multiply matrices, element-wise, overriding original matrix with result.
|
|
fn mul_mut(&mut self, other: &Self) -> &Self;
|
|
|
|
/// Divide matrices, element-wise, overriding original matrix with result.
|
|
fn div_mut(&mut self, other: &Self) -> &Self;
|
|
|
|
/// Divide single element of the matrix by `x`, write result to original matrix.
|
|
fn div_element_mut(&mut self, row: usize, col: usize, x: T);
|
|
|
|
/// Multiply single element of the matrix by `x`, write result to original matrix.
|
|
fn mul_element_mut(&mut self, row: usize, col: usize, x: T);
|
|
|
|
/// Add single element of the matrix to `x`, write result to original matrix.
|
|
fn add_element_mut(&mut self, row: usize, col: usize, x: T);
|
|
|
|
/// Subtract `x` from single element of the matrix, write result to original matrix.
|
|
fn sub_element_mut(&mut self, row: usize, col: usize, x: T);
|
|
|
|
/// Add matrices, element-wise
|
|
fn add(&self, other: &Self) -> Self {
|
|
let mut r = self.clone();
|
|
r.add_mut(other);
|
|
r
|
|
}
|
|
|
|
/// Subtract matrices, element-wise
|
|
fn sub(&self, other: &Self) -> Self {
|
|
let mut r = self.clone();
|
|
r.sub_mut(other);
|
|
r
|
|
}
|
|
|
|
/// Multiply matrices, element-wise
|
|
fn mul(&self, other: &Self) -> Self {
|
|
let mut r = self.clone();
|
|
r.mul_mut(other);
|
|
r
|
|
}
|
|
|
|
/// Divide matrices, element-wise
|
|
fn div(&self, other: &Self) -> Self {
|
|
let mut r = self.clone();
|
|
r.div_mut(other);
|
|
r
|
|
}
|
|
|
|
/// Add `scalar` to the matrix, override original matrix with result.
|
|
fn add_scalar_mut(&mut self, scalar: T) -> &Self;
|
|
|
|
/// Subtract `scalar` from the elements of matrix, override original matrix with result.
|
|
fn sub_scalar_mut(&mut self, scalar: T) -> &Self;
|
|
|
|
/// Multiply `scalar` by the elements of matrix, override original matrix with result.
|
|
fn mul_scalar_mut(&mut self, scalar: T) -> &Self;
|
|
|
|
/// Divide elements of the matrix by `scalar`, override original matrix with result.
|
|
fn div_scalar_mut(&mut self, scalar: T) -> &Self;
|
|
|
|
/// Add `scalar` to the matrix.
|
|
fn add_scalar(&self, scalar: T) -> Self {
|
|
let mut r = self.clone();
|
|
r.add_scalar_mut(scalar);
|
|
r
|
|
}
|
|
|
|
/// Subtract `scalar` from the elements of matrix.
|
|
fn sub_scalar(&self, scalar: T) -> Self {
|
|
let mut r = self.clone();
|
|
r.sub_scalar_mut(scalar);
|
|
r
|
|
}
|
|
|
|
/// Multiply `scalar` by the elements of matrix.
|
|
fn mul_scalar(&self, scalar: T) -> Self {
|
|
let mut r = self.clone();
|
|
r.mul_scalar_mut(scalar);
|
|
r
|
|
}
|
|
|
|
/// Divide elements of the matrix by `scalar`.
|
|
fn div_scalar(&self, scalar: T) -> Self {
|
|
let mut r = self.clone();
|
|
r.div_scalar_mut(scalar);
|
|
r
|
|
}
|
|
|
|
/// Reverse or permute the axes of the matrix, return new matrix.
|
|
fn transpose(&self) -> Self;
|
|
|
|
/// Create new `nrows` by `ncols` matrix and populate it with random samples from a uniform distribution over [0, 1).
|
|
fn rand(nrows: usize, ncols: usize) -> Self;
|
|
|
|
/// Returns [L2 norm](https://en.wikipedia.org/wiki/Matrix_norm).
|
|
fn norm2(&self) -> T;
|
|
|
|
/// Returns [matrix norm](https://en.wikipedia.org/wiki/Matrix_norm) of order `p`.
|
|
fn norm(&self, p: T) -> T;
|
|
|
|
/// Returns the average of the matrix columns.
|
|
fn column_mean(&self) -> Vec<T>;
|
|
|
|
/// Numerical negative, element-wise. Overrides original matrix.
|
|
fn negative_mut(&mut self);
|
|
|
|
/// Numerical negative, element-wise.
|
|
fn negative(&self) -> Self {
|
|
let mut result = self.clone();
|
|
result.negative_mut();
|
|
result
|
|
}
|
|
|
|
/// Returns new matrix of shape `nrows` by `ncols` with data copied from original matrix.
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// let a = DenseMatrix::from_array(1, 6, &[1., 2., 3., 4., 5., 6.]);
|
|
/// let expected = DenseMatrix::from_2d_array(&[
|
|
/// &[1., 2., 3.],
|
|
/// &[4., 5., 6.]
|
|
/// ]);
|
|
///
|
|
/// assert_eq!(a.reshape(2, 3), expected);
|
|
/// ```
|
|
fn reshape(&self, nrows: usize, ncols: usize) -> Self;
|
|
|
|
/// Copies content of `other` matrix.
|
|
fn copy_from(&mut self, other: &Self);
|
|
|
|
/// Calculate the absolute value element-wise. Overrides original matrix.
|
|
fn abs_mut(&mut self) -> &Self;
|
|
|
|
/// Calculate the absolute value element-wise.
|
|
fn abs(&self) -> Self {
|
|
let mut result = self.clone();
|
|
result.abs_mut();
|
|
result
|
|
}
|
|
|
|
/// Calculates sum of all elements of the matrix.
|
|
fn sum(&self) -> T;
|
|
|
|
/// Calculates max of all elements of the matrix.
|
|
fn max(&self) -> T;
|
|
|
|
/// Calculates min of all elements of the matrix.
|
|
fn min(&self) -> T;
|
|
|
|
/// Calculates max(|a - b|) of two matrices
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
///
|
|
/// let a = DenseMatrix::from_array(2, 3, &[1., 2., 3., 4., -5., 6.]);
|
|
/// let b = DenseMatrix::from_array(2, 3, &[2., 3., 4., 1., 0., -12.]);
|
|
///
|
|
/// assert_eq!(a.max_diff(&b), 18.);
|
|
/// assert_eq!(b.max_diff(&b), 0.);
|
|
/// ```
|
|
fn max_diff(&self, other: &Self) -> T {
|
|
self.sub(other).abs().max()
|
|
}
|
|
|
|
/// Calculates [Softmax function](https://en.wikipedia.org/wiki/Softmax_function). Overrides the matrix with result.
|
|
fn softmax_mut(&mut self);
|
|
|
|
/// Raises elements of the matrix to the power of `p`
|
|
fn pow_mut(&mut self, p: T) -> &Self;
|
|
|
|
/// Returns new matrix with elements raised to the power of `p`
|
|
fn pow(&mut self, p: T) -> Self {
|
|
let mut result = self.clone();
|
|
result.pow_mut(p);
|
|
result
|
|
}
|
|
|
|
/// Returns the indices of the maximum values in each row.
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
/// let a = DenseMatrix::from_array(2, 3, &[1., 2., 3., -5., -6., -7.]);
|
|
///
|
|
/// assert_eq!(a.argmax(), vec![2, 0]);
|
|
/// ```
|
|
fn argmax(&self) -> Vec<usize>;
|
|
|
|
/// Returns vector with unique values from the matrix.
|
|
/// ```
|
|
/// use smartcore::linalg::naive::dense_matrix::*;
|
|
/// let a = DenseMatrix::from_array(3, 3, &[1., 2., 2., -2., -6., -7., 2., 3., 4.]);
|
|
///
|
|
///assert_eq!(a.unique(), vec![-7., -6., -2., 1., 2., 3., 4.]);
|
|
/// ```
|
|
fn unique(&self) -> Vec<T>;
|
|
|
|
/// Calculates the covariance matrix
|
|
fn cov(&self) -> Self;
|
|
}
|
|
|
|
/// Generic matrix with additional mixins like various factorization methods.
|
|
pub trait Matrix<T: RealNumber>:
|
|
BaseMatrix<T>
|
|
+ SVDDecomposableMatrix<T>
|
|
+ EVDDecomposableMatrix<T>
|
|
+ QRDecomposableMatrix<T>
|
|
+ LUDecomposableMatrix<T>
|
|
+ PartialEq
|
|
+ Display
|
|
{
|
|
}
|
|
|
|
pub(crate) fn row_iter<F: RealNumber, M: BaseMatrix<F>>(m: &M) -> RowIter<F, M> {
|
|
RowIter {
|
|
m: m,
|
|
pos: 0,
|
|
max_pos: m.shape().0,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
|
|
pub(crate) struct RowIter<'a, T: RealNumber, M: BaseMatrix<T>> {
|
|
m: &'a M,
|
|
pos: usize,
|
|
max_pos: usize,
|
|
phantom: PhantomData<&'a T>,
|
|
}
|
|
|
|
impl<'a, T: RealNumber, M: BaseMatrix<T>> Iterator for RowIter<'a, T, M> {
|
|
type Item = Vec<T>;
|
|
|
|
fn next(&mut self) -> Option<Vec<T>> {
|
|
let res;
|
|
if self.pos < self.max_pos {
|
|
res = Some(self.m.get_row_as_vec(self.pos))
|
|
} else {
|
|
res = None
|
|
}
|
|
self.pos += 1;
|
|
res
|
|
}
|
|
}
|