From 521dad9d9f037d4aad6d554d33ea3efec38fc774 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Thu, 20 Jun 2024 23:00:07 +0100 Subject: [PATCH 01/24] feat: add tensor struct --- Cargo.toml | 2 + src/iter.rs | 82 +++++++++++ src/lib.rs | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/shape.rs | 65 +++++++++ 4 files changed, 523 insertions(+), 5 deletions(-) create mode 100644 src/iter.rs create mode 100644 src/shape.rs diff --git a/Cargo.toml b/Cargo.toml index d381f8a..8c4e112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ homepage = "https://github.com/Rust-Scientific-Computing/feotensor" repository = "https://github.com/Rust-Scientific-Computing/feotensor" [dependencies] +itertools = "0.13.0" +num = "0.4.3" diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000..2970769 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,82 @@ +pub struct IndexIterator { + shape: Vec, + current: Vec, + done: bool, +} + +impl IndexIterator { + pub fn new(shape: &[usize]) -> Self { + let current = vec![0; shape.len()]; + IndexIterator { + shape: shape.to_vec(), + current, + done: false, + } + } +} + +impl Iterator for IndexIterator { + type Item = Vec; + + fn next(&mut self) -> Option { + if self.done || self.shape.len() == 0 { + return None; + } + + let result = self.current.clone(); + + for i in (0..self.shape.len()).rev() { + if self.current[i] + 1 < self.shape[i] { + self.current[i] += 1; + break; + } else { + self.current[i] = 0; + if i == 0 { + self.done = true; + } + } + } + + Some(result) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_index_iterator() { + let shape = vec![2, 3]; + let mut iter = IndexIterator::new(&shape); + + assert_eq!(iter.next(), Some(vec![0, 0])); + assert_eq!(iter.next(), Some(vec![0, 1])); + assert_eq!(iter.next(), Some(vec![0, 2])); + assert_eq!(iter.next(), Some(vec![1, 0])); + assert_eq!(iter.next(), Some(vec![1, 1])); + assert_eq!(iter.next(), Some(vec![1, 2])); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_index_iterator_single_dimension() { + let shape = vec![4]; + let mut iter = IndexIterator::new(&shape); + + assert_eq!(iter.next(), Some(vec![0])); + assert_eq!(iter.next(), Some(vec![1])); + assert_eq!(iter.next(), Some(vec![2])); + assert_eq!(iter.next(), Some(vec![3])); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_index_iterator_empty_tensor() { + let shape = vec![]; + let mut iter = IndexIterator::new(&shape); + + assert_eq!(iter.next(), None); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 7d12d9a..db1f665 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,383 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +pub mod shape; +pub mod iter; + +use num::Num; +use std::ops::Add; +use std::ops::Sub; +use std::ops::Mul; +use std::ops::Div; + +use crate::shape::Shape; +use crate::iter::IndexIterator; + +pub type Axes = Vec; + +#[derive(Debug, Clone)] +pub struct Tensor { + data: Vec, + shape: Shape +} + +impl Tensor { + + pub fn new(shape: &Shape, data: &[T]) -> Tensor { + assert!(data.len() == shape.size()); + Tensor {data: data.to_vec(), shape: shape.clone()} + } + + pub fn zeros(shape: &Shape) -> Tensor { + let total_size = shape.size(); + let mut vec = Vec::with_capacity(total_size); + for _ in 0..total_size { vec.push(T::zero()); } + Tensor::new(shape, &vec) + } + + // Properties + pub fn shape(&self) -> &Shape { &self.shape } + pub fn size(&self) -> usize { self.shape.size() } + + // Access methods + pub fn get_element(&self, indices: &[usize]) -> &T { + self.assert_indices(indices).unwrap(); + &self.data[self.calculate_index(indices)] + } + pub fn set_element(&mut self, indices: &[usize], value: T) { + self.assert_indices(indices).unwrap(); + let index = self.calculate_index(indices); + self.data[index] = value; + } + + // // Reduction operations + pub fn sum(&self, axes: Axes) -> Tensor { + let all_axes = (0..self.shape.len()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let sum: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x); + return Tensor::new(&Shape::new(vec![1]), &[sum]); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims); + let mut t: Tensor = Tensor::zeros(&new_shape); + + for target in IndexIterator::new(&new_shape.dims) { + let sum_iter = IndexIterator::new(&removing_dims); + for sum_index in sum_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices.insert(axis, sum_index[i]); + } + + let value = *t.get_element(&target) + *self.get_element(&indices); + t.set_element(&target, value); + } + } + + t + } + + // pub fn var(&self, axes: Axes) -> Tensor {} + // pub fn mean(&self, axes: Axes) -> Tensor {} + // pub fn max(&self, axes: Option) -> Tensor {} + // pub fn min(&self, axes: Axes) -> Tensor {} + + /// For the maths see: https://bit.ly/3KQjPa3 + fn calculate_index(&self, indices: &[usize]) -> usize { + let mut index = 0; + for k in 0..self.shape.len() { + let stride = self.shape.dims[k+1..].iter().product::(); + index += indices[k] * stride; + } + index + } + + fn assert_indices(&self, indices: &[usize]) -> Result<(), String> { + if indices.len() != self.shape.len() { + return Err(format!("Incorrect number of dimensions ({} vs {}).", indices.len(), self.shape.len())); + } + for (i, &index) in indices.iter().enumerate() { + if index >= self.shape.dims[i] { + return Err(format!("Index out of bounds for dimension {}", i)); + } + } + Ok(()) + } + +} + +// Element-wise Multiplication +impl Mul for Tensor { + type Output = Tensor; + + fn mul(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() * rhs; + } + result + } +} + +// Element-wise Addition +impl Add for Tensor { + type Output = Tensor; + + fn add(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() + rhs; + } + result + } } +// Element-wise Subtraction +impl Sub for Tensor +{ + type Output = Tensor; + + fn sub(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() - rhs; + } + result + } +} + +// Element-wise Division +impl Div for Tensor +{ + type Output = Tensor; + + fn div(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() / rhs; + } + result + } +} + + #[cfg(test)] mod tests { use super::*; #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn test_new_tensor() { + let shape = shape![2, 2]; + let data = vec![1.0, 2.0, 3.0, 4.0]; + + let tensor = Tensor::new(&shape, &data); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, data); + } + + #[test] + fn test_zeros_tensor() { + let shape = shape![2, 3]; + let tensor: Tensor = Tensor::zeros(&shape); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, vec![0.0; shape.size()]); + } + + #[test] + fn test_tensor_shape() { + let shape = shape![2, 3]; + let tensor: Tensor = Tensor::zeros(&shape); + + assert_eq!(tensor.shape(), &shape); + } + + #[test] + fn test_tensor_size() { + let shape = shape![2, 3]; + let tensor: Tensor = Tensor::zeros(&shape); + + assert_eq!(tensor.size(), 6); + } + + #[test] + fn test_tensor_get() { + let shape = shape![2, 2]; + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = Tensor::new(&shape, &data); + + assert_eq!(*tensor.get_element(&[0, 0]), 1.0); + assert_eq!(*tensor.get_element(&[0, 1]), 2.0); + assert_eq!(*tensor.get_element(&[1, 0]), 3.0); + assert_eq!(*tensor.get_element(&[1, 1]), 4.0); + } + + #[test] + fn test_tensor_set() { + let shape = shape![2, 2]; + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut tensor = Tensor::new(&shape, &data); + + tensor.set_element(&[0, 0], 5.0); + tensor.set_element(&[0, 1], 6.0); + tensor.set_element(&[1, 0], 7.0); + tensor.set_element(&[1, 1], 8.0); + + assert_eq!(*tensor.get_element(&[0, 0]), 5.0); + assert_eq!(*tensor.get_element(&[0, 1]), 6.0); + assert_eq!(*tensor.get_element(&[1, 0]), 7.0); + assert_eq!(*tensor.get_element(&[1, 1]), 8.0); + } + + #[test] + fn test_tensor_sum_no_axis_1d() { + let shape = shape![5]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![15.0]); + } + + #[test] + fn test_tensor_sum_no_axis_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![21.0]); + } + + #[test] + fn test_tensor_sum_no_axis_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![78.0]); + } + + #[test] + fn test_tensor_sum_one_axis_1d() { + let shape = shape![5]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![0]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![15.0]); + } + + #[test] + fn test_tensor_sum_one_axis_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![0]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![5.0, 7.0, 9.0]); + } + + #[test] + fn test_tensor_sum_one_axis_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![0]); + + assert_eq!(result.shape(), &shape![2, 3]); + assert_eq!(result.data, vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0]); + } + + #[test] + fn test_tensor_sum_multiple_axes_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![0, 1]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![21.0]); + } + + #[test] + fn test_tensor_sum_multiple_axes_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.sum(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![22.0, 26.0, 30.0]); + } + + #[test] + fn test_add_tensor() { + let shape = shape![4]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape, &data1); + + let result = tensor1 + 3.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![4.0, 5.0, 6.0, 7.0]); + } + + #[test] + fn test_sub_tensor() { + let shape = shape![4]; + let data1 = vec![5.0, 6.0, 7.0, 8.0]; + + let tensor1 = Tensor::new(&shape, &data1); + + let result = tensor1 - 3.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![2.0, 3.0, 4.0, 5.0]); + } + + #[test] + fn test_mul_tensor() { + let shape = shape![4]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + + let tensor1 = Tensor::new(&shape, &data1); + + let result = tensor1 * 2.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![2.0, 4.0, 6.0, 8.0]); + } + + #[test] + fn test_div_tensor() { + let shape = shape![4]; + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + + let tensor1 = Tensor::new(&shape, &data1); + + let result = tensor1 / 2.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![2.0, 3.0, 4.0, 5.0]); } } + diff --git a/src/shape.rs b/src/shape.rs new file mode 100644 index 0000000..daedf38 --- /dev/null +++ b/src/shape.rs @@ -0,0 +1,65 @@ +use std::fmt; + +#[derive(Debug, Clone, PartialEq)] +pub struct Shape { + pub dims: Vec, +} + +impl Shape { + pub fn new(dims: Vec) -> Shape { + Shape { dims } + } + + pub fn size(&self) -> usize { + self.dims.iter().product() + } + + pub fn len(&self) -> usize { + self.dims.len() + } +} + +impl fmt::Display for Shape { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use itertools::Itertools; + let dims = self.dims.iter().map(|&x| format!("{}", x)).join(", "); + write!(f, "({})", dims) + } +} + +#[macro_export] +macro_rules! shape { + ($($dim:expr),*) => { + Shape::new(vec![$($dim),*]) + }; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shape_new() { + let dims = vec![2, 3, 4]; + let shape = Shape::new(dims.clone()); + assert_eq!(shape.dims, dims); + } + + #[test] + fn test_shape_size() { + let shape = Shape::new(vec![2, 3, 4]); + assert_eq!(shape.size(), 24); + } + + #[test] + fn test_shape_display() { + let shape = Shape::new(vec![2, 3, 4]); + assert_eq!(format!("{}", shape), "(2, 3, 4)"); + } + + #[test] + fn test_shape_macro() { + let shape = shape![2, 3, 4]; + assert_eq!(shape.dims, vec![2, 3, 4]); + } +} \ No newline at end of file From 3b6dffafafa4fefa2b365d6fda3903318377e55b Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 21 Jun 2024 18:09:22 +0100 Subject: [PATCH 02/24] feat: implement more tensor methods --- src/lib.rs | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 361 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db1f665..4cb9dab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,20 +18,41 @@ pub struct Tensor { shape: Shape } -impl Tensor { +impl Tensor { pub fn new(shape: &Shape, data: &[T]) -> Tensor { assert!(data.len() == shape.size()); Tensor {data: data.to_vec(), shape: shape.clone()} } - pub fn zeros(shape: &Shape) -> Tensor { + pub fn fill(shape: &Shape, value: T) -> Tensor { let total_size = shape.size(); let mut vec = Vec::with_capacity(total_size); - for _ in 0..total_size { vec.push(T::zero()); } + for _ in 0..total_size { vec.push(value); } Tensor::new(shape, &vec) } + pub fn zeros(shape: &Shape) -> Tensor { + Tensor::fill(shape, T::zero()) + } + + pub fn eye(shape: &Shape) -> Tensor { + let mut t = Tensor::zeros(shape); + for i in 0..shape.dims[0] { + t.set_element(&[i, i], T::one()); + } + t + } + + // Element-wise operations + pub fn pow(&self, power: usize) -> Tensor { + let mut t = self.clone(); + for i in 0..t.size() { + t.data[i] = num::pow(t.data[i], power); + } + t + } + // Properties pub fn shape(&self) -> &Shape { &self.shape } pub fn size(&self) -> usize { self.shape.size() } @@ -80,10 +101,132 @@ impl Tensor { t } - // pub fn var(&self, axes: Axes) -> Tensor {} - // pub fn mean(&self, axes: Axes) -> Tensor {} - // pub fn max(&self, axes: Option) -> Tensor {} - // pub fn min(&self, axes: Axes) -> Tensor {} + pub fn mean(&self, axes: Axes) -> Tensor { + let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { + let mut result = T::zero(); + for _ in 0..dim { + result = result + T::one(); + } + result + }).collect(); + let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); + self.sum(axes) / n + } + + pub fn var(&self, axes: Axes) -> Tensor { + let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { + let mut result = T::zero(); + for _ in 0..dim { + result = result + T::one(); + } + result + }).collect(); + let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); + + let all_axes = (0..self.shape.len()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let avg: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x) / n; + let var: T = self.data.iter().map(|&x| (x - avg) * (x - avg)).fold(T::zero(), |acc, x| acc + x) / n; + return Tensor::new(&Shape::new(vec![1]), &[var]); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims); + let mut t: Tensor = Tensor::zeros(&new_shape); + + for target in IndexIterator::new(&new_shape.dims) { + let sum_iter = IndexIterator::new(&removing_dims); + let mean = self.mean(axes.clone()); + + for sum_index in sum_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices.insert(axis, sum_index[i]); + } + + let centered = *self.get_element(&indices) - *mean.get_element(&target); + let value = *t.get_element(&target) + centered * centered; + t.set_element(&target, value); + } + } + + t / n + } + + pub fn max(&self, axes: Axes) -> Tensor { + let all_axes = (0..self.shape.len()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + return Tensor::new(&Shape::new(vec![1]), &[max]); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims); + let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + let mut t: Tensor = Tensor::fill(&new_shape, min); + + for target in IndexIterator::new(&new_shape.dims) { + let max_iter = IndexIterator::new(&removing_dims); + for max_index in max_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices.insert(axis, max_index[i]); + } + + if self.get_element(&indices) > t.get_element(&target) { + t.set_element(&target, *self.get_element(&indices)); + } + } + } + + t + } + + pub fn min(&self, axes: Axes) -> Tensor { + let all_axes = (0..self.shape.len()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + return Tensor::new(&Shape::new(vec![1]), &[min]); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims); + let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + let mut t: Tensor = Tensor::fill(&new_shape, max); + + for target in IndexIterator::new(&new_shape.dims) { + let min_iter = IndexIterator::new(&removing_dims); + for min_index in min_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices.insert(axis, min_index[i]); + } + + if self.get_element(&indices) < t.get_element(&target) { + t.set_element(&target, *self.get_element(&indices)); + } + } + } + + t + } /// For the maths see: https://bit.ly/3KQjPa3 fn calculate_index(&self, indices: &[usize]) -> usize { @@ -110,7 +253,7 @@ impl Tensor { } // Element-wise Multiplication -impl Mul for Tensor { +impl Mul for Tensor { type Output = Tensor; fn mul(self, rhs: T) -> Tensor { @@ -123,7 +266,7 @@ impl Mul for Tensor { } // Element-wise Addition -impl Add for Tensor { +impl Add for Tensor { type Output = Tensor; fn add(self, rhs: T) -> Tensor { @@ -136,7 +279,7 @@ impl Add for Tensor { } // Element-wise Subtraction -impl Sub for Tensor +impl Sub for Tensor { type Output = Tensor; @@ -150,7 +293,7 @@ impl Sub for Tensor } // Element-wise Division -impl Div for Tensor +impl Div for Tensor { type Output = Tensor; @@ -188,6 +331,28 @@ mod tests { assert_eq!(tensor.data, vec![0.0; shape.size()]); } + #[test] + fn test_fill_tensor() { + let shape = shape![2, 3]; + let tensor: Tensor = Tensor::fill(&shape, 7.0); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, vec![7.0; shape.size()]); + } + + #[test] + fn test_eye_tensor() { + let shape = shape![3, 3]; + let tensor: Tensor = Tensor::eye(&shape); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, vec![ + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ]); + } + #[test] fn test_tensor_shape() { let shape = shape![2, 3]; @@ -204,6 +369,23 @@ mod tests { assert_eq!(tensor.size(), 6); } + #[test] + fn test_tensor_pow() { + let shape = shape![2, 2]; + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.pow(2); + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![1.0, 4.0, 9.0, 16.0]); + + let result = tensor.pow(3); + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![1.0, 8.0, 27.0, 64.0]); + } + #[test] fn test_tensor_get() { let shape = shape![2, 2]; @@ -329,6 +511,174 @@ mod tests { assert_eq!(result.data, vec![22.0, 26.0, 30.0]); } + #[test] + fn test_tensor_mean_one_axis_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.mean(vec![0]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![2.5, 3.5, 4.5]); + } + + #[test] + fn test_tensor_mean_one_axis_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.mean(vec![0]); + + assert_eq!(result.shape(), &shape![2, 3]); + assert_eq!(result.data, vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0]); + } + + #[test] + fn test_tensor_mean_multiple_axes_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.mean(vec![0, 1]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![3.5]); + } + + #[test] + fn test_tensor_mean_multiple_axes_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.mean(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![5.5, 6.5, 7.5]); + } + + #[test] + fn test_tensor_var_one_axis_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.var(vec![0]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![2.25, 2.25, 2.25]); + } + + #[test] + fn test_tensor_var_one_axis_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.var(vec![0]); + + assert_eq!(result.shape(), &shape![2, 3]); + assert_eq!(result.data, vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0]); + } + + #[test] + fn test_tensor_var_multiple_axes_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, 1.0, 1.0, 7.0, 7.0, 7.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.var(vec![0, 1]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![9.0]); + } + + #[test] + fn test_tensor_var_multiple_axes_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.var(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![45.0, 45.0, 45.0]); + } + + #[test] + fn test_tensor_max_no_axis_1d() { + let shape = shape![5]; + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.max(vec![]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![5.0]); + } + + #[test] + fn test_tensor_max_one_axis_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.max(vec![0]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![1.0, 5.0, 3.0]); + } + + #[test] + fn test_tensor_max_multiple_axes_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.max(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![7.0, 11.0, 9.0]); + } + + #[test] + fn test_tensor_min_no_axis_1d() { + let shape = shape![5]; + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.min(vec![]); + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![-4.0]); + } + + #[test] + fn test_tensor_min_one_axis_2d() { + let shape = shape![2, 3]; + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.min(vec![0]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![-4.0, -2.0, -6.0]); + } + + #[test] + fn test_tensor_min_multiple_axes_3d() { + let shape = shape![2, 2, 3]; + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; + let tensor = Tensor::new(&shape, &data); + + let result = tensor.min(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![-10.0, -8.0, -12.0]); + } + #[test] fn test_add_tensor() { let shape = shape![4]; From 918cf6dbda83ee098839c03554fadc8e6b11685c Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 21 Jun 2024 18:24:05 +0100 Subject: [PATCH 03/24] feat: add tensor add/sub --- src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4cb9dab..1176380 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,6 +278,20 @@ impl Add for Tensor { } } +// Tensor Addition +impl Add> for Tensor { + type Output = Tensor; + + fn add(self, rhs: Tensor) -> Tensor { + assert!(self.shape == rhs.shape); + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() + rhs.data[i].clone(); + } + result + } +} + // Element-wise Subtraction impl Sub for Tensor { @@ -292,6 +306,20 @@ impl Sub for Tensor } } +// Tensor Subtraction +impl Sub> for Tensor { + type Output = Tensor; + + fn sub(self, rhs: Tensor) -> Tensor { + assert!(self.shape == rhs.shape); + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() - rhs.data[i].clone(); + } + result + } +} + // Element-wise Division impl Div for Tensor { @@ -691,6 +719,20 @@ mod tests { assert_eq!(result.data, vec![4.0, 5.0, 6.0, 7.0]); } + #[test] + fn test_add_tensors() { + let shape = shape![2, 2]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![5.0, 6.0, 7.0, 8.0]; + let tensor1 = Tensor::new(&shape, &data1); + let tensor2 = Tensor::new(&shape, &data2); + + let result = tensor1 + tensor2; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![6.0, 8.0, 10.0, 12.0]); + } + #[test] fn test_sub_tensor() { let shape = shape![4]; @@ -704,6 +746,20 @@ mod tests { assert_eq!(result.data, vec![2.0, 3.0, 4.0, 5.0]); } + #[test] + fn test_sub_tensors() { + let shape = shape![2, 2]; + let data1 = vec![5.0, 6.0, 7.0, 8.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape, &data1); + let tensor2 = Tensor::new(&shape, &data2); + + let result = tensor1 - tensor2; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, vec![4.0, 4.0, 4.0, 4.0]); + } + #[test] fn test_mul_tensor() { let shape = shape![4]; From eba8f496eb78bf9d120cbb572bb42c49c2223e00 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 21 Jun 2024 18:46:06 +0100 Subject: [PATCH 04/24] feat: add matrix multiplication --- src/lib.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/shape.rs | 9 ++++ 2 files changed, 144 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1176380..0da6e96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,6 +265,63 @@ impl Mul for Tensor { } } +// Vector/Matrix Multiplication +impl Mul> for Tensor { + type Output = Tensor; + + fn mul(self, rhs: Tensor) -> Tensor { + if self.shape.len() == 1 && rhs.shape.len() == 1 { + // Vector-Vector multiplication (dot product) + assert!(self.shape[0] == rhs.shape[0], "Vectors must be of the same length for dot product."); + let mut result = T::zero(); + for i in 0..self.shape[0] { + result = result + self.data[i] * rhs.data[i]; + } + Tensor::new(&shape![1], &vec![result]) + } else if self.shape.len() == 1 && rhs.shape.len() == 2 { + // Vector-Matrix multiplication + assert!(self.shape[0] == rhs.shape[0], "The length of the vector must be equal to the number of rows in the matrix."); + let mut result = Tensor::zeros(&shape![rhs.shape[1]]); + for j in 0..rhs.shape[1] { + let mut sum = T::zero(); + for i in 0..self.shape[0] { + sum = sum + self.data[i] * rhs.data[i * rhs.shape[1] + j]; + } + result.data[j] = sum; + } + result + } else if self.shape.len() == 2 && rhs.shape.len() == 1 { + // Matrix-Vector multiplication + assert!(self.shape[1] == rhs.shape[0], "The number of columns in the matrix must be equal to the length of the vector."); + let mut result = Tensor::zeros(&shape![self.shape[0]]); + for i in 0..self.shape[0] { + let mut sum = T::zero(); + for j in 0..self.shape[1] { + sum = sum + self.data[i * self.shape[1] + j] * rhs.data[j]; + } + result.data[i] = sum; + } + result + } else if self.shape.len() == 2 && rhs.shape.len() == 2 { + // Matrix-Matrix multiplication + assert!(self.shape[1] == rhs.shape[0], "The number of columns in the first matrix must be equal to the number of rows in the second matrix."); + let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]]); + for i in 0..self.shape[0] { + for j in 0..rhs.shape[1] { + let mut sum = T::zero(); + for k in 0..self.shape[1] { + sum = sum + self.data[i * self.shape[1] + k] * rhs.data[k * rhs.shape[1] + j]; + } + result.data[i * rhs.shape[1] + j] = sum; + } + } + result + } else { + panic!("Unsupported shapes for multiplication."); + } + } +} + // Element-wise Addition impl Add for Tensor { type Output = Tensor; @@ -773,6 +830,84 @@ mod tests { assert_eq!(result.data, vec![2.0, 4.0, 6.0, 8.0]); } + #[test] + fn test_vec_vec_mul_single() { + let shape = shape![1]; + let data1 = vec![2.0]; + let data2 = vec![5.0]; + + let tensor1 = Tensor::new(&shape, &data1); + let tensor2 = Tensor::new(&shape, &data2); + + let result = tensor1 * tensor2; + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![10.0]); + } + + #[test] + fn test_vec_vec_mul() { + let shape = shape![4]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + + let tensor1 = Tensor::new(&shape, &data1); + let tensor2 = Tensor::new(&shape, &data2); + + let result = tensor1 * tensor2; + + assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.data, vec![40.0]); + } + + #[test] + fn test_vec_matrix_mul() { + let shape_vec = shape![2]; + let shape_matrix = shape![2, 3]; + let data_vec = vec![1.0, 2.0]; + let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + + let tensor_vec = Tensor::new(&shape_vec, &data_vec); + let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix); + + let result = tensor_vec * tensor_matrix; + + assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.data, vec![9.0, 12.0, 15.0]); + } + + #[test] + fn test_matrix_vec_mul() { + let shape_matrix = shape![2, 3]; + let shape_vec = shape![3]; + let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let data_vec = vec![1.0, 2.0, 3.0]; + + let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix); + let tensor_vec = Tensor::new(&shape_vec, &data_vec); + + let result = tensor_matrix * tensor_vec; + + assert_eq!(result.shape(), &shape![2]); + assert_eq!(result.data, vec![14.0, 32.0]); + } + + #[test] + fn test_matrix_matrix_mul() { + let shape1 = shape![2, 3]; + let shape2 = shape![3, 2]; + let data1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let data2 = vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + + let tensor1 = Tensor::new(&shape1, &data1); + let tensor2 = Tensor::new(&shape2, &data2); + + let result = tensor1 * tensor2; + + assert_eq!(result.shape(), &shape![2, 2]); + assert_eq!(result.data, vec![58.0, 64.0, 139.0, 154.0]); + } + #[test] fn test_div_tensor() { let shape = shape![4]; diff --git a/src/shape.rs b/src/shape.rs index daedf38..e9f6b0f 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::ops::Index; #[derive(Debug, Clone, PartialEq)] pub struct Shape { @@ -19,6 +20,14 @@ impl Shape { } } +impl Index for Shape { + type Output = usize; + + fn index(&self, index: usize) -> &Self::Output { + &self.dims[index] + } +} + impl fmt::Display for Shape { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use itertools::Itertools; From fd4ecfef255408dd6df1e4d1afc249beff11b160 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 21 Jun 2024 19:08:22 +0100 Subject: [PATCH 05/24] feat: add tensor product --- src/lib.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0da6e96..aab08b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,6 +228,23 @@ impl Tensor { t } + // Tensor Product + // Consistent with numpy.tensordot(a, b, axis=0) + pub fn prod(&self, other: &Tensor) -> Tensor { + let mut new_dims = self.shape.dims.clone(); + new_dims.extend(&other.shape.dims); + let new_shape = Shape::new(new_dims); + + let mut new_data = Vec::with_capacity(self.size() * other.size()); + for &a in &self.data { + for &b in &other.data { + new_data.push(a * b); + } + } + + Tensor::new(&new_shape, &new_data) + } + /// For the maths see: https://bit.ly/3KQjPa3 fn calculate_index(&self, indices: &[usize]) -> usize { let mut index = 0; @@ -764,6 +781,54 @@ mod tests { assert_eq!(result.data, vec![-10.0, -8.0, -12.0]); } + #[test] + fn test_tensor_prod_1d_1d() { + let shape1 = shape![3]; + let data1 = vec![1.0, 2.0, 3.0]; + let tensor1 = Tensor::new(&shape1, &data1); + + let shape2 = shape![2]; + let data2 = vec![4.0, 5.0]; + let tensor2 = Tensor::new(&shape2, &data2); + + let result = tensor1.prod(&tensor2); + + assert_eq!(result.shape(), &shape![3, 2]); + assert_eq!(result.data, vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0]); + } + + #[test] + fn test_tensor_prod_2d_1d() { + let shape1 = shape![2, 2]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape1, &data1); + + let shape2 = shape![2]; + let data2 = vec![5.0, 6.0]; + let tensor2 = Tensor::new(&shape2, &data2); + + let result = tensor1.prod(&tensor2); + + assert_eq!(result.shape(), &shape![2, 2, 2]); + assert_eq!(result.data, vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0]); + } + + #[test] + fn test_tensor_prod_2d_2d() { + let shape1 = shape![2, 2]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape1, &data1); + + let shape2 = shape![2, 2]; + let data2 = vec![5.0, 6.0, 7.0, 8.0]; + let tensor2 = Tensor::new(&shape2, &data2); + + let result = tensor1.prod(&tensor2); + + assert_eq!(result.shape(), &shape![2, 2, 2, 2]); + assert_eq!(result.data, vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0]); + } + #[test] fn test_add_tensor() { let shape = shape![4]; From 34dee9abe24c42da22b898bc8d4015179f157dd1 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 21 Jun 2024 19:20:41 +0100 Subject: [PATCH 06/24] feat: add ones method --- src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index aab08b4..c9819ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,10 @@ impl Tensor { Tensor::fill(shape, T::zero()) } + pub fn ones(shape: &Shape) -> Tensor { + Tensor::fill(shape, T::one()) + } + pub fn eye(shape: &Shape) -> Tensor { let mut t = Tensor::zeros(shape); for i in 0..shape.dims[0] { @@ -433,6 +437,15 @@ mod tests { assert_eq!(tensor.data, vec![0.0; shape.size()]); } + #[test] + fn test_ones_tensor() { + let shape = shape![2, 3]; + let tensor: Tensor = Tensor::ones(&shape); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, vec![1.0; shape.size()]); + } + #[test] fn test_fill_tensor() { let shape = shape![2, 3]; From eebe35fcafd5448a0863abb3eba0d91ed559912f Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 12:45:11 +0100 Subject: [PATCH 07/24] feat: add ShapeError --- src/error.rs | 20 +++ src/lib.rs | 418 ++++++++++++++++++++++++++------------------------- src/shape.rs | 24 ++- 3 files changed, 255 insertions(+), 207 deletions(-) create mode 100644 src/error.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..c74013b --- /dev/null +++ b/src/error.rs @@ -0,0 +1,20 @@ +use std::fmt; + +#[derive(Debug, Clone)] +pub struct ShapeError { + reason: String, +} + +impl ShapeError { + pub fn new(reason: &str) -> Self { + ShapeError { + reason: reason.to_string(), + } + } +} + +impl fmt::Display for ShapeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ShapeError: {}", self.reason) + } +} diff --git a/src/lib.rs b/src/lib.rs index c9819ae..84ca2a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod shape; pub mod iter; +pub mod error; use num::Num; use std::ops::Add; @@ -9,6 +10,7 @@ use std::ops::Div; use crate::shape::Shape; use crate::iter::IndexIterator; +use crate::error::ShapeError; pub type Axes = Vec; @@ -20,16 +22,18 @@ pub struct Tensor { impl Tensor { - pub fn new(shape: &Shape, data: &[T]) -> Tensor { - assert!(data.len() == shape.size()); - Tensor {data: data.to_vec(), shape: shape.clone()} + pub fn new(shape: &Shape, data: &[T]) -> Result, ShapeError> { + if data.len() != shape.size() { + return Err(ShapeError::new("Data length does not match shape size")); + } + Ok(Tensor {data: data.to_vec(), shape: shape.clone()}) } pub fn fill(shape: &Shape, value: T) -> Tensor { let total_size = shape.size(); let mut vec = Vec::with_capacity(total_size); for _ in 0..total_size { vec.push(value); } - Tensor::new(shape, &vec) + Tensor::new(shape, &vec).unwrap() } pub fn zeros(shape: &Shape) -> Tensor { @@ -40,14 +44,6 @@ impl Tensor { Tensor::fill(shape, T::one()) } - pub fn eye(shape: &Shape) -> Tensor { - let mut t = Tensor::zeros(shape); - for i in 0..shape.dims[0] { - t.set_element(&[i, i], T::one()); - } - t - } - // Element-wise operations pub fn pow(&self, power: usize) -> Tensor { let mut t = self.clone(); @@ -61,15 +57,16 @@ impl Tensor { pub fn shape(&self) -> &Shape { &self.shape } pub fn size(&self) -> usize { self.shape.size() } - // Access methods - pub fn get_element(&self, indices: &[usize]) -> &T { - self.assert_indices(indices).unwrap(); - &self.data[self.calculate_index(indices)] + pub fn get_element(&self, indices: &[usize]) -> Result<&T, ShapeError> { + self.assert_indices(indices)?; + Ok(&self.data[self.calculate_index(indices)]) } - pub fn set_element(&mut self, indices: &[usize], value: T) { - self.assert_indices(indices).unwrap(); + + pub fn set_element(&mut self, indices: &[usize], value: T) -> Result<(), ShapeError> { + self.assert_indices(indices)?; let index = self.calculate_index(indices); self.data[index] = value; + Ok(()) } // // Reduction operations @@ -82,11 +79,11 @@ impl Tensor { // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { let sum: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x); - return Tensor::new(&Shape::new(vec![1]), &[sum]); + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[sum]).unwrap(); } // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims); + let new_shape = Shape::new(remaining_dims).unwrap(); let mut t: Tensor = Tensor::zeros(&new_shape); for target in IndexIterator::new(&new_shape.dims) { @@ -97,8 +94,8 @@ impl Tensor { indices.insert(axis, sum_index[i]); } - let value = *t.get_element(&target) + *self.get_element(&indices); - t.set_element(&target, value); + let value = *t.get_element(&target).unwrap() + *self.get_element(&indices).unwrap(); + let _ = t.set_element(&target, value).unwrap(); } } @@ -138,11 +135,11 @@ impl Tensor { if axes.is_empty() | (remaining_dims.len() == 0) { let avg: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x) / n; let var: T = self.data.iter().map(|&x| (x - avg) * (x - avg)).fold(T::zero(), |acc, x| acc + x) / n; - return Tensor::new(&Shape::new(vec![1]), &[var]); + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[var]).unwrap(); } // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims); + let new_shape = Shape::new(remaining_dims).unwrap(); let mut t: Tensor = Tensor::zeros(&new_shape); for target in IndexIterator::new(&new_shape.dims) { @@ -155,9 +152,9 @@ impl Tensor { indices.insert(axis, sum_index[i]); } - let centered = *self.get_element(&indices) - *mean.get_element(&target); - let value = *t.get_element(&target) + centered * centered; - t.set_element(&target, value); + let centered = *self.get_element(&indices).unwrap() - *mean.get_element(&target).unwrap(); + let value = *t.get_element(&target).unwrap() + centered * centered; + let _ = t.set_element(&target, value).unwrap(); } } @@ -173,11 +170,11 @@ impl Tensor { // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); - return Tensor::new(&Shape::new(vec![1]), &[max]); + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[max]).unwrap(); } // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims); + let new_shape = Shape::new(remaining_dims).unwrap(); let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); let mut t: Tensor = Tensor::fill(&new_shape, min); @@ -189,8 +186,8 @@ impl Tensor { indices.insert(axis, max_index[i]); } - if self.get_element(&indices) > t.get_element(&target) { - t.set_element(&target, *self.get_element(&indices)); + if self.get_element(&indices).unwrap() > t.get_element(&target).unwrap() { + let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); } } } @@ -207,11 +204,11 @@ impl Tensor { // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); - return Tensor::new(&Shape::new(vec![1]), &[min]); + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[min]).unwrap(); } // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims); + let new_shape = Shape::new(remaining_dims).unwrap(); let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); let mut t: Tensor = Tensor::fill(&new_shape, max); @@ -223,8 +220,8 @@ impl Tensor { indices.insert(axis, min_index[i]); } - if self.get_element(&indices) < t.get_element(&target) { - t.set_element(&target, *self.get_element(&indices)); + if self.get_element(&indices).unwrap() < t.get_element(&target).unwrap() { + let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); } } } @@ -237,7 +234,7 @@ impl Tensor { pub fn prod(&self, other: &Tensor) -> Tensor { let mut new_dims = self.shape.dims.clone(); new_dims.extend(&other.shape.dims); - let new_shape = Shape::new(new_dims); + let new_shape = Shape::new(new_dims).unwrap(); let mut new_data = Vec::with_capacity(self.size() * other.size()); for &a in &self.data { @@ -246,7 +243,7 @@ impl Tensor { } } - Tensor::new(&new_shape, &new_data) + Tensor::new(&new_shape, &new_data).unwrap() } /// For the maths see: https://bit.ly/3KQjPa3 @@ -259,18 +256,18 @@ impl Tensor { index } - fn assert_indices(&self, indices: &[usize]) -> Result<(), String> { + fn assert_indices(&self, indices: &[usize]) -> Result<(), ShapeError> { if indices.len() != self.shape.len() { - return Err(format!("Incorrect number of dimensions ({} vs {}).", indices.len(), self.shape.len())); + let msg = format!("incorrect order ({} vs {}).", indices.len(), self.shape.len()); + return Err(ShapeError::new(msg.as_str())); } for (i, &index) in indices.iter().enumerate() { if index >= self.shape.dims[i] { - return Err(format!("Index out of bounds for dimension {}", i)); + return Err(ShapeError::new(format!("out of bounds for dimension {}", i).as_str())); } } Ok(()) } - } // Element-wise Multiplication @@ -298,11 +295,11 @@ impl Mul> for Tensor { for i in 0..self.shape[0] { result = result + self.data[i] * rhs.data[i]; } - Tensor::new(&shape![1], &vec![result]) + Tensor::new(&shape![1].unwrap(), &vec![result]).unwrap() } else if self.shape.len() == 1 && rhs.shape.len() == 2 { // Vector-Matrix multiplication assert!(self.shape[0] == rhs.shape[0], "The length of the vector must be equal to the number of rows in the matrix."); - let mut result = Tensor::zeros(&shape![rhs.shape[1]]); + let mut result = Tensor::zeros(&shape![rhs.shape[1]].unwrap()); for j in 0..rhs.shape[1] { let mut sum = T::zero(); for i in 0..self.shape[0] { @@ -314,7 +311,7 @@ impl Mul> for Tensor { } else if self.shape.len() == 2 && rhs.shape.len() == 1 { // Matrix-Vector multiplication assert!(self.shape[1] == rhs.shape[0], "The number of columns in the matrix must be equal to the length of the vector."); - let mut result = Tensor::zeros(&shape![self.shape[0]]); + let mut result = Tensor::zeros(&shape![self.shape[0]].unwrap()); for i in 0..self.shape[0] { let mut sum = T::zero(); for j in 0..self.shape[1] { @@ -326,7 +323,7 @@ impl Mul> for Tensor { } else if self.shape.len() == 2 && rhs.shape.len() == 2 { // Matrix-Matrix multiplication assert!(self.shape[1] == rhs.shape[0], "The number of columns in the first matrix must be equal to the number of rows in the second matrix."); - let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]]); + let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]].unwrap()); for i in 0..self.shape[0] { for j in 0..rhs.shape[1] { let mut sum = T::zero(); @@ -419,18 +416,28 @@ mod tests { #[test] fn test_new_tensor() { - let shape = shape![2, 2]; + let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); assert_eq!(tensor.shape(), &shape); assert_eq!(tensor.data, data); } + #[test] + fn test_new_tensor_shape_data_mismatch() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0]; // Mismatched data length + + let result = Tensor::new(&shape, &data); + + assert!(result.is_err()); + } + #[test] fn test_zeros_tensor() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let tensor: Tensor = Tensor::zeros(&shape); assert_eq!(tensor.shape(), &shape); @@ -439,7 +446,7 @@ mod tests { #[test] fn test_ones_tensor() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let tensor: Tensor = Tensor::ones(&shape); assert_eq!(tensor.shape(), &shape); @@ -448,29 +455,16 @@ mod tests { #[test] fn test_fill_tensor() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let tensor: Tensor = Tensor::fill(&shape, 7.0); assert_eq!(tensor.shape(), &shape); assert_eq!(tensor.data, vec![7.0; shape.size()]); } - #[test] - fn test_eye_tensor() { - let shape = shape![3, 3]; - let tensor: Tensor = Tensor::eye(&shape); - - assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, vec![ - 1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0 - ]); - } - #[test] fn test_tensor_shape() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let tensor: Tensor = Tensor::zeros(&shape); assert_eq!(tensor.shape(), &shape); @@ -478,7 +472,7 @@ mod tests { #[test] fn test_tensor_size() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let tensor: Tensor = Tensor::zeros(&shape); assert_eq!(tensor.size(), 6); @@ -486,9 +480,9 @@ mod tests { #[test] fn test_tensor_pow() { - let shape = shape![2, 2]; + let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.pow(2); @@ -503,350 +497,372 @@ mod tests { #[test] fn test_tensor_get() { - let shape = shape![2, 2]; + let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); - assert_eq!(*tensor.get_element(&[0, 0]), 1.0); - assert_eq!(*tensor.get_element(&[0, 1]), 2.0); - assert_eq!(*tensor.get_element(&[1, 0]), 3.0); - assert_eq!(*tensor.get_element(&[1, 1]), 4.0); + assert_eq!(*tensor.get_element(&[0, 0]).unwrap(), 1.0); + assert_eq!(*tensor.get_element(&[0, 1]).unwrap(), 2.0); + assert_eq!(*tensor.get_element(&[1, 0]).unwrap(), 3.0); + assert_eq!(*tensor.get_element(&[1, 1]).unwrap(), 4.0); } #[test] fn test_tensor_set() { - let shape = shape![2, 2]; + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut tensor = Tensor::new(&shape, &data).unwrap(); + + tensor.set_element(&[0, 0], 5.0).unwrap(); + tensor.set_element(&[0, 1], 6.0).unwrap(); + tensor.set_element(&[1, 0], 7.0).unwrap(); + tensor.set_element(&[1, 1], 8.0).unwrap(); + + assert_eq!(*tensor.get_element(&[0, 0]).unwrap(), 5.0); + assert_eq!(*tensor.get_element(&[0, 1]).unwrap(), 6.0); + assert_eq!(*tensor.get_element(&[1, 0]).unwrap(), 7.0); + assert_eq!(*tensor.get_element(&[1, 1]).unwrap(), 8.0); + } + + #[test] + fn test_tensor_get_out_of_bounds() { + let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; - let mut tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); - tensor.set_element(&[0, 0], 5.0); - tensor.set_element(&[0, 1], 6.0); - tensor.set_element(&[1, 0], 7.0); - tensor.set_element(&[1, 1], 8.0); + assert!(tensor.get_element(&[2, 0]).is_err()); + assert!(tensor.get_element(&[0, 2]).is_err()); + assert!(tensor.get_element(&[2, 2]).is_err()); + } + + #[test] + fn test_tensor_set_out_of_bounds() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut tensor = Tensor::new(&shape, &data).unwrap(); - assert_eq!(*tensor.get_element(&[0, 0]), 5.0); - assert_eq!(*tensor.get_element(&[0, 1]), 6.0); - assert_eq!(*tensor.get_element(&[1, 0]), 7.0); - assert_eq!(*tensor.get_element(&[1, 1]), 8.0); + assert!(tensor.set_element(&[2, 0], 5.0).is_err()); + assert!(tensor.set_element(&[0, 2], 6.0).is_err()); + assert!(tensor.set_element(&[2, 2], 7.0).is_err()); } #[test] fn test_tensor_sum_no_axis_1d() { - let shape = shape![5]; + let shape = shape![5].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![15.0]); } #[test] fn test_tensor_sum_no_axis_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![21.0]); } #[test] fn test_tensor_sum_no_axis_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![78.0]); } #[test] fn test_tensor_sum_one_axis_1d() { - let shape = shape![5]; + let shape = shape![5].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![15.0]); } #[test] fn test_tensor_sum_one_axis_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![5.0, 7.0, 9.0]); } #[test] fn test_tensor_sum_one_axis_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0]); - assert_eq!(result.shape(), &shape![2, 3]); + assert_eq!(result.shape(), &shape![2, 3].unwrap()); assert_eq!(result.data, vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0]); } #[test] fn test_tensor_sum_multiple_axes_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0, 1]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![21.0]); } #[test] fn test_tensor_sum_multiple_axes_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0, 1]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![22.0, 26.0, 30.0]); } #[test] fn test_tensor_mean_one_axis_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.mean(vec![0]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![2.5, 3.5, 4.5]); } #[test] fn test_tensor_mean_one_axis_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.mean(vec![0]); - assert_eq!(result.shape(), &shape![2, 3]); + assert_eq!(result.shape(), &shape![2, 3].unwrap()); assert_eq!(result.data, vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0]); } #[test] fn test_tensor_mean_multiple_axes_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.mean(vec![0, 1]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![3.5]); } #[test] fn test_tensor_mean_multiple_axes_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.mean(vec![0, 1]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![5.5, 6.5, 7.5]); } #[test] fn test_tensor_var_one_axis_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.var(vec![0]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![2.25, 2.25, 2.25]); } #[test] fn test_tensor_var_one_axis_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.var(vec![0]); - assert_eq!(result.shape(), &shape![2, 3]); + assert_eq!(result.shape(), &shape![2, 3].unwrap()); assert_eq!(result.data, vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0]); } #[test] fn test_tensor_var_multiple_axes_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, 1.0, 1.0, 7.0, 7.0, 7.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.var(vec![0, 1]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![9.0]); } #[test] fn test_tensor_var_multiple_axes_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.var(vec![0, 1]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![45.0, 45.0, 45.0]); } #[test] fn test_tensor_max_no_axis_1d() { - let shape = shape![5]; + let shape = shape![5].unwrap(); let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.max(vec![]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![5.0]); } #[test] fn test_tensor_max_one_axis_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.max(vec![0]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![1.0, 5.0, 3.0]); } #[test] fn test_tensor_max_multiple_axes_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.max(vec![0, 1]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![7.0, 11.0, 9.0]); } #[test] fn test_tensor_min_no_axis_1d() { - let shape = shape![5]; + let shape = shape![5].unwrap(); let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.min(vec![]); - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![-4.0]); } #[test] fn test_tensor_min_one_axis_2d() { - let shape = shape![2, 3]; + let shape = shape![2, 3].unwrap(); let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.min(vec![0]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![-4.0, -2.0, -6.0]); } #[test] fn test_tensor_min_multiple_axes_3d() { - let shape = shape![2, 2, 3]; + let shape = shape![2, 2, 3].unwrap(); let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; - let tensor = Tensor::new(&shape, &data); + let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.min(vec![0, 1]); - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![-10.0, -8.0, -12.0]); } #[test] fn test_tensor_prod_1d_1d() { - let shape1 = shape![3]; + let shape1 = shape![3].unwrap(); let data1 = vec![1.0, 2.0, 3.0]; - let tensor1 = Tensor::new(&shape1, &data1); + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - let shape2 = shape![2]; + let shape2 = shape![2].unwrap(); let data2 = vec![4.0, 5.0]; - let tensor2 = Tensor::new(&shape2, &data2); + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); let result = tensor1.prod(&tensor2); - assert_eq!(result.shape(), &shape![3, 2]); + assert_eq!(result.shape(), &shape![3, 2].unwrap()); assert_eq!(result.data, vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0]); } #[test] fn test_tensor_prod_2d_1d() { - let shape1 = shape![2, 2]; + let shape1 = shape![2, 2].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape1, &data1); + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - let shape2 = shape![2]; + let shape2 = shape![2].unwrap(); let data2 = vec![5.0, 6.0]; - let tensor2 = Tensor::new(&shape2, &data2); + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); let result = tensor1.prod(&tensor2); - assert_eq!(result.shape(), &shape![2, 2, 2]); + assert_eq!(result.shape(), &shape![2, 2, 2].unwrap()); assert_eq!(result.data, vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0]); } #[test] fn test_tensor_prod_2d_2d() { - let shape1 = shape![2, 2]; + let shape1 = shape![2, 2].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape1, &data1); + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - let shape2 = shape![2, 2]; + let shape2 = shape![2, 2].unwrap(); let data2 = vec![5.0, 6.0, 7.0, 8.0]; - let tensor2 = Tensor::new(&shape2, &data2); + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); let result = tensor1.prod(&tensor2); - assert_eq!(result.shape(), &shape![2, 2, 2, 2]); + assert_eq!(result.shape(), &shape![2, 2, 2, 2].unwrap()); assert_eq!(result.data, vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0]); } #[test] fn test_add_tensor() { - let shape = shape![4]; + let shape = shape![4].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape, &data1); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); let result = tensor1 + 3.0; @@ -856,11 +872,11 @@ mod tests { #[test] fn test_add_tensors() { - let shape = shape![2, 2]; + let shape = shape![2, 2].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0]; let data2 = vec![5.0, 6.0, 7.0, 8.0]; - let tensor1 = Tensor::new(&shape, &data1); - let tensor2 = Tensor::new(&shape, &data2); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); let result = tensor1 + tensor2; @@ -870,10 +886,10 @@ mod tests { #[test] fn test_sub_tensor() { - let shape = shape![4]; + let shape = shape![4].unwrap(); let data1 = vec![5.0, 6.0, 7.0, 8.0]; - let tensor1 = Tensor::new(&shape, &data1); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); let result = tensor1 - 3.0; @@ -883,11 +899,11 @@ mod tests { #[test] fn test_sub_tensors() { - let shape = shape![2, 2]; + let shape = shape![2, 2].unwrap(); let data1 = vec![5.0, 6.0, 7.0, 8.0]; let data2 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape, &data1); - let tensor2 = Tensor::new(&shape, &data2); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); let result = tensor1 - tensor2; @@ -897,10 +913,10 @@ mod tests { #[test] fn test_mul_tensor() { - let shape = shape![4]; + let shape = shape![4].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape, &data1); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); let result = tensor1 * 2.0; @@ -910,88 +926,88 @@ mod tests { #[test] fn test_vec_vec_mul_single() { - let shape = shape![1]; + let shape = shape![1].unwrap(); let data1 = vec![2.0]; let data2 = vec![5.0]; - let tensor1 = Tensor::new(&shape, &data1); - let tensor2 = Tensor::new(&shape, &data2); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); let result = tensor1 * tensor2; - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![10.0]); } #[test] fn test_vec_vec_mul() { - let shape = shape![4]; + let shape = shape![4].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0]; let data2 = vec![2.0, 3.0, 4.0, 5.0]; - let tensor1 = Tensor::new(&shape, &data1); - let tensor2 = Tensor::new(&shape, &data2); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); let result = tensor1 * tensor2; - assert_eq!(result.shape(), &shape![1]); + assert_eq!(result.shape(), &shape![1].unwrap()); assert_eq!(result.data, vec![40.0]); } #[test] fn test_vec_matrix_mul() { - let shape_vec = shape![2]; - let shape_matrix = shape![2, 3]; + let shape_vec = shape![2].unwrap(); + let shape_matrix = shape![2, 3].unwrap(); let data_vec = vec![1.0, 2.0]; let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor_vec = Tensor::new(&shape_vec, &data_vec); - let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix); + let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); + let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); let result = tensor_vec * tensor_matrix; - assert_eq!(result.shape(), &shape![3]); + assert_eq!(result.shape(), &shape![3].unwrap()); assert_eq!(result.data, vec![9.0, 12.0, 15.0]); } #[test] fn test_matrix_vec_mul() { - let shape_matrix = shape![2, 3]; - let shape_vec = shape![3]; + let shape_matrix = shape![2, 3].unwrap(); + let shape_vec = shape![3].unwrap(); let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let data_vec = vec![1.0, 2.0, 3.0]; - let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix); - let tensor_vec = Tensor::new(&shape_vec, &data_vec); + let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); + let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); let result = tensor_matrix * tensor_vec; - assert_eq!(result.shape(), &shape![2]); + assert_eq!(result.shape(), &shape![2].unwrap()); assert_eq!(result.data, vec![14.0, 32.0]); } #[test] fn test_matrix_matrix_mul() { - let shape1 = shape![2, 3]; - let shape2 = shape![3, 2]; + let shape1 = shape![2, 3].unwrap(); + let shape2 = shape![3, 2].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let data2 = vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor1 = Tensor::new(&shape1, &data1); - let tensor2 = Tensor::new(&shape2, &data2); + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); let result = tensor1 * tensor2; - assert_eq!(result.shape(), &shape![2, 2]); + assert_eq!(result.shape(), &shape![2, 2].unwrap()); assert_eq!(result.data, vec![58.0, 64.0, 139.0, 154.0]); } #[test] fn test_div_tensor() { - let shape = shape![4]; + let shape = shape![4].unwrap(); let data1 = vec![4.0, 6.0, 8.0, 10.0]; - let tensor1 = Tensor::new(&shape, &data1); + let tensor1 = Tensor::new(&shape, &data1).unwrap(); let result = tensor1 / 2.0; diff --git a/src/shape.rs b/src/shape.rs index e9f6b0f..ee48f96 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -1,14 +1,19 @@ use std::fmt; use std::ops::Index; +use crate::error::ShapeError; + #[derive(Debug, Clone, PartialEq)] pub struct Shape { pub dims: Vec, } impl Shape { - pub fn new(dims: Vec) -> Shape { - Shape { dims } + pub fn new(dims: Vec) -> Result { + if dims.iter().any(|&x| x == 0) { + return Err(ShapeError::new("Dimension cannot be zero")); + } + Ok(Shape { dims }) } pub fn size(&self) -> usize { @@ -50,25 +55,32 @@ mod tests { #[test] fn test_shape_new() { let dims = vec![2, 3, 4]; - let shape = Shape::new(dims.clone()); + let shape = Shape::new(dims.clone()).unwrap(); assert_eq!(shape.dims, dims); } + #[test] + fn test_shape_new_with_zero_dimension() { + let dims = vec![2, 0, 4]; + let result = Shape::new(dims); + assert!(result.is_err()); + } + #[test] fn test_shape_size() { - let shape = Shape::new(vec![2, 3, 4]); + let shape = Shape::new(vec![2, 3, 4]).unwrap(); assert_eq!(shape.size(), 24); } #[test] fn test_shape_display() { - let shape = Shape::new(vec![2, 3, 4]); + let shape = Shape::new(vec![2, 3, 4]).unwrap(); assert_eq!(format!("{}", shape), "(2, 3, 4)"); } #[test] fn test_shape_macro() { - let shape = shape![2, 3, 4]; + let shape = shape![2, 3, 4].unwrap(); assert_eq!(shape.dims, vec![2, 3, 4]); } } \ No newline at end of file From e6e9b939a4a2b6c16050069cdd54e9e3d4073c0c Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 13:13:44 +0100 Subject: [PATCH 08/24] feat!: rename shape len to order --- src/lib.rs | 22 +++++++++++----------- src/shape.rs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 84ca2a2..5c8113a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ impl Tensor { // // Reduction operations pub fn sum(&self, axes: Axes) -> Tensor { - let all_axes = (0..self.shape.len()).collect::>(); + let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); @@ -126,7 +126,7 @@ impl Tensor { }).collect(); let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); - let all_axes = (0..self.shape.len()).collect::>(); + let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); @@ -162,7 +162,7 @@ impl Tensor { } pub fn max(&self, axes: Axes) -> Tensor { - let all_axes = (0..self.shape.len()).collect::>(); + let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); @@ -196,7 +196,7 @@ impl Tensor { } pub fn min(&self, axes: Axes) -> Tensor { - let all_axes = (0..self.shape.len()).collect::>(); + let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); @@ -249,7 +249,7 @@ impl Tensor { /// For the maths see: https://bit.ly/3KQjPa3 fn calculate_index(&self, indices: &[usize]) -> usize { let mut index = 0; - for k in 0..self.shape.len() { + for k in 0..self.shape.order() { let stride = self.shape.dims[k+1..].iter().product::(); index += indices[k] * stride; } @@ -257,8 +257,8 @@ impl Tensor { } fn assert_indices(&self, indices: &[usize]) -> Result<(), ShapeError> { - if indices.len() != self.shape.len() { - let msg = format!("incorrect order ({} vs {}).", indices.len(), self.shape.len()); + if indices.len() != self.shape.order() { + let msg = format!("incorrect order ({} vs {}).", indices.len(), self.shape.order()); return Err(ShapeError::new(msg.as_str())); } for (i, &index) in indices.iter().enumerate() { @@ -288,7 +288,7 @@ impl Mul> for Tensor { type Output = Tensor; fn mul(self, rhs: Tensor) -> Tensor { - if self.shape.len() == 1 && rhs.shape.len() == 1 { + if self.shape.order() == 1 && rhs.shape.order() == 1 { // Vector-Vector multiplication (dot product) assert!(self.shape[0] == rhs.shape[0], "Vectors must be of the same length for dot product."); let mut result = T::zero(); @@ -296,7 +296,7 @@ impl Mul> for Tensor { result = result + self.data[i] * rhs.data[i]; } Tensor::new(&shape![1].unwrap(), &vec![result]).unwrap() - } else if self.shape.len() == 1 && rhs.shape.len() == 2 { + } else if self.shape.order() == 1 && rhs.shape.order() == 2 { // Vector-Matrix multiplication assert!(self.shape[0] == rhs.shape[0], "The length of the vector must be equal to the number of rows in the matrix."); let mut result = Tensor::zeros(&shape![rhs.shape[1]].unwrap()); @@ -308,7 +308,7 @@ impl Mul> for Tensor { result.data[j] = sum; } result - } else if self.shape.len() == 2 && rhs.shape.len() == 1 { + } else if self.shape.order() == 2 && rhs.shape.order() == 1 { // Matrix-Vector multiplication assert!(self.shape[1] == rhs.shape[0], "The number of columns in the matrix must be equal to the length of the vector."); let mut result = Tensor::zeros(&shape![self.shape[0]].unwrap()); @@ -320,7 +320,7 @@ impl Mul> for Tensor { result.data[i] = sum; } result - } else if self.shape.len() == 2 && rhs.shape.len() == 2 { + } else if self.shape.order() == 2 && rhs.shape.order() == 2 { // Matrix-Matrix multiplication assert!(self.shape[1] == rhs.shape[0], "The number of columns in the first matrix must be equal to the number of rows in the second matrix."); let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]].unwrap()); diff --git a/src/shape.rs b/src/shape.rs index ee48f96..e91b89e 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -20,7 +20,7 @@ impl Shape { self.dims.iter().product() } - pub fn len(&self) -> usize { + pub fn order(&self) -> usize { self.dims.len() } } From 6035f1249c14487ee4424db8d6b2776fa87bb14c Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 19:38:15 +0100 Subject: [PATCH 09/24] feat: add Coordinate and DynamicStorage --- src/coordinate.rs | 61 +++++++++++++ src/iter.rs | 47 +++++----- src/lib.rs | 217 +++++++++++++++++++--------------------------- src/storage.rs | 87 +++++++++++++++++++ 4 files changed, 262 insertions(+), 150 deletions(-) create mode 100644 src/coordinate.rs create mode 100644 src/storage.rs diff --git a/src/coordinate.rs b/src/coordinate.rs new file mode 100644 index 0000000..dce862b --- /dev/null +++ b/src/coordinate.rs @@ -0,0 +1,61 @@ +use std::fmt; +use std::ops::Index; +use std::ops::IndexMut; + +#[derive(Debug, Clone, PartialEq)] +pub struct Coordinate { + indices: Vec, +} + +impl Coordinate { + pub fn new(indices: Vec) -> Self { + Self { indices } + } + + pub fn len(&self) -> usize { + self.indices.len() + } + + pub fn iter(&self) -> std::slice::Iter<'_, usize> { + self.indices.iter() + } + + pub fn insert(&self, index: usize, axis: usize) -> Self { + let mut new_indices = self.indices.clone(); + new_indices.insert(index, axis); + Self { indices: new_indices } + } +} + +impl Index for Coordinate { + type Output = usize; + + fn index(&self, index: usize) -> &Self::Output { + &self.indices[index] + } +} + +impl IndexMut for Coordinate { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.indices[index] + } +} + +impl fmt::Display for Coordinate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use itertools::Itertools; + let idxs = self.indices.iter().map(|&x| format!("{}", x)).join(", "); + write!(f, "({})", idxs) + } +} + +#[macro_export] +macro_rules! coord { + ($($index:expr),*) => { + Coordinate::new(vec![$($index),*]) + }; + + ($index:expr; $count:expr) => { + Coordinate::new(vec![$index; $count]) + }; +} diff --git a/src/iter.rs b/src/iter.rs index 2970769..6299335 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,14 +1,18 @@ +use crate::coord; +use crate::coordinate::Coordinate; +use crate::shape::Shape; + pub struct IndexIterator { - shape: Vec, - current: Vec, + shape: Shape, + current: Coordinate, done: bool, } impl IndexIterator { - pub fn new(shape: &[usize]) -> Self { - let current = vec![0; shape.len()]; + pub fn new(shape: &Shape) -> Self { + let current = coord![0; shape.order()]; IndexIterator { - shape: shape.to_vec(), + shape: shape.clone(), current, done: false, } @@ -16,16 +20,16 @@ impl IndexIterator { } impl Iterator for IndexIterator { - type Item = Vec; + type Item = Coordinate; fn next(&mut self) -> Option { - if self.done || self.shape.len() == 0 { + if self.done || self.shape.order() == 0 { return None; } let result = self.current.clone(); - for i in (0..self.shape.len()).rev() { + for i in (0..self.shape.order()).rev() { if self.current[i] + 1 < self.shape[i] { self.current[i] += 1; break; @@ -45,36 +49,37 @@ impl Iterator for IndexIterator { #[cfg(test)] mod tests { use super::*; + use crate::shape; #[test] fn test_index_iterator() { - let shape = vec![2, 3]; + let shape = shape![2, 3].unwrap(); let mut iter = IndexIterator::new(&shape); - assert_eq!(iter.next(), Some(vec![0, 0])); - assert_eq!(iter.next(), Some(vec![0, 1])); - assert_eq!(iter.next(), Some(vec![0, 2])); - assert_eq!(iter.next(), Some(vec![1, 0])); - assert_eq!(iter.next(), Some(vec![1, 1])); - assert_eq!(iter.next(), Some(vec![1, 2])); + assert_eq!(iter.next(), Some(coord![0, 0])); + assert_eq!(iter.next(), Some(coord![0, 1])); + assert_eq!(iter.next(), Some(coord![0, 2])); + assert_eq!(iter.next(), Some(coord![1, 0])); + assert_eq!(iter.next(), Some(coord![1, 1])); + assert_eq!(iter.next(), Some(coord![1, 2])); assert_eq!(iter.next(), None); } #[test] fn test_index_iterator_single_dimension() { - let shape = vec![4]; + let shape = shape![4].unwrap(); let mut iter = IndexIterator::new(&shape); - assert_eq!(iter.next(), Some(vec![0])); - assert_eq!(iter.next(), Some(vec![1])); - assert_eq!(iter.next(), Some(vec![2])); - assert_eq!(iter.next(), Some(vec![3])); + assert_eq!(iter.next(), Some(coord![0])); + assert_eq!(iter.next(), Some(coord![1])); + assert_eq!(iter.next(), Some(coord![2])); + assert_eq!(iter.next(), Some(coord![3])); assert_eq!(iter.next(), None); } #[test] fn test_index_iterator_empty_tensor() { - let shape = vec![]; + let shape = shape![].unwrap(); let mut iter = IndexIterator::new(&shape); assert_eq!(iter.next(), None); diff --git a/src/lib.rs b/src/lib.rs index 5c8113a..7684bdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ pub mod shape; pub mod iter; pub mod error; +pub mod coordinate; +pub mod storage; use num::Num; use std::ops::Add; @@ -11,14 +13,17 @@ use std::ops::Div; use crate::shape::Shape; use crate::iter::IndexIterator; use crate::error::ShapeError; +use crate::coordinate::Coordinate; +use crate::storage::DynamicStorage; pub type Axes = Vec; -#[derive(Debug, Clone)] -pub struct Tensor { - data: Vec, +#[derive(Debug)] +pub struct DynamicTensor { + data: DynamicStorage, shape: Shape } +type Tensor = DynamicTensor; // Alias for convenience impl Tensor { @@ -26,7 +31,7 @@ impl Tensor { if data.len() != shape.size() { return Err(ShapeError::new("Data length does not match shape size")); } - Ok(Tensor {data: data.to_vec(), shape: shape.clone()}) + Ok(Tensor {data: DynamicStorage::new(data.to_vec()), shape: shape.clone()}) } pub fn fill(shape: &Shape, value: T) -> Tensor { @@ -44,27 +49,16 @@ impl Tensor { Tensor::fill(shape, T::one()) } - // Element-wise operations - pub fn pow(&self, power: usize) -> Tensor { - let mut t = self.clone(); - for i in 0..t.size() { - t.data[i] = num::pow(t.data[i], power); - } - t - } - // Properties pub fn shape(&self) -> &Shape { &self.shape } pub fn size(&self) -> usize { self.shape.size() } - pub fn get_element(&self, indices: &[usize]) -> Result<&T, ShapeError> { - self.assert_indices(indices)?; - Ok(&self.data[self.calculate_index(indices)]) + pub fn get_element(&self, coord: &Coordinate) -> Result<&T, ShapeError> { + Ok(&self.data[self.data.flatten(coord, &self.shape)?]) } - pub fn set_element(&mut self, indices: &[usize], value: T) -> Result<(), ShapeError> { - self.assert_indices(indices)?; - let index = self.calculate_index(indices); + pub fn set_element(&mut self, coord: &Coordinate, value: T) -> Result<(), ShapeError> { + let index = self.data.flatten(coord, &self.shape)?; self.data[index] = value; Ok(()) } @@ -79,19 +73,20 @@ impl Tensor { // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { let sum: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x); - return Tensor::new(&Shape::new(vec![1]).unwrap(), &[sum]).unwrap(); + return Tensor::new(&shape![1].unwrap(), &[sum]).unwrap(); } // Create new tensor with right shape let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); let mut t: Tensor = Tensor::zeros(&new_shape); - for target in IndexIterator::new(&new_shape.dims) { - let sum_iter = IndexIterator::new(&removing_dims); + for target in IndexIterator::new(&new_shape) { + let sum_iter = IndexIterator::new(&remove_shape); for sum_index in sum_iter { let mut indices = target.clone(); for (i, &axis) in axes.iter().enumerate() { - indices.insert(axis, sum_index[i]); + indices = indices.insert(axis, sum_index[i]); } let value = *t.get_element(&target).unwrap() + *self.get_element(&indices).unwrap(); @@ -140,16 +135,17 @@ impl Tensor { // Create new tensor with right shape let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); let mut t: Tensor = Tensor::zeros(&new_shape); - for target in IndexIterator::new(&new_shape.dims) { - let sum_iter = IndexIterator::new(&removing_dims); + for target in IndexIterator::new(&new_shape) { + let sum_iter = IndexIterator::new(&remove_shape); let mean = self.mean(axes.clone()); for sum_index in sum_iter { let mut indices = target.clone(); for (i, &axis) in axes.iter().enumerate() { - indices.insert(axis, sum_index[i]); + indices = indices.insert(axis, sum_index[i]); } let centered = *self.get_element(&indices).unwrap() - *mean.get_element(&target).unwrap(); @@ -175,15 +171,16 @@ impl Tensor { // Create new tensor with right shape let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); let mut t: Tensor = Tensor::fill(&new_shape, min); - for target in IndexIterator::new(&new_shape.dims) { - let max_iter = IndexIterator::new(&removing_dims); + for target in IndexIterator::new(&new_shape) { + let max_iter = IndexIterator::new(&remove_shape); for max_index in max_iter { let mut indices = target.clone(); for (i, &axis) in axes.iter().enumerate() { - indices.insert(axis, max_index[i]); + indices = indices.insert(axis, max_index[i]); } if self.get_element(&indices).unwrap() > t.get_element(&target).unwrap() { @@ -209,15 +206,16 @@ impl Tensor { // Create new tensor with right shape let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); let mut t: Tensor = Tensor::fill(&new_shape, max); - for target in IndexIterator::new(&new_shape.dims) { - let min_iter = IndexIterator::new(&removing_dims); + for target in IndexIterator::new(&new_shape) { + let min_iter = IndexIterator::new(&remove_shape); for min_index in min_iter { let mut indices = target.clone(); for (i, &axis) in axes.iter().enumerate() { - indices.insert(axis, min_index[i]); + indices = indices.insert(axis, min_index[i]); } if self.get_element(&indices).unwrap() < t.get_element(&target).unwrap() { @@ -245,29 +243,6 @@ impl Tensor { Tensor::new(&new_shape, &new_data).unwrap() } - - /// For the maths see: https://bit.ly/3KQjPa3 - fn calculate_index(&self, indices: &[usize]) -> usize { - let mut index = 0; - for k in 0..self.shape.order() { - let stride = self.shape.dims[k+1..].iter().product::(); - index += indices[k] * stride; - } - index - } - - fn assert_indices(&self, indices: &[usize]) -> Result<(), ShapeError> { - if indices.len() != self.shape.order() { - let msg = format!("incorrect order ({} vs {}).", indices.len(), self.shape.order()); - return Err(ShapeError::new(msg.as_str())); - } - for (i, &index) in indices.iter().enumerate() { - if index >= self.shape.dims[i] { - return Err(ShapeError::new(format!("out of bounds for dimension {}", i).as_str())); - } - } - Ok(()) - } } // Element-wise Multiplication @@ -422,7 +397,7 @@ mod tests { let tensor = Tensor::new(&shape, &data).unwrap(); assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, data); + assert_eq!(tensor.data, DynamicStorage::new(data)); } #[test] @@ -441,7 +416,7 @@ mod tests { let tensor: Tensor = Tensor::zeros(&shape); assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, vec![0.0; shape.size()]); + assert_eq!(tensor.data, DynamicStorage::new(vec![0.0; shape.size()])); } #[test] @@ -450,7 +425,7 @@ mod tests { let tensor: Tensor = Tensor::ones(&shape); assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, vec![1.0; shape.size()]); + assert_eq!(tensor.data, DynamicStorage::new(vec![1.0; shape.size()])); } #[test] @@ -459,7 +434,7 @@ mod tests { let tensor: Tensor = Tensor::fill(&shape, 7.0); assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, vec![7.0; shape.size()]); + assert_eq!(tensor.data, DynamicStorage::new(vec![7.0; shape.size()])); } #[test] @@ -478,22 +453,6 @@ mod tests { assert_eq!(tensor.size(), 6); } - #[test] - fn test_tensor_pow() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.pow(2); - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![1.0, 4.0, 9.0, 16.0]); - - let result = tensor.pow(3); - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![1.0, 8.0, 27.0, 64.0]); - } #[test] fn test_tensor_get() { @@ -501,10 +460,10 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = Tensor::new(&shape, &data).unwrap(); - assert_eq!(*tensor.get_element(&[0, 0]).unwrap(), 1.0); - assert_eq!(*tensor.get_element(&[0, 1]).unwrap(), 2.0); - assert_eq!(*tensor.get_element(&[1, 0]).unwrap(), 3.0); - assert_eq!(*tensor.get_element(&[1, 1]).unwrap(), 4.0); + assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 1.0); + assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 2.0); + assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 3.0); + assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 4.0); } #[test] @@ -513,15 +472,15 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut tensor = Tensor::new(&shape, &data).unwrap(); - tensor.set_element(&[0, 0], 5.0).unwrap(); - tensor.set_element(&[0, 1], 6.0).unwrap(); - tensor.set_element(&[1, 0], 7.0).unwrap(); - tensor.set_element(&[1, 1], 8.0).unwrap(); + tensor.set_element(&coord![0, 0], 5.0).unwrap(); + tensor.set_element(&coord![0, 1], 6.0).unwrap(); + tensor.set_element(&coord![1, 0], 7.0).unwrap(); + tensor.set_element(&coord![1, 1], 8.0).unwrap(); - assert_eq!(*tensor.get_element(&[0, 0]).unwrap(), 5.0); - assert_eq!(*tensor.get_element(&[0, 1]).unwrap(), 6.0); - assert_eq!(*tensor.get_element(&[1, 0]).unwrap(), 7.0); - assert_eq!(*tensor.get_element(&[1, 1]).unwrap(), 8.0); + assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 5.0); + assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 6.0); + assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 7.0); + assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 8.0); } #[test] @@ -530,9 +489,9 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = Tensor::new(&shape, &data).unwrap(); - assert!(tensor.get_element(&[2, 0]).is_err()); - assert!(tensor.get_element(&[0, 2]).is_err()); - assert!(tensor.get_element(&[2, 2]).is_err()); + assert!(tensor.get_element(&coord![2, 0]).is_err()); + assert!(tensor.get_element(&coord![0, 2]).is_err()); + assert!(tensor.get_element(&coord![2, 2]).is_err()); } #[test] @@ -541,9 +500,9 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut tensor = Tensor::new(&shape, &data).unwrap(); - assert!(tensor.set_element(&[2, 0], 5.0).is_err()); - assert!(tensor.set_element(&[0, 2], 6.0).is_err()); - assert!(tensor.set_element(&[2, 2], 7.0).is_err()); + assert!(tensor.set_element(&coord![2, 0], 5.0).is_err()); + assert!(tensor.set_element(&coord![0, 2], 6.0).is_err()); + assert!(tensor.set_element(&coord![2, 2], 7.0).is_err()); } #[test] @@ -555,7 +514,7 @@ mod tests { let result = tensor.sum(vec![]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![15.0]); + assert_eq!(result.data, DynamicStorage::new(vec![15.0])); } #[test] @@ -567,7 +526,7 @@ mod tests { let result = tensor.sum(vec![]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![21.0]); + assert_eq!(result.data, DynamicStorage::new(vec![21.0])); } #[test] @@ -579,7 +538,7 @@ mod tests { let result = tensor.sum(vec![]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![78.0]); + assert_eq!(result.data, DynamicStorage::new(vec![78.0])); } #[test] @@ -591,7 +550,7 @@ mod tests { let result = tensor.sum(vec![0]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![15.0]); + assert_eq!(result.data, DynamicStorage::new(vec![15.0])); } #[test] @@ -603,7 +562,7 @@ mod tests { let result = tensor.sum(vec![0]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![5.0, 7.0, 9.0]); + assert_eq!(result.data, DynamicStorage::new(vec![5.0, 7.0, 9.0])); } #[test] @@ -615,7 +574,7 @@ mod tests { let result = tensor.sum(vec![0]); assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0]); + assert_eq!(result.data, DynamicStorage::new(vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0])); } #[test] @@ -627,7 +586,7 @@ mod tests { let result = tensor.sum(vec![0, 1]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![21.0]); + assert_eq!(result.data, DynamicStorage::new(vec![21.0])); } #[test] @@ -639,7 +598,7 @@ mod tests { let result = tensor.sum(vec![0, 1]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![22.0, 26.0, 30.0]); + assert_eq!(result.data, DynamicStorage::new(vec![22.0, 26.0, 30.0])); } #[test] @@ -651,7 +610,7 @@ mod tests { let result = tensor.mean(vec![0]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![2.5, 3.5, 4.5]); + assert_eq!(result.data, DynamicStorage::new(vec![2.5, 3.5, 4.5])); } #[test] @@ -663,7 +622,7 @@ mod tests { let result = tensor.mean(vec![0]); assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0]); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0])); } #[test] @@ -675,7 +634,7 @@ mod tests { let result = tensor.mean(vec![0, 1]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![3.5]); + assert_eq!(result.data, DynamicStorage::new(vec![3.5])); } #[test] @@ -687,7 +646,7 @@ mod tests { let result = tensor.mean(vec![0, 1]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![5.5, 6.5, 7.5]); + assert_eq!(result.data, DynamicStorage::new(vec![5.5, 6.5, 7.5])); } #[test] @@ -699,7 +658,7 @@ mod tests { let result = tensor.var(vec![0]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![2.25, 2.25, 2.25]); + assert_eq!(result.data, DynamicStorage::new(vec![2.25, 2.25, 2.25])); } #[test] @@ -711,7 +670,7 @@ mod tests { let result = tensor.var(vec![0]); assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0]); + assert_eq!(result.data, DynamicStorage::new(vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0])); } #[test] @@ -723,7 +682,7 @@ mod tests { let result = tensor.var(vec![0, 1]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![9.0]); + assert_eq!(result.data, DynamicStorage::new(vec![9.0])); } #[test] @@ -735,7 +694,7 @@ mod tests { let result = tensor.var(vec![0, 1]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![45.0, 45.0, 45.0]); + assert_eq!(result.data, DynamicStorage::new(vec![45.0, 45.0, 45.0])); } #[test] @@ -747,7 +706,7 @@ mod tests { let result = tensor.max(vec![]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![5.0]); + assert_eq!(result.data, DynamicStorage::new(vec![5.0])); } #[test] @@ -759,7 +718,7 @@ mod tests { let result = tensor.max(vec![0]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![1.0, 5.0, 3.0]); + assert_eq!(result.data, DynamicStorage::new(vec![1.0, 5.0, 3.0])); } #[test] @@ -771,7 +730,7 @@ mod tests { let result = tensor.max(vec![0, 1]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![7.0, 11.0, 9.0]); + assert_eq!(result.data, DynamicStorage::new(vec![7.0, 11.0, 9.0])); } #[test] @@ -783,7 +742,7 @@ mod tests { let result = tensor.min(vec![]); assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![-4.0]); + assert_eq!(result.data, DynamicStorage::new(vec![-4.0])); } #[test] @@ -795,7 +754,7 @@ mod tests { let result = tensor.min(vec![0]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![-4.0, -2.0, -6.0]); + assert_eq!(result.data, DynamicStorage::new(vec![-4.0, -2.0, -6.0])); } #[test] @@ -807,7 +766,7 @@ mod tests { let result = tensor.min(vec![0, 1]); assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![-10.0, -8.0, -12.0]); + assert_eq!(result.data, DynamicStorage::new(vec![-10.0, -8.0, -12.0])); } #[test] @@ -823,7 +782,7 @@ mod tests { let result = tensor1.prod(&tensor2); assert_eq!(result.shape(), &shape![3, 2].unwrap()); - assert_eq!(result.data, vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0]); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0])); } #[test] @@ -839,7 +798,7 @@ mod tests { let result = tensor1.prod(&tensor2); assert_eq!(result.shape(), &shape![2, 2, 2].unwrap()); - assert_eq!(result.data, vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0]); + assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0])); } #[test] @@ -855,7 +814,7 @@ mod tests { let result = tensor1.prod(&tensor2); assert_eq!(result.shape(), &shape![2, 2, 2, 2].unwrap()); - assert_eq!(result.data, vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0]); + assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0])); } #[test] @@ -867,7 +826,7 @@ mod tests { let result = tensor1 + 3.0; assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![4.0, 5.0, 6.0, 7.0]); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0])); } #[test] @@ -881,7 +840,7 @@ mod tests { let result = tensor1 + tensor2; assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![6.0, 8.0, 10.0, 12.0]); + assert_eq!(result.data, DynamicStorage::new(vec![6.0, 8.0, 10.0, 12.0])); } #[test] @@ -894,7 +853,7 @@ mod tests { let result = tensor1 - 3.0; assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![2.0, 3.0, 4.0, 5.0]); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); } #[test] @@ -908,7 +867,7 @@ mod tests { let result = tensor1 - tensor2; assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![4.0, 4.0, 4.0, 4.0]); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 4.0, 4.0, 4.0])); } #[test] @@ -921,7 +880,7 @@ mod tests { let result = tensor1 * 2.0; assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![2.0, 4.0, 6.0, 8.0]); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 4.0, 6.0, 8.0])); } #[test] @@ -936,7 +895,7 @@ mod tests { let result = tensor1 * tensor2; assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![10.0]); + assert_eq!(result.data, DynamicStorage::new(vec![10.0])); } #[test] @@ -951,7 +910,7 @@ mod tests { let result = tensor1 * tensor2; assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, vec![40.0]); + assert_eq!(result.data, DynamicStorage::new(vec![40.0])); } #[test] @@ -967,7 +926,7 @@ mod tests { let result = tensor_vec * tensor_matrix; assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, vec![9.0, 12.0, 15.0]); + assert_eq!(result.data, DynamicStorage::new(vec![9.0, 12.0, 15.0])); } #[test] @@ -983,7 +942,7 @@ mod tests { let result = tensor_matrix * tensor_vec; assert_eq!(result.shape(), &shape![2].unwrap()); - assert_eq!(result.data, vec![14.0, 32.0]); + assert_eq!(result.data, DynamicStorage::new(vec![14.0, 32.0])); } #[test] @@ -999,7 +958,7 @@ mod tests { let result = tensor1 * tensor2; assert_eq!(result.shape(), &shape![2, 2].unwrap()); - assert_eq!(result.data, vec![58.0, 64.0, 139.0, 154.0]); + assert_eq!(result.data, DynamicStorage::new(vec![58.0, 64.0, 139.0, 154.0])); } #[test] @@ -1012,7 +971,7 @@ mod tests { let result = tensor1 / 2.0; assert_eq!(result.shape(), &shape); - assert_eq!(result.data, vec![2.0, 3.0, 4.0, 5.0]); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); } } diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 0000000..aee06b9 --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,87 @@ +use std::ops::Index; +use std::ops::IndexMut; + +use crate::coordinate::Coordinate; +use crate::shape::Shape; +use crate::error::ShapeError; + +#[derive(Debug, PartialEq)] +pub struct DynamicStorage { + data: Vec, +} + +impl DynamicStorage { + pub fn new(data: Vec) -> Self { + Self { data } + } + + /// For the row-wise maths see: https://bit.ly/3KQjPa3 + pub fn flatten(&self, coord: &Coordinate, shape: &Shape) -> Result { + if coord.len() != shape.order() { + let msg = format!("incorrect order ({} vs {}).", coord.len(), shape.order()); + return Err(ShapeError::new(msg.as_str())); + } + + for (i, &dim) in coord.iter().enumerate() { + if dim >= shape.dims[i] { + return Err(ShapeError::new(format!("out of bounds for dimension {}", i).as_str())); + } + } + + let mut index = 0; + for k in 0..shape.order() { + let stride = shape.dims[k+1..].iter().product::(); + index += coord[k] * stride; + } + Ok(index) + } + + pub fn iter(&self) -> std::slice::Iter<'_, T> { + self.data.iter() + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { + self.data.iter_mut() + } +} + +impl Index for DynamicStorage { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.data[index] + } +} + +impl IndexMut for DynamicStorage { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.data[index] + } +} + +impl IntoIterator for DynamicStorage { + type Item = T; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.data.into_iter() + } +} + +impl<'a, T> IntoIterator for &'a DynamicStorage { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.data.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut DynamicStorage { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.data.iter_mut() + } +} From 44fa3640f8bd389c0d55de7f21a34dc31f197c1e Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 20:16:31 +0100 Subject: [PATCH 10/24] feat: add tests --- src/coordinate.rs | 58 +++++++++++++++++++++++++++++++++++ src/storage.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/src/coordinate.rs b/src/coordinate.rs index dce862b..edf39b7 100644 --- a/src/coordinate.rs +++ b/src/coordinate.rs @@ -59,3 +59,61 @@ macro_rules! coord { Coordinate::new(vec![$index; $count]) }; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_len() { + let coord = coord![1, 2, 3]; + assert_eq!(coord.len(), 3); + } + + #[test] + fn test_iter() { + let coord = coord![1, 2, 3]; + let mut iter = coord.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_insert() { + let coord = coord![1, 2, 3]; + let new_coord = coord.insert(1, 4); + assert_eq!(new_coord, coord![1, 4, 2, 3]); + } + + #[test] + fn test_index() { + let coord = coord![1, 2, 3]; + assert_eq!(coord[0], 1); + assert_eq!(coord[1], 2); + assert_eq!(coord[2], 3); + } + + #[test] + fn test_index_mut() { + let mut coord = coord![1, 2, 3]; + coord[1] = 4; + assert_eq!(coord[1], 4); + } + + #[test] + fn test_display() { + let coord = coord![1, 2, 3]; + assert_eq!(format!("{}", coord), "(1, 2, 3)"); + } + + #[test] + fn test_coord_macro() { + let coord = coord![1, 2, 3]; + assert_eq!(coord, Coordinate::new(vec![1, 2, 3])); + + let coord_repeated = coord![1; 3]; + assert_eq!(coord_repeated, Coordinate::new(vec![1, 1, 1])); + } +} diff --git a/src/storage.rs b/src/storage.rs index aee06b9..2aef72f 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -85,3 +85,81 @@ impl<'a, T> IntoIterator for &'a mut DynamicStorage { self.data.iter_mut() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_iter() { + let storage = DynamicStorage::new(vec![1, 2, 3, 4]); + let mut iter = storage.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_iter_mut() { + let mut storage = DynamicStorage::new(vec![1, 2, 3, 4]); + { + let mut iter = storage.iter_mut(); + if let Some(x) = iter.next() { + *x = 10; + } + } + assert_eq!(storage.data, vec![10, 2, 3, 4]); + } + + #[test] + fn test_index() { + let storage = DynamicStorage::new(vec![1, 2, 3, 4]); + assert_eq!(storage[0], 1); + assert_eq!(storage[1], 2); + assert_eq!(storage[2], 3); + assert_eq!(storage[3], 4); + } + + #[test] + fn test_index_mut() { + let mut storage = DynamicStorage::new(vec![1, 2, 3, 4]); + storage[0] = 10; + assert_eq!(storage[0], 10); + } + + #[test] + fn test_into_iter() { + let storage = DynamicStorage::new(vec![1, 2, 3, 4]); + let mut iter = storage.into_iter(); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(4)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_into_iter_ref() { + let storage = DynamicStorage::new(vec![1, 2, 3, 4]); + let mut iter = (&storage).into_iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_into_iter_mut() { + let mut storage = DynamicStorage::new(vec![1, 2, 3, 4]); + { + let mut iter = (&mut storage).into_iter(); + if let Some(x) = iter.next() { + *x = 10; + } + } + assert_eq!(storage.data, vec![10, 2, 3, 4]); + } +} From da4f2f845ffef41d6fe451243bb8e20630d356c4 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 20:20:24 +0100 Subject: [PATCH 11/24] feat: collapse uses --- src/coordinate.rs | 3 +-- src/lib.rs | 5 +---- src/storage.rs | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/coordinate.rs b/src/coordinate.rs index edf39b7..d64665d 100644 --- a/src/coordinate.rs +++ b/src/coordinate.rs @@ -1,6 +1,5 @@ use std::fmt; -use std::ops::Index; -use std::ops::IndexMut; +use std::ops::{Index, IndexMut}; #[derive(Debug, Clone, PartialEq)] pub struct Coordinate { diff --git a/src/lib.rs b/src/lib.rs index 7684bdf..1a68663 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,7 @@ pub mod coordinate; pub mod storage; use num::Num; -use std::ops::Add; -use std::ops::Sub; -use std::ops::Mul; -use std::ops::Div; +use std::ops::{Add, Sub, Mul, Div}; use crate::shape::Shape; use crate::iter::IndexIterator; diff --git a/src/storage.rs b/src/storage.rs index 2aef72f..23fc302 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,5 +1,4 @@ -use std::ops::Index; -use std::ops::IndexMut; +use std::ops::{Index, IndexMut}; use crate::coordinate::Coordinate; use crate::shape::Shape; From a8bd42fd4507adf077d948bb156c14fa054953a9 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 21:59:37 +0100 Subject: [PATCH 12/24] feat!: make dims private in Shape --- src/lib.rs | 39 +++++++++++++++------------------------ src/shape.rs | 25 ++++++++++++++++++++++++- src/storage.rs | 4 ++-- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1a68663..c93eebd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,19 +32,12 @@ impl Tensor { } pub fn fill(shape: &Shape, value: T) -> Tensor { - let total_size = shape.size(); - let mut vec = Vec::with_capacity(total_size); - for _ in 0..total_size { vec.push(value); } + let mut vec = Vec::with_capacity(shape.size()); + for _ in 0..shape.size() { vec.push(value); } Tensor::new(shape, &vec).unwrap() } - - pub fn zeros(shape: &Shape) -> Tensor { - Tensor::fill(shape, T::zero()) - } - - pub fn ones(shape: &Shape) -> Tensor { - Tensor::fill(shape, T::one()) - } + pub fn zeros(shape: &Shape) -> Tensor {Tensor::fill(shape, T::zero())} + pub fn ones(shape: &Shape) -> Tensor {Tensor::fill(shape, T::one())} // Properties pub fn shape(&self) -> &Shape { &self.shape } @@ -64,8 +57,8 @@ impl Tensor { pub fn sum(&self, axes: Axes) -> Tensor { let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { @@ -95,7 +88,7 @@ impl Tensor { } pub fn mean(&self, axes: Axes) -> Tensor { - let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { let mut result = T::zero(); for _ in 0..dim { @@ -108,7 +101,7 @@ impl Tensor { } pub fn var(&self, axes: Axes) -> Tensor { - let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { let mut result = T::zero(); for _ in 0..dim { @@ -120,8 +113,8 @@ impl Tensor { let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { @@ -157,8 +150,8 @@ impl Tensor { pub fn max(&self, axes: Axes) -> Tensor { let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { @@ -192,8 +185,8 @@ impl Tensor { pub fn min(&self, axes: Axes) -> Tensor { let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape.dims[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape.dims[i]).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { @@ -227,9 +220,7 @@ impl Tensor { // Tensor Product // Consistent with numpy.tensordot(a, b, axis=0) pub fn prod(&self, other: &Tensor) -> Tensor { - let mut new_dims = self.shape.dims.clone(); - new_dims.extend(&other.shape.dims); - let new_shape = Shape::new(new_dims).unwrap(); + let new_shape = self.shape.stack(&other.shape); let mut new_data = Vec::with_capacity(self.size() * other.size()); for &a in &self.data { diff --git a/src/shape.rs b/src/shape.rs index e91b89e..80a2f47 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -5,7 +5,7 @@ use crate::error::ShapeError; #[derive(Debug, Clone, PartialEq)] pub struct Shape { - pub dims: Vec, + dims: Vec, } impl Shape { @@ -23,6 +23,12 @@ impl Shape { pub fn order(&self) -> usize { self.dims.len() } + + pub fn stack(&self, rhs: &Shape) -> Shape { + let mut new_dims = self.dims.clone(); + new_dims.extend(rhs.dims.iter()); + Shape { dims: new_dims } + } } impl Index for Shape { @@ -33,6 +39,14 @@ impl Index for Shape { } } +impl Index> for Shape { + type Output = [usize]; + + fn index(&self, index: std::ops::RangeFrom) -> &Self::Output { + &self.dims[index] + } +} + impl fmt::Display for Shape { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use itertools::Itertools; @@ -83,4 +97,13 @@ mod tests { let shape = shape![2, 3, 4].unwrap(); assert_eq!(shape.dims, vec![2, 3, 4]); } + + + #[test] + fn test_shape_extend() { + let shape1 = Shape::new(vec![2, 3]).unwrap(); + let shape2 = Shape::new(vec![4, 5]).unwrap(); + let extended_shape = shape1.stack(&shape2); + assert_eq!(extended_shape.dims, vec![2, 3, 4, 5]); + } } \ No newline at end of file diff --git a/src/storage.rs b/src/storage.rs index 23fc302..cf9c791 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -22,14 +22,14 @@ impl DynamicStorage { } for (i, &dim) in coord.iter().enumerate() { - if dim >= shape.dims[i] { + if dim >= shape[i] { return Err(ShapeError::new(format!("out of bounds for dimension {}", i).as_str())); } } let mut index = 0; for k in 0..shape.order() { - let stride = shape.dims[k+1..].iter().product::(); + let stride = shape[k+1..].iter().product::(); index += coord[k] * stride; } Ok(index) From dcaf8d2e76ee7513a1aec60a50fcb1e9d12132c2 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Wed, 26 Jun 2024 22:12:37 +0100 Subject: [PATCH 13/24] feat: add one more test case --- src/shape.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shape.rs b/src/shape.rs index 80a2f47..dcb77d2 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -96,9 +96,11 @@ mod tests { fn test_shape_macro() { let shape = shape![2, 3, 4].unwrap(); assert_eq!(shape.dims, vec![2, 3, 4]); + + let shape = shape![1].unwrap(); + assert_eq!(shape.dims, vec![1]); } - #[test] fn test_shape_extend() { let shape1 = Shape::new(vec![2, 3]).unwrap(); From cc91cbde86be67ca7809300550106ca4d8eadd86 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Thu, 27 Jun 2024 14:25:10 +0100 Subject: [PATCH 14/24] feat: move tensor to separate file --- src/lib.rs | 961 +------------------------------------------------- src/tensor.rs | 961 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 962 insertions(+), 960 deletions(-) create mode 100644 src/tensor.rs diff --git a/src/lib.rs b/src/lib.rs index c93eebd..f6d29a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,963 +3,4 @@ pub mod iter; pub mod error; pub mod coordinate; pub mod storage; - -use num::Num; -use std::ops::{Add, Sub, Mul, Div}; - -use crate::shape::Shape; -use crate::iter::IndexIterator; -use crate::error::ShapeError; -use crate::coordinate::Coordinate; -use crate::storage::DynamicStorage; - -pub type Axes = Vec; - -#[derive(Debug)] -pub struct DynamicTensor { - data: DynamicStorage, - shape: Shape -} -type Tensor = DynamicTensor; // Alias for convenience - -impl Tensor { - - pub fn new(shape: &Shape, data: &[T]) -> Result, ShapeError> { - if data.len() != shape.size() { - return Err(ShapeError::new("Data length does not match shape size")); - } - Ok(Tensor {data: DynamicStorage::new(data.to_vec()), shape: shape.clone()}) - } - - pub fn fill(shape: &Shape, value: T) -> Tensor { - let mut vec = Vec::with_capacity(shape.size()); - for _ in 0..shape.size() { vec.push(value); } - Tensor::new(shape, &vec).unwrap() - } - pub fn zeros(shape: &Shape) -> Tensor {Tensor::fill(shape, T::zero())} - pub fn ones(shape: &Shape) -> Tensor {Tensor::fill(shape, T::one())} - - // Properties - pub fn shape(&self) -> &Shape { &self.shape } - pub fn size(&self) -> usize { self.shape.size() } - - pub fn get_element(&self, coord: &Coordinate) -> Result<&T, ShapeError> { - Ok(&self.data[self.data.flatten(coord, &self.shape)?]) - } - - pub fn set_element(&mut self, coord: &Coordinate, value: T) -> Result<(), ShapeError> { - let index = self.data.flatten(coord, &self.shape)?; - self.data[index] = value; - Ok(()) - } - - // // Reduction operations - pub fn sum(&self, axes: Axes) -> Tensor { - let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - - // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { - let sum: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x); - return Tensor::new(&shape![1].unwrap(), &[sum]).unwrap(); - } - - // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims).unwrap(); - let remove_shape = Shape::new(removing_dims).unwrap(); - let mut t: Tensor = Tensor::zeros(&new_shape); - - for target in IndexIterator::new(&new_shape) { - let sum_iter = IndexIterator::new(&remove_shape); - for sum_index in sum_iter { - let mut indices = target.clone(); - for (i, &axis) in axes.iter().enumerate() { - indices = indices.insert(axis, sum_index[i]); - } - - let value = *t.get_element(&target).unwrap() + *self.get_element(&indices).unwrap(); - let _ = t.set_element(&target, value).unwrap(); - } - } - - t - } - - pub fn mean(&self, axes: Axes) -> Tensor { - let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { - let mut result = T::zero(); - for _ in 0..dim { - result = result + T::one(); - } - result - }).collect(); - let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); - self.sum(axes) / n - } - - pub fn var(&self, axes: Axes) -> Tensor { - let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { - let mut result = T::zero(); - for _ in 0..dim { - result = result + T::one(); - } - result - }).collect(); - let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); - - let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - - // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { - let avg: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x) / n; - let var: T = self.data.iter().map(|&x| (x - avg) * (x - avg)).fold(T::zero(), |acc, x| acc + x) / n; - return Tensor::new(&Shape::new(vec![1]).unwrap(), &[var]).unwrap(); - } - - // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims).unwrap(); - let remove_shape = Shape::new(removing_dims).unwrap(); - let mut t: Tensor = Tensor::zeros(&new_shape); - - for target in IndexIterator::new(&new_shape) { - let sum_iter = IndexIterator::new(&remove_shape); - let mean = self.mean(axes.clone()); - - for sum_index in sum_iter { - let mut indices = target.clone(); - for (i, &axis) in axes.iter().enumerate() { - indices = indices.insert(axis, sum_index[i]); - } - - let centered = *self.get_element(&indices).unwrap() - *mean.get_element(&target).unwrap(); - let value = *t.get_element(&target).unwrap() + centered * centered; - let _ = t.set_element(&target, value).unwrap(); - } - } - - t / n - } - - pub fn max(&self, axes: Axes) -> Tensor { - let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - - // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { - let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); - return Tensor::new(&Shape::new(vec![1]).unwrap(), &[max]).unwrap(); - } - - // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims).unwrap(); - let remove_shape = Shape::new(removing_dims).unwrap(); - let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); - let mut t: Tensor = Tensor::fill(&new_shape, min); - - for target in IndexIterator::new(&new_shape) { - let max_iter = IndexIterator::new(&remove_shape); - for max_index in max_iter { - let mut indices = target.clone(); - for (i, &axis) in axes.iter().enumerate() { - indices = indices.insert(axis, max_index[i]); - } - - if self.get_element(&indices).unwrap() > t.get_element(&target).unwrap() { - let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); - } - } - } - - t - } - - pub fn min(&self, axes: Axes) -> Tensor { - let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - - // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { - let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); - return Tensor::new(&Shape::new(vec![1]).unwrap(), &[min]).unwrap(); - } - - // Create new tensor with right shape - let new_shape = Shape::new(remaining_dims).unwrap(); - let remove_shape = Shape::new(removing_dims).unwrap(); - let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); - let mut t: Tensor = Tensor::fill(&new_shape, max); - - for target in IndexIterator::new(&new_shape) { - let min_iter = IndexIterator::new(&remove_shape); - for min_index in min_iter { - let mut indices = target.clone(); - for (i, &axis) in axes.iter().enumerate() { - indices = indices.insert(axis, min_index[i]); - } - - if self.get_element(&indices).unwrap() < t.get_element(&target).unwrap() { - let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); - } - } - } - - t - } - - // Tensor Product - // Consistent with numpy.tensordot(a, b, axis=0) - pub fn prod(&self, other: &Tensor) -> Tensor { - let new_shape = self.shape.stack(&other.shape); - - let mut new_data = Vec::with_capacity(self.size() * other.size()); - for &a in &self.data { - for &b in &other.data { - new_data.push(a * b); - } - } - - Tensor::new(&new_shape, &new_data).unwrap() - } -} - -// Element-wise Multiplication -impl Mul for Tensor { - type Output = Tensor; - - fn mul(self, rhs: T) -> Tensor { - let mut result = Tensor::zeros(&self.shape); - for i in 0..self.size() { - result.data[i] = self.data[i].clone() * rhs; - } - result - } -} - -// Vector/Matrix Multiplication -impl Mul> for Tensor { - type Output = Tensor; - - fn mul(self, rhs: Tensor) -> Tensor { - if self.shape.order() == 1 && rhs.shape.order() == 1 { - // Vector-Vector multiplication (dot product) - assert!(self.shape[0] == rhs.shape[0], "Vectors must be of the same length for dot product."); - let mut result = T::zero(); - for i in 0..self.shape[0] { - result = result + self.data[i] * rhs.data[i]; - } - Tensor::new(&shape![1].unwrap(), &vec![result]).unwrap() - } else if self.shape.order() == 1 && rhs.shape.order() == 2 { - // Vector-Matrix multiplication - assert!(self.shape[0] == rhs.shape[0], "The length of the vector must be equal to the number of rows in the matrix."); - let mut result = Tensor::zeros(&shape![rhs.shape[1]].unwrap()); - for j in 0..rhs.shape[1] { - let mut sum = T::zero(); - for i in 0..self.shape[0] { - sum = sum + self.data[i] * rhs.data[i * rhs.shape[1] + j]; - } - result.data[j] = sum; - } - result - } else if self.shape.order() == 2 && rhs.shape.order() == 1 { - // Matrix-Vector multiplication - assert!(self.shape[1] == rhs.shape[0], "The number of columns in the matrix must be equal to the length of the vector."); - let mut result = Tensor::zeros(&shape![self.shape[0]].unwrap()); - for i in 0..self.shape[0] { - let mut sum = T::zero(); - for j in 0..self.shape[1] { - sum = sum + self.data[i * self.shape[1] + j] * rhs.data[j]; - } - result.data[i] = sum; - } - result - } else if self.shape.order() == 2 && rhs.shape.order() == 2 { - // Matrix-Matrix multiplication - assert!(self.shape[1] == rhs.shape[0], "The number of columns in the first matrix must be equal to the number of rows in the second matrix."); - let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]].unwrap()); - for i in 0..self.shape[0] { - for j in 0..rhs.shape[1] { - let mut sum = T::zero(); - for k in 0..self.shape[1] { - sum = sum + self.data[i * self.shape[1] + k] * rhs.data[k * rhs.shape[1] + j]; - } - result.data[i * rhs.shape[1] + j] = sum; - } - } - result - } else { - panic!("Unsupported shapes for multiplication."); - } - } -} - -// Element-wise Addition -impl Add for Tensor { - type Output = Tensor; - - fn add(self, rhs: T) -> Tensor { - let mut result = Tensor::zeros(&self.shape); - for i in 0..self.size() { - result.data[i] = self.data[i].clone() + rhs; - } - result - } -} - -// Tensor Addition -impl Add> for Tensor { - type Output = Tensor; - - fn add(self, rhs: Tensor) -> Tensor { - assert!(self.shape == rhs.shape); - let mut result = Tensor::zeros(&self.shape); - for i in 0..self.size() { - result.data[i] = self.data[i].clone() + rhs.data[i].clone(); - } - result - } -} - -// Element-wise Subtraction -impl Sub for Tensor -{ - type Output = Tensor; - - fn sub(self, rhs: T) -> Tensor { - let mut result = Tensor::zeros(&self.shape); - for i in 0..self.size() { - result.data[i] = self.data[i].clone() - rhs; - } - result - } -} - -// Tensor Subtraction -impl Sub> for Tensor { - type Output = Tensor; - - fn sub(self, rhs: Tensor) -> Tensor { - assert!(self.shape == rhs.shape); - let mut result = Tensor::zeros(&self.shape); - for i in 0..self.size() { - result.data[i] = self.data[i].clone() - rhs.data[i].clone(); - } - result - } -} - -// Element-wise Division -impl Div for Tensor -{ - type Output = Tensor; - - fn div(self, rhs: T) -> Tensor { - let mut result = Tensor::zeros(&self.shape); - for i in 0..self.size() { - result.data[i] = self.data[i].clone() / rhs; - } - result - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_new_tensor() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0]; - - let tensor = Tensor::new(&shape, &data).unwrap(); - - assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, DynamicStorage::new(data)); - } - - #[test] - fn test_new_tensor_shape_data_mismatch() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0]; // Mismatched data length - - let result = Tensor::new(&shape, &data); - - assert!(result.is_err()); - } - - #[test] - fn test_zeros_tensor() { - let shape = shape![2, 3].unwrap(); - let tensor: Tensor = Tensor::zeros(&shape); - - assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, DynamicStorage::new(vec![0.0; shape.size()])); - } - - #[test] - fn test_ones_tensor() { - let shape = shape![2, 3].unwrap(); - let tensor: Tensor = Tensor::ones(&shape); - - assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, DynamicStorage::new(vec![1.0; shape.size()])); - } - - #[test] - fn test_fill_tensor() { - let shape = shape![2, 3].unwrap(); - let tensor: Tensor = Tensor::fill(&shape, 7.0); - - assert_eq!(tensor.shape(), &shape); - assert_eq!(tensor.data, DynamicStorage::new(vec![7.0; shape.size()])); - } - - #[test] - fn test_tensor_shape() { - let shape = shape![2, 3].unwrap(); - let tensor: Tensor = Tensor::zeros(&shape); - - assert_eq!(tensor.shape(), &shape); - } - - #[test] - fn test_tensor_size() { - let shape = shape![2, 3].unwrap(); - let tensor: Tensor = Tensor::zeros(&shape); - - assert_eq!(tensor.size(), 6); - } - - - #[test] - fn test_tensor_get() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 1.0); - assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 2.0); - assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 3.0); - assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 4.0); - } - - #[test] - fn test_tensor_set() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0]; - let mut tensor = Tensor::new(&shape, &data).unwrap(); - - tensor.set_element(&coord![0, 0], 5.0).unwrap(); - tensor.set_element(&coord![0, 1], 6.0).unwrap(); - tensor.set_element(&coord![1, 0], 7.0).unwrap(); - tensor.set_element(&coord![1, 1], 8.0).unwrap(); - - assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 5.0); - assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 6.0); - assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 7.0); - assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 8.0); - } - - #[test] - fn test_tensor_get_out_of_bounds() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - assert!(tensor.get_element(&coord![2, 0]).is_err()); - assert!(tensor.get_element(&coord![0, 2]).is_err()); - assert!(tensor.get_element(&coord![2, 2]).is_err()); - } - - #[test] - fn test_tensor_set_out_of_bounds() { - let shape = shape![2, 2].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0]; - let mut tensor = Tensor::new(&shape, &data).unwrap(); - - assert!(tensor.set_element(&coord![2, 0], 5.0).is_err()); - assert!(tensor.set_element(&coord![0, 2], 6.0).is_err()); - assert!(tensor.set_element(&coord![2, 2], 7.0).is_err()); - } - - #[test] - fn test_tensor_sum_no_axis_1d() { - let shape = shape![5].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![15.0])); - } - - #[test] - fn test_tensor_sum_no_axis_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![21.0])); - } - - #[test] - fn test_tensor_sum_no_axis_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![78.0])); - } - - #[test] - fn test_tensor_sum_one_axis_1d() { - let shape = shape![5].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![0]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![15.0])); - } - - #[test] - fn test_tensor_sum_one_axis_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![0]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.0, 7.0, 9.0])); - } - - #[test] - fn test_tensor_sum_one_axis_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![0]); - - assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0])); - } - - #[test] - fn test_tensor_sum_multiple_axes_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![0, 1]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![21.0])); - } - - #[test] - fn test_tensor_sum_multiple_axes_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.sum(vec![0, 1]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![22.0, 26.0, 30.0])); - } - - #[test] - fn test_tensor_mean_one_axis_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.mean(vec![0]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![2.5, 3.5, 4.5])); - } - - #[test] - fn test_tensor_mean_one_axis_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.mean(vec![0]); - - assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0])); - } - - #[test] - fn test_tensor_mean_multiple_axes_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.mean(vec![0, 1]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![3.5])); - } - - #[test] - fn test_tensor_mean_multiple_axes_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.mean(vec![0, 1]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.5, 6.5, 7.5])); - } - - #[test] - fn test_tensor_var_one_axis_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.var(vec![0]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![2.25, 2.25, 2.25])); - } - - #[test] - fn test_tensor_var_one_axis_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.var(vec![0]); - - assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0])); - } - - #[test] - fn test_tensor_var_multiple_axes_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, 1.0, 1.0, 7.0, 7.0, 7.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.var(vec![0, 1]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![9.0])); - } - - #[test] - fn test_tensor_var_multiple_axes_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.var(vec![0, 1]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![45.0, 45.0, 45.0])); - } - - #[test] - fn test_tensor_max_no_axis_1d() { - let shape = shape![5].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.max(vec![]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.0])); - } - - #[test] - fn test_tensor_max_one_axis_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.max(vec![0]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![1.0, 5.0, 3.0])); - } - - #[test] - fn test_tensor_max_multiple_axes_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.max(vec![0, 1]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![7.0, 11.0, 9.0])); - } - - #[test] - fn test_tensor_min_no_axis_1d() { - let shape = shape![5].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.min(vec![]); - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![-4.0])); - } - - #[test] - fn test_tensor_min_one_axis_2d() { - let shape = shape![2, 3].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.min(vec![0]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![-4.0, -2.0, -6.0])); - } - - #[test] - fn test_tensor_min_multiple_axes_3d() { - let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; - let tensor = Tensor::new(&shape, &data).unwrap(); - - let result = tensor.min(vec![0, 1]); - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![-10.0, -8.0, -12.0])); - } - - #[test] - fn test_tensor_prod_1d_1d() { - let shape1 = shape![3].unwrap(); - let data1 = vec![1.0, 2.0, 3.0]; - let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - - let shape2 = shape![2].unwrap(); - let data2 = vec![4.0, 5.0]; - let tensor2 = Tensor::new(&shape2, &data2).unwrap(); - - let result = tensor1.prod(&tensor2); - - assert_eq!(result.shape(), &shape![3, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0])); - } - - #[test] - fn test_tensor_prod_2d_1d() { - let shape1 = shape![2, 2].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - - let shape2 = shape![2].unwrap(); - let data2 = vec![5.0, 6.0]; - let tensor2 = Tensor::new(&shape2, &data2).unwrap(); - - let result = tensor1.prod(&tensor2); - - assert_eq!(result.shape(), &shape![2, 2, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0])); - } - - #[test] - fn test_tensor_prod_2d_2d() { - let shape1 = shape![2, 2].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - - let shape2 = shape![2, 2].unwrap(); - let data2 = vec![5.0, 6.0, 7.0, 8.0]; - let tensor2 = Tensor::new(&shape2, &data2).unwrap(); - - let result = tensor1.prod(&tensor2); - - assert_eq!(result.shape(), &shape![2, 2, 2, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0])); - } - - #[test] - fn test_add_tensor() { - let shape = shape![4].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - - let result = tensor1 + 3.0; - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0])); - } - - #[test] - fn test_add_tensors() { - let shape = shape![2, 2].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let data2 = vec![5.0, 6.0, 7.0, 8.0]; - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - let tensor2 = Tensor::new(&shape, &data2).unwrap(); - - let result = tensor1 + tensor2; - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![6.0, 8.0, 10.0, 12.0])); - } - - #[test] - fn test_sub_tensor() { - let shape = shape![4].unwrap(); - let data1 = vec![5.0, 6.0, 7.0, 8.0]; - - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - - let result = tensor1 - 3.0; - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); - } - - #[test] - fn test_sub_tensors() { - let shape = shape![2, 2].unwrap(); - let data1 = vec![5.0, 6.0, 7.0, 8.0]; - let data2 = vec![1.0, 2.0, 3.0, 4.0]; - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - let tensor2 = Tensor::new(&shape, &data2).unwrap(); - - let result = tensor1 - tensor2; - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![4.0, 4.0, 4.0, 4.0])); - } - - #[test] - fn test_mul_tensor() { - let shape = shape![4].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0]; - - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - - let result = tensor1 * 2.0; - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![2.0, 4.0, 6.0, 8.0])); - } - - #[test] - fn test_vec_vec_mul_single() { - let shape = shape![1].unwrap(); - let data1 = vec![2.0]; - let data2 = vec![5.0]; - - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - let tensor2 = Tensor::new(&shape, &data2).unwrap(); - - let result = tensor1 * tensor2; - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![10.0])); - } - - #[test] - fn test_vec_vec_mul() { - let shape = shape![4].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0]; - let data2 = vec![2.0, 3.0, 4.0, 5.0]; - - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - let tensor2 = Tensor::new(&shape, &data2).unwrap(); - - let result = tensor1 * tensor2; - - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![40.0])); - } - - #[test] - fn test_vec_matrix_mul() { - let shape_vec = shape![2].unwrap(); - let shape_matrix = shape![2, 3].unwrap(); - let data_vec = vec![1.0, 2.0]; - let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - - let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); - let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); - - let result = tensor_vec * tensor_matrix; - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![9.0, 12.0, 15.0])); - } - - #[test] - fn test_matrix_vec_mul() { - let shape_matrix = shape![2, 3].unwrap(); - let shape_vec = shape![3].unwrap(); - let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let data_vec = vec![1.0, 2.0, 3.0]; - - let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); - let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); - - let result = tensor_matrix * tensor_vec; - - assert_eq!(result.shape(), &shape![2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![14.0, 32.0])); - } - - #[test] - fn test_matrix_matrix_mul() { - let shape1 = shape![2, 3].unwrap(); - let shape2 = shape![3, 2].unwrap(); - let data1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let data2 = vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; - - let tensor1 = Tensor::new(&shape1, &data1).unwrap(); - let tensor2 = Tensor::new(&shape2, &data2).unwrap(); - - let result = tensor1 * tensor2; - - assert_eq!(result.shape(), &shape![2, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![58.0, 64.0, 139.0, 154.0])); - } - - #[test] - fn test_div_tensor() { - let shape = shape![4].unwrap(); - let data1 = vec![4.0, 6.0, 8.0, 10.0]; - - let tensor1 = Tensor::new(&shape, &data1).unwrap(); - - let result = tensor1 / 2.0; - - assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); - } -} - +pub mod tensor; \ No newline at end of file diff --git a/src/tensor.rs b/src/tensor.rs new file mode 100644 index 0000000..faedde8 --- /dev/null +++ b/src/tensor.rs @@ -0,0 +1,961 @@ +use num::Num; +use std::ops::{Add, Sub, Mul, Div}; + +use crate::shape; +use crate::shape::Shape; +use crate::iter::IndexIterator; +use crate::error::ShapeError; +use crate::coordinate::Coordinate; +use crate::storage::DynamicStorage; + +pub type Axes = Vec; + +#[derive(Debug)] +pub struct DynamicTensor { + data: DynamicStorage, + shape: Shape +} +type Tensor = DynamicTensor; // Alias for convenience + +impl Tensor { + + pub fn new(shape: &Shape, data: &[T]) -> Result, ShapeError> { + if data.len() != shape.size() { + return Err(ShapeError::new("Data length does not match shape size")); + } + Ok(Tensor {data: DynamicStorage::new(data.to_vec()), shape: shape.clone()}) + } + + pub fn fill(shape: &Shape, value: T) -> Tensor { + let mut vec = Vec::with_capacity(shape.size()); + for _ in 0..shape.size() { vec.push(value); } + Tensor::new(shape, &vec).unwrap() + } + pub fn zeros(shape: &Shape) -> Tensor {Tensor::fill(shape, T::zero())} + pub fn ones(shape: &Shape) -> Tensor {Tensor::fill(shape, T::one())} + + // Properties + pub fn shape(&self) -> &Shape { &self.shape } + pub fn size(&self) -> usize { self.shape.size() } + + pub fn get_element(&self, coord: &Coordinate) -> Result<&T, ShapeError> { + Ok(&self.data[self.data.flatten(coord, &self.shape)?]) + } + + pub fn set_element(&mut self, coord: &Coordinate, value: T) -> Result<(), ShapeError> { + let index = self.data.flatten(coord, &self.shape)?; + self.data[index] = value; + Ok(()) + } + + // // Reduction operations + pub fn sum(&self, axes: Axes) -> Tensor { + let all_axes = (0..self.shape.order()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let sum: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x); + return Tensor::new(&shape![1].unwrap(), &[sum]).unwrap(); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); + let mut t: Tensor = Tensor::zeros(&new_shape); + + for target in IndexIterator::new(&new_shape) { + let sum_iter = IndexIterator::new(&remove_shape); + for sum_index in sum_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices = indices.insert(axis, sum_index[i]); + } + + let value = *t.get_element(&target).unwrap() + *self.get_element(&indices).unwrap(); + let _ = t.set_element(&target, value).unwrap(); + } + } + + t + } + + pub fn mean(&self, axes: Axes) -> Tensor { + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { + let mut result = T::zero(); + for _ in 0..dim { + result = result + T::one(); + } + result + }).collect(); + let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); + self.sum(axes) / n + } + + pub fn var(&self, axes: Axes) -> Tensor { + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { + let mut result = T::zero(); + for _ in 0..dim { + result = result + T::one(); + } + result + }).collect(); + let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); + + let all_axes = (0..self.shape.order()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let avg: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x) / n; + let var: T = self.data.iter().map(|&x| (x - avg) * (x - avg)).fold(T::zero(), |acc, x| acc + x) / n; + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[var]).unwrap(); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); + let mut t: Tensor = Tensor::zeros(&new_shape); + + for target in IndexIterator::new(&new_shape) { + let sum_iter = IndexIterator::new(&remove_shape); + let mean = self.mean(axes.clone()); + + for sum_index in sum_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices = indices.insert(axis, sum_index[i]); + } + + let centered = *self.get_element(&indices).unwrap() - *mean.get_element(&target).unwrap(); + let value = *t.get_element(&target).unwrap() + centered * centered; + let _ = t.set_element(&target, value).unwrap(); + } + } + + t / n + } + + pub fn max(&self, axes: Axes) -> Tensor { + let all_axes = (0..self.shape.order()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[max]).unwrap(); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); + let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + let mut t: Tensor = Tensor::fill(&new_shape, min); + + for target in IndexIterator::new(&new_shape) { + let max_iter = IndexIterator::new(&remove_shape); + for max_index in max_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices = indices.insert(axis, max_index[i]); + } + + if self.get_element(&indices).unwrap() > t.get_element(&target).unwrap() { + let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); + } + } + } + + t + } + + pub fn min(&self, axes: Axes) -> Tensor { + let all_axes = (0..self.shape.order()).collect::>(); + let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); + let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); + + // We resolve to a scalar value + if axes.is_empty() | (remaining_dims.len() == 0) { + let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + return Tensor::new(&Shape::new(vec![1]).unwrap(), &[min]).unwrap(); + } + + // Create new tensor with right shape + let new_shape = Shape::new(remaining_dims).unwrap(); + let remove_shape = Shape::new(removing_dims).unwrap(); + let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + let mut t: Tensor = Tensor::fill(&new_shape, max); + + for target in IndexIterator::new(&new_shape) { + let min_iter = IndexIterator::new(&remove_shape); + for min_index in min_iter { + let mut indices = target.clone(); + for (i, &axis) in axes.iter().enumerate() { + indices = indices.insert(axis, min_index[i]); + } + + if self.get_element(&indices).unwrap() < t.get_element(&target).unwrap() { + let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); + } + } + } + + t + } + + // Tensor Product + // Consistent with numpy.tensordot(a, b, axis=0) + pub fn prod(&self, other: &Tensor) -> Tensor { + let new_shape = self.shape.stack(&other.shape); + + let mut new_data = Vec::with_capacity(self.size() * other.size()); + for &a in &self.data { + for &b in &other.data { + new_data.push(a * b); + } + } + + Tensor::new(&new_shape, &new_data).unwrap() + } +} + +// Element-wise Multiplication +impl Mul for Tensor { + type Output = Tensor; + + fn mul(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() * rhs; + } + result + } +} + +// Vector/Matrix Multiplication +impl Mul> for Tensor { + type Output = Tensor; + + fn mul(self, rhs: Tensor) -> Tensor { + if self.shape.order() == 1 && rhs.shape.order() == 1 { + // Vector-Vector multiplication (dot product) + assert!(self.shape[0] == rhs.shape[0], "Vectors must be of the same length for dot product."); + let mut result = T::zero(); + for i in 0..self.shape[0] { + result = result + self.data[i] * rhs.data[i]; + } + Tensor::new(&shape![1].unwrap(), &vec![result]).unwrap() + } else if self.shape.order() == 1 && rhs.shape.order() == 2 { + // Vector-Matrix multiplication + assert!(self.shape[0] == rhs.shape[0], "The length of the vector must be equal to the number of rows in the matrix."); + let mut result = Tensor::zeros(&shape![rhs.shape[1]].unwrap()); + for j in 0..rhs.shape[1] { + let mut sum = T::zero(); + for i in 0..self.shape[0] { + sum = sum + self.data[i] * rhs.data[i * rhs.shape[1] + j]; + } + result.data[j] = sum; + } + result + } else if self.shape.order() == 2 && rhs.shape.order() == 1 { + // Matrix-Vector multiplication + assert!(self.shape[1] == rhs.shape[0], "The number of columns in the matrix must be equal to the length of the vector."); + let mut result = Tensor::zeros(&shape![self.shape[0]].unwrap()); + for i in 0..self.shape[0] { + let mut sum = T::zero(); + for j in 0..self.shape[1] { + sum = sum + self.data[i * self.shape[1] + j] * rhs.data[j]; + } + result.data[i] = sum; + } + result + } else if self.shape.order() == 2 && rhs.shape.order() == 2 { + // Matrix-Matrix multiplication + assert!(self.shape[1] == rhs.shape[0], "The number of columns in the first matrix must be equal to the number of rows in the second matrix."); + let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]].unwrap()); + for i in 0..self.shape[0] { + for j in 0..rhs.shape[1] { + let mut sum = T::zero(); + for k in 0..self.shape[1] { + sum = sum + self.data[i * self.shape[1] + k] * rhs.data[k * rhs.shape[1] + j]; + } + result.data[i * rhs.shape[1] + j] = sum; + } + } + result + } else { + panic!("Unsupported shapes for multiplication."); + } + } +} + +// Element-wise Addition +impl Add for Tensor { + type Output = Tensor; + + fn add(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() + rhs; + } + result + } +} + +// Tensor Addition +impl Add> for Tensor { + type Output = Tensor; + + fn add(self, rhs: Tensor) -> Tensor { + assert!(self.shape == rhs.shape); + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() + rhs.data[i].clone(); + } + result + } +} + +// Element-wise Subtraction +impl Sub for Tensor +{ + type Output = Tensor; + + fn sub(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() - rhs; + } + result + } +} + +// Tensor Subtraction +impl Sub> for Tensor { + type Output = Tensor; + + fn sub(self, rhs: Tensor) -> Tensor { + assert!(self.shape == rhs.shape); + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() - rhs.data[i].clone(); + } + result + } +} + +// Element-wise Division +impl Div for Tensor +{ + type Output = Tensor; + + fn div(self, rhs: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() / rhs; + } + result + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use crate::coord; + + #[test] + fn test_new_tensor() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + + let tensor = Tensor::new(&shape, &data).unwrap(); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, DynamicStorage::new(data)); + } + + #[test] + fn test_new_tensor_shape_data_mismatch() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0]; // Mismatched data length + + let result = Tensor::new(&shape, &data); + + assert!(result.is_err()); + } + + #[test] + fn test_zeros_tensor() { + let shape = shape![2, 3].unwrap(); + let tensor: Tensor = Tensor::zeros(&shape); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, DynamicStorage::new(vec![0.0; shape.size()])); + } + + #[test] + fn test_ones_tensor() { + let shape = shape![2, 3].unwrap(); + let tensor: Tensor = Tensor::ones(&shape); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, DynamicStorage::new(vec![1.0; shape.size()])); + } + + #[test] + fn test_fill_tensor() { + let shape = shape![2, 3].unwrap(); + let tensor: Tensor = Tensor::fill(&shape, 7.0); + + assert_eq!(tensor.shape(), &shape); + assert_eq!(tensor.data, DynamicStorage::new(vec![7.0; shape.size()])); + } + + #[test] + fn test_tensor_shape() { + let shape = shape![2, 3].unwrap(); + let tensor: Tensor = Tensor::zeros(&shape); + + assert_eq!(tensor.shape(), &shape); + } + + #[test] + fn test_tensor_size() { + let shape = shape![2, 3].unwrap(); + let tensor: Tensor = Tensor::zeros(&shape); + + assert_eq!(tensor.size(), 6); + } + + + #[test] + fn test_tensor_get() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 1.0); + assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 2.0); + assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 3.0); + assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 4.0); + } + + #[test] + fn test_tensor_set() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut tensor = Tensor::new(&shape, &data).unwrap(); + + tensor.set_element(&coord![0, 0], 5.0).unwrap(); + tensor.set_element(&coord![0, 1], 6.0).unwrap(); + tensor.set_element(&coord![1, 0], 7.0).unwrap(); + tensor.set_element(&coord![1, 1], 8.0).unwrap(); + + assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 5.0); + assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 6.0); + assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 7.0); + assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 8.0); + } + + #[test] + fn test_tensor_get_out_of_bounds() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + assert!(tensor.get_element(&coord![2, 0]).is_err()); + assert!(tensor.get_element(&coord![0, 2]).is_err()); + assert!(tensor.get_element(&coord![2, 2]).is_err()); + } + + #[test] + fn test_tensor_set_out_of_bounds() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut tensor = Tensor::new(&shape, &data).unwrap(); + + assert!(tensor.set_element(&coord![2, 0], 5.0).is_err()); + assert!(tensor.set_element(&coord![0, 2], 6.0).is_err()); + assert!(tensor.set_element(&coord![2, 2], 7.0).is_err()); + } + + #[test] + fn test_tensor_sum_no_axis_1d() { + let shape = shape![5].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![15.0])); + } + + #[test] + fn test_tensor_sum_no_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![21.0])); + } + + #[test] + fn test_tensor_sum_no_axis_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![78.0])); + } + + #[test] + fn test_tensor_sum_one_axis_1d() { + let shape = shape![5].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![0]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![15.0])); + } + + #[test] + fn test_tensor_sum_one_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![0]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![5.0, 7.0, 9.0])); + } + + #[test] + fn test_tensor_sum_one_axis_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![0]); + + assert_eq!(result.shape(), &shape![2, 3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0])); + } + + #[test] + fn test_tensor_sum_multiple_axes_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![0, 1]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![21.0])); + } + + #[test] + fn test_tensor_sum_multiple_axes_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.sum(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![22.0, 26.0, 30.0])); + } + + #[test] + fn test_tensor_mean_one_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.mean(vec![0]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![2.5, 3.5, 4.5])); + } + + #[test] + fn test_tensor_mean_one_axis_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.mean(vec![0]); + + assert_eq!(result.shape(), &shape![2, 3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0])); + } + + #[test] + fn test_tensor_mean_multiple_axes_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.mean(vec![0, 1]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![3.5])); + } + + #[test] + fn test_tensor_mean_multiple_axes_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.mean(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![5.5, 6.5, 7.5])); + } + + #[test] + fn test_tensor_var_one_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.var(vec![0]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![2.25, 2.25, 2.25])); + } + + #[test] + fn test_tensor_var_one_axis_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.var(vec![0]); + + assert_eq!(result.shape(), &shape![2, 3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0])); + } + + #[test] + fn test_tensor_var_multiple_axes_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 1.0, 1.0, 7.0, 7.0, 7.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.var(vec![0, 1]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![9.0])); + } + + #[test] + fn test_tensor_var_multiple_axes_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.var(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![45.0, 45.0, 45.0])); + } + + #[test] + fn test_tensor_max_no_axis_1d() { + let shape = shape![5].unwrap(); + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.max(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![5.0])); + } + + #[test] + fn test_tensor_max_one_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.max(vec![0]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![1.0, 5.0, 3.0])); + } + + #[test] + fn test_tensor_max_multiple_axes_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.max(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![7.0, 11.0, 9.0])); + } + + #[test] + fn test_tensor_min_no_axis_1d() { + let shape = shape![5].unwrap(); + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.min(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![-4.0])); + } + + #[test] + fn test_tensor_min_one_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.min(vec![0]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![-4.0, -2.0, -6.0])); + } + + #[test] + fn test_tensor_min_multiple_axes_3d() { + let shape = shape![2, 2, 3].unwrap(); + let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.min(vec![0, 1]); + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![-10.0, -8.0, -12.0])); + } + + #[test] + fn test_tensor_prod_1d_1d() { + let shape1 = shape![3].unwrap(); + let data1 = vec![1.0, 2.0, 3.0]; + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); + + let shape2 = shape![2].unwrap(); + let data2 = vec![4.0, 5.0]; + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); + + let result = tensor1.prod(&tensor2); + + assert_eq!(result.shape(), &shape![3, 2].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0])); + } + + #[test] + fn test_tensor_prod_2d_1d() { + let shape1 = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); + + let shape2 = shape![2].unwrap(); + let data2 = vec![5.0, 6.0]; + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); + + let result = tensor1.prod(&tensor2); + + assert_eq!(result.shape(), &shape![2, 2, 2].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0])); + } + + #[test] + fn test_tensor_prod_2d_2d() { + let shape1 = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); + + let shape2 = shape![2, 2].unwrap(); + let data2 = vec![5.0, 6.0, 7.0, 8.0]; + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); + + let result = tensor1.prod(&tensor2); + + assert_eq!(result.shape(), &shape![2, 2, 2, 2].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0])); + } + + #[test] + fn test_add_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + + let result = tensor1 + 3.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0])); + } + + #[test] + fn test_add_tensors() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![5.0, 6.0, 7.0, 8.0]; + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); + + let result = tensor1 + tensor2; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![6.0, 8.0, 10.0, 12.0])); + } + + #[test] + fn test_sub_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![5.0, 6.0, 7.0, 8.0]; + + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + + let result = tensor1 - 3.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); + } + + #[test] + fn test_sub_tensors() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![5.0, 6.0, 7.0, 8.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); + + let result = tensor1 - tensor2; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![4.0, 4.0, 4.0, 4.0])); + } + + #[test] + fn test_mul_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + + let result = tensor1 * 2.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 4.0, 6.0, 8.0])); + } + + #[test] + fn test_vec_vec_mul_single() { + let shape = shape![1].unwrap(); + let data1 = vec![2.0]; + let data2 = vec![5.0]; + + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); + + let result = tensor1 * tensor2; + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![10.0])); + } + + #[test] + fn test_vec_vec_mul() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + let tensor2 = Tensor::new(&shape, &data2).unwrap(); + + let result = tensor1 * tensor2; + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![40.0])); + } + + #[test] + fn test_vec_matrix_mul() { + let shape_vec = shape![2].unwrap(); + let shape_matrix = shape![2, 3].unwrap(); + let data_vec = vec![1.0, 2.0]; + let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + + let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); + let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); + + let result = tensor_vec * tensor_matrix; + + assert_eq!(result.shape(), &shape![3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![9.0, 12.0, 15.0])); + } + + #[test] + fn test_matrix_vec_mul() { + let shape_matrix = shape![2, 3].unwrap(); + let shape_vec = shape![3].unwrap(); + let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let data_vec = vec![1.0, 2.0, 3.0]; + + let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); + let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); + + let result = tensor_matrix * tensor_vec; + + assert_eq!(result.shape(), &shape![2].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![14.0, 32.0])); + } + + #[test] + fn test_matrix_matrix_mul() { + let shape1 = shape![2, 3].unwrap(); + let shape2 = shape![3, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let data2 = vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + + let tensor1 = Tensor::new(&shape1, &data1).unwrap(); + let tensor2 = Tensor::new(&shape2, &data2).unwrap(); + + let result = tensor1 * tensor2; + + assert_eq!(result.shape(), &shape![2, 2].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![58.0, 64.0, 139.0, 154.0])); + } + + #[test] + fn test_div_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + + let result = tensor1 / 2.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); + } +} + From 03cfc0c6bdf7d04b6d7dce621216901ac0662899 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 28 Jun 2024 18:29:58 +0100 Subject: [PATCH 15/24] feat: add matrix and vector structs --- src/axes.rs | 1 + src/coordinate.rs | 10 +- src/lib.rs | 5 +- src/matrix.rs | 609 ++++++++++++++++++++++++++++++++++++++++++++++ src/shape.rs | 5 +- src/tensor.rs | 442 ++++++++++++++++++++++----------- src/vector.rs | 588 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1520 insertions(+), 140 deletions(-) create mode 100644 src/axes.rs create mode 100644 src/matrix.rs create mode 100644 src/vector.rs diff --git a/src/axes.rs b/src/axes.rs new file mode 100644 index 0000000..a2cb0d2 --- /dev/null +++ b/src/axes.rs @@ -0,0 +1 @@ +pub type Axes = Vec; diff --git a/src/coordinate.rs b/src/coordinate.rs index d64665d..0f34839 100644 --- a/src/coordinate.rs +++ b/src/coordinate.rs @@ -51,11 +51,17 @@ impl fmt::Display for Coordinate { #[macro_export] macro_rules! coord { ($($index:expr),*) => { - Coordinate::new(vec![$($index),*]) + { + use $crate::coordinate::Coordinate; + Coordinate::new(vec![$($index),*]) + } }; ($index:expr; $count:expr) => { - Coordinate::new(vec![$index; $count]) + { + use $crate::coordinate::Coordinate; + Coordinate::new(vec![$index; $count]) + } }; } diff --git a/src/lib.rs b/src/lib.rs index f6d29a3..5a899a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,4 +3,7 @@ pub mod iter; pub mod error; pub mod coordinate; pub mod storage; -pub mod tensor; \ No newline at end of file +pub mod tensor; +pub mod vector; +pub mod matrix; +pub mod axes; \ No newline at end of file diff --git a/src/matrix.rs b/src/matrix.rs new file mode 100644 index 0000000..6254541 --- /dev/null +++ b/src/matrix.rs @@ -0,0 +1,609 @@ +use std::ops::{Add, Sub, Mul, Div, Deref, Index, IndexMut, DerefMut}; + +use num::Num; +use crate::error::ShapeError; +use crate::shape; +use crate::coord; +use crate::shape::Shape; +use crate::tensor::DynamicTensor; +use crate::vector::DynamicVector; +use crate::axes::Axes; +use crate::coordinate::Coordinate; + +pub struct DynamicMatrix { + tensor: DynamicTensor, +} +pub type Matrix = DynamicMatrix; + +impl DynamicMatrix { + pub fn new(shape: &Shape, data: &[T]) -> Result, ShapeError> { + Ok(DynamicMatrix { tensor: DynamicTensor::new(shape, data)? }) + } + + pub fn from_tensor(tensor: DynamicTensor) -> Result, ShapeError> { + if tensor.shape().order() != 2 { + return Err(ShapeError::new("Shape must have order of 2")); + } + Ok(DynamicMatrix { tensor }) + } + + pub fn fill(shape: &Shape, value: T) -> Result, ShapeError> { + let data = vec![value; shape.size()]; + DynamicMatrix::new(shape, &data) + } + pub fn zeros(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::zero()) } + pub fn ones(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::one()) } + + pub fn sum(&self, axes: Axes) -> DynamicVector { + let result = self.tensor.sum(axes); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn mean(&self, axes: Axes) -> DynamicVector { + let result = self.tensor.mean(axes); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn var(&self, axes: Axes) -> DynamicVector { + let result = self.tensor.var(axes); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn max(&self, axes: Axes) -> DynamicVector { + let result = self.tensor.max(axes); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn min(&self, axes: Axes) -> DynamicVector { + let result = self.tensor.min(axes); + DynamicVector::from_tensor(result).unwrap() + } + + // Vector/Matrix Multiplication + pub fn matmul(&self, rhs: &Self) -> DynamicMatrix { + // Matrix-Matrix multiplication + assert_eq!(self.shape()[1], rhs.shape()[0]); + let mut result = DynamicTensor::zeros(&shape![self.shape()[0], rhs.shape()[1]].unwrap()); + for i in 0..self.shape()[0] { + for j in 0..rhs.shape()[1] { + let mut sum = T::zero(); + for k in 0..self.shape()[1] { + sum = sum + self[coord![i, k]] * rhs[coord![k, j]]; + } + result.set(&coord![i, j], sum).unwrap(); + } + } + DynamicMatrix::from_tensor(result).unwrap() + } + + pub fn vecmul(&self, rhs: &DynamicVector) -> DynamicVector { + assert_eq!(self.shape()[1], rhs.shape()[0]); + let mut result = DynamicTensor::zeros(&shape![self.shape()[0]].unwrap()); + for i in 0..self.shape()[0] { + let mut sum = T::zero(); + for j in 0..self.shape()[1] { + sum = sum + self[coord![i, j]] * rhs[j]; + } + result.set(&coord![i], sum).unwrap(); + } + DynamicVector::from_tensor(result).unwrap() + } +} + +// Scalar Addition +impl Add for DynamicMatrix { + type Output = DynamicMatrix; + + fn add(self, rhs: T) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor + rhs).unwrap() + } +} + +// Tensor Addition +impl Add> for DynamicMatrix { + type Output = DynamicMatrix; + + fn add(self, rhs: DynamicMatrix) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor + rhs.tensor).unwrap() + } +} + +impl Add> for DynamicMatrix { + type Output = DynamicMatrix; + + fn add(self, rhs: DynamicTensor) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor + rhs).unwrap() + } +} + +// Scalar Subtraction +impl Sub for DynamicMatrix { + type Output = DynamicMatrix; + + fn sub(self, rhs: T) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor - rhs).unwrap() + } +} + +// Tensor Subtraction +impl Sub> for DynamicMatrix { + type Output = DynamicMatrix; + + fn sub(self, rhs: DynamicMatrix) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor - rhs.tensor).unwrap() + } +} + +impl Sub> for DynamicMatrix { + type Output = DynamicMatrix; + + fn sub(self, rhs: DynamicTensor) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor - rhs).unwrap() + } +} + +// Scalar Multiplication +impl Mul for DynamicMatrix { + type Output = DynamicMatrix; + + fn mul(self, rhs: T) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor * rhs).unwrap() + } +} + +// Tensor Multiplication +impl Mul> for DynamicMatrix { + type Output = DynamicMatrix; + + fn mul(self, rhs: DynamicMatrix) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor * rhs.tensor).unwrap() + } +} + +impl Mul> for DynamicMatrix { + type Output = DynamicMatrix; + + fn mul(self, rhs: DynamicTensor) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor * rhs).unwrap() + } +} + +// Scalar Division +impl Div for DynamicMatrix { + type Output = DynamicMatrix; + + fn div(self, rhs: T) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor / rhs).unwrap() + } +} + +// Tensor Division +impl Div> for DynamicMatrix { + type Output = DynamicMatrix; + + fn div(self, rhs: DynamicMatrix) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor / rhs.tensor).unwrap() + } +} + +impl Div> for DynamicMatrix { + type Output = DynamicMatrix; + + fn div(self, rhs: DynamicTensor) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor / rhs).unwrap() + } +} + +impl Deref for DynamicMatrix { + type Target = DynamicTensor; + + fn deref(&self) -> &DynamicTensor { + &self.tensor + } +} + +impl DerefMut for DynamicMatrix { + fn deref_mut(&mut self) -> &mut DynamicTensor { + &mut self.tensor + } +} + +impl Index for DynamicMatrix { + type Output = T; + + fn index(&self, index: Coordinate) -> &Self::Output { + &self.tensor.get(&index).unwrap() + } +} + +impl IndexMut for DynamicMatrix { + fn index_mut(&mut self, index: Coordinate) -> &mut Self::Output { + self.tensor.get_mut(&index).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 2.0); + assert_eq!(matrix[coord![1, 0]], 3.0); + assert_eq!(matrix[coord![1, 1]], 4.0); + } + + #[test] + fn test_from_tensor() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data).unwrap(); + let matrix = DynamicMatrix::from_tensor(tensor).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 2.0); + assert_eq!(matrix[coord![1, 0]], 3.0); + assert_eq!(matrix[coord![1, 1]], 4.0); + } + + #[test] + fn test_from_tensor_fail() { + let shape = shape![2, 2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; + let tensor = DynamicTensor::new(&shape, &data).unwrap(); + let result = DynamicMatrix::from_tensor(tensor); + assert!(result.is_err()); + } + + #[test] + fn test_fill() { + let shape = shape![2, 2].unwrap(); + let matrix = DynamicMatrix::fill(&shape, 3.0).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 3.0); + assert_eq!(matrix[coord![0, 1]], 3.0); + assert_eq!(matrix[coord![1, 0]], 3.0); + assert_eq!(matrix[coord![1, 1]], 3.0); + } + + #[test] + fn test_zeros() { + let shape = shape![2, 2].unwrap(); + let matrix = DynamicMatrix::::zeros(&shape).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 0.0); + assert_eq!(matrix[coord![0, 1]], 0.0); + assert_eq!(matrix[coord![1, 0]], 0.0); + assert_eq!(matrix[coord![1, 1]], 0.0); + } + + #[test] + fn test_ones() { + let shape = shape![2, 2].unwrap(); + let matrix = DynamicMatrix::::ones(&shape).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 1.0); + assert_eq!(matrix[coord![1, 0]], 1.0); + assert_eq!(matrix[coord![1, 1]], 1.0); + } + + #[test] + fn test_size() { + let shape = shape![2, 2].unwrap(); + let matrix = DynamicMatrix::::zeros(&shape).unwrap(); + assert_eq!(matrix.size(), 4); + } + + #[test] + fn test_get() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + assert_eq!(matrix[coord![1, 0]], 3.0); + } + + #[test] + fn test_get_mut() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut matrix = DynamicMatrix::new(&shape, &data).unwrap(); + matrix[coord![1, 0]] = 5.0; + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 2.0); + assert_eq!(matrix[coord![1, 0]], 5.0); + assert_eq!(matrix[coord![1, 1]], 4.0); + } + + #[test] + fn test_set() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut matrix = DynamicMatrix::new(&shape, &data).unwrap(); + matrix.set(&coord![1, 0], 5.0).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 2.0); + assert_eq!(matrix[coord![1, 0]], 5.0); + assert_eq!(matrix[coord![1, 1]], 4.0); + } + + #[test] + fn test_sum() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix.sum(vec![0, 1]); + assert_eq!(result[0], 10.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_mean() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix.mean(vec![0, 1]); + assert_eq!(result[0], 2.5); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_var() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix.var(vec![0, 1]); + assert_eq!(result[0], 1.25); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_min() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix.min(vec![0, 1]); + assert_eq!(result[0], 1.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_max() { + let shape = shape![2, 2].unwrap(); + let data = vec![-1.0, -2.0, -3.0, -4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix.max(vec![0, 1]); + assert_eq!(result[0], -1.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_matmul() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); + let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = matrix1.matmul(&matrix2); + assert_eq!(result.shape(), &shape); + assert_eq!(result[coord![0, 0]], 10.0); + assert_eq!(result[coord![0, 1]], 13.0); + assert_eq!(result[coord![1, 0]], 22.0); + assert_eq!(result[coord![1, 1]], 29.0); + } + + #[test] + fn test_vecmul() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let vector_data = vec![1.0, 2.0]; + let vector = DynamicVector::new(&vector_data).unwrap(); + let result = matrix.vecmul(&vector); + assert_eq!(result.shape(), &shape![2].unwrap()); + assert_eq!(result[0], 5.0); + assert_eq!(result[1], 11.0); + } + + #[test] + fn test_add_scalar() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix + 2.0; + assert_eq!(result[coord![0, 0]], 3.0); + assert_eq!(result[coord![0, 1]], 4.0); + assert_eq!(result[coord![1, 0]], 5.0); + assert_eq!(result[coord![1, 1]], 6.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_add_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); + let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = matrix1 + matrix2; + assert_eq!(result[coord![0, 0]], 3.0); + assert_eq!(result[coord![0, 1]], 5.0); + assert_eq!(result[coord![1, 0]], 7.0); + assert_eq!(result[coord![1, 1]], 9.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_add_matrix_tensor() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = matrix + tensor; + assert_eq!(result[coord![0, 0]], 3.0); + assert_eq!(result[coord![0, 1]], 5.0); + assert_eq!(result[coord![1, 0]], 7.0); + assert_eq!(result[coord![1, 1]], 9.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_scalar() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix - 2.0; + assert_eq!(result[coord![0, 0]], -1.0); + assert_eq!(result[coord![0, 1]], 0.0); + assert_eq!(result[coord![1, 0]], 1.0); + assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); + let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = matrix1 - matrix2; + assert_eq!(result[coord![0, 0]], -1.0); + assert_eq!(result[coord![0, 1]], -1.0); + assert_eq!(result[coord![1, 0]], -1.0); + assert_eq!(result[coord![1, 1]], -1.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_matrix_tensor() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = matrix - tensor; + assert_eq!(result[coord![0, 0]], -1.0); + assert_eq!(result[coord![0, 1]], -1.0); + assert_eq!(result[coord![1, 0]], -1.0); + assert_eq!(result[coord![1, 1]], -1.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_scalar() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix * 2.0; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 4.0); + assert_eq!(result[coord![1, 0]], 6.0); + assert_eq!(result[coord![1, 1]], 8.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); + let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = matrix1 * matrix2; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 6.0); + assert_eq!(result[coord![1, 0]], 12.0); + assert_eq!(result[coord![1, 1]], 20.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_matrix_tensor() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = matrix * tensor; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 6.0); + assert_eq!(result[coord![1, 0]], 12.0); + assert_eq!(result[coord![1, 1]], 20.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_scalar() { + let shape = shape![2, 2].unwrap(); + let data = vec![4.0, 6.0, 8.0, 10.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix / 2.0; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 3.0); + assert_eq!(result[coord![1, 0]], 4.0); + assert_eq!(result[coord![1, 1]], 5.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); + let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = matrix1 / matrix2; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 2.0); + assert_eq!(result[coord![1, 0]], 2.0); + assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_matrix_tensor() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = matrix / tensor; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 2.0); + assert_eq!(result[coord![1, 0]], 2.0); + assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_index_coord() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 2.0); + assert_eq!(matrix[coord![1, 0]], 3.0); + assert_eq!(matrix[coord![1, 1]], 4.0); + } + + #[test] + fn test_index_mut_coord() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut matrix = DynamicMatrix::new(&shape, &data).unwrap(); + matrix[coord![0, 0]] = 5.0; + assert_eq!(matrix[coord![0, 0]], 5.0); + assert_eq!(matrix[coord![0, 1]], 2.0); + assert_eq!(matrix[coord![1, 0]], 3.0); + assert_eq!(matrix[coord![1, 1]], 4.0); + } +} \ No newline at end of file diff --git a/src/shape.rs b/src/shape.rs index dcb77d2..567b095 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -58,7 +58,10 @@ impl fmt::Display for Shape { #[macro_export] macro_rules! shape { ($($dim:expr),*) => { - Shape::new(vec![$($dim),*]) + { + use $crate::shape::Shape; + Shape::new(vec![$($dim),*]) + } }; } diff --git a/src/tensor.rs b/src/tensor.rs index faedde8..421e297 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -7,15 +7,16 @@ use crate::iter::IndexIterator; use crate::error::ShapeError; use crate::coordinate::Coordinate; use crate::storage::DynamicStorage; - -pub type Axes = Vec; +use crate::axes::Axes; +use crate::vector::DynamicVector; +use crate::matrix::DynamicMatrix; #[derive(Debug)] pub struct DynamicTensor { data: DynamicStorage, shape: Shape } -type Tensor = DynamicTensor; // Alias for convenience +pub type Tensor = DynamicTensor; // Alias for convenience impl Tensor { @@ -38,11 +39,16 @@ impl Tensor { pub fn shape(&self) -> &Shape { &self.shape } pub fn size(&self) -> usize { self.shape.size() } - pub fn get_element(&self, coord: &Coordinate) -> Result<&T, ShapeError> { + pub fn get(&self, coord: &Coordinate) -> Result<&T, ShapeError> { Ok(&self.data[self.data.flatten(coord, &self.shape)?]) } - pub fn set_element(&mut self, coord: &Coordinate, value: T) -> Result<(), ShapeError> { + pub fn get_mut(&mut self, coord: &Coordinate) -> Result<&mut T, ShapeError> { + let index = self.data.flatten(coord, &self.shape)?; + Ok(&mut self.data[index]) + } + + pub fn set(&mut self, coord: &Coordinate, value: T) -> Result<(), ShapeError> { let index = self.data.flatten(coord, &self.shape)?; self.data[index] = value; Ok(()) @@ -74,8 +80,8 @@ impl Tensor { indices = indices.insert(axis, sum_index[i]); } - let value = *t.get_element(&target).unwrap() + *self.get_element(&indices).unwrap(); - let _ = t.set_element(&target, value).unwrap(); + let value = *t.get(&target).unwrap() + *self.get(&indices).unwrap(); + let _ = t.set(&target, value).unwrap(); } } @@ -91,7 +97,15 @@ impl Tensor { } result }).collect(); - let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); + let n = if removing_dims_t.len() != 0 { + removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x) + } else { + let mut sum = T::zero(); + for _ in 0..self.shape().size() { + sum = sum + T::one(); + } + sum + }; self.sum(axes) / n } @@ -104,7 +118,16 @@ impl Tensor { } result }).collect(); - let n = removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x); + + let n = if removing_dims_t.len() != 0 { + removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x) + } else { + let mut sum = T::zero(); + for _ in 0..self.shape().size() { + sum = sum + T::one(); + } + sum + }; let all_axes = (0..self.shape.order()).collect::>(); let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); @@ -133,9 +156,9 @@ impl Tensor { indices = indices.insert(axis, sum_index[i]); } - let centered = *self.get_element(&indices).unwrap() - *mean.get_element(&target).unwrap(); - let value = *t.get_element(&target).unwrap() + centered * centered; - let _ = t.set_element(&target, value).unwrap(); + let centered = *self.get(&indices).unwrap() - *mean.get(&target).unwrap(); + let value = *t.get(&target).unwrap() + centered * centered; + let _ = t.set(&target, value).unwrap(); } } @@ -150,7 +173,8 @@ impl Tensor { // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { - let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + let max: T = self.data.iter().fold(min, |acc, x| if acc > *x { acc } else { *x }); return Tensor::new(&Shape::new(vec![1]).unwrap(), &[max]).unwrap(); } @@ -168,8 +192,8 @@ impl Tensor { indices = indices.insert(axis, max_index[i]); } - if self.get_element(&indices).unwrap() > t.get_element(&target).unwrap() { - let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); + if self.get(&indices).unwrap() > t.get(&target).unwrap() { + let _ = t.set(&target, *self.get(&indices).unwrap()); } } } @@ -185,7 +209,8 @@ impl Tensor { // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { - let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + let min: T = self.data.iter().fold(max, |acc, x| if acc < *x { acc } else { *x }); return Tensor::new(&Shape::new(vec![1]).unwrap(), &[min]).unwrap(); } @@ -203,8 +228,8 @@ impl Tensor { indices = indices.insert(axis, min_index[i]); } - if self.get_element(&indices).unwrap() < t.get_element(&target).unwrap() { - let _ = t.set_element(&target, *self.get_element(&indices).unwrap()); + if self.get(&indices).unwrap() < t.get(&target).unwrap() { + let _ = t.set(&target, *self.get(&indices).unwrap()); } } } @@ -241,63 +266,6 @@ impl Mul for Tensor { } } -// Vector/Matrix Multiplication -impl Mul> for Tensor { - type Output = Tensor; - - fn mul(self, rhs: Tensor) -> Tensor { - if self.shape.order() == 1 && rhs.shape.order() == 1 { - // Vector-Vector multiplication (dot product) - assert!(self.shape[0] == rhs.shape[0], "Vectors must be of the same length for dot product."); - let mut result = T::zero(); - for i in 0..self.shape[0] { - result = result + self.data[i] * rhs.data[i]; - } - Tensor::new(&shape![1].unwrap(), &vec![result]).unwrap() - } else if self.shape.order() == 1 && rhs.shape.order() == 2 { - // Vector-Matrix multiplication - assert!(self.shape[0] == rhs.shape[0], "The length of the vector must be equal to the number of rows in the matrix."); - let mut result = Tensor::zeros(&shape![rhs.shape[1]].unwrap()); - for j in 0..rhs.shape[1] { - let mut sum = T::zero(); - for i in 0..self.shape[0] { - sum = sum + self.data[i] * rhs.data[i * rhs.shape[1] + j]; - } - result.data[j] = sum; - } - result - } else if self.shape.order() == 2 && rhs.shape.order() == 1 { - // Matrix-Vector multiplication - assert!(self.shape[1] == rhs.shape[0], "The number of columns in the matrix must be equal to the length of the vector."); - let mut result = Tensor::zeros(&shape![self.shape[0]].unwrap()); - for i in 0..self.shape[0] { - let mut sum = T::zero(); - for j in 0..self.shape[1] { - sum = sum + self.data[i * self.shape[1] + j] * rhs.data[j]; - } - result.data[i] = sum; - } - result - } else if self.shape.order() == 2 && rhs.shape.order() == 2 { - // Matrix-Matrix multiplication - assert!(self.shape[1] == rhs.shape[0], "The number of columns in the first matrix must be equal to the number of rows in the second matrix."); - let mut result = Tensor::zeros(&shape![self.shape[0], rhs.shape[1]].unwrap()); - for i in 0..self.shape[0] { - for j in 0..rhs.shape[1] { - let mut sum = T::zero(); - for k in 0..self.shape[1] { - sum = sum + self.data[i * self.shape[1] + k] * rhs.data[k * rhs.shape[1] + j]; - } - result.data[i * rhs.shape[1] + j] = sum; - } - } - result - } else { - panic!("Unsupported shapes for multiplication."); - } - } -} - // Element-wise Addition impl Add for Tensor { type Output = Tensor; @@ -325,6 +293,22 @@ impl Add> for Tensor { } } +impl Add> for Tensor { + type Output = DynamicVector; + + fn add(self, rhs: DynamicVector) -> DynamicVector { + rhs + self + } +} + +impl Add> for Tensor { + type Output = DynamicMatrix; + + fn add(self, rhs: DynamicMatrix) -> DynamicMatrix { + rhs + self + } +} + // Element-wise Subtraction impl Sub for Tensor { @@ -353,6 +337,52 @@ impl Sub> for Tensor { } } +impl Sub> for Tensor { + type Output = DynamicVector; + + fn sub(self, rhs: DynamicVector) -> DynamicVector { + (rhs * (T::zero() - T::one())) + self + } +} + +impl Sub> for Tensor { + type Output = DynamicMatrix; + + fn sub(self, rhs: DynamicMatrix) -> DynamicMatrix { + (rhs * (T::zero() - T::one())) + self + } +} + +// Tensor Multiplication +impl Mul> for Tensor { + type Output = Tensor; + + fn mul(self, rhs: Tensor) -> Tensor { + assert!(self.shape == rhs.shape); + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() * rhs.data[i].clone(); + } + result + } +} + +impl Mul> for Tensor { + type Output = DynamicVector; + + fn mul(self, rhs: DynamicVector) -> DynamicVector { + rhs * self + } +} + +impl Mul> for Tensor { + type Output = DynamicMatrix; + + fn mul(self, rhs: DynamicMatrix) -> DynamicMatrix { + rhs * self + } +} + // Element-wise Division impl Div for Tensor { @@ -367,6 +397,35 @@ impl Div for Tensor } } +// Tensor Division +impl Div> for Tensor { + type Output = Tensor; + + fn div(self, rhs: Tensor) -> Tensor { + assert!(self.shape == rhs.shape); + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone() / rhs.data[i].clone(); + } + result + } +} + +impl Div> for Tensor { + type Output = DynamicVector; + + fn div(self, rhs: DynamicVector) -> DynamicVector { + DynamicVector::::from_tensor(self).unwrap() / rhs + } +} + +impl Div> for Tensor { + type Output = DynamicMatrix; + + fn div(self, rhs: DynamicMatrix) -> DynamicMatrix { + DynamicMatrix::::from_tensor(self).unwrap() / rhs + } +} #[cfg(test)] mod tests { @@ -437,17 +496,16 @@ mod tests { assert_eq!(tensor.size(), 6); } - #[test] fn test_tensor_get() { let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = Tensor::new(&shape, &data).unwrap(); - assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 1.0); - assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 2.0); - assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 3.0); - assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 4.0); + assert_eq!(*tensor.get(&coord![0, 0]).unwrap(), 1.0); + assert_eq!(*tensor.get(&coord![0, 1]).unwrap(), 2.0); + assert_eq!(*tensor.get(&coord![1, 0]).unwrap(), 3.0); + assert_eq!(*tensor.get(&coord![1, 1]).unwrap(), 4.0); } #[test] @@ -456,15 +514,15 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut tensor = Tensor::new(&shape, &data).unwrap(); - tensor.set_element(&coord![0, 0], 5.0).unwrap(); - tensor.set_element(&coord![0, 1], 6.0).unwrap(); - tensor.set_element(&coord![1, 0], 7.0).unwrap(); - tensor.set_element(&coord![1, 1], 8.0).unwrap(); + tensor.set(&coord![0, 0], 5.0).unwrap(); + tensor.set(&coord![0, 1], 6.0).unwrap(); + tensor.set(&coord![1, 0], 7.0).unwrap(); + tensor.set(&coord![1, 1], 8.0).unwrap(); - assert_eq!(*tensor.get_element(&coord![0, 0]).unwrap(), 5.0); - assert_eq!(*tensor.get_element(&coord![0, 1]).unwrap(), 6.0); - assert_eq!(*tensor.get_element(&coord![1, 0]).unwrap(), 7.0); - assert_eq!(*tensor.get_element(&coord![1, 1]).unwrap(), 8.0); + assert_eq!(*tensor.get(&coord![0, 0]).unwrap(), 5.0); + assert_eq!(*tensor.get(&coord![0, 1]).unwrap(), 6.0); + assert_eq!(*tensor.get(&coord![1, 0]).unwrap(), 7.0); + assert_eq!(*tensor.get(&coord![1, 1]).unwrap(), 8.0); } #[test] @@ -473,9 +531,9 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = Tensor::new(&shape, &data).unwrap(); - assert!(tensor.get_element(&coord![2, 0]).is_err()); - assert!(tensor.get_element(&coord![0, 2]).is_err()); - assert!(tensor.get_element(&coord![2, 2]).is_err()); + assert!(tensor.get(&coord![2, 0]).is_err()); + assert!(tensor.get(&coord![0, 2]).is_err()); + assert!(tensor.get(&coord![2, 2]).is_err()); } #[test] @@ -484,9 +542,9 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut tensor = Tensor::new(&shape, &data).unwrap(); - assert!(tensor.set_element(&coord![2, 0], 5.0).is_err()); - assert!(tensor.set_element(&coord![0, 2], 6.0).is_err()); - assert!(tensor.set_element(&coord![2, 2], 7.0).is_err()); + assert!(tensor.set(&coord![2, 0], 5.0).is_err()); + assert!(tensor.set(&coord![0, 2], 6.0).is_err()); + assert!(tensor.set(&coord![2, 2], 7.0).is_err()); } #[test] @@ -585,6 +643,18 @@ mod tests { assert_eq!(result.data, DynamicStorage::new(vec![22.0, 26.0, 30.0])); } + #[test] + fn test_tensor_mean_no_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.mean(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![3.5])); + } + #[test] fn test_tensor_mean_one_axis_2d() { let shape = shape![2, 3].unwrap(); @@ -633,6 +703,18 @@ mod tests { assert_eq!(result.data, DynamicStorage::new(vec![5.5, 6.5, 7.5])); } + #[test] + fn test_tensor_var_no_axis_2d() { + let shape = shape![2, 3].unwrap(); + let data = vec![1.0, 1.0, 1.0, 7.0, 7.0, 7.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + + let result = tensor.var(vec![]); + + assert_eq!(result.shape(), &shape![1].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![9.0])); + } + #[test] fn test_tensor_var_one_axis_2d() { let shape = shape![2, 3].unwrap(); @@ -867,6 +949,19 @@ mod tests { assert_eq!(result.data, DynamicStorage::new(vec![2.0, 4.0, 6.0, 8.0])); } + #[test] + fn test_div_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + + let tensor1 = Tensor::new(&shape, &data1).unwrap(); + + let result = tensor1 / 2.0; + + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); + } + #[test] fn test_vec_vec_mul_single() { let shape = shape![1].unwrap(); @@ -893,46 +988,14 @@ mod tests { let result = tensor1 * tensor2; - assert_eq!(result.shape(), &shape![1].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![40.0])); - } - - #[test] - fn test_vec_matrix_mul() { - let shape_vec = shape![2].unwrap(); - let shape_matrix = shape![2, 3].unwrap(); - let data_vec = vec![1.0, 2.0]; - let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - - let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); - let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); - - let result = tensor_vec * tensor_matrix; - - assert_eq!(result.shape(), &shape![3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![9.0, 12.0, 15.0])); - } - - #[test] - fn test_matrix_vec_mul() { - let shape_matrix = shape![2, 3].unwrap(); - let shape_vec = shape![3].unwrap(); - let data_matrix = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let data_vec = vec![1.0, 2.0, 3.0]; - - let tensor_matrix = Tensor::new(&shape_matrix, &data_matrix).unwrap(); - let tensor_vec = Tensor::new(&shape_vec, &data_vec).unwrap(); - - let result = tensor_matrix * tensor_vec; - - assert_eq!(result.shape(), &shape![2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![14.0, 32.0])); + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![2.0, 6.0, 12.0, 20.0])); } #[test] fn test_matrix_matrix_mul() { let shape1 = shape![2, 3].unwrap(); - let shape2 = shape![3, 2].unwrap(); + let shape2 = shape![2, 3].unwrap(); let data1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let data2 = vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; @@ -941,21 +1004,128 @@ mod tests { let result = tensor1 * tensor2; - assert_eq!(result.shape(), &shape![2, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![58.0, 64.0, 139.0, 154.0])); + assert_eq!(result.shape(), &shape![2, 3].unwrap()); + assert_eq!(result.data, DynamicStorage::new(vec![7.0, 16.0, 27.0, 40.0, 55.0, 72.0])); } #[test] - fn test_div_tensor() { + fn test_add_tensor_vector() { let shape = shape![4].unwrap(); - let data1 = vec![4.0, 6.0, 8.0, 10.0]; + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let vector = DynamicVector::new(&data2).unwrap(); + let result = tensor + vector; + assert_eq!(result[0], 3.0); + assert_eq!(result[1], 5.0); + assert_eq!(result[2], 7.0); + assert_eq!(result[3], 9.0); + assert_eq!(result.shape(), &shape); + } - let tensor1 = Tensor::new(&shape, &data1).unwrap(); + #[test] + fn test_sub_tensor_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![2.0, 3.0, 4.0, 5.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let vector = DynamicVector::new(&data2).unwrap(); + let result = tensor - vector; + assert_eq!(result[0], 1.0); + assert_eq!(result[1], 1.0); + assert_eq!(result[2], 1.0); + assert_eq!(result[3], 1.0); + assert_eq!(result.shape(), &shape); + } - let result = tensor1 / 2.0; + #[test] + fn test_mul_tensor_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![2.0, 3.0, 4.0, 5.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let vector = DynamicVector::new(&data2).unwrap(); + let result = tensor * vector; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 6.0); + assert_eq!(result[2], 12.0); + assert_eq!(result[3], 20.0); + assert_eq!(result.shape(), &shape); + } + #[test] + fn test_div_tensor_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![2.0, 4.0, 6.0, 8.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let vector = DynamicVector::new(&data2).unwrap(); + let result = tensor / vector; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 2.0); + assert_eq!(result[2], 2.0); + assert_eq!(result[3], 2.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_add_tensor_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = tensor + matrix; + assert_eq!(result[coord![0, 0]], 3.0); + assert_eq!(result[coord![0, 1]], 5.0); + assert_eq!(result[coord![1, 0]], 7.0); + assert_eq!(result[coord![1, 1]], 9.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_tensor_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![2.0, 3.0, 4.0, 5.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = tensor - matrix; + assert_eq!(result[coord![0, 0]], 1.0); + assert_eq!(result[coord![0, 1]], 1.0); + assert_eq!(result[coord![1, 0]], 1.0); + assert_eq!(result[coord![1, 1]], 1.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_tensor_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![2.0, 3.0, 4.0, 5.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = tensor * matrix; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 6.0); + assert_eq!(result[coord![1, 0]], 12.0); + assert_eq!(result[coord![1, 1]], 20.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_tensor_matrix() { + let shape = shape![2, 2].unwrap(); + let data1 = vec![2.0, 4.0, 6.0, 8.0]; + let data2 = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data1).unwrap(); + let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); + let result = tensor / matrix; + assert_eq!(result[coord![0, 0]], 2.0); + assert_eq!(result[coord![0, 1]], 2.0); + assert_eq!(result[coord![1, 0]], 2.0); + assert_eq!(result[coord![1, 1]], 2.0); assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![2.0, 3.0, 4.0, 5.0])); } } diff --git a/src/vector.rs b/src/vector.rs new file mode 100644 index 0000000..0f2783c --- /dev/null +++ b/src/vector.rs @@ -0,0 +1,588 @@ +use std::ops::{Add, Sub, Mul, Div, Deref, Index, IndexMut, DerefMut}; + +use num::Num; +use crate::error::ShapeError; +use crate::shape; +use crate::coord; +use crate::shape::Shape; +use crate::tensor::DynamicTensor; +use crate::matrix::DynamicMatrix; + +pub struct DynamicVector { + tensor: DynamicTensor, +} +pub type Vector = DynamicVector; + +impl DynamicVector { + pub fn new(data: &[T]) -> Result, ShapeError> { + Ok(DynamicVector { tensor: DynamicTensor::new(&shape![data.len()].unwrap(), data)? }) + } + + pub fn from_tensor(tensor: DynamicTensor) -> Result, ShapeError> { + if tensor.shape().order() != 1 { + return Err(ShapeError::new("Shape must have order of 1")); + } + Ok(DynamicVector { tensor }) + } + + pub fn fill(shape: &Shape, value: T) -> Result, ShapeError> { + if shape.order() != 1 { + return Err(ShapeError::new("Shape must have order of 1")); + } + let data = vec![value; shape[0]]; + DynamicVector::new(&data) + } + pub fn zeros(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::zero()) } + pub fn ones(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::one()) } + + pub fn sum(&self) -> DynamicVector { + let result = self.tensor.sum(vec![]); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn mean(&self) -> DynamicVector { + let result = self.tensor.mean(vec![]); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn var(&self) -> DynamicVector { + let result = self.tensor.var(vec![]); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn max(&self) -> DynamicVector { + let result = self.tensor.max(vec![]); + DynamicVector::from_tensor(result).unwrap() + } + + pub fn min(&self) -> DynamicVector { + let result = self.tensor.min(vec![]); + DynamicVector::from_tensor(result).unwrap() + } + + // Vector/Matrix Multiplication + pub fn vecmul(&self, rhs: &DynamicVector) -> DynamicVector { + assert!(self.shape() == rhs.shape()); + let mut result = T::zero(); + for i in 0..self.size() { + result = result + self[i] * rhs[i]; + } + DynamicVector::new(&[result]).unwrap() + } + + pub fn matmul(&self, rhs: &DynamicMatrix) -> DynamicVector { + assert_eq!(self.shape()[0], rhs.shape()[0]); + let mut result = DynamicTensor::zeros(&shape![rhs.shape()[1]].unwrap()); + for j in 0..rhs.shape()[1] { + let mut sum = T::zero(); + for i in 0..self.shape()[0] { + sum = sum + self[i] * rhs[coord![i, j]]; + } + result.set(&coord![j], sum).unwrap(); + } + DynamicVector::from_tensor(result).unwrap() + } +} + +// Scalar Addition +impl Add for DynamicVector { + type Output = DynamicVector; + + fn add(self, rhs: T) -> DynamicVector { + DynamicVector::from_tensor(self.tensor + rhs).unwrap() + } +} + +// Tensor Addition +impl Add> for DynamicVector { + type Output = DynamicVector; + + fn add(self, rhs: DynamicVector) -> DynamicVector { + DynamicVector::from_tensor(self.tensor + rhs.tensor).unwrap() + } +} + +impl Add> for DynamicVector { + type Output = DynamicVector; + + fn add(self, rhs: DynamicTensor) -> DynamicVector { + DynamicVector::from_tensor(self.tensor + rhs).unwrap() + } +} + +// Scalar Subtraction +impl Sub for DynamicVector { + type Output = DynamicVector; + + fn sub(self, rhs: T) -> DynamicVector { + DynamicVector::from_tensor(self.tensor - rhs).unwrap() + } +} + +// Tensor Subtraction +impl Sub> for DynamicVector { + type Output = DynamicVector; + + fn sub(self, rhs: DynamicVector) -> DynamicVector { + DynamicVector::from_tensor(self.tensor - rhs.tensor).unwrap() + } +} + +impl Sub> for DynamicVector { + type Output = DynamicVector; + + fn sub(self, rhs: DynamicTensor) -> DynamicVector { + DynamicVector::from_tensor(self.tensor - rhs).unwrap() + } +} + +// Scalar Multiplication +impl Mul for DynamicVector { + type Output = DynamicVector; + + fn mul(self, rhs: T) -> DynamicVector { + DynamicVector::from_tensor(self.tensor * rhs).unwrap() + } +} + +// Tensor Multiplication +impl Mul> for DynamicVector { + type Output = DynamicVector; + + fn mul(self, rhs: DynamicVector) -> DynamicVector { + DynamicVector::from_tensor(self.tensor * rhs.tensor).unwrap() + } +} + +impl Mul> for DynamicVector { + type Output = DynamicVector; + + fn mul(self, rhs: DynamicTensor) -> DynamicVector { + DynamicVector::from_tensor(self.tensor * rhs).unwrap() + } +} + +// Scalar Division +impl Div for DynamicVector { + type Output = DynamicVector; + + fn div(self, rhs: T) -> DynamicVector { + DynamicVector::from_tensor(self.tensor / rhs).unwrap() + } +} + +// Tensor Division +impl Div> for DynamicVector { + type Output = DynamicVector; + + fn div(self, rhs: DynamicVector) -> DynamicVector { + DynamicVector::from_tensor(self.tensor / rhs.tensor).unwrap() + } +} + +impl Div> for DynamicVector { + type Output = DynamicVector; + + fn div(self, rhs: DynamicTensor) -> DynamicVector { + DynamicVector::from_tensor(self.tensor / rhs).unwrap() + } +} + +impl Deref for DynamicVector { + type Target = DynamicTensor; + + fn deref(&self) -> &DynamicTensor { + &self.tensor + } +} + +impl DerefMut for DynamicVector { + fn deref_mut(&mut self) -> &mut DynamicTensor { + &mut self.tensor + } +} + +impl Index for DynamicVector { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.tensor.get(&coord![index]).unwrap() + } +} + +impl IndexMut for DynamicVector { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.tensor.get_mut(&coord![index]).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let shape = shape![4].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + assert_eq!(vector.shape(), &shape); + assert_eq!(vector[0], 1.0); + assert_eq!(vector[1], 2.0); + assert_eq!(vector[2], 3.0); + assert_eq!(vector[3], 4.0); + } + + #[test] + fn test_from_tensor() { + let shape = shape![4].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data).unwrap(); + let vector = DynamicVector::from_tensor(tensor).unwrap(); + assert_eq!(vector.shape(), &shape); + assert_eq!(vector[0], 1.0); + assert_eq!(vector[1], 2.0); + assert_eq!(vector[2], 3.0); + assert_eq!(vector[3], 4.0); + } + + #[test] + fn test_from_tensor_fail() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = DynamicTensor::new(&shape, &data).unwrap(); + let result = DynamicVector::from_tensor(tensor); + assert!(result.is_err()); + } + + #[test] + fn test_fill() { + let shape = shape![4].unwrap(); + let vector = DynamicVector::fill(&shape, 3.0).unwrap(); + assert_eq!(vector.shape(), &shape); + assert_eq!(vector[0], 3.0); + assert_eq!(vector[1], 3.0); + assert_eq!(vector[2], 3.0); + assert_eq!(vector[3], 3.0); + } + + #[test] + fn test_zeros() { + let shape = shape![4].unwrap(); + let vector = DynamicVector::::zeros(&shape).unwrap(); + assert_eq!(vector.shape(), &shape); + assert_eq!(vector[0], 0.0); + assert_eq!(vector[1], 0.0); + assert_eq!(vector[2], 0.0); + assert_eq!(vector[3], 0.0); + } + + #[test] + fn test_ones() { + let shape = shape![4].unwrap(); + let vector = DynamicVector::::ones(&shape).unwrap(); + assert_eq!(vector.shape(), &shape); + assert_eq!(vector[0], 1.0); + assert_eq!(vector[1], 1.0); + assert_eq!(vector[2], 1.0); + assert_eq!(vector[3], 1.0); + } + + #[test] + fn test_size() { + let shape = shape![4].unwrap(); + let vector = DynamicVector::::zeros(&shape).unwrap(); + assert_eq!(vector.size(), 4); + } + + #[test] + fn test_get() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + assert_eq!(vector[2], 3.0); + } + + #[test] + fn test_get_mut() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut vector = DynamicVector::new(&data).unwrap(); + vector[2] = 5.0; + assert_eq!(vector[2], 5.0); + } + + #[test] + fn test_set() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let mut vector = DynamicVector::new(&data).unwrap(); + vector.set(&coord![2], 5.0).unwrap(); + assert_eq!(*vector.get(&coord![2]).unwrap(), 5.0); + } + + #[test] + fn test_sum() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector.sum(); + assert_eq!(result[0], 10.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_mean() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector.mean(); + assert_eq!(result[0], 2.5); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_var() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector.var(); + assert_eq!(result[0], 1.25); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_min() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector.min(); + assert_eq!(result[0], 1.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_max() { + let data = vec![-1.0, -2.0, -3.0, -4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector.max(); + assert_eq!(result[0], -1.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_vecmul() { + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector1 = DynamicVector::new(&data1).unwrap(); + let vector2 = DynamicVector::new(&data2).unwrap(); + let result = vector1.vecmul(&vector2); + assert_eq!(result[0], 40.0); + assert_eq!(result.shape(), &shape![1].unwrap()); + } + + #[test] + fn test_matmul() { + let data_vector = vec![1.0, 2.0]; + let data_matrix = vec![ + 1.0, 2.0, + 3.0, 4.0 + ]; + let vector = DynamicVector::new(&data_vector).unwrap(); + let matrix = DynamicMatrix::new(&shape![2, 2].unwrap(), &data_matrix).unwrap(); + let result = vector.matmul(&matrix); + assert_eq!(result.shape(), &shape![2].unwrap()); + assert_eq!(result[0], 7.0); + assert_eq!(result[1], 10.0); + } + + #[test] + fn test_prod() { + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector1 = DynamicVector::new(&data1).unwrap(); + let vector2 = DynamicVector::new(&data2).unwrap(); + let result = vector1.prod(&vector2); + + let expected_data = vec![ + 2.0, 3.0, 4.0, 5.0, + 4.0, 6.0, 8.0, 10.0, + 6.0, 9.0, 12.0, 15.0, + 8.0, 12.0, 16.0, 20.0 + ]; + let expected_shape = shape![4, 4].unwrap(); + let expected_tensor = DynamicTensor::new(&expected_shape, &expected_data).unwrap(); + + assert_eq!(result.shape(), &expected_shape); + for i in 0..result.shape()[0] { + for j in 0..result.shape()[1] { + let x = result.get(&coord![i, j]).unwrap(); + let y = expected_tensor.get(&coord![i, j]).unwrap(); + assert_eq!(*x, *y); + } + } + } + + #[test] + fn test_add_scalar() { + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector + 2.0; + assert_eq!(result[0], 3.0); + assert_eq!(result[1], 4.0); + assert_eq!(result[2], 5.0); + assert_eq!(result[3], 6.0); + assert_eq!(result.shape(), &shape![4].unwrap()); + } + + #[test] + fn test_add_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector1 = DynamicVector::new(&data1).unwrap(); + let vector2 = DynamicVector::new(&data2).unwrap(); + let result = vector1 + vector2; + assert_eq!(result[0], 3.0); + assert_eq!(result[1], 5.0); + assert_eq!(result[2], 7.0); + assert_eq!(result[3], 9.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_add_vector_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector = DynamicVector::new(&data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = vector + tensor; + assert_eq!(result[0], 3.0); + assert_eq!(result[1], 5.0); + assert_eq!(result[2], 7.0); + assert_eq!(result[3], 9.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_scalar() { + let shape = shape![4].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector - 2.0; + assert_eq!(result[0], -1.0); + assert_eq!(result[1], 0.0); + assert_eq!(result[2], 1.0); + assert_eq!(result[3], 2.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector1 = DynamicVector::new(&data1).unwrap(); + let vector2 = DynamicVector::new(&data2).unwrap(); + let result = vector1 - vector2; + assert_eq!(result[0], -1.0); + assert_eq!(result[1], -1.0); + assert_eq!(result[2], -1.0); + assert_eq!(result[3], -1.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_sub_vector_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector = DynamicVector::new(&data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = vector - tensor; + assert_eq!(result[0], -1.0); + assert_eq!(result[1], -1.0); + assert_eq!(result[2], -1.0); + assert_eq!(result[3], -1.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_scalar() { + let shape = shape![4].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector * 2.0; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 4.0); + assert_eq!(result[2], 6.0); + assert_eq!(result[3], 8.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector1 = DynamicVector::new(&data1).unwrap(); + let vector2 = DynamicVector::new(&data2).unwrap(); + let result = vector1 * vector2; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 6.0); + assert_eq!(result[2], 12.0); + assert_eq!(result[3], 20.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_mul_vector_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![1.0, 2.0, 3.0, 4.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector = DynamicVector::new(&data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = vector * tensor; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 6.0); + assert_eq!(result[2], 12.0); + assert_eq!(result[3], 20.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_scalar() { + let shape = shape![4].unwrap(); + let data = vec![4.0, 6.0, 8.0, 10.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector / 2.0; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 3.0); + assert_eq!(result[2], 4.0); + assert_eq!(result[3], 5.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_vector() { + let shape = shape![4].unwrap(); + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector1 = DynamicVector::new(&data1).unwrap(); + let vector2 = DynamicVector::new(&data2).unwrap(); + let result = vector1 / vector2; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 2.0); + assert_eq!(result[2], 2.0); + assert_eq!(result[3], 2.0); + assert_eq!(result.shape(), &shape); + } + + #[test] + fn test_div_vector_tensor() { + let shape = shape![4].unwrap(); + let data1 = vec![4.0, 6.0, 8.0, 10.0]; + let data2 = vec![2.0, 3.0, 4.0, 5.0]; + let vector = DynamicVector::new(&data1).unwrap(); + let tensor = DynamicTensor::new(&shape, &data2).unwrap(); + let result = vector / tensor; + assert_eq!(result[0], 2.0); + assert_eq!(result[1], 2.0); + assert_eq!(result[2], 2.0); + assert_eq!(result[3], 2.0); + assert_eq!(result.shape(), &shape); + } +} \ No newline at end of file From bf3ae68c0fe708c9a9c2e2205ddd51c6dad2f551 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 28 Jun 2024 18:37:36 +0100 Subject: [PATCH 16/24] feat: display method for tensor --- src/tensor.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/tensor.rs b/src/tensor.rs index 421e297..0714211 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -427,6 +427,41 @@ impl Div> for Tensor { } } +impl Tensor { + pub fn display(&self) -> String { + fn format_tensor(data: &[T], shape: &[usize], level: usize) -> String { + if shape.len() == 1 { + let mut result = String::from("["); + for (i, item) in data.iter().enumerate() { + result.push_str(&format!("{}", item)); + if i < data.len() - 1 { + result.push_str(", "); + } + } + result.push(']'); + return result; + } + + let mut result = String::from("["); + let sub_size = shape[1..].iter().product(); + for i in 0..shape[0] { + if i > 0 { + result.push_str(",\n"); + for _ in 0..level { + result.push(' '); + } + } + result.push_str(&format_tensor(&data[i * sub_size..(i + 1) * sub_size], &shape[1..], level + 1)); + } + result.push(']'); + result + } + + format_tensor(&self.data, &self.shape.dims(), 1) + } +} + + #[cfg(test)] mod tests { use super::*; @@ -1127,5 +1162,44 @@ mod tests { assert_eq!(result[coord![1, 1]], 2.0); assert_eq!(result.shape(), &shape); } + + #[test] + fn test_display_1d_tensor() { + let shape = shape![3].unwrap(); + let data = vec![1.0, 2.0, 3.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let display = tensor.display(); + assert_eq!(display, "[1, 2, 3]"); + } + + #[test] + fn test_display_2d_tensor() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let display = tensor.display(); + assert_eq!(display, "[[1, 2],\n [3, 4]]"); + } + + #[test] + fn test_display_3d_tensor() { + let shape = shape![2, 2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let display = tensor.display(); + assert_eq!(display, "[[[1, 2],\n [3, 4]],\n\n [[5, 6],\n [7, 8]]]"); + } + + #[test] + fn test_display_4d_tensor() { + let shape = shape![2, 2, 2, 2].unwrap(); + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0 + ]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let display = tensor.display(); + assert_eq!(display, "[[[[1, 2],\n [3, 4]],\n\n [[5, 6],\n [7, 8]]],\n\n\n [[[9, 10],\n [11, 12]],\n\n [[13, 14],\n [15, 16]]]]"); + } } From 251f5d74934f3c453e4adb6b6de1cc0e61a3512d Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 28 Jun 2024 20:01:29 +0100 Subject: [PATCH 17/24] fix: display method --- src/storage.rs | 13 +++++++++++++ src/tensor.rs | 16 ++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/storage.rs b/src/storage.rs index cf9c791..a500de2 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -35,6 +35,10 @@ impl DynamicStorage { Ok(index) } + pub fn size(&self) -> usize { + self.data.len() + } + pub fn iter(&self) -> std::slice::Iter<'_, T> { self.data.iter() } @@ -50,6 +54,15 @@ impl Index for DynamicStorage { fn index(&self, index: usize) -> &Self::Output { &self.data[index] } + +} + +impl Index> for DynamicStorage { + type Output = [T]; + + fn index(&self, range: std::ops::Range) -> &Self::Output { + &self.data[range] + } } impl IndexMut for DynamicStorage { diff --git a/src/tensor.rs b/src/tensor.rs index 0714211..621ac66 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -429,12 +429,12 @@ impl Div> for Tensor { impl Tensor { pub fn display(&self) -> String { - fn format_tensor(data: &[T], shape: &[usize], level: usize) -> String { - if shape.len() == 1 { + fn format_tensor(data: &DynamicStorage, shape: &Shape, level: usize) -> String { + if shape.order() == 1 { let mut result = String::from("["); for (i, item) in data.iter().enumerate() { result.push_str(&format!("{}", item)); - if i < data.len() - 1 { + if i < data.size() - 1 { result.push_str(", "); } } @@ -443,21 +443,25 @@ impl Tensor { } let mut result = String::from("["); - let sub_size = shape[1..].iter().product(); + let sub_size = Shape::new(shape[1..].to_vec()).unwrap().size(); for i in 0..shape[0] { if i > 0 { result.push_str(",\n"); + for _ in 0..shape.order() - 2 { + result.push('\n'); + } for _ in 0..level { result.push(' '); } } - result.push_str(&format_tensor(&data[i * sub_size..(i + 1) * sub_size], &shape[1..], level + 1)); + let sub_data = DynamicStorage::new(data[i * sub_size..(i + 1) * sub_size].to_vec()); + result.push_str(&format_tensor(&sub_data, &Shape::new(shape[1..].to_vec()).unwrap(), level + 1)); } result.push(']'); result } - format_tensor(&self.data, &self.shape.dims(), 1) + format_tensor(&self.data, &self.shape, 1) } } From 6bcc07aa29181423de5253d38a2a6cdf33b30188 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 28 Jun 2024 21:12:36 +0100 Subject: [PATCH 18/24] feat: add pow method --- src/matrix.rs | 21 ++++++++++++++++++++- src/tensor.rs | 42 +++++++++++++++++++++++++++++++++++++++++- src/vector.rs | 20 ++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/matrix.rs b/src/matrix.rs index 6254541..395ff20 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,6 +1,6 @@ use std::ops::{Add, Sub, Mul, Div, Deref, Index, IndexMut, DerefMut}; -use num::Num; +use num::{Num, Float}; use crate::error::ShapeError; use crate::shape; use crate::coord; @@ -90,6 +90,12 @@ impl DynamicMatrix { } } +impl DynamicMatrix { + pub fn pow(&self, power: T) -> DynamicMatrix { + DynamicMatrix::from_tensor(self.tensor.pow(power)).unwrap() + } +} + // Scalar Addition impl Add for DynamicMatrix { type Output = DynamicMatrix; @@ -606,4 +612,17 @@ mod tests { assert_eq!(matrix[coord![1, 0]], 3.0); assert_eq!(matrix[coord![1, 1]], 4.0); } + + #[test] + fn test_pow_matrix() { + let shape = shape![2, 2].unwrap(); + let data = vec![2.0, 3.0, 4.0, 5.0]; + let matrix = DynamicMatrix::new(&shape, &data).unwrap(); + let result = matrix.pow(2.0); + assert_eq!(result[coord![0, 0]], 4.0); + assert_eq!(result[coord![0, 1]], 9.0); + assert_eq!(result[coord![1, 0]], 16.0); + assert_eq!(result[coord![1, 1]], 25.0); + assert_eq!(result.shape(), &shape); + } } \ No newline at end of file diff --git a/src/tensor.rs b/src/tensor.rs index 621ac66..1bef7fc 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -1,4 +1,4 @@ -use num::Num; +use num::{Num, Float}; use std::ops::{Add, Sub, Mul, Div}; use crate::shape; @@ -253,6 +253,16 @@ impl Tensor { } } +impl Tensor { + pub fn pow(&self, power: T) -> Tensor { + let mut result = Tensor::zeros(&self.shape); + for i in 0..self.size() { + result.data[i] = self.data[i].clone().powf(power); + } + result + } +} + // Element-wise Multiplication impl Mul for Tensor { type Output = Tensor; @@ -1205,5 +1215,35 @@ mod tests { let display = tensor.display(); assert_eq!(display, "[[[[1, 2],\n [3, 4]],\n\n [[5, 6],\n [7, 8]]],\n\n\n [[[9, 10],\n [11, 12]],\n\n [[13, 14],\n [15, 16]]]]"); } + + #[test] + fn test_pow_tensor_square() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 3.0, 4.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let result = tensor.pow(2.0); + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![1.0, 4.0, 9.0, 16.0])); + } + + #[test] + fn test_pow_tensor_sqrt() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 4.0, 9.0, 16.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let result = tensor.pow(0.5); + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![1.0, 2.0, 3.0, 4.0])); + } + + #[test] + fn test_pow_tensor_negative_exponent() { + let shape = shape![2, 2].unwrap(); + let data = vec![1.0, 2.0, 4.0, 8.0]; + let tensor = Tensor::new(&shape, &data).unwrap(); + let result = tensor.pow(-1.0); + assert_eq!(result.shape(), &shape); + assert_eq!(result.data, DynamicStorage::new(vec![1.0, 0.5, 0.25, 0.125])); + } } diff --git a/src/vector.rs b/src/vector.rs index 0f2783c..719438a 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -1,6 +1,7 @@ use std::ops::{Add, Sub, Mul, Div, Deref, Index, IndexMut, DerefMut}; use num::Num; +use num::Float; use crate::error::ShapeError; use crate::shape; use crate::coord; @@ -84,6 +85,12 @@ impl DynamicVector { } } +impl DynamicVector { + pub fn pow(&self, power: T) -> DynamicVector { + DynamicVector::from_tensor(self.tensor.pow(power)).unwrap() + } +} + // Scalar Addition impl Add for DynamicVector { type Output = DynamicVector; @@ -585,4 +592,17 @@ mod tests { assert_eq!(result[3], 2.0); assert_eq!(result.shape(), &shape); } + + #[test] + fn test_pow_vector() { + let shape = shape![4].unwrap(); + let data = vec![2.0, 3.0, 4.0, 5.0]; + let vector = DynamicVector::new(&data).unwrap(); + let result = vector.pow(2.0); + assert_eq!(result[0], 4.0); + assert_eq!(result[1], 9.0); + assert_eq!(result[2], 16.0); + assert_eq!(result[3], 25.0); + assert_eq!(result.shape(), &shape); + } } \ No newline at end of file From d12849de17c8b38849f1046dd2e30e28d25a1726 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Fri, 28 Jun 2024 21:21:19 +0100 Subject: [PATCH 19/24] feat: add eye method to matrix --- src/matrix.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/matrix.rs b/src/matrix.rs index 395ff20..29f1d2d 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -31,6 +31,13 @@ impl DynamicMatrix { let data = vec![value; shape.size()]; DynamicMatrix::new(shape, &data) } + pub fn eye(shape: &Shape) -> Result, ShapeError> { + let mut result = DynamicMatrix::zeros(shape).unwrap(); + for i in 0..shape[0] { + result.set(&coord![i, i], T::one()).unwrap(); + } + Ok(result) + } pub fn zeros(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::zero()) } pub fn ones(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::one()) } @@ -277,6 +284,22 @@ mod tests { assert_eq!(matrix[coord![1, 1]], 3.0); } + #[test] + fn test_eye() { + let shape = shape![3, 3].unwrap(); + let matrix = DynamicMatrix::::eye(&shape).unwrap(); + assert_eq!(matrix.shape(), &shape); + assert_eq!(matrix[coord![0, 0]], 1.0); + assert_eq!(matrix[coord![0, 1]], 0.0); + assert_eq!(matrix[coord![0, 2]], 0.0); + assert_eq!(matrix[coord![1, 0]], 0.0); + assert_eq!(matrix[coord![1, 1]], 1.0); + assert_eq!(matrix[coord![1, 2]], 0.0); + assert_eq!(matrix[coord![2, 0]], 0.0); + assert_eq!(matrix[coord![2, 1]], 0.0); + assert_eq!(matrix[coord![2, 2]], 1.0); + } + #[test] fn test_zeros() { let shape = shape![2, 2].unwrap(); From c9459b8821afa459087188f5cc5db165f3066582 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Sun, 30 Jun 2024 11:20:47 +0100 Subject: [PATCH 20/24] style: run fmt --- src/coordinate.rs | 4 +- src/iter.rs | 3 +- src/lib.rs | 10 +- src/matrix.rs | 28 +++-- src/shape.rs | 2 +- src/storage.rs | 9 +- src/tensor.rs | 275 +++++++++++++++++++++++++++++++++------------- src/vector.rs | 38 +++---- 8 files changed, 247 insertions(+), 122 deletions(-) diff --git a/src/coordinate.rs b/src/coordinate.rs index 0f34839..9e58696 100644 --- a/src/coordinate.rs +++ b/src/coordinate.rs @@ -22,7 +22,9 @@ impl Coordinate { pub fn insert(&self, index: usize, axis: usize) -> Self { let mut new_indices = self.indices.clone(); new_indices.insert(index, axis); - Self { indices: new_indices } + Self { + indices: new_indices, + } } } diff --git a/src/iter.rs b/src/iter.rs index 6299335..0f5a6c6 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -45,7 +45,6 @@ impl Iterator for IndexIterator { } } - #[cfg(test)] mod tests { use super::*; @@ -84,4 +83,4 @@ mod tests { assert_eq!(iter.next(), None); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 5a899a0..718f954 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ -pub mod shape; -pub mod iter; -pub mod error; +pub mod axes; pub mod coordinate; +pub mod error; +pub mod iter; +pub mod matrix; +pub mod shape; pub mod storage; pub mod tensor; pub mod vector; -pub mod matrix; -pub mod axes; \ No newline at end of file diff --git a/src/matrix.rs b/src/matrix.rs index 29f1d2d..5cb2b56 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,14 +1,14 @@ -use std::ops::{Add, Sub, Mul, Div, Deref, Index, IndexMut, DerefMut}; +use std::ops::{Add, Deref, DerefMut, Div, Index, IndexMut, Mul, Sub}; -use num::{Num, Float}; +use crate::axes::Axes; +use crate::coord; +use crate::coordinate::Coordinate; use crate::error::ShapeError; use crate::shape; -use crate::coord; use crate::shape::Shape; use crate::tensor::DynamicTensor; use crate::vector::DynamicVector; -use crate::axes::Axes; -use crate::coordinate::Coordinate; +use num::{Float, Num}; pub struct DynamicMatrix { tensor: DynamicTensor, @@ -17,9 +17,11 @@ pub type Matrix = DynamicMatrix; impl DynamicMatrix { pub fn new(shape: &Shape, data: &[T]) -> Result, ShapeError> { - Ok(DynamicMatrix { tensor: DynamicTensor::new(shape, data)? }) + Ok(DynamicMatrix { + tensor: DynamicTensor::new(shape, data)?, + }) } - + pub fn from_tensor(tensor: DynamicTensor) -> Result, ShapeError> { if tensor.shape().order() != 2 { return Err(ShapeError::new("Shape must have order of 2")); @@ -38,8 +40,12 @@ impl DynamicMatrix { } Ok(result) } - pub fn zeros(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::zero()) } - pub fn ones(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::one()) } + pub fn zeros(shape: &Shape) -> Result, ShapeError> { + Self::fill(shape, T::zero()) + } + pub fn ones(shape: &Shape) -> Result, ShapeError> { + Self::fill(shape, T::one()) + } pub fn sum(&self, axes: Axes) -> DynamicVector { let result = self.tensor.sum(axes); @@ -597,7 +603,7 @@ mod tests { assert_eq!(result[coord![1, 1]], 2.0); assert_eq!(result.shape(), &shape); } - + #[test] fn test_div_matrix_tensor() { let shape = shape![2, 2].unwrap(); @@ -648,4 +654,4 @@ mod tests { assert_eq!(result[coord![1, 1]], 25.0); assert_eq!(result.shape(), &shape); } -} \ No newline at end of file +} diff --git a/src/shape.rs b/src/shape.rs index 567b095..7901559 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -111,4 +111,4 @@ mod tests { let extended_shape = shape1.stack(&shape2); assert_eq!(extended_shape.dims, vec![2, 3, 4, 5]); } -} \ No newline at end of file +} diff --git a/src/storage.rs b/src/storage.rs index a500de2..fb66dfa 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,8 +1,8 @@ use std::ops::{Index, IndexMut}; use crate::coordinate::Coordinate; -use crate::shape::Shape; use crate::error::ShapeError; +use crate::shape::Shape; #[derive(Debug, PartialEq)] pub struct DynamicStorage { @@ -23,13 +23,15 @@ impl DynamicStorage { for (i, &dim) in coord.iter().enumerate() { if dim >= shape[i] { - return Err(ShapeError::new(format!("out of bounds for dimension {}", i).as_str())); + return Err(ShapeError::new( + format!("out of bounds for dimension {}", i).as_str(), + )); } } let mut index = 0; for k in 0..shape.order() { - let stride = shape[k+1..].iter().product::(); + let stride = shape[k + 1..].iter().product::(); index += coord[k] * stride; } Ok(index) @@ -54,7 +56,6 @@ impl Index for DynamicStorage { fn index(&self, index: usize) -> &Self::Output { &self.data[index] } - } impl Index> for DynamicStorage { diff --git a/src/tensor.rs b/src/tensor.rs index 1bef7fc..5b1cfcd 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -1,43 +1,55 @@ -use num::{Num, Float}; -use std::ops::{Add, Sub, Mul, Div}; +use num::{Float, Num}; +use std::ops::{Add, Div, Mul, Sub}; +use crate::axes::Axes; +use crate::coordinate::Coordinate; +use crate::error::ShapeError; +use crate::iter::IndexIterator; +use crate::matrix::DynamicMatrix; use crate::shape; use crate::shape::Shape; -use crate::iter::IndexIterator; -use crate::error::ShapeError; -use crate::coordinate::Coordinate; use crate::storage::DynamicStorage; -use crate::axes::Axes; use crate::vector::DynamicVector; -use crate::matrix::DynamicMatrix; #[derive(Debug)] pub struct DynamicTensor { data: DynamicStorage, - shape: Shape + shape: Shape, } -pub type Tensor = DynamicTensor; // Alias for convenience +pub type Tensor = DynamicTensor; // Alias for convenience impl Tensor { - pub fn new(shape: &Shape, data: &[T]) -> Result, ShapeError> { if data.len() != shape.size() { return Err(ShapeError::new("Data length does not match shape size")); } - Ok(Tensor {data: DynamicStorage::new(data.to_vec()), shape: shape.clone()}) + Ok(Tensor { + data: DynamicStorage::new(data.to_vec()), + shape: shape.clone(), + }) } pub fn fill(shape: &Shape, value: T) -> Tensor { let mut vec = Vec::with_capacity(shape.size()); - for _ in 0..shape.size() { vec.push(value); } + for _ in 0..shape.size() { + vec.push(value); + } Tensor::new(shape, &vec).unwrap() } - pub fn zeros(shape: &Shape) -> Tensor {Tensor::fill(shape, T::zero())} - pub fn ones(shape: &Shape) -> Tensor {Tensor::fill(shape, T::one())} + pub fn zeros(shape: &Shape) -> Tensor { + Tensor::fill(shape, T::zero()) + } + pub fn ones(shape: &Shape) -> Tensor { + Tensor::fill(shape, T::one()) + } // Properties - pub fn shape(&self) -> &Shape { &self.shape } - pub fn size(&self) -> usize { self.shape.size() } + pub fn shape(&self) -> &Shape { + &self.shape + } + pub fn size(&self) -> usize { + self.shape.size() + } pub fn get(&self, coord: &Coordinate) -> Result<&T, ShapeError> { Ok(&self.data[self.data.flatten(coord, &self.shape)?]) @@ -57,8 +69,15 @@ impl Tensor { // // Reduction operations pub fn sum(&self, axes: Axes) -> Tensor { let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let remaining_axes = all_axes + .clone() + .into_iter() + .filter(|&i| !axes.contains(&i)) + .collect::>(); + let remaining_dims = remaining_axes + .iter() + .map(|&i| self.shape[i]) + .collect::>(); let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value @@ -90,13 +109,16 @@ impl Tensor { pub fn mean(&self, axes: Axes) -> Tensor { let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { - let mut result = T::zero(); - for _ in 0..dim { - result = result + T::one(); - } - result - }).collect(); + let removing_dims_t: Vec = removing_dims + .iter() + .map(|&dim| { + let mut result = T::zero(); + for _ in 0..dim { + result = result + T::one(); + } + result + }) + .collect(); let n = if removing_dims_t.len() != 0 { removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x) } else { @@ -111,13 +133,16 @@ impl Tensor { pub fn var(&self, axes: Axes) -> Tensor { let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); - let removing_dims_t: Vec = removing_dims.iter().map(|&dim| { - let mut result = T::zero(); - for _ in 0..dim { - result = result + T::one(); - } - result - }).collect(); + let removing_dims_t: Vec = removing_dims + .iter() + .map(|&dim| { + let mut result = T::zero(); + for _ in 0..dim { + result = result + T::one(); + } + result + }) + .collect(); let n = if removing_dims_t.len() != 0 { removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x) @@ -128,16 +153,28 @@ impl Tensor { } sum }; - + let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let remaining_axes = all_axes + .clone() + .into_iter() + .filter(|&i| !axes.contains(&i)) + .collect::>(); + let remaining_dims = remaining_axes + .iter() + .map(|&i| self.shape[i]) + .collect::>(); let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { let avg: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x) / n; - let var: T = self.data.iter().map(|&x| (x - avg) * (x - avg)).fold(T::zero(), |acc, x| acc + x) / n; + let var: T = self + .data + .iter() + .map(|&x| (x - avg) * (x - avg)) + .fold(T::zero(), |acc, x| acc + x) + / n; return Tensor::new(&Shape::new(vec![1]).unwrap(), &[var]).unwrap(); } @@ -167,21 +204,37 @@ impl Tensor { pub fn max(&self, axes: Axes) -> Tensor { let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let remaining_axes = all_axes + .clone() + .into_iter() + .filter(|&i| !axes.contains(&i)) + .collect::>(); + let remaining_dims = remaining_axes + .iter() + .map(|&i| self.shape[i]) + .collect::>(); let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { - let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); - let max: T = self.data.iter().fold(min, |acc, x| if acc > *x { acc } else { *x }); + let min: T = self + .data + .iter() + .fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + let max: T = self + .data + .iter() + .fold(min, |acc, x| if acc > *x { acc } else { *x }); return Tensor::new(&Shape::new(vec![1]).unwrap(), &[max]).unwrap(); } // Create new tensor with right shape let new_shape = Shape::new(remaining_dims).unwrap(); let remove_shape = Shape::new(removing_dims).unwrap(); - let min: T = self.data.iter().fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); + let min: T = self + .data + .iter() + .fold(T::zero(), |acc, x| if acc < *x { acc } else { *x }); let mut t: Tensor = Tensor::fill(&new_shape, min); for target in IndexIterator::new(&new_shape) { @@ -203,21 +256,37 @@ impl Tensor { pub fn min(&self, axes: Axes) -> Tensor { let all_axes = (0..self.shape.order()).collect::>(); - let remaining_axes = all_axes.clone().into_iter().filter(|&i| !axes.contains(&i)).collect::>(); - let remaining_dims = remaining_axes.iter().map(|&i| self.shape[i]).collect::>(); + let remaining_axes = all_axes + .clone() + .into_iter() + .filter(|&i| !axes.contains(&i)) + .collect::>(); + let remaining_dims = remaining_axes + .iter() + .map(|&i| self.shape[i]) + .collect::>(); let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value if axes.is_empty() | (remaining_dims.len() == 0) { - let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); - let min: T = self.data.iter().fold(max, |acc, x| if acc < *x { acc } else { *x }); + let max: T = self + .data + .iter() + .fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + let min: T = self + .data + .iter() + .fold(max, |acc, x| if acc < *x { acc } else { *x }); return Tensor::new(&Shape::new(vec![1]).unwrap(), &[min]).unwrap(); } // Create new tensor with right shape let new_shape = Shape::new(remaining_dims).unwrap(); let remove_shape = Shape::new(removing_dims).unwrap(); - let max: T = self.data.iter().fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); + let max: T = self + .data + .iter() + .fold(T::zero(), |acc, x| if acc > *x { acc } else { *x }); let mut t: Tensor = Tensor::fill(&new_shape, max); for target in IndexIterator::new(&new_shape) { @@ -307,7 +376,7 @@ impl Add> for Tensor { type Output = DynamicVector; fn add(self, rhs: DynamicVector) -> DynamicVector { - rhs + self + rhs + self } } @@ -315,13 +384,12 @@ impl Add> for Tensor { type Output = DynamicMatrix; fn add(self, rhs: DynamicMatrix) -> DynamicMatrix { - rhs + self + rhs + self } } // Element-wise Subtraction -impl Sub for Tensor -{ +impl Sub for Tensor { type Output = Tensor; fn sub(self, rhs: T) -> Tensor { @@ -351,7 +419,7 @@ impl Sub> for Tensor { type Output = DynamicVector; fn sub(self, rhs: DynamicVector) -> DynamicVector { - (rhs * (T::zero() - T::one())) + self + (rhs * (T::zero() - T::one())) + self } } @@ -359,7 +427,7 @@ impl Sub> for Tensor { type Output = DynamicMatrix; fn sub(self, rhs: DynamicMatrix) -> DynamicMatrix { - (rhs * (T::zero() - T::one())) + self + (rhs * (T::zero() - T::one())) + self } } @@ -381,7 +449,7 @@ impl Mul> for Tensor { type Output = DynamicVector; fn mul(self, rhs: DynamicVector) -> DynamicVector { - rhs * self + rhs * self } } @@ -389,13 +457,12 @@ impl Mul> for Tensor { type Output = DynamicMatrix; fn mul(self, rhs: DynamicMatrix) -> DynamicMatrix { - rhs * self + rhs * self } } // Element-wise Division -impl Div for Tensor -{ +impl Div for Tensor { type Output = Tensor; fn div(self, rhs: T) -> Tensor { @@ -439,7 +506,11 @@ impl Div> for Tensor { impl Tensor { pub fn display(&self) -> String { - fn format_tensor(data: &DynamicStorage, shape: &Shape, level: usize) -> String { + fn format_tensor( + data: &DynamicStorage, + shape: &Shape, + level: usize, + ) -> String { if shape.order() == 1 { let mut result = String::from("["); for (i, item) in data.iter().enumerate() { @@ -465,7 +536,11 @@ impl Tensor { } } let sub_data = DynamicStorage::new(data[i * sub_size..(i + 1) * sub_size].to_vec()); - result.push_str(&format_tensor(&sub_data, &Shape::new(shape[1..].to_vec()).unwrap(), level + 1)); + result.push_str(&format_tensor( + &sub_data, + &Shape::new(shape[1..].to_vec()).unwrap(), + level + 1, + )); } result.push(']'); result @@ -475,7 +550,6 @@ impl Tensor { } } - #[cfg(test)] mod tests { use super::*; @@ -623,7 +697,9 @@ mod tests { #[test] fn test_tensor_sum_no_axis_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![]); @@ -659,13 +735,18 @@ mod tests { #[test] fn test_tensor_sum_one_axis_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0]); assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![8.0, 10.0, 12.0, 14.0, 16.0, 18.0]) + ); } #[test] @@ -683,7 +764,9 @@ mod tests { #[test] fn test_tensor_sum_multiple_axes_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.sum(vec![0, 1]); @@ -719,13 +802,18 @@ mod tests { #[test] fn test_tensor_mean_one_axis_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.mean(vec![0]); assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) + ); } #[test] @@ -743,7 +831,9 @@ mod tests { #[test] fn test_tensor_mean_multiple_axes_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.mean(vec![0, 1]); @@ -779,13 +869,18 @@ mod tests { #[test] fn test_tensor_var_one_axis_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]; + let data = vec![ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.var(vec![0]); assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![9.0, 9.0, 9.0, 9.0, 9.0, 9.0]) + ); } #[test] @@ -803,7 +898,9 @@ mod tests { #[test] fn test_tensor_var_multiple_axes_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0]; + let data = vec![ + 1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.var(vec![0, 1]); @@ -839,7 +936,9 @@ mod tests { #[test] fn test_tensor_max_multiple_axes_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; + let data = vec![ + 1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.max(vec![0, 1]); @@ -875,7 +974,9 @@ mod tests { #[test] fn test_tensor_min_multiple_axes_3d() { let shape = shape![2, 2, 3].unwrap(); - let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0]; + let data = vec![ + 1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0, 11.0, -12.0, + ]; let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.min(vec![0, 1]); @@ -897,7 +998,10 @@ mod tests { let result = tensor1.prod(&tensor2); assert_eq!(result.shape(), &shape![3, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![4.0, 5.0, 8.0, 10.0, 12.0, 15.0]) + ); } #[test] @@ -913,7 +1017,10 @@ mod tests { let result = tensor1.prod(&tensor2); assert_eq!(result.shape(), &shape![2, 2, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![5.0, 6.0, 10.0, 12.0, 15.0, 18.0, 20.0, 24.0]) + ); } #[test] @@ -929,7 +1036,13 @@ mod tests { let result = tensor1.prod(&tensor2); assert_eq!(result.shape(), &shape![2, 2, 2, 2].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, 28.0, 32.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![ + 5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 15.0, 18.0, 21.0, 24.0, 20.0, 24.0, + 28.0, 32.0 + ]) + ); } #[test] @@ -1054,7 +1167,10 @@ mod tests { let result = tensor1 * tensor2; assert_eq!(result.shape(), &shape![2, 3].unwrap()); - assert_eq!(result.data, DynamicStorage::new(vec![7.0, 16.0, 27.0, 40.0, 55.0, 72.0])); + assert_eq!( + result.data, + DynamicStorage::new(vec![7.0, 16.0, 27.0, 40.0, 55.0, 72.0]) + ); } #[test] @@ -1208,8 +1324,7 @@ mod tests { fn test_display_4d_tensor() { let shape = shape![2, 2, 2, 2].unwrap(); let data = vec![ - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, - 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, ]; let tensor = Tensor::new(&shape, &data).unwrap(); let display = tensor.display(); @@ -1243,7 +1358,9 @@ mod tests { let tensor = Tensor::new(&shape, &data).unwrap(); let result = tensor.pow(-1.0); assert_eq!(result.shape(), &shape); - assert_eq!(result.data, DynamicStorage::new(vec![1.0, 0.5, 0.25, 0.125])); + assert_eq!( + result.data, + DynamicStorage::new(vec![1.0, 0.5, 0.25, 0.125]) + ); } } - diff --git a/src/vector.rs b/src/vector.rs index 719438a..1974855 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -1,13 +1,13 @@ -use std::ops::{Add, Sub, Mul, Div, Deref, Index, IndexMut, DerefMut}; +use std::ops::{Add, Deref, DerefMut, Div, Index, IndexMut, Mul, Sub}; -use num::Num; -use num::Float; +use crate::coord; use crate::error::ShapeError; +use crate::matrix::DynamicMatrix; use crate::shape; -use crate::coord; use crate::shape::Shape; use crate::tensor::DynamicTensor; -use crate::matrix::DynamicMatrix; +use num::Float; +use num::Num; pub struct DynamicVector { tensor: DynamicTensor, @@ -16,9 +16,11 @@ pub type Vector = DynamicVector; impl DynamicVector { pub fn new(data: &[T]) -> Result, ShapeError> { - Ok(DynamicVector { tensor: DynamicTensor::new(&shape![data.len()].unwrap(), data)? }) + Ok(DynamicVector { + tensor: DynamicTensor::new(&shape![data.len()].unwrap(), data)?, + }) } - + pub fn from_tensor(tensor: DynamicTensor) -> Result, ShapeError> { if tensor.shape().order() != 1 { return Err(ShapeError::new("Shape must have order of 1")); @@ -33,8 +35,12 @@ impl DynamicVector { let data = vec![value; shape[0]]; DynamicVector::new(&data) } - pub fn zeros(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::zero()) } - pub fn ones(shape: &Shape) -> Result, ShapeError> { Self::fill(shape, T::one()) } + pub fn zeros(shape: &Shape) -> Result, ShapeError> { + Self::fill(shape, T::zero()) + } + pub fn ones(shape: &Shape) -> Result, ShapeError> { + Self::fill(shape, T::one()) + } pub fn sum(&self) -> DynamicVector { let result = self.tensor.sum(vec![]); @@ -383,10 +389,7 @@ mod tests { #[test] fn test_matmul() { let data_vector = vec![1.0, 2.0]; - let data_matrix = vec![ - 1.0, 2.0, - 3.0, 4.0 - ]; + let data_matrix = vec![1.0, 2.0, 3.0, 4.0]; let vector = DynamicVector::new(&data_vector).unwrap(); let matrix = DynamicMatrix::new(&shape![2, 2].unwrap(), &data_matrix).unwrap(); let result = vector.matmul(&matrix); @@ -404,10 +407,7 @@ mod tests { let result = vector1.prod(&vector2); let expected_data = vec![ - 2.0, 3.0, 4.0, 5.0, - 4.0, 6.0, 8.0, 10.0, - 6.0, 9.0, 12.0, 15.0, - 8.0, 12.0, 16.0, 20.0 + 2.0, 3.0, 4.0, 5.0, 4.0, 6.0, 8.0, 10.0, 6.0, 9.0, 12.0, 15.0, 8.0, 12.0, 16.0, 20.0, ]; let expected_shape = shape![4, 4].unwrap(); let expected_tensor = DynamicTensor::new(&expected_shape, &expected_data).unwrap(); @@ -577,7 +577,7 @@ mod tests { assert_eq!(result[3], 2.0); assert_eq!(result.shape(), &shape); } - + #[test] fn test_div_vector_tensor() { let shape = shape![4].unwrap(); @@ -605,4 +605,4 @@ mod tests { assert_eq!(result[3], 25.0); assert_eq!(result.shape(), &shape); } -} \ No newline at end of file +} From 9b9783b29c4923840dad6c4634e380a9c12ea314 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Sun, 30 Jun 2024 11:56:57 +0100 Subject: [PATCH 21/24] feat!: prevent empty coord --- src/coordinate.rs | 31 ++++--- src/iter.rs | 24 ++--- src/matrix.rs | 220 +++++++++++++++++++++++----------------------- src/tensor.rs | 68 +++++++------- src/vector.rs | 16 ++-- 5 files changed, 183 insertions(+), 176 deletions(-) diff --git a/src/coordinate.rs b/src/coordinate.rs index 9e58696..cb8a703 100644 --- a/src/coordinate.rs +++ b/src/coordinate.rs @@ -1,14 +1,19 @@ use std::fmt; use std::ops::{Index, IndexMut}; +use crate::error::ShapeError; + #[derive(Debug, Clone, PartialEq)] pub struct Coordinate { indices: Vec, } impl Coordinate { - pub fn new(indices: Vec) -> Self { - Self { indices } + pub fn new(indices: Vec) -> Result { + if indices.is_empty() { + return Err(ShapeError::new("Coordinate cannot be empty")); + } + Ok(Self { indices }) } pub fn len(&self) -> usize { @@ -73,13 +78,13 @@ mod tests { #[test] fn test_len() { - let coord = coord![1, 2, 3]; + let coord = coord![1, 2, 3].unwrap(); assert_eq!(coord.len(), 3); } #[test] fn test_iter() { - let coord = coord![1, 2, 3]; + let coord = coord![1, 2, 3].unwrap(); let mut iter = coord.iter(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); @@ -89,14 +94,14 @@ mod tests { #[test] fn test_insert() { - let coord = coord![1, 2, 3]; + let coord = coord![1, 2, 3].unwrap(); let new_coord = coord.insert(1, 4); - assert_eq!(new_coord, coord![1, 4, 2, 3]); + assert_eq!(new_coord, coord![1, 4, 2, 3].unwrap()); } #[test] fn test_index() { - let coord = coord![1, 2, 3]; + let coord = coord![1, 2, 3].unwrap(); assert_eq!(coord[0], 1); assert_eq!(coord[1], 2); assert_eq!(coord[2], 3); @@ -104,23 +109,23 @@ mod tests { #[test] fn test_index_mut() { - let mut coord = coord![1, 2, 3]; + let mut coord = coord![1, 2, 3].unwrap(); coord[1] = 4; assert_eq!(coord[1], 4); } #[test] fn test_display() { - let coord = coord![1, 2, 3]; + let coord = coord![1, 2, 3].unwrap(); assert_eq!(format!("{}", coord), "(1, 2, 3)"); } #[test] fn test_coord_macro() { - let coord = coord![1, 2, 3]; - assert_eq!(coord, Coordinate::new(vec![1, 2, 3])); + let coord = coord![1, 2, 3].unwrap(); + assert_eq!(coord, Coordinate::new(vec![1, 2, 3]).unwrap()); - let coord_repeated = coord![1; 3]; - assert_eq!(coord_repeated, Coordinate::new(vec![1, 1, 1])); + let coord_repeated = coord![1; 3].unwrap(); + assert_eq!(coord_repeated, Coordinate::new(vec![1, 1, 1]).unwrap()); } } diff --git a/src/iter.rs b/src/iter.rs index 0f5a6c6..90788b6 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,6 +1,7 @@ use crate::coord; use crate::coordinate::Coordinate; use crate::shape::Shape; +use std::cmp::max; pub struct IndexIterator { shape: Shape, @@ -10,7 +11,8 @@ pub struct IndexIterator { impl IndexIterator { pub fn new(shape: &Shape) -> Self { - let current = coord![0; shape.order()]; + // (shape.order() == 0) => `next` returns None before `current` is used + let current = coord![0; max(shape.order(), 1)].unwrap(); IndexIterator { shape: shape.clone(), current, @@ -55,12 +57,12 @@ mod tests { let shape = shape![2, 3].unwrap(); let mut iter = IndexIterator::new(&shape); - assert_eq!(iter.next(), Some(coord![0, 0])); - assert_eq!(iter.next(), Some(coord![0, 1])); - assert_eq!(iter.next(), Some(coord![0, 2])); - assert_eq!(iter.next(), Some(coord![1, 0])); - assert_eq!(iter.next(), Some(coord![1, 1])); - assert_eq!(iter.next(), Some(coord![1, 2])); + assert_eq!(iter.next(), Some(coord![0, 0].unwrap())); + assert_eq!(iter.next(), Some(coord![0, 1].unwrap())); + assert_eq!(iter.next(), Some(coord![0, 2].unwrap())); + assert_eq!(iter.next(), Some(coord![1, 0].unwrap())); + assert_eq!(iter.next(), Some(coord![1, 1].unwrap())); + assert_eq!(iter.next(), Some(coord![1, 2].unwrap())); assert_eq!(iter.next(), None); } @@ -69,10 +71,10 @@ mod tests { let shape = shape![4].unwrap(); let mut iter = IndexIterator::new(&shape); - assert_eq!(iter.next(), Some(coord![0])); - assert_eq!(iter.next(), Some(coord![1])); - assert_eq!(iter.next(), Some(coord![2])); - assert_eq!(iter.next(), Some(coord![3])); + assert_eq!(iter.next(), Some(coord![0].unwrap())); + assert_eq!(iter.next(), Some(coord![1].unwrap())); + assert_eq!(iter.next(), Some(coord![2].unwrap())); + assert_eq!(iter.next(), Some(coord![3].unwrap())); assert_eq!(iter.next(), None); } diff --git a/src/matrix.rs b/src/matrix.rs index 5cb2b56..48f21b3 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -36,7 +36,7 @@ impl DynamicMatrix { pub fn eye(shape: &Shape) -> Result, ShapeError> { let mut result = DynamicMatrix::zeros(shape).unwrap(); for i in 0..shape[0] { - result.set(&coord![i, i], T::one()).unwrap(); + result.set(&coord![i, i].unwrap(), T::one()).unwrap(); } Ok(result) } @@ -81,9 +81,9 @@ impl DynamicMatrix { for j in 0..rhs.shape()[1] { let mut sum = T::zero(); for k in 0..self.shape()[1] { - sum = sum + self[coord![i, k]] * rhs[coord![k, j]]; + sum = sum + self[coord![i, k].unwrap()] * rhs[coord![k, j].unwrap()]; } - result.set(&coord![i, j], sum).unwrap(); + result.set(&coord![i, j].unwrap(), sum).unwrap(); } } DynamicMatrix::from_tensor(result).unwrap() @@ -95,9 +95,9 @@ impl DynamicMatrix { for i in 0..self.shape()[0] { let mut sum = T::zero(); for j in 0..self.shape()[1] { - sum = sum + self[coord![i, j]] * rhs[j]; + sum = sum + self[coord![i, j].unwrap()] * rhs[j]; } - result.set(&coord![i], sum).unwrap(); + result.set(&coord![i].unwrap(), sum).unwrap(); } DynamicVector::from_tensor(result).unwrap() } @@ -251,10 +251,10 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 2.0); - assert_eq!(matrix[coord![1, 0]], 3.0); - assert_eq!(matrix[coord![1, 1]], 4.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 2.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 3.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 4.0); } #[test] @@ -264,10 +264,10 @@ mod tests { let tensor = DynamicTensor::new(&shape, &data).unwrap(); let matrix = DynamicMatrix::from_tensor(tensor).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 2.0); - assert_eq!(matrix[coord![1, 0]], 3.0); - assert_eq!(matrix[coord![1, 1]], 4.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 2.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 3.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 4.0); } #[test] @@ -284,10 +284,10 @@ mod tests { let shape = shape![2, 2].unwrap(); let matrix = DynamicMatrix::fill(&shape, 3.0).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 3.0); - assert_eq!(matrix[coord![0, 1]], 3.0); - assert_eq!(matrix[coord![1, 0]], 3.0); - assert_eq!(matrix[coord![1, 1]], 3.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 3.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 3.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 3.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 3.0); } #[test] @@ -295,15 +295,15 @@ mod tests { let shape = shape![3, 3].unwrap(); let matrix = DynamicMatrix::::eye(&shape).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 0.0); - assert_eq!(matrix[coord![0, 2]], 0.0); - assert_eq!(matrix[coord![1, 0]], 0.0); - assert_eq!(matrix[coord![1, 1]], 1.0); - assert_eq!(matrix[coord![1, 2]], 0.0); - assert_eq!(matrix[coord![2, 0]], 0.0); - assert_eq!(matrix[coord![2, 1]], 0.0); - assert_eq!(matrix[coord![2, 2]], 1.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 0.0); + assert_eq!(matrix[coord![0, 2].unwrap()], 0.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 0.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 1.0); + assert_eq!(matrix[coord![1, 2].unwrap()], 0.0); + assert_eq!(matrix[coord![2, 0].unwrap()], 0.0); + assert_eq!(matrix[coord![2, 1].unwrap()], 0.0); + assert_eq!(matrix[coord![2, 2].unwrap()], 1.0); } #[test] @@ -311,10 +311,10 @@ mod tests { let shape = shape![2, 2].unwrap(); let matrix = DynamicMatrix::::zeros(&shape).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 0.0); - assert_eq!(matrix[coord![0, 1]], 0.0); - assert_eq!(matrix[coord![1, 0]], 0.0); - assert_eq!(matrix[coord![1, 1]], 0.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 0.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 0.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 0.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 0.0); } #[test] @@ -322,10 +322,10 @@ mod tests { let shape = shape![2, 2].unwrap(); let matrix = DynamicMatrix::::ones(&shape).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 1.0); - assert_eq!(matrix[coord![1, 0]], 1.0); - assert_eq!(matrix[coord![1, 1]], 1.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 1.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 1.0); } #[test] @@ -340,7 +340,7 @@ mod tests { let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); - assert_eq!(matrix[coord![1, 0]], 3.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 3.0); } #[test] @@ -348,12 +348,12 @@ mod tests { let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; let mut matrix = DynamicMatrix::new(&shape, &data).unwrap(); - matrix[coord![1, 0]] = 5.0; + matrix[coord![1, 0].unwrap()] = 5.0; assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 2.0); - assert_eq!(matrix[coord![1, 0]], 5.0); - assert_eq!(matrix[coord![1, 1]], 4.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 2.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 5.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 4.0); } #[test] @@ -361,12 +361,12 @@ mod tests { let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; let mut matrix = DynamicMatrix::new(&shape, &data).unwrap(); - matrix.set(&coord![1, 0], 5.0).unwrap(); + matrix.set(&coord![1, 0].unwrap(), 5.0).unwrap(); assert_eq!(matrix.shape(), &shape); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 2.0); - assert_eq!(matrix[coord![1, 0]], 5.0); - assert_eq!(matrix[coord![1, 1]], 4.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 2.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 5.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 4.0); } #[test] @@ -428,10 +428,10 @@ mod tests { let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); let result = matrix1.matmul(&matrix2); assert_eq!(result.shape(), &shape); - assert_eq!(result[coord![0, 0]], 10.0); - assert_eq!(result[coord![0, 1]], 13.0); - assert_eq!(result[coord![1, 0]], 22.0); - assert_eq!(result[coord![1, 1]], 29.0); + assert_eq!(result[coord![0, 0].unwrap()], 10.0); + assert_eq!(result[coord![0, 1].unwrap()], 13.0); + assert_eq!(result[coord![1, 0].unwrap()], 22.0); + assert_eq!(result[coord![1, 1].unwrap()], 29.0); } #[test] @@ -453,10 +453,10 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); let result = matrix + 2.0; - assert_eq!(result[coord![0, 0]], 3.0); - assert_eq!(result[coord![0, 1]], 4.0); - assert_eq!(result[coord![1, 0]], 5.0); - assert_eq!(result[coord![1, 1]], 6.0); + assert_eq!(result[coord![0, 0].unwrap()], 3.0); + assert_eq!(result[coord![0, 1].unwrap()], 4.0); + assert_eq!(result[coord![1, 0].unwrap()], 5.0); + assert_eq!(result[coord![1, 1].unwrap()], 6.0); assert_eq!(result.shape(), &shape); } @@ -468,10 +468,10 @@ mod tests { let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); let result = matrix1 + matrix2; - assert_eq!(result[coord![0, 0]], 3.0); - assert_eq!(result[coord![0, 1]], 5.0); - assert_eq!(result[coord![1, 0]], 7.0); - assert_eq!(result[coord![1, 1]], 9.0); + assert_eq!(result[coord![0, 0].unwrap()], 3.0); + assert_eq!(result[coord![0, 1].unwrap()], 5.0); + assert_eq!(result[coord![1, 0].unwrap()], 7.0); + assert_eq!(result[coord![1, 1].unwrap()], 9.0); assert_eq!(result.shape(), &shape); } @@ -483,10 +483,10 @@ mod tests { let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); let tensor = DynamicTensor::new(&shape, &data2).unwrap(); let result = matrix + tensor; - assert_eq!(result[coord![0, 0]], 3.0); - assert_eq!(result[coord![0, 1]], 5.0); - assert_eq!(result[coord![1, 0]], 7.0); - assert_eq!(result[coord![1, 1]], 9.0); + assert_eq!(result[coord![0, 0].unwrap()], 3.0); + assert_eq!(result[coord![0, 1].unwrap()], 5.0); + assert_eq!(result[coord![1, 0].unwrap()], 7.0); + assert_eq!(result[coord![1, 1].unwrap()], 9.0); assert_eq!(result.shape(), &shape); } @@ -496,10 +496,10 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); let result = matrix - 2.0; - assert_eq!(result[coord![0, 0]], -1.0); - assert_eq!(result[coord![0, 1]], 0.0); - assert_eq!(result[coord![1, 0]], 1.0); - assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result[coord![0, 0].unwrap()], -1.0); + assert_eq!(result[coord![0, 1].unwrap()], 0.0); + assert_eq!(result[coord![1, 0].unwrap()], 1.0); + assert_eq!(result[coord![1, 1].unwrap()], 2.0); assert_eq!(result.shape(), &shape); } @@ -511,10 +511,10 @@ mod tests { let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); let result = matrix1 - matrix2; - assert_eq!(result[coord![0, 0]], -1.0); - assert_eq!(result[coord![0, 1]], -1.0); - assert_eq!(result[coord![1, 0]], -1.0); - assert_eq!(result[coord![1, 1]], -1.0); + assert_eq!(result[coord![0, 0].unwrap()], -1.0); + assert_eq!(result[coord![0, 1].unwrap()], -1.0); + assert_eq!(result[coord![1, 0].unwrap()], -1.0); + assert_eq!(result[coord![1, 1].unwrap()], -1.0); assert_eq!(result.shape(), &shape); } @@ -526,10 +526,10 @@ mod tests { let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); let tensor = DynamicTensor::new(&shape, &data2).unwrap(); let result = matrix - tensor; - assert_eq!(result[coord![0, 0]], -1.0); - assert_eq!(result[coord![0, 1]], -1.0); - assert_eq!(result[coord![1, 0]], -1.0); - assert_eq!(result[coord![1, 1]], -1.0); + assert_eq!(result[coord![0, 0].unwrap()], -1.0); + assert_eq!(result[coord![0, 1].unwrap()], -1.0); + assert_eq!(result[coord![1, 0].unwrap()], -1.0); + assert_eq!(result[coord![1, 1].unwrap()], -1.0); assert_eq!(result.shape(), &shape); } @@ -539,10 +539,10 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); let result = matrix * 2.0; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 4.0); - assert_eq!(result[coord![1, 0]], 6.0); - assert_eq!(result[coord![1, 1]], 8.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 4.0); + assert_eq!(result[coord![1, 0].unwrap()], 6.0); + assert_eq!(result[coord![1, 1].unwrap()], 8.0); assert_eq!(result.shape(), &shape); } @@ -554,10 +554,10 @@ mod tests { let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); let result = matrix1 * matrix2; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 6.0); - assert_eq!(result[coord![1, 0]], 12.0); - assert_eq!(result[coord![1, 1]], 20.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 6.0); + assert_eq!(result[coord![1, 0].unwrap()], 12.0); + assert_eq!(result[coord![1, 1].unwrap()], 20.0); assert_eq!(result.shape(), &shape); } @@ -569,10 +569,10 @@ mod tests { let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); let tensor = DynamicTensor::new(&shape, &data2).unwrap(); let result = matrix * tensor; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 6.0); - assert_eq!(result[coord![1, 0]], 12.0); - assert_eq!(result[coord![1, 1]], 20.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 6.0); + assert_eq!(result[coord![1, 0].unwrap()], 12.0); + assert_eq!(result[coord![1, 1].unwrap()], 20.0); assert_eq!(result.shape(), &shape); } @@ -582,10 +582,10 @@ mod tests { let data = vec![4.0, 6.0, 8.0, 10.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); let result = matrix / 2.0; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 3.0); - assert_eq!(result[coord![1, 0]], 4.0); - assert_eq!(result[coord![1, 1]], 5.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 3.0); + assert_eq!(result[coord![1, 0].unwrap()], 4.0); + assert_eq!(result[coord![1, 1].unwrap()], 5.0); assert_eq!(result.shape(), &shape); } @@ -597,10 +597,10 @@ mod tests { let matrix1 = DynamicMatrix::new(&shape, &data1).unwrap(); let matrix2 = DynamicMatrix::new(&shape, &data2).unwrap(); let result = matrix1 / matrix2; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 2.0); - assert_eq!(result[coord![1, 0]], 2.0); - assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 2.0); + assert_eq!(result[coord![1, 0].unwrap()], 2.0); + assert_eq!(result[coord![1, 1].unwrap()], 2.0); assert_eq!(result.shape(), &shape); } @@ -612,10 +612,10 @@ mod tests { let matrix = DynamicMatrix::new(&shape, &data1).unwrap(); let tensor = DynamicTensor::new(&shape, &data2).unwrap(); let result = matrix / tensor; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 2.0); - assert_eq!(result[coord![1, 0]], 2.0); - assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 2.0); + assert_eq!(result[coord![1, 0].unwrap()], 2.0); + assert_eq!(result[coord![1, 1].unwrap()], 2.0); assert_eq!(result.shape(), &shape); } @@ -624,10 +624,10 @@ mod tests { let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); - assert_eq!(matrix[coord![0, 0]], 1.0); - assert_eq!(matrix[coord![0, 1]], 2.0); - assert_eq!(matrix[coord![1, 0]], 3.0); - assert_eq!(matrix[coord![1, 1]], 4.0); + assert_eq!(matrix[coord![0, 0].unwrap()], 1.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 2.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 3.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 4.0); } #[test] @@ -635,11 +635,11 @@ mod tests { let shape = shape![2, 2].unwrap(); let data = vec![1.0, 2.0, 3.0, 4.0]; let mut matrix = DynamicMatrix::new(&shape, &data).unwrap(); - matrix[coord![0, 0]] = 5.0; - assert_eq!(matrix[coord![0, 0]], 5.0); - assert_eq!(matrix[coord![0, 1]], 2.0); - assert_eq!(matrix[coord![1, 0]], 3.0); - assert_eq!(matrix[coord![1, 1]], 4.0); + matrix[coord![0, 0].unwrap()] = 5.0; + assert_eq!(matrix[coord![0, 0].unwrap()], 5.0); + assert_eq!(matrix[coord![0, 1].unwrap()], 2.0); + assert_eq!(matrix[coord![1, 0].unwrap()], 3.0); + assert_eq!(matrix[coord![1, 1].unwrap()], 4.0); } #[test] @@ -648,10 +648,10 @@ mod tests { let data = vec![2.0, 3.0, 4.0, 5.0]; let matrix = DynamicMatrix::new(&shape, &data).unwrap(); let result = matrix.pow(2.0); - assert_eq!(result[coord![0, 0]], 4.0); - assert_eq!(result[coord![0, 1]], 9.0); - assert_eq!(result[coord![1, 0]], 16.0); - assert_eq!(result[coord![1, 1]], 25.0); + assert_eq!(result[coord![0, 0].unwrap()], 4.0); + assert_eq!(result[coord![0, 1].unwrap()], 9.0); + assert_eq!(result[coord![1, 0].unwrap()], 16.0); + assert_eq!(result[coord![1, 1].unwrap()], 25.0); assert_eq!(result.shape(), &shape); } } diff --git a/src/tensor.rs b/src/tensor.rs index 5b1cfcd..1b27979 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -625,10 +625,10 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = Tensor::new(&shape, &data).unwrap(); - assert_eq!(*tensor.get(&coord![0, 0]).unwrap(), 1.0); - assert_eq!(*tensor.get(&coord![0, 1]).unwrap(), 2.0); - assert_eq!(*tensor.get(&coord![1, 0]).unwrap(), 3.0); - assert_eq!(*tensor.get(&coord![1, 1]).unwrap(), 4.0); + assert_eq!(*tensor.get(&coord![0, 0].unwrap()).unwrap(), 1.0); + assert_eq!(*tensor.get(&coord![0, 1].unwrap()).unwrap(), 2.0); + assert_eq!(*tensor.get(&coord![1, 0].unwrap()).unwrap(), 3.0); + assert_eq!(*tensor.get(&coord![1, 1].unwrap()).unwrap(), 4.0); } #[test] @@ -637,15 +637,15 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut tensor = Tensor::new(&shape, &data).unwrap(); - tensor.set(&coord![0, 0], 5.0).unwrap(); - tensor.set(&coord![0, 1], 6.0).unwrap(); - tensor.set(&coord![1, 0], 7.0).unwrap(); - tensor.set(&coord![1, 1], 8.0).unwrap(); + tensor.set(&coord![0, 0].unwrap(), 5.0).unwrap(); + tensor.set(&coord![0, 1].unwrap(), 6.0).unwrap(); + tensor.set(&coord![1, 0].unwrap(), 7.0).unwrap(); + tensor.set(&coord![1, 1].unwrap(), 8.0).unwrap(); - assert_eq!(*tensor.get(&coord![0, 0]).unwrap(), 5.0); - assert_eq!(*tensor.get(&coord![0, 1]).unwrap(), 6.0); - assert_eq!(*tensor.get(&coord![1, 0]).unwrap(), 7.0); - assert_eq!(*tensor.get(&coord![1, 1]).unwrap(), 8.0); + assert_eq!(*tensor.get(&coord![0, 0].unwrap()).unwrap(), 5.0); + assert_eq!(*tensor.get(&coord![0, 1].unwrap()).unwrap(), 6.0); + assert_eq!(*tensor.get(&coord![1, 0].unwrap()).unwrap(), 7.0); + assert_eq!(*tensor.get(&coord![1, 1].unwrap()).unwrap(), 8.0); } #[test] @@ -654,9 +654,9 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = Tensor::new(&shape, &data).unwrap(); - assert!(tensor.get(&coord![2, 0]).is_err()); - assert!(tensor.get(&coord![0, 2]).is_err()); - assert!(tensor.get(&coord![2, 2]).is_err()); + assert!(tensor.get(&coord![2, 0].unwrap()).is_err()); + assert!(tensor.get(&coord![0, 2].unwrap()).is_err()); + assert!(tensor.get(&coord![2, 2].unwrap()).is_err()); } #[test] @@ -665,9 +665,9 @@ mod tests { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut tensor = Tensor::new(&shape, &data).unwrap(); - assert!(tensor.set(&coord![2, 0], 5.0).is_err()); - assert!(tensor.set(&coord![0, 2], 6.0).is_err()); - assert!(tensor.set(&coord![2, 2], 7.0).is_err()); + assert!(tensor.set(&coord![2, 0].unwrap(), 5.0).is_err()); + assert!(tensor.set(&coord![0, 2].unwrap(), 6.0).is_err()); + assert!(tensor.set(&coord![2, 2].unwrap(), 7.0).is_err()); } #[test] @@ -1241,10 +1241,10 @@ mod tests { let tensor = DynamicTensor::new(&shape, &data1).unwrap(); let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); let result = tensor + matrix; - assert_eq!(result[coord![0, 0]], 3.0); - assert_eq!(result[coord![0, 1]], 5.0); - assert_eq!(result[coord![1, 0]], 7.0); - assert_eq!(result[coord![1, 1]], 9.0); + assert_eq!(result[coord![0, 0].unwrap()], 3.0); + assert_eq!(result[coord![0, 1].unwrap()], 5.0); + assert_eq!(result[coord![1, 0].unwrap()], 7.0); + assert_eq!(result[coord![1, 1].unwrap()], 9.0); assert_eq!(result.shape(), &shape); } @@ -1256,10 +1256,10 @@ mod tests { let tensor = DynamicTensor::new(&shape, &data1).unwrap(); let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); let result = tensor - matrix; - assert_eq!(result[coord![0, 0]], 1.0); - assert_eq!(result[coord![0, 1]], 1.0); - assert_eq!(result[coord![1, 0]], 1.0); - assert_eq!(result[coord![1, 1]], 1.0); + assert_eq!(result[coord![0, 0].unwrap()], 1.0); + assert_eq!(result[coord![0, 1].unwrap()], 1.0); + assert_eq!(result[coord![1, 0].unwrap()], 1.0); + assert_eq!(result[coord![1, 1].unwrap()], 1.0); assert_eq!(result.shape(), &shape); } @@ -1271,10 +1271,10 @@ mod tests { let tensor = DynamicTensor::new(&shape, &data1).unwrap(); let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); let result = tensor * matrix; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 6.0); - assert_eq!(result[coord![1, 0]], 12.0); - assert_eq!(result[coord![1, 1]], 20.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 6.0); + assert_eq!(result[coord![1, 0].unwrap()], 12.0); + assert_eq!(result[coord![1, 1].unwrap()], 20.0); assert_eq!(result.shape(), &shape); } @@ -1286,10 +1286,10 @@ mod tests { let tensor = DynamicTensor::new(&shape, &data1).unwrap(); let matrix = DynamicMatrix::new(&shape, &data2).unwrap(); let result = tensor / matrix; - assert_eq!(result[coord![0, 0]], 2.0); - assert_eq!(result[coord![0, 1]], 2.0); - assert_eq!(result[coord![1, 0]], 2.0); - assert_eq!(result[coord![1, 1]], 2.0); + assert_eq!(result[coord![0, 0].unwrap()], 2.0); + assert_eq!(result[coord![0, 1].unwrap()], 2.0); + assert_eq!(result[coord![1, 0].unwrap()], 2.0); + assert_eq!(result[coord![1, 1].unwrap()], 2.0); assert_eq!(result.shape(), &shape); } diff --git a/src/vector.rs b/src/vector.rs index 1974855..ee22a2d 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -83,9 +83,9 @@ impl DynamicVector { for j in 0..rhs.shape()[1] { let mut sum = T::zero(); for i in 0..self.shape()[0] { - sum = sum + self[i] * rhs[coord![i, j]]; + sum = sum + self[i] * rhs[coord![i, j].unwrap()]; } - result.set(&coord![j], sum).unwrap(); + result.set(&coord![j].unwrap(), sum).unwrap(); } DynamicVector::from_tensor(result).unwrap() } @@ -219,13 +219,13 @@ impl Index for DynamicVector { type Output = T; fn index(&self, index: usize) -> &Self::Output { - &self.tensor.get(&coord![index]).unwrap() + &self.tensor.get(&coord![index].unwrap()).unwrap() } } impl IndexMut for DynamicVector { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.tensor.get_mut(&coord![index]).unwrap() + self.tensor.get_mut(&coord![index].unwrap()).unwrap() } } @@ -326,8 +326,8 @@ mod tests { fn test_set() { let data = vec![1.0, 2.0, 3.0, 4.0]; let mut vector = DynamicVector::new(&data).unwrap(); - vector.set(&coord![2], 5.0).unwrap(); - assert_eq!(*vector.get(&coord![2]).unwrap(), 5.0); + vector.set(&coord![2].unwrap(), 5.0).unwrap(); + assert_eq!(*vector.get(&coord![2].unwrap()).unwrap(), 5.0); } #[test] @@ -415,8 +415,8 @@ mod tests { assert_eq!(result.shape(), &expected_shape); for i in 0..result.shape()[0] { for j in 0..result.shape()[1] { - let x = result.get(&coord![i, j]).unwrap(); - let y = expected_tensor.get(&coord![i, j]).unwrap(); + let x = result.get(&coord![i, j].unwrap()).unwrap(); + let y = expected_tensor.get(&coord![i, j].unwrap()).unwrap(); assert_eq!(*x, *y); } } From e4d91e1bb9ccf406eadde99f0d6fa55e40015023 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Sun, 30 Jun 2024 11:57:10 +0100 Subject: [PATCH 22/24] feat!: rename len to order --- src/coordinate.rs | 6 +++--- src/storage.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coordinate.rs b/src/coordinate.rs index cb8a703..ad03454 100644 --- a/src/coordinate.rs +++ b/src/coordinate.rs @@ -16,7 +16,7 @@ impl Coordinate { Ok(Self { indices }) } - pub fn len(&self) -> usize { + pub fn order(&self) -> usize { self.indices.len() } @@ -77,9 +77,9 @@ mod tests { use super::*; #[test] - fn test_len() { + fn test_order() { let coord = coord![1, 2, 3].unwrap(); - assert_eq!(coord.len(), 3); + assert_eq!(coord.order(), 3); } #[test] diff --git a/src/storage.rs b/src/storage.rs index fb66dfa..b51fbdf 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -16,8 +16,8 @@ impl DynamicStorage { /// For the row-wise maths see: https://bit.ly/3KQjPa3 pub fn flatten(&self, coord: &Coordinate, shape: &Shape) -> Result { - if coord.len() != shape.order() { - let msg = format!("incorrect order ({} vs {}).", coord.len(), shape.order()); + if coord.order() != shape.order() { + let msg = format!("incorrect order ({} vs {}).", coord.order(), shape.order()); return Err(ShapeError::new(msg.as_str())); } From d4a0faeeb84fdda774bd9e263ffeb256bb4f5ca5 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Sun, 30 Jun 2024 12:03:21 +0100 Subject: [PATCH 23/24] style: fix clippy warnings --- src/matrix.rs | 2 +- src/tensor.rs | 34 +++++++++++++++++----------------- src/vector.rs | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/matrix.rs b/src/matrix.rs index 48f21b3..215be90 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -231,7 +231,7 @@ impl Index for DynamicMatrix { type Output = T; fn index(&self, index: Coordinate) -> &Self::Output { - &self.tensor.get(&index).unwrap() + self.tensor.get(&index).unwrap() } } diff --git a/src/tensor.rs b/src/tensor.rs index 1b27979..595dc26 100644 --- a/src/tensor.rs +++ b/src/tensor.rs @@ -81,7 +81,7 @@ impl Tensor { let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { + if axes.is_empty() | remaining_dims.is_empty() { let sum: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x); return Tensor::new(&shape![1].unwrap(), &[sum]).unwrap(); } @@ -100,7 +100,7 @@ impl Tensor { } let value = *t.get(&target).unwrap() + *self.get(&indices).unwrap(); - let _ = t.set(&target, value).unwrap(); + t.set(&target, value).unwrap(); } } @@ -119,7 +119,7 @@ impl Tensor { result }) .collect(); - let n = if removing_dims_t.len() != 0 { + let n = if !removing_dims_t.is_empty() { removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x) } else { let mut sum = T::zero(); @@ -144,7 +144,7 @@ impl Tensor { }) .collect(); - let n = if removing_dims_t.len() != 0 { + let n = if !removing_dims_t.is_empty() { removing_dims_t.iter().fold(T::one(), |acc, x| acc * *x) } else { let mut sum = T::zero(); @@ -167,7 +167,7 @@ impl Tensor { let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { + if axes.is_empty() | remaining_dims.is_empty() { let avg: T = self.data.iter().fold(T::zero(), |acc, x| acc + *x) / n; let var: T = self .data @@ -195,7 +195,7 @@ impl Tensor { let centered = *self.get(&indices).unwrap() - *mean.get(&target).unwrap(); let value = *t.get(&target).unwrap() + centered * centered; - let _ = t.set(&target, value).unwrap(); + t.set(&target, value).unwrap(); } } @@ -216,7 +216,7 @@ impl Tensor { let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { + if axes.is_empty() | remaining_dims.is_empty() { let min: T = self .data .iter() @@ -268,7 +268,7 @@ impl Tensor { let removing_dims = axes.iter().map(|&i| self.shape[i]).collect::>(); // We resolve to a scalar value - if axes.is_empty() | (remaining_dims.len() == 0) { + if axes.is_empty() | remaining_dims.is_empty() { let max: T = self .data .iter() @@ -326,7 +326,7 @@ impl Tensor { pub fn pow(&self, power: T) -> Tensor { let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone().powf(power); + result.data[i] = self.data[i].powf(power); } result } @@ -339,7 +339,7 @@ impl Mul for Tensor { fn mul(self, rhs: T) -> Tensor { let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() * rhs; + result.data[i] = self.data[i] * rhs; } result } @@ -352,7 +352,7 @@ impl Add for Tensor { fn add(self, rhs: T) -> Tensor { let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() + rhs; + result.data[i] = self.data[i] + rhs; } result } @@ -366,7 +366,7 @@ impl Add> for Tensor { assert!(self.shape == rhs.shape); let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() + rhs.data[i].clone(); + result.data[i] = self.data[i] + rhs.data[i]; } result } @@ -395,7 +395,7 @@ impl Sub for Tensor { fn sub(self, rhs: T) -> Tensor { let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() - rhs; + result.data[i] = self.data[i] - rhs; } result } @@ -409,7 +409,7 @@ impl Sub> for Tensor { assert!(self.shape == rhs.shape); let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() - rhs.data[i].clone(); + result.data[i] = self.data[i] - rhs.data[i]; } result } @@ -439,7 +439,7 @@ impl Mul> for Tensor { assert!(self.shape == rhs.shape); let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() * rhs.data[i].clone(); + result.data[i] = self.data[i] * rhs.data[i]; } result } @@ -468,7 +468,7 @@ impl Div for Tensor { fn div(self, rhs: T) -> Tensor { let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() / rhs; + result.data[i] = self.data[i] / rhs; } result } @@ -482,7 +482,7 @@ impl Div> for Tensor { assert!(self.shape == rhs.shape); let mut result = Tensor::zeros(&self.shape); for i in 0..self.size() { - result.data[i] = self.data[i].clone() / rhs.data[i].clone(); + result.data[i] = self.data[i] / rhs.data[i]; } result } diff --git a/src/vector.rs b/src/vector.rs index ee22a2d..ff2b939 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -219,7 +219,7 @@ impl Index for DynamicVector { type Output = T; fn index(&self, index: usize) -> &Self::Output { - &self.tensor.get(&coord![index].unwrap()).unwrap() + self.tensor.get(&coord![index].unwrap()).unwrap() } } From 0df55fe3ed1b75e90c09e76243e1cb9a5fdb59a4 Mon Sep 17 00:00:00 2001 From: Angus Stewart Date: Sun, 30 Jun 2024 12:04:19 +0100 Subject: [PATCH 24/24] build: remove license-file to remove build warning --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8c4e112..e0fae0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2021" description = "A tensor library for scientific computing in Rust" license = "MIT" -license-file = "LICENSE" homepage = "https://github.com/Rust-Scientific-Computing/feotensor" repository = "https://github.com/Rust-Scientific-Computing/feotensor"