mirror of
https://github.com/SinTan1729/matrix-basic.git
synced 2025-04-18 08:57:40 -05:00
Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
4cdc29aaa1 | |||
59119e2c30 | |||
d3c8ffd11e | |||
4ce9554e1f | |||
55ac044c39 |
5 changed files with 97 additions and 65 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "matrix-basic"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
authors = ["Sayantan Santra <sayantan[dot]santra689[at]gmail[dot]com"]
|
||||
license = "GPL-3.0"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
[](https://crates.io/crates/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,
|
||||
and multiplication. Additional properties might be needed for certain operations.
|
||||
This is a crate for very basic matrix operations with any type that supports addition, substraction, multiplication,
|
||||
negation, has a zero defined, and implements the Copy trait. Additional properties (e.g. division, existence of one etc.)
|
||||
might be needed for certain operations.
|
||||
|
||||
I created it mostly to learn how to use generic types and traits.
|
||||
|
||||
|
|
29
src/errors.rs
Normal file
29
src/errors.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
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 {}
|
122
src/lib.rs
122
src/lib.rs
|
@ -8,6 +8,7 @@
|
|||
//!
|
||||
//! Sayantan Santra (2023)
|
||||
|
||||
use errors::MatrixError;
|
||||
use num::{
|
||||
traits::{One, Zero},
|
||||
Integer,
|
||||
|
@ -18,6 +19,7 @@ use std::{
|
|||
result::Result,
|
||||
};
|
||||
|
||||
pub mod errors;
|
||||
mod tests;
|
||||
|
||||
/// Trait a type must satisfy to be element of a matrix. This is
|
||||
|
@ -52,7 +54,7 @@ pub struct Matrix<T: ToMatrix> {
|
|||
}
|
||||
|
||||
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.
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -62,7 +64,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
/// will create the following matrix:
|
||||
/// ⌈1, 2, 3⌉
|
||||
/// ⌊4, 5, 6⌋
|
||||
pub fn from(entries: Vec<Vec<T>>) -> Result<Matrix<T>, &'static str> {
|
||||
pub fn from(entries: Vec<Vec<T>>) -> Result<Matrix<T>, MatrixError> {
|
||||
let mut equal_rows = true;
|
||||
let row_len = entries[0].len();
|
||||
for row in &entries {
|
||||
|
@ -74,7 +76,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
if equal_rows {
|
||||
Ok(Matrix { entries })
|
||||
} else {
|
||||
Err("Unequal rows.")
|
||||
Err(MatrixError::UnequalRows)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +154,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
/// let m = Matrix::from(vec![vec![1, 2], vec![3, 4]]).unwrap();
|
||||
/// assert_eq!(m.det(), Ok(-2));
|
||||
/// ```
|
||||
pub fn det(&self) -> Result<T, &'static str> {
|
||||
pub fn det(&self) -> Result<T, MatrixError> {
|
||||
if self.is_square() {
|
||||
// It's a recursive algorithm using minors.
|
||||
// TODO: Implement a faster algorithm.
|
||||
|
@ -173,7 +175,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
};
|
||||
Ok(out)
|
||||
} else {
|
||||
Err("Provided matrix isn't square.")
|
||||
Err(MatrixError::NotSquare)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,7 +189,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
/// 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));
|
||||
/// ```
|
||||
pub fn det_in_field(&self) -> Result<T, &'static str>
|
||||
pub fn det_in_field(&self) -> Result<T, MatrixError>
|
||||
where
|
||||
T: One,
|
||||
T: PartialEq,
|
||||
|
@ -227,7 +229,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
}
|
||||
Ok(multiplier)
|
||||
} else {
|
||||
Err("Provided matrix isn't square.")
|
||||
Err(MatrixError::NotSquare)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,7 +353,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
/// let m = Matrix::from(vec![vec![1, 2], vec![3, 4]]).unwrap();
|
||||
/// assert_eq!(m.trace(), Ok(5));
|
||||
/// ```
|
||||
pub fn trace(self) -> Result<T, &'static str> {
|
||||
pub fn trace(self) -> Result<T, MatrixError> {
|
||||
if self.is_square() {
|
||||
let mut out = self.entries[0][0];
|
||||
for i in 1..self.height() {
|
||||
|
@ -359,7 +361,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
}
|
||||
Ok(out)
|
||||
} else {
|
||||
Err("Provided matrix isn't square.")
|
||||
Err(MatrixError::NotSquare)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,7 +410,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
/// let n = Matrix::from(vec![vec![-2.0, 1.0], vec![1.5, -0.5]]).unwrap();
|
||||
/// assert_eq!(m.inverse(), Ok(n));
|
||||
/// ```
|
||||
pub fn inverse(&self) -> Result<Self, &'static str>
|
||||
pub fn inverse(&self) -> Result<Self, MatrixError>
|
||||
where
|
||||
T: Div<Output = T>,
|
||||
T: One,
|
||||
|
@ -436,7 +438,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
}
|
||||
}
|
||||
if zero_column {
|
||||
return Err("Provided matrix is singular.");
|
||||
return Err(MatrixError::Singular);
|
||||
}
|
||||
}
|
||||
for j in (i + 1)..h {
|
||||
|
@ -454,7 +456,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
// Then we reduce the rows
|
||||
for i in 0..h {
|
||||
if rows[i][i] == T::zero() {
|
||||
return Err("Provided matrix is singular.");
|
||||
return Err(MatrixError::Singular);
|
||||
}
|
||||
let divisor = rows[i][i];
|
||||
for entry in rows[i].iter_mut().skip(i) {
|
||||
|
@ -477,7 +479,7 @@ impl<T: ToMatrix> Matrix<T> {
|
|||
|
||||
Ok(Matrix { entries: out })
|
||||
} else {
|
||||
Err("Provided matrix isn't square.")
|
||||
Err(MatrixError::NotSquare)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,7 +498,7 @@ impl<T: Mul<Output = T> + ToMatrix> Mul for Matrix<T> {
|
|||
fn mul(self, other: Self) -> Self::Output {
|
||||
let width = self.width();
|
||||
if width != other.height() {
|
||||
panic!("Row length of first matrix must be same as column length of second matrix.");
|
||||
panic!("row length of first matrix != column length of second matrix");
|
||||
} else {
|
||||
let mut out = Vec::new();
|
||||
for row in self.rows() {
|
||||
|
@ -527,7 +529,7 @@ impl<T: Mul<Output = T> + ToMatrix> Add for Matrix<T> {
|
|||
}
|
||||
Matrix { entries: out }
|
||||
} else {
|
||||
panic!("Both matrices must be of same dimensions.");
|
||||
panic!("provided matrices have different dimensions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,54 +553,20 @@ impl<T: ToMatrix> Sub for Matrix<T> {
|
|||
if self.height() == other.height() && self.width() == other.width() {
|
||||
self + -other
|
||||
} else {
|
||||
panic!("Both matrices must be of same dimensions.");
|
||||
panic!("provided matrices have different dimensions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for conversion between matrices of different types.
|
||||
/// It only has a [`matrix_into()`](Self::matrix_into()) method.
|
||||
/// It only has a [`matrix_from()`](Self::matrix_from()) method.
|
||||
/// This is needed since negative trait bound are not supported in stable Rust
|
||||
/// 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
|
||||
/// of specialization system is implemented.
|
||||
/// You can track this issue [here](https://github.com/rust-lang/rust/issues/42721).
|
||||
pub trait MatrixInto<T: ToMatrix> {
|
||||
/// 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>`].
|
||||
pub trait MatrixFrom<T: ToMatrix> {
|
||||
/// Method for getting a matrix of a new type from a matrix of type [`Matrix<T>`].
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use matrix_basic::Matrix;
|
||||
|
@ -610,14 +578,48 @@ pub trait MatrixFrom<T> {
|
|||
///
|
||||
/// assert_eq!(c, b);
|
||||
/// ```
|
||||
fn matrix_from(input: T) -> Self;
|
||||
fn matrix_from(input: Matrix<T>) -> Self;
|
||||
}
|
||||
|
||||
/// Blanket implementation of [`MatrixFrom`] for [`Matrix<S>`] whenever `T` (which is actually some)[`Matrix<U>`] implements
|
||||
/// [`MatrixInto<S>`].
|
||||
impl<T: MatrixInto<S>, S: ToMatrix> MatrixFrom<T> for Matrix<S> {
|
||||
fn matrix_from(input: T) -> Self {
|
||||
let out: Matrix<S> = input.matrix_into();
|
||||
out
|
||||
/// Blanket implementation of [`MatrixFrom<T>`] for converting [`Matrix<S>`] to [`Matrix<T>`] whenever
|
||||
/// `S` implements [`From(T)`]. Look at [`matrix_into`](Self::matrix_into()).
|
||||
impl<T: ToMatrix, S: ToMatrix + From<T>> MatrixFrom<T> for Matrix<S> {
|
||||
fn matrix_from(input: Matrix<T>) -> Self {
|
||||
let mut out = Vec::new();
|
||||
for row in input.entries {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
use crate::MatrixInto;
|
||||
assert_eq!(a.clone().matrix_into(), b);
|
||||
assert_eq!(b, a.clone().matrix_into());
|
||||
|
||||
use crate::MatrixFrom;
|
||||
let c = Matrix::<f64>::matrix_from(a);
|
||||
|
|
Loading…
Reference in a new issue