Compare commits

..

No commits in common. "main" and "0.4.0" have entirely different histories.
main ... 0.4.0

5 changed files with 65 additions and 97 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "matrix-basic" name = "matrix-basic"
version = "0.5.0" version = "0.4.0"
edition = "2021" edition = "2021"
authors = ["Sayantan Santra <sayantan[dot]santra689[at]gmail[dot]com"] authors = ["Sayantan Santra <sayantan[dot]santra689[at]gmail[dot]com"]
license = "GPL-3.0" license = "GPL-3.0"

View file

@ -1,11 +1,10 @@
[![crate.io badge](https://img.shields.io/crates/d/matrix-basic)](https://crates.io/crates/matrix-basic) [![crate.io badge](https://img.shields.io/crates/d/matrix-basic)](https://crates.io/crates/matrix-basic)
# `matrix-basic` # `matrix-basic`
### A Rust crate for very basic matrix operations. ### A Rust crate for very basic matrix operations
This is a crate for very basic matrix operations with any type that supports addition, substraction, multiplication, This is a crate for very basic matrix operations with any type that supports addition, substraction,
negation, has a zero defined, and implements the Copy trait. Additional properties (e.g. division, existence of one etc.) and multiplication. Additional properties might be needed for certain operations.
might be needed for certain operations.
I created it mostly to learn how to use generic types and traits. I created it mostly to learn how to use generic types and traits.

View file

@ -1,29 +0,0 @@
use std::{
error::Error,
fmt::{self, Display, Formatter},
};
/// Error type for using in this crate. Mostly to reduce writing
/// error description every time.
#[derive(Debug, PartialEq)]
pub enum MatrixError {
/// Provided matrix isn't square.
NotSquare,
/// provided matrix is singular.
Singular,
/// Provided array has unequal rows.
UnequalRows,
}
impl Display for MatrixError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let out = match *self {
Self::NotSquare => "provided matrix isn't square",
Self::Singular => "provided matrix is singular",
Self::UnequalRows => "provided array has unequal rows",
};
write!(f, "{out}")
}
}
impl Error for MatrixError {}

View file

@ -8,7 +8,6 @@
//! //!
//! Sayantan Santra (2023) //! Sayantan Santra (2023)
use errors::MatrixError;
use num::{ use num::{
traits::{One, Zero}, traits::{One, Zero},
Integer, Integer,
@ -19,7 +18,6 @@ use std::{
result::Result, result::Result,
}; };
pub mod errors;
mod tests; mod tests;
/// Trait a type must satisfy to be element of a matrix. This is /// Trait a type must satisfy to be element of a matrix. This is
@ -54,7 +52,7 @@ pub struct Matrix<T: ToMatrix> {
} }
impl<T: ToMatrix> Matrix<T> { impl<T: ToMatrix> Matrix<T> {
/// Creates a matrix from given 2D "array" in a [`Vec<Vec<T>>`] form. /// Creates a matrix from given 2D "array" in a `Vec<Vec<T>>` form.
/// It'll throw an error if all the given rows aren't of the same size. /// It'll throw an error if all the given rows aren't of the same size.
/// # Example /// # Example
/// ``` /// ```
@ -64,7 +62,7 @@ impl<T: ToMatrix> Matrix<T> {
/// will create the following matrix: /// will create the following matrix:
/// ⌈1, 2, 3⌉ /// ⌈1, 2, 3⌉
/// ⌊4, 5, 6⌋ /// ⌊4, 5, 6⌋
pub fn from(entries: Vec<Vec<T>>) -> Result<Matrix<T>, MatrixError> { pub fn from(entries: Vec<Vec<T>>) -> Result<Matrix<T>, &'static str> {
let mut equal_rows = true; let mut equal_rows = true;
let row_len = entries[0].len(); let row_len = entries[0].len();
for row in &entries { for row in &entries {
@ -76,7 +74,7 @@ impl<T: ToMatrix> Matrix<T> {
if equal_rows { if equal_rows {
Ok(Matrix { entries }) Ok(Matrix { entries })
} else { } else {
Err(MatrixError::UnequalRows) Err("Unequal rows.")
} }
} }
@ -154,7 +152,7 @@ impl<T: ToMatrix> Matrix<T> {
/// let m = Matrix::from(vec![vec![1, 2], vec![3, 4]]).unwrap(); /// let m = Matrix::from(vec![vec![1, 2], vec![3, 4]]).unwrap();
/// assert_eq!(m.det(), Ok(-2)); /// assert_eq!(m.det(), Ok(-2));
/// ``` /// ```
pub fn det(&self) -> Result<T, MatrixError> { pub fn det(&self) -> Result<T, &'static str> {
if self.is_square() { if self.is_square() {
// It's a recursive algorithm using minors. // It's a recursive algorithm using minors.
// TODO: Implement a faster algorithm. // TODO: Implement a faster algorithm.
@ -175,7 +173,7 @@ impl<T: ToMatrix> Matrix<T> {
}; };
Ok(out) Ok(out)
} else { } else {
Err(MatrixError::NotSquare) Err("Provided matrix isn't square.")
} }
} }
@ -189,7 +187,7 @@ impl<T: ToMatrix> Matrix<T> {
/// let m = Matrix::from(vec![vec![1.0, 2.0], vec![3.0, 4.0]]).unwrap(); /// let m = Matrix::from(vec![vec![1.0, 2.0], vec![3.0, 4.0]]).unwrap();
/// assert_eq!(m.det_in_field(), Ok(-2.0)); /// assert_eq!(m.det_in_field(), Ok(-2.0));
/// ``` /// ```
pub fn det_in_field(&self) -> Result<T, MatrixError> pub fn det_in_field(&self) -> Result<T, &'static str>
where where
T: One, T: One,
T: PartialEq, T: PartialEq,
@ -229,7 +227,7 @@ impl<T: ToMatrix> Matrix<T> {
} }
Ok(multiplier) Ok(multiplier)
} else { } else {
Err(MatrixError::NotSquare) Err("Provided matrix isn't square.")
} }
} }
@ -353,7 +351,7 @@ impl<T: ToMatrix> Matrix<T> {
/// let m = Matrix::from(vec![vec![1, 2], vec![3, 4]]).unwrap(); /// let m = Matrix::from(vec![vec![1, 2], vec![3, 4]]).unwrap();
/// assert_eq!(m.trace(), Ok(5)); /// assert_eq!(m.trace(), Ok(5));
/// ``` /// ```
pub fn trace(self) -> Result<T, MatrixError> { pub fn trace(self) -> Result<T, &'static str> {
if self.is_square() { if self.is_square() {
let mut out = self.entries[0][0]; let mut out = self.entries[0][0];
for i in 1..self.height() { for i in 1..self.height() {
@ -361,7 +359,7 @@ impl<T: ToMatrix> Matrix<T> {
} }
Ok(out) Ok(out)
} else { } else {
Err(MatrixError::NotSquare) Err("Provided matrix isn't square.")
} }
} }
@ -410,7 +408,7 @@ impl<T: ToMatrix> Matrix<T> {
/// let n = Matrix::from(vec![vec![-2.0, 1.0], vec![1.5, -0.5]]).unwrap(); /// let n = Matrix::from(vec![vec![-2.0, 1.0], vec![1.5, -0.5]]).unwrap();
/// assert_eq!(m.inverse(), Ok(n)); /// assert_eq!(m.inverse(), Ok(n));
/// ``` /// ```
pub fn inverse(&self) -> Result<Self, MatrixError> pub fn inverse(&self) -> Result<Self, &'static str>
where where
T: Div<Output = T>, T: Div<Output = T>,
T: One, T: One,
@ -438,7 +436,7 @@ impl<T: ToMatrix> Matrix<T> {
} }
} }
if zero_column { if zero_column {
return Err(MatrixError::Singular); return Err("Provided matrix is singular.");
} }
} }
for j in (i + 1)..h { for j in (i + 1)..h {
@ -456,7 +454,7 @@ impl<T: ToMatrix> Matrix<T> {
// Then we reduce the rows // Then we reduce the rows
for i in 0..h { for i in 0..h {
if rows[i][i] == T::zero() { if rows[i][i] == T::zero() {
return Err(MatrixError::Singular); return Err("Provided matrix is singular.");
} }
let divisor = rows[i][i]; let divisor = rows[i][i];
for entry in rows[i].iter_mut().skip(i) { for entry in rows[i].iter_mut().skip(i) {
@ -479,7 +477,7 @@ impl<T: ToMatrix> Matrix<T> {
Ok(Matrix { entries: out }) Ok(Matrix { entries: out })
} else { } else {
Err(MatrixError::NotSquare) Err("Provided matrix isn't square.")
} }
} }
@ -498,7 +496,7 @@ impl<T: Mul<Output = T> + ToMatrix> Mul for Matrix<T> {
fn mul(self, other: Self) -> Self::Output { fn mul(self, other: Self) -> Self::Output {
let width = self.width(); let width = self.width();
if width != other.height() { if width != other.height() {
panic!("row length of first matrix != column length of second matrix"); panic!("Row length of first matrix must be same as column length of second matrix.");
} else { } else {
let mut out = Vec::new(); let mut out = Vec::new();
for row in self.rows() { for row in self.rows() {
@ -529,7 +527,7 @@ impl<T: Mul<Output = T> + ToMatrix> Add for Matrix<T> {
} }
Matrix { entries: out } Matrix { entries: out }
} else { } else {
panic!("provided matrices have different dimensions"); panic!("Both matrices must be of same dimensions.");
} }
} }
} }
@ -553,20 +551,54 @@ impl<T: ToMatrix> Sub for Matrix<T> {
if self.height() == other.height() && self.width() == other.width() { if self.height() == other.height() && self.width() == other.width() {
self + -other self + -other
} else { } else {
panic!("provided matrices have different dimensions"); panic!("Both matrices must be of same dimensions.");
} }
} }
} }
/// Trait for conversion between matrices of different types. /// Trait for conversion between matrices of different types.
/// It only has a [`matrix_from()`](Self::matrix_from()) method. /// It only has a [`matrix_into()`](Self::matrix_into()) method.
/// This is needed since negative trait bound are not supported in stable Rust /// This is needed since negative trait bound are not supported in stable Rust
/// yet, so we'll have a conflict trying to implement [`From`]. /// yet, so we'll have a conflict trying to implement [`From`].
/// I plan to change this to the default From trait as soon as some sort /// I plan to change this to the default From trait as soon as some sort
/// of specialization system is implemented. /// of specialization system is implemented.
/// You can track this issue [here](https://github.com/rust-lang/rust/issues/42721). /// You can track this issue [here](https://github.com/rust-lang/rust/issues/42721).
pub trait MatrixFrom<T: ToMatrix> { pub trait MatrixInto<T: ToMatrix> {
/// Method for getting a matrix of a new type from a matrix of type [`Matrix<T>`]. /// Method for converting a matrix into a matrix of type [`Matrix<T>`].
/// # Example
/// ```
/// use matrix_basic::Matrix;
/// use matrix_basic::MatrixInto;
///
/// let a = Matrix::from(vec![vec![1, 2, 3], vec![0, 1, 2]]).unwrap();
/// let b = Matrix::from(vec![vec![1.0, 2.0, 3.0], vec![0.0, 1.0, 2.0]]).unwrap();
/// let c: Matrix<f64> = a.matrix_into(); // Type annotation is needed here
///
/// assert_eq!(c, b);
/// ```
fn matrix_into(self) -> Matrix<T>;
}
/// Blanket implementation of [`MatrixInto`] for converting [`Matrix<S>`] to [`Matrix<T>`] whenever
/// `S` implements [`Into(T)`]. Look at [`matrix_into`](Self::matrix_into()).
impl<T: ToMatrix, S: ToMatrix + Into<T>> MatrixInto<T> for Matrix<S> {
fn matrix_into(self) -> Matrix<T> {
let mut out = Vec::new();
for row in self.entries {
let mut new_row: Vec<T> = Vec::new();
for entry in row {
new_row.push(entry.into());
}
out.push(new_row)
}
Matrix { entries: out }
}
}
/// Sister trait of [`MatrixInto`]. Basically does the same thing, just with a
/// different syntax.
pub trait MatrixFrom<T> {
/// Method for getting a matrix from another matrix of type [`Matrix<T>`].
/// # Example /// # Example
/// ``` /// ```
/// use matrix_basic::Matrix; /// use matrix_basic::Matrix;
@ -578,48 +610,14 @@ pub trait MatrixFrom<T: ToMatrix> {
/// ///
/// assert_eq!(c, b); /// assert_eq!(c, b);
/// ``` /// ```
fn matrix_from(input: Matrix<T>) -> Self; fn matrix_from(input: T) -> Self;
} }
/// Blanket implementation of [`MatrixFrom<T>`] for converting [`Matrix<S>`] to [`Matrix<T>`] whenever /// Blanket implementation of [`MatrixFrom`] for [`Matrix<S>`] whenever `T` (which is actually some)[`Matrix<U>`] implements
/// `S` implements [`From(T)`]. Look at [`matrix_into`](Self::matrix_into()). /// [`MatrixInto<S>`].
impl<T: ToMatrix, S: ToMatrix + From<T>> MatrixFrom<T> for Matrix<S> { impl<T: MatrixInto<S>, S: ToMatrix> MatrixFrom<T> for Matrix<S> {
fn matrix_from(input: Matrix<T>) -> Self { fn matrix_from(input: T) -> Self {
let mut out = Vec::new(); let out: Matrix<S> = input.matrix_into();
for row in input.entries { out
let mut new_row: Vec<S> = Vec::new();
for entry in row {
new_row.push(entry.into());
}
out.push(new_row)
}
Matrix { entries: out }
}
}
/// Sister trait of [`MatrixFrom`]. Basically does the same thing, just with a
/// different syntax.
pub trait MatrixInto<T> {
/// Method for converting a matrix [`Matrix<T>`] to another type.
/// # Example
/// ```
/// use matrix_basic::Matrix;
/// use matrix_basic::MatrixInto;
///
/// let a = Matrix::from(vec![vec![1, 2, 3], vec![0, 1, 2]]).unwrap();
/// let b = Matrix::from(vec![vec![1.0, 2.0, 3.0], vec![0.0, 1.0, 2.0]]).unwrap();
/// let c: Matrix<f64> = a.matrix_into(); // Type annotation is needed here
///
///
/// assert_eq!(c, b);
/// ```
fn matrix_into(self) -> T;
}
/// Blanket implementation of [`MatrixInto<T>`] for [`Matrix<S>`] whenever `T`
/// (which is actually some)[`Matrix<U>`] implements [`MatrixFrom<S>`].
impl<T: MatrixFrom<S>, S: ToMatrix> MatrixInto<T> for Matrix<S> {
fn matrix_into(self) -> T {
T::matrix_from(self)
} }
} }

View file

@ -72,7 +72,7 @@ fn conversion_test() {
let b = Matrix::from(vec![vec![1.0, 2.0, 3.0], vec![0.0, 1.0, 2.0]]).unwrap(); let b = Matrix::from(vec![vec![1.0, 2.0, 3.0], vec![0.0, 1.0, 2.0]]).unwrap();
use crate::MatrixInto; use crate::MatrixInto;
assert_eq!(b, a.clone().matrix_into()); assert_eq!(a.clone().matrix_into(), b);
use crate::MatrixFrom; use crate::MatrixFrom;
let c = Matrix::<f64>::matrix_from(a); let c = Matrix::<f64>::matrix_from(a);