use crate::error::QuantError;
#[derive(Clone, Debug)]
pub struct Matrix2d<T> {
data: Vec<T>,
width: usize,
height: usize,
}
impl<T: Clone + Default> Matrix2d<T> {
pub fn new(width: usize, height: usize) -> Self {
Self {
data: vec![T::default(); width * height],
width,
height,
}
}
pub fn from_vec(data: Vec<T>, width: usize, height: usize) -> Self {
assert!(width * height == data.len());
Self {
data,
width,
height,
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn get(&self, i: usize, j: usize) -> Option<&T> {
j.checked_mul(self.width)
.map_or_else(|| None, |a| a.checked_add(i))
.map_or_else(|| None, |index| self.data.get(index))
}
pub fn get_mut(&mut self, i: usize, j: usize) -> Option<&mut T> {
j.checked_mul(self.width)
.map_or_else(|| None, |a| a.checked_add(i))
.map_or_else(|| None, move |index| self.data.get_mut(index))
}
pub fn into_raw_vec(self) -> Vec<T> {
self.data
}
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.data.iter()
}
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
self.data.iter_mut()
}
}
impl<T: Clone + Default + Copy + core::ops::MulAssign<T>> Matrix2d<T> {
pub fn multiply_row_by_scalar(&mut self, row: usize, factor: T) -> Result<(), QuantError> {
for i in 0..self.width {
*self
.get_mut(i, row)
.ok_or("Could not multiply row by scalar")? *= factor;
}
Ok(())
}
}
impl<T> Matrix2d<T>
where
T: Clone + Default + Copy + core::ops::AddAssign<T> + core::ops::Mul<T, Output = T>,
{
pub fn add_row_multiple(
&mut self,
from_row: usize,
to_row: usize,
factor: T,
) -> Result<(), QuantError> {
for i in 0..self.width {
let from_item = *self
.get(i, from_row)
.ok_or("Index out of range in add_row_multiply")?
* factor;
*self
.get_mut(i, to_row)
.ok_or("Index out of range in add_row_multiply")? += from_item;
}
Ok(())
}
}
impl<T> Matrix2d<T>
where
T: Clone
+ Default
+ Copy
+ core::ops::AddAssign<T>
+ core::ops::Mul<T, Output = T>
+ core::ops::MulAssign<T>
+ core::ops::Neg<Output = T>
+ crate::MatrixComponent,
{
pub fn matrix_inverse(&self) -> Result<Self, QuantError> {
let mut a = self.clone();
let mut result = Self::new(a.width, a.height);
for i in 0..a.width {
if let Some(identity) = result.get_mut(i, i) {
*identity = T::identity();
}
}
for i in 0..a.width {
result.multiply_row_by_scalar(
i,
T::inverse(a.get(i, i).ok_or("Could not reduce matrix")?),
)?;
a.multiply_row_by_scalar(i, T::inverse(a.get(i, i).ok_or("Could not reduce matrix")?))?;
for j in (i + 1)..a.height {
result.add_row_multiple(
i,
j,
-*a.get(i, j).ok_or("Could not reduce matrix, inner loop")?,
)?;
a.add_row_multiple(
i,
j,
-*a.get(i, j).ok_or("Could not reduce matrix, inner loop")?,
)?;
}
}
for i in (0..a.width).rev() {
let mut j = i as isize - 1;
while j >= 0 {
result.add_row_multiple(
i,
j as usize,
-*a.get(i, j as usize).ok_or("Could not back substitute")?,
)?;
a.add_row_multiple(
i,
j as usize,
-*a.get(i, j as usize).ok_or("Could not back substitute")?,
)?;
j -= 1;
}
}
Ok(result)
}
}
impl<T> core::ops::Mul<Vec<T>> for Matrix2d<T>
where
T: Clone + Default + Copy + core::ops::AddAssign<T> + core::ops::Mul<T, Output = T>,
{
type Output = Vec<T>;
fn mul(self, other: Vec<T>) -> Self::Output {
assert!(other.len() == self.width());
let mut result = Vec::with_capacity(self.height());
for row in 0..self.height() {
let mut sum = T::default();
for col in 0..self.width() {
sum += *self
.get(col, row)
.expect("valid access for row and col in Matrix2d")
* *other.get(col).expect("index in range for other");
}
result.push(sum);
}
result
}
}
impl<T> core::ops::Mul<f64> for Matrix2d<T>
where
T: Clone + Default + Copy + core::ops::Mul<f64, Output = T>,
{
type Output = Self;
fn mul(self, other: f64) -> Self {
Self {
data: self.iter().map(|&a| a * other).collect(),
width: self.width,
height: self.height,
}
}
}
#[derive(Clone, Debug)]
pub struct Matrix3d<T> {
data: Vec<T>,
width: usize,
height: usize,
depth: usize,
}
impl<T: Clone + Default> Matrix3d<T> {
pub fn new(width: usize, height: usize, depth: usize) -> Self {
Self {
data: vec![T::default(); width * height * depth],
width,
height,
depth,
}
}
pub fn from_vec(data: Vec<T>, width: usize, height: usize, depth: usize) -> Self {
assert!(width * height * depth == data.len());
Self {
data,
width,
height,
depth,
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn get(&self, i: usize, j: usize, k: usize) -> Option<&T> {
j.checked_mul(self.width)
.map_or_else(|| None, |a| a.checked_mul(self.depth))
.map_or_else(
|| None,
|a| {
i.checked_mul(self.depth)
.map_or_else(|| None, |i_term| a.checked_add(i_term))
},
)
.map_or_else(|| None, |a| a.checked_add(k))
.map_or_else(|| None, |index| self.data.get(index))
}
pub fn get_mut(&mut self, i: usize, j: usize, k: usize) -> Option<&mut T> {
j.checked_mul(self.width)
.map_or_else(|| None, |a| a.checked_mul(self.depth))
.map_or_else(
|| None,
|a| {
i.checked_mul(self.depth)
.map_or_else(|| None, |i_term| a.checked_add(i_term))
},
)
.map_or_else(|| None, |a| a.checked_add(k))
.map_or_else(|| None, move |index| self.data.get_mut(index))
}
pub fn into_raw_vec(self) -> Vec<T> {
self.data
}
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.data.iter()
}
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
self.data.iter_mut()
}
}
#[cfg(test)]
mod tests {
use crate::matrix::{Matrix2d, Matrix3d};
#[test]
fn array_into_raw() {
let mat = Matrix2d::<i32>::new(2, 2);
assert_eq!(mat.into_raw_vec(), vec![0; 4]);
}
#[test]
fn array_indexing_2d() {
let mat = Matrix2d::from_vec(vec![0, 1, 2, 3], 2, 2);
assert_eq!(*mat.get(0, 0).unwrap(), 0);
assert_eq!(*mat.get(1, 0).unwrap(), 1);
assert_eq!(*mat.get(0, 1).unwrap(), 2);
assert_eq!(*mat.get(1, 1).unwrap(), 3);
let mat = Matrix2d::from_vec(vec![0, 1, 2, 3, 4, 5], 2, 3);
assert_eq!(*mat.get(0, 0).unwrap(), 0);
assert_eq!(*mat.get(1, 0).unwrap(), 1);
assert_eq!(*mat.get(0, 1).unwrap(), 2);
assert_eq!(*mat.get(1, 1).unwrap(), 3);
assert_eq!(*mat.get(0, 2).unwrap(), 4);
assert_eq!(*mat.get(1, 2).unwrap(), 5);
let mat = Matrix2d::from_vec(vec![0, 1, 2, 3, 4, 5], 3, 2);
assert_eq!(*mat.get(0, 0).unwrap(), 0);
assert_eq!(*mat.get(1, 0).unwrap(), 1);
assert_eq!(*mat.get(2, 0).unwrap(), 2);
assert_eq!(*mat.get(0, 1).unwrap(), 3);
assert_eq!(*mat.get(1, 1).unwrap(), 4);
assert_eq!(*mat.get(2, 1).unwrap(), 5);
}
#[test]
fn array_indexing_3d() {
let mat = Matrix3d::from_vec(vec![0, 1, 2, 3, 4, 5, 6, 7], 2, 2, 2);
assert_eq!(*mat.get(0, 0, 0).unwrap(), 0);
assert_eq!(*mat.get(0, 0, 1).unwrap(), 1);
assert_eq!(*mat.get(1, 0, 0).unwrap(), 2);
assert_eq!(*mat.get(1, 0, 1).unwrap(), 3);
assert_eq!(*mat.get(0, 1, 0).unwrap(), 4);
assert_eq!(*mat.get(0, 1, 1).unwrap(), 5);
assert_eq!(*mat.get(1, 1, 0).unwrap(), 6);
assert_eq!(*mat.get(1, 1, 1).unwrap(), 7);
}
#[test]
fn matrix_inverse() {
let mat = Matrix2d::from_vec(vec![4.0, 7., 2., 6.], 2, 2);
let inv_result = mat.matrix_inverse().unwrap();
let inv_expected = Matrix2d::from_vec(vec![0.6, -0.7, -0.2, 0.4], 2, 2);
for (result, expected) in inv_result.iter().zip(inv_expected.iter()) {
assert!(f32::abs(result - expected) <= 0.005);
}
}
}