#![warn(missing_docs)]
mod determinant_helpers;
use determinant_helpers::*;
use itertools::{iproduct, Itertools};
use rand::Rng;
use rayon::prelude::*;
use std::fs;
use std::{
fmt::{Debug, Error},
str::FromStr,
};
pub type Shape = (usize, usize);
macro_rules! at {
($i:ident, $j:ident, $cols:expr) => {
$i * $cols + $j
};
}
#[derive(Clone, PartialEq, PartialOrd)]
pub struct Matrix {
pub data: Vec<f32>,
pub shape: Shape,
pub nrows: usize,
pub ncols: usize,
}
impl Debug for Matrix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[");
for i in 0..self.rows() {
for j in 0..self.cols() {
if i == 0 {
write!(f, "{} ", self.data[at!(i, j, self.cols())]);
} else {
write!(f, " {}", self.data[at!(i, j, self.cols())]);
}
}
if i == self.shape.0 - 1 {
break;
}
write!(f, "\n");
}
write!(f, "], dtype=f32")
}
}
impl FromStr for Matrix {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v: Vec<f32> = s
.trim()
.lines()
.map(|l| {
l.split_whitespace()
.map(|num| num.parse::<f32>().unwrap())
.collect::<Vec<f32>>()
})
.collect::<Vec<Vec<f32>>>()
.into_iter()
.flatten()
.collect();
let rows = s.trim().lines().count();
let cols = s.trim().lines().nth(0).unwrap().split_whitespace().count();
Ok(Self::new(v, (rows, cols)))
}
}
impl Matrix {
pub fn print(&self) {
print!("[");
if self.rows() > 10 || self.cols() > 10 {
print!("...");
}
for i in 0..self.rows() {
for j in 0..self.cols() {
if i == 0 {
print!("{:.2} ", self.data[at!(i, j, self.cols())]);
} else {
print!(" {:.2}", self.data[at!(i, j, self.cols())]);
}
}
if i == self.shape.0 - 1 {
break;
}
print!("\n");
}
println!("], dtype=f32")
}
}
impl Matrix {
pub fn new(data: Vec<f32>, shape: Shape) -> Self {
if shape.0 * shape.1 != data.len() {
return Self::default();
}
Self {
data,
shape,
nrows: shape.0,
ncols: shape.1,
}
}
pub fn default() -> Self {
Self {
data: vec![0f32; 9],
shape: (3, 3),
nrows: 3,
ncols: 3,
}
}
pub fn init(value: f32, shape: Shape) -> Self {
Self::from_shape(value, shape)
}
pub fn eye(size: usize) -> Self {
let mut data = vec![0f32; size * size];
(0..size).for_each(|i| data[i * size + i] = 1f32);
Self::new(data, (size, size))
}
pub fn identity(size: usize) -> Self {
Self::eye(size)
}
pub fn from_slice(arr: &[f32], shape: Shape) -> Option<Self> {
if shape.0 * shape.1 != arr.len() {
return None;
}
Some(Self::new(arr.to_owned(), shape))
}
pub fn zeros(shape: Shape) -> Self {
Self::from_shape(0f32, shape)
}
pub fn ones(shape: Shape) -> Self {
Self::from_shape(1f32, shape)
}
pub fn zeros_like(other: &Self) -> Self {
Self::from_shape(0f32, other.shape)
}
pub fn ones_like(other: &Self) -> Self {
Self::from_shape(1f32, other.shape)
}
pub fn random_like(matrix: &Self) -> Self {
Self::randomize_range(0f32, 1f32, matrix.shape)
}
pub fn randomize_range(start: f32, end: f32, shape: Shape) -> Self {
let mut rng = rand::thread_rng();
let (rows, cols) = shape;
let len: usize = rows * cols;
let data: Vec<f32> = (0..len).map(|_| rng.gen_range(start..=end)).collect();
Self::new(data, shape)
}
pub fn randomize(shape: Shape) -> Self {
Self::randomize_range(-1f32, 1f32, shape)
}
pub fn from_file(path: &'static str) -> Self {
let data =
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read file: {}", path));
data.parse::<Self>().unwrap_or_else(|_| Self::default())
}
fn from_shape(value: f32, shape: Shape) -> Self {
let (rows, cols) = shape;
let len: usize = rows * cols;
let data = vec![value; len];
Self::new(data, shape)
}
}
pub enum Dimension {
Row = 0,
Col = 1,
}
impl Matrix {
pub fn to_tensor(&self) {
todo!()
}
pub fn reshape(&mut self, new_shape: Shape) {
if new_shape.0 * new_shape.1 != self.size() {
println!("Can not reshape.. Keeping old dimensions for now");
}
self.shape = new_shape;
}
pub fn cols(&self) -> usize {
self.shape.1
}
pub fn rows(&self) -> usize {
self.shape.0
}
pub fn size(&self) -> usize {
self.shape.0 * self.shape.1
}
pub fn get(&self, i: usize, j: usize) -> f32 {
self.data[at!(i, j, self.cols())]
}
pub fn get_vec_slice(
&self,
start_row: usize,
start_col: usize,
dy: usize,
dx: usize,
) -> Vec<f32> {
iproduct!(start_row..start_row + dy, start_col..start_col + dx)
.map(|(i, j)| self.get(i, j))
.collect()
}
pub fn set(&mut self, i: usize, j: usize, value: f32) {
self.data[i * j + self.ncols] = value;
}
pub fn inverse_at(&self, idx: usize) -> Shape {
let row = idx / self.cols();
let col = idx % self.cols();
(row, col)
}
pub fn max(&self) -> f32 {
*self
.data
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap()
}
pub fn min(&self) -> f32 {
*self
.data
.iter()
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap()
}
pub fn argmax(&self, rowcol: usize, dimension: Dimension) -> Option<f32> {
match dimension {
Dimension::Row => {
if rowcol >= self.rows() - 1 {
return None;
}
self.data
.par_iter()
.skip(rowcol * self.cols())
.take(self.cols())
.max_by(|a, b| a.partial_cmp(b).unwrap())
.copied()
}
Dimension::Col => {
if rowcol >= self.cols() - 1 {
return None;
}
self.data
.par_iter()
.skip(rowcol)
.step_by(self.cols())
.max_by(|a, b| a.partial_cmp(b).unwrap())
.copied()
}
}
}
pub fn argmin(&self, rowcol: usize, dimension: Dimension) -> Option<f32> {
match dimension {
Dimension::Row => {
if rowcol >= self.rows() - 1 {
return None;
}
self.data
.par_iter()
.skip(rowcol * self.cols())
.take(self.cols())
.min_by(|a, b| a.partial_cmp(b).unwrap())
.copied()
}
Dimension::Col => {
if rowcol >= self.cols() - 1 {
return None;
}
self.data
.par_iter()
.skip(rowcol)
.step_by(self.cols())
.min_by(|a, b| a.partial_cmp(b).unwrap())
.copied()
}
}
}
pub fn cumsum(&self) -> f32 {
self.data.par_iter().sum()
}
pub fn cumprod(&self) -> f32 {
self.data.par_iter().product()
}
pub fn avg(&self) -> f32 {
self.data.par_iter().sum::<f32>() / self.data.len() as f32
}
pub fn mean(&self) -> f32 {
self.avg()
}
pub fn median(&self) -> f32 {
match self.data.len() % 2 {
0 => {
let half: usize = self.data.len() / 2;
self.data
.iter()
.sorted_by(|a, b| a.partial_cmp(&b).unwrap())
.skip(half - 1)
.take(2)
.sum::<f32>()
/ 2.0
}
1 => {
let half: usize = self.data.len() / 2;
self.data
.iter()
.sorted_by(|a, b| a.partial_cmp(&b).unwrap())
.nth(half)
.copied()
.unwrap()
}
_ => unreachable!(),
}
}
pub fn sum(&self, rowcol: usize, dimension: Dimension) -> f32 {
match dimension {
Dimension::Row => self
.data
.par_iter()
.skip(rowcol * self.cols())
.take(self.cols())
.sum(),
Dimension::Col => self.data.par_iter().skip(rowcol).step_by(self.cols()).sum(),
}
}
pub fn prod(&self, rowcol: usize, dimension: Dimension) -> f32 {
match dimension {
Dimension::Row => self
.data
.par_iter()
.skip(rowcol * self.cols())
.take(self.cols())
.product(),
Dimension::Col => self
.data
.par_iter()
.skip(rowcol)
.step_by(self.cols())
.product(),
}
}
}
pub trait MatrixLinAlg {
fn add(&self, other: &Self) -> Self;
fn sub(&self, other: &Self) -> Self;
fn sub_abs(&self, other: &Self) -> Self;
fn mul(&self, other: &Self) -> Self;
fn div(&self, other: &Self) -> Self;
fn add_val(&self, val: f32) -> Self;
fn sub_val(&self, val: f32) -> Self;
fn mul_val(&self, val: f32) -> Self;
fn div_val(&self, val: f32) -> Self;
fn log(&self, base: f32) -> Self;
fn ln(&self) -> Self;
fn tanh(&self) -> Self;
fn pow(&self, val: i32) -> Self;
fn abs(&self) -> Self;
fn add_self(&mut self, other: &Self);
fn sub_self(&mut self, other: &Self);
fn mul_self(&mut self, other: &Self);
fn div_self(&mut self, other: &Self);
fn abs_self(&mut self);
fn add_val_self(&mut self, val: f32);
fn sub_val_self(&mut self, val: f32);
fn mul_val_self(&mut self, val: f32);
fn div_val_self(&mut self, val: f32);
fn matmul(&self, other: &Self) -> Self;
fn determinant(&self) -> f32;
fn transpose(&mut self);
fn t(&mut self);
fn transpose_copy(&self) -> Self;
fn eigenvalue(&self) -> f32;
}
impl MatrixLinAlg for Matrix {
fn add(&self, other: &Self) -> Self {
if self.rows() != other.rows() || self.cols() != other.cols() {
panic!("NOOO!");
}
let mut mat = Self::zeros_like(self);
for i in 0..self.rows() {
for j in 0..self.cols() {
let a = self.get(i, j);
let b = other.get(i, j);
mat.data[at!(i, j, self.cols())] = a + b;
}
}
mat
}
fn sub(&self, other: &Self) -> Self {
if self.rows() != other.rows() || self.cols() != other.cols() {
panic!("NOOO!");
}
let mut mat = Self::zeros_like(self);
for i in 0..self.rows() {
for j in 0..self.cols() {
let a = self.get(i, j);
let b = other.get(i, j);
mat.data[at!(i, j, self.cols())] = a - b;
}
}
mat
}
fn sub_abs(&self, other: &Self) -> Self {
if self.rows() != other.rows() || self.cols() != other.cols() {
panic!("NOOO!");
}
let mut mat = Self::zeros_like(self);
for i in 0..self.rows() {
for j in 0..self.cols() {
let a = self.get(i, j);
let b = other.get(i, j);
mat.data[at!(i, j, self.cols())] = (a - b).abs();
}
}
mat
}
fn mul(&self, other: &Self) -> Self {
if self.rows() != other.rows() || self.cols() != other.cols() {
panic!("NOOO!");
}
let mut mat = Self::zeros_like(self);
for i in 0..self.rows() {
for j in 0..self.cols() {
let a = self.get(i, j);
let b = other.get(i, j);
mat.data[at!(i, j, self.cols())] = a * b;
}
}
mat
}
fn div(&self, other: &Self) -> Self {
if self.rows() != other.rows() || self.cols() != other.cols() {
panic!("NOOO!");
}
if other.any(|e| e == &0f32) {
panic!("NOOOOO")
}
let mut mat = Self::zeros_like(self);
for i in 0..self.rows() {
for j in 0..self.cols() {
let a = self.get(i, j);
let b = other.get(i, j);
mat.data[at!(i, j, self.cols())] = a / b;
}
}
mat
}
fn add_val(&self, val: f32) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e + val).collect();
Self::new(data, self.shape)
}
fn sub_val(&self, val: f32) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e - val).collect();
Self::new(data, self.shape)
}
fn mul_val(&self, val: f32) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e * val).collect();
Self::new(data, self.shape)
}
fn div_val(&self, val: f32) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e / val).collect();
Self::new(data, self.shape)
}
fn log(&self, base: f32) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e.log(base)).collect();
Self::new(data, self.shape)
}
fn ln(&self) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e.ln()).collect();
Self::new(data, self.shape)
}
fn tanh(&self) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e.tanh()).collect();
Self::new(data, self.shape)
}
fn pow(&self, val: i32) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e.powi(val)).collect();
Self::new(data, self.shape)
}
fn abs(&self) -> Self {
let data: Vec<f32> = self.data.par_iter().map(|&e| e.abs()).collect();
Self::new(data, self.shape)
}
fn add_self(&mut self, other: &Self) {
self.data
.par_iter_mut()
.zip(&other.data)
.for_each(|(a, b)| *a += b);
}
fn sub_self(&mut self, other: &Self) {
self.data
.par_iter_mut()
.zip(&other.data)
.for_each(|(a, b)| *a -= b);
}
fn mul_self(&mut self, other: &Self) {
self.data
.par_iter_mut()
.zip(&other.data)
.for_each(|(a, b)| *a *= b);
}
fn div_self(&mut self, other: &Self) {
self.data
.par_iter_mut()
.zip(&other.data)
.for_each(|(a, b)| *a /= b);
}
fn abs_self(&mut self) {
self.data.par_iter_mut().for_each(|e| *e = e.abs());
}
fn add_val_self(&mut self, val: f32) {
self.data.par_iter_mut().for_each(|e| *e += val);
}
fn sub_val_self(&mut self, val: f32) {
self.data.par_iter_mut().for_each(|e| *e -= val);
}
fn mul_val_self(&mut self, val: f32) {
self.data.par_iter_mut().for_each(|e| *e *= val);
}
fn div_val_self(&mut self, val: f32) {
self.data.par_iter_mut().for_each(|e| *e /= val);
}
fn matmul(&self, other: &Self) -> Self {
if self.cols() != other.rows() {
panic!("Oops, dimensions do not match");
}
let r1 = self.rows();
let c1 = self.cols();
let c2 = other.cols();
let mut data = vec![0f32; c2 * r1];
let t_other = other.transpose_copy();
for i in 0..r1 {
for j in 0..c2 {
let dot_product = (0..c1)
.into_par_iter()
.map(|k| self.data[at!(i, k, c1)] * t_other.data[at!(j, k, t_other.cols())])
.sum();
data[at!(i, j, c2)] = dot_product;
}
}
Self::new(data, (c2, r1))
}
fn determinant(&self) -> f32 {
match self.size() {
1 => self.data[0],
4 => determinant_2x2(self),
9 => determinant_3x3(self),
_ => {
todo!()
}
}
}
fn transpose(&mut self) {
for i in 0..self.rows() {
for j in (i + 1)..self.cols() {
let lhs = at!(i, j, self.cols());
let rhs = at!(j, i, self.rows());
self.data.swap(lhs, rhs);
}
}
swap(&mut self.shape.0, &mut self.shape.1);
}
fn t(&mut self) {
self.transpose()
}
fn transpose_copy(&self) -> Matrix {
let mut res = self.clone();
res.transpose();
res
}
fn eigenvalue(&self) -> f32 {
todo!()
}
}
pub trait MatrixPredicates {
fn count_where<'a, F>(&'a self, pred: F) -> usize
where
F: Fn(&'a f32) -> bool + 'static;
fn sum_where<'a, F>(&'a self, pred: F) -> f32
where
F: Fn(&'a f32) -> bool + 'static;
fn any<'a, F>(&'a self, pred: F) -> bool
where
F: Fn(&'a f32) -> bool + 'static;
fn all<'a, F>(&'a self, pred: F) -> bool
where
F: Fn(&'a f32) -> bool + 'static;
fn find<'a, F>(&'a self, pred: F) -> Option<Shape>
where
F: Fn(&'a f32) -> bool + 'static;
fn find_all<'a, F>(&'a self, pred: F) -> Option<Vec<Shape>>
where
F: Fn(&'a f32) -> bool + 'static;
}
impl MatrixPredicates for Matrix {
fn count_where<'a, F>(&'a self, pred: F) -> usize
where
F: Fn(&'a f32) -> bool + 'static,
{
self.data.iter().filter(|&e| pred(e)).count()
}
fn sum_where<'a, F>(&'a self, pred: F) -> f32
where
F: Fn(&'a f32) -> bool + 'static,
{
self.data.iter().filter(|&e| pred(e)).sum()
}
fn any<'a, F>(&'a self, pred: F) -> bool
where
F: Fn(&'a f32) -> bool + 'static,
{
self.data.iter().any(pred)
}
fn all<'a, F>(&'a self, pred: F) -> bool
where
F: Fn(&'a f32) -> bool + 'static,
{
self.data.iter().all(pred)
}
fn find<'a, F>(&'a self, pred: F) -> Option<Shape>
where
F: Fn(&'a f32) -> bool + 'static,
{
if let Some((idx, _)) = self.data.iter().find_position(|&e| pred(e)) {
return Some(self.inverse_at(idx));
}
None
}
fn find_all<'a, F>(&'a self, pred: F) -> Option<Vec<Shape>>
where
F: Fn(&'a f32) -> bool + 'static,
{
let data: Vec<Shape> = self
.data
.iter()
.enumerate()
.filter_map(|(idx, elem)| {
if pred(elem) {
Some(self.inverse_at(idx))
} else {
None
}
})
.collect();
if data.is_empty() {
None
} else {
Some(data)
}
}
}
fn swap(lhs: &mut usize, rhs: &mut usize) {
let temp = *lhs;
*lhs = *rhs;
*rhs = temp;
}