use super::DimensionMismatch;
use crate::algorithm::vector;
use crate::scalar::Scalar;
use crate::storage::Storage;
pub fn add<S1, S2, T>(
a: &S1,
a_rows: usize,
a_cols: usize,
b: &S2,
b_rows: usize,
b_cols: usize,
out: &mut [T],
) -> Result<(), DimensionMismatch>
where
S1: Storage<Item = T>,
S2: Storage<Item = T>,
T: Scalar,
{
if a_rows != b_rows || a_cols != b_cols {
return Err(DimensionMismatch);
}
let len = a_rows * a_cols;
if a.len() != len || b.len() != len || out.len() != len {
return Err(DimensionMismatch);
}
for (i, slot) in out.iter_mut().enumerate() {
let (Some(&x), Some(&y)) = (a.get(i), b.get(i)) else {
return Err(DimensionMismatch);
};
*slot = x.add(y);
}
Ok(())
}
pub fn sub<S1, S2, T>(
a: &S1,
a_rows: usize,
a_cols: usize,
b: &S2,
b_rows: usize,
b_cols: usize,
out: &mut [T],
) -> Result<(), DimensionMismatch>
where
S1: Storage<Item = T>,
S2: Storage<Item = T>,
T: Scalar,
{
if a_rows != b_rows || a_cols != b_cols {
return Err(DimensionMismatch);
}
let len = a_rows * a_cols;
if a.len() != len || b.len() != len || out.len() != len {
return Err(DimensionMismatch);
}
for (i, slot) in out.iter_mut().enumerate() {
let (Some(&x), Some(&y)) = (a.get(i), b.get(i)) else {
return Err(DimensionMismatch);
};
*slot = x.sub(y);
}
Ok(())
}
pub fn mul_scalar<S, T>(
a: &S,
a_rows: usize,
a_cols: usize,
factor: T,
out: &mut [T],
) -> Result<(), DimensionMismatch>
where
S: Storage<Item = T>,
T: Scalar,
{
let len = a_rows * a_cols;
if a.len() != len || out.len() != len {
return Err(DimensionMismatch);
}
for (i, slot) in out.iter_mut().enumerate() {
let Some(&x) = a.get(i) else {
return Err(DimensionMismatch);
};
*slot = x.mul(factor);
}
Ok(())
}
struct Row<'a, S> {
storage: &'a S,
start: usize,
len: usize,
}
impl<S: Storage> Storage for Row<'_, S> {
type Item = S::Item;
fn len(&self) -> usize {
self.len
}
fn get(&self, index: usize) -> Option<&Self::Item> {
if index >= self.len {
return None;
}
self.storage.get(self.start + index)
}
}
struct Column<'a, S> {
storage: &'a S,
start: usize,
stride: usize,
len: usize,
}
impl<S: Storage> Storage for Column<'_, S> {
type Item = S::Item;
fn len(&self) -> usize {
self.len
}
fn get(&self, index: usize) -> Option<&Self::Item> {
if index >= self.len {
return None;
}
self.storage.get(self.start + index * self.stride)
}
}
pub fn mul_vector<S, V, T>(
a: &S,
a_rows: usize,
a_cols: usize,
v: &V,
out: &mut [T],
) -> Result<(), DimensionMismatch>
where
S: Storage<Item = T>,
V: Storage<Item = T>,
T: Scalar,
{
if a.len() != a_rows * a_cols || v.len() != a_cols || out.len() != a_rows {
return Err(DimensionMismatch);
}
for i in 0..a_rows {
let row = Row {
storage: a,
start: i * a_cols,
len: a_cols,
};
let Ok(value) = vector::dot(&row, v) else {
return Err(DimensionMismatch);
};
let Some(slot) = out.get_mut(i) else {
return Err(DimensionMismatch);
};
*slot = value;
}
Ok(())
}
pub fn mul_matrix<S1, S2, T>(
a: &S1,
a_rows: usize,
a_cols: usize,
b: &S2,
b_rows: usize,
b_cols: usize,
out: &mut [T],
) -> Result<(), DimensionMismatch>
where
S1: Storage<Item = T>,
S2: Storage<Item = T>,
T: Scalar,
{
if a_cols != b_rows {
return Err(DimensionMismatch);
}
if a.len() != a_rows * a_cols || b.len() != b_rows * b_cols || out.len() != a_rows * b_cols {
return Err(DimensionMismatch);
}
for i in 0..a_rows {
let row = Row {
storage: a,
start: i * a_cols,
len: a_cols,
};
for j in 0..b_cols {
let col = Column {
storage: b,
start: j,
stride: b_cols,
len: b_rows,
};
let Ok(value) = vector::dot(&row, &col) else {
return Err(DimensionMismatch);
};
let Some(slot) = out.get_mut(i * b_cols + j) else {
return Err(DimensionMismatch);
};
*slot = value;
}
}
Ok(())
}
pub fn transpose<S, T>(
a: &S,
a_rows: usize,
a_cols: usize,
out: &mut [T],
) -> Result<(), DimensionMismatch>
where
S: Storage<Item = T>,
T: Copy,
{
let len = a_rows * a_cols;
if a.len() != len || out.len() != len {
return Err(DimensionMismatch);
}
for r in 0..a_rows {
for c in 0..a_cols {
let Some(&value) = a.get(r * a_cols + c) else {
return Err(DimensionMismatch);
};
let Some(slot) = out.get_mut(c * a_rows + r) else {
return Err(DimensionMismatch);
};
*slot = value;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{DimensionMismatch, add, mul_matrix, mul_scalar, mul_vector, sub, transpose};
use crate::storage::StaticStorage;
#[test]
fn adds_matching_dimensions_element_wise() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let b = StaticStorage::new([5.0, 6.0, 7.0, 8.0]);
let mut out = [0.0; 4];
assert_eq!(add(&a, 2, 2, &b, 2, 2, &mut out), Ok(()));
assert_eq!(out, [6.0, 8.0, 10.0, 12.0]);
}
#[test]
fn mismatched_row_counts_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let b = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let mut out = [0.0; 4];
assert_eq!(add(&a, 3, 2, &b, 2, 2, &mut out), Err(DimensionMismatch));
}
#[test]
fn mismatched_column_counts_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let b = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let mut out = [0.0; 6];
assert_eq!(add(&a, 2, 3, &b, 3, 2, &mut out), Err(DimensionMismatch));
}
#[test]
fn mismatched_output_length_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let b = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let mut out = [0.0; 3];
assert_eq!(add(&a, 2, 2, &b, 2, 2, &mut out), Err(DimensionMismatch));
}
#[test]
fn subs_matching_dimensions_element_wise() {
let a = StaticStorage::new([5.0, 6.0, 7.0, 8.0]);
let b = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let mut out = [0.0; 4];
assert_eq!(sub(&a, 2, 2, &b, 2, 2, &mut out), Ok(()));
assert_eq!(out, [4.0, 4.0, 4.0, 4.0]);
}
#[test]
fn sub_mismatched_dimensions_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let b = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let mut out = [0.0; 4];
assert_eq!(sub(&a, 3, 2, &b, 2, 2, &mut out), Err(DimensionMismatch));
}
#[test]
fn mul_scalar_by_known_factor() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let mut out = [0.0; 4];
assert_eq!(mul_scalar(&a, 2, 2, 2.0, &mut out), Ok(()));
assert_eq!(out, [2.0, 4.0, 6.0, 8.0]);
}
#[test]
fn mul_scalar_mismatched_dimensions_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let mut out = [0.0; 3];
assert_eq!(mul_scalar(&a, 2, 2, 2.0, &mut out), Err(DimensionMismatch));
}
#[test]
fn mul_vector_of_known_matrix_and_vector() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let v = StaticStorage::new([1.0, 1.0]);
let mut out = [0.0; 2];
assert_eq!(mul_vector(&a, 2, 2, &v, &mut out), Ok(()));
assert_eq!(out, [3.0, 7.0]);
}
#[test]
fn mul_vector_mismatched_inner_dimension_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let v = StaticStorage::new([1.0, 1.0, 1.0]);
let mut out = [0.0; 2];
assert_eq!(mul_vector(&a, 2, 2, &v, &mut out), Err(DimensionMismatch));
}
#[test]
fn mul_matrix_of_known_matrices() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0]);
let b = StaticStorage::new([5.0, 6.0, 7.0, 8.0]);
let mut out = [0.0; 4];
assert_eq!(mul_matrix(&a, 2, 2, &b, 2, 2, &mut out), Ok(()));
assert_eq!(out, [19.0, 22.0, 43.0, 50.0]);
}
#[test]
fn mul_matrix_mismatched_inner_dimension_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); let b = StaticStorage::new([1.0, 2.0, 3.0, 4.0]); let mut out = [0.0; 4];
assert_eq!(
mul_matrix(&a, 2, 3, &b, 2, 2, &mut out),
Err(DimensionMismatch)
);
}
#[test]
fn transposes_known_matrix() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let mut out = [0.0; 6];
assert_eq!(transpose(&a, 2, 3, &mut out), Ok(()));
assert_eq!(out, [1.0, 4.0, 2.0, 5.0, 3.0, 6.0]);
}
#[test]
fn transpose_mismatched_output_length_is_an_error_not_a_panic() {
let a = StaticStorage::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let mut out = [0.0; 5];
assert_eq!(transpose(&a, 2, 3, &mut out), Err(DimensionMismatch));
}
}