From caaf8c941199dbd5616dffa2986f519ae96298a8 Mon Sep 17 00:00:00 2001 From: Palash Tyagi <23239946+Magnus167@users.noreply.github.com> Date: Sun, 27 Apr 2025 01:37:42 +0100 Subject: [PATCH 1/6] Refactor test module imports --- src/frame/ops.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frame/ops.rs b/src/frame/ops.rs index f52ee9d..c35625e 100644 --- a/src/frame/ops.rs +++ b/src/frame/ops.rs @@ -96,9 +96,10 @@ impl BoolOps for Frame { // } // } +#[cfg(test)] mod tests { - use crate::frame::*; - use crate::matrix::*; + use super::*; + use crate::matrix::Matrix; #[test] fn test_series_ops() { From 38c5c284544282c804803934f14d543d9b096aa9 Mon Sep 17 00:00:00 2001 From: Palash Tyagi <23239946+Magnus167@users.noreply.github.com> Date: Thu, 1 May 2025 23:10:51 +0100 Subject: [PATCH 2/6] Enhance matrix assertions for row and column operations to allow 0-row matrices with empty columns and improve error messages for index out of bounds. --- src/matrix/mat.rs | 62 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/matrix/mat.rs b/src/matrix/mat.rs index 02a042b..07a3d3d 100644 --- a/src/matrix/mat.rs +++ b/src/matrix/mat.rs @@ -14,7 +14,11 @@ impl Matrix { let cols = cols_data.len(); assert!(cols > 0, "need at least one column"); let rows = cols_data[0].len(); - assert!(rows > 0, "need at least one row"); + // Allow 0-row matrices if columns are empty, but not 0-col matrices if rows > 0 + assert!( + rows > 0 || cols == 0, + "need at least one row if columns exist" + ); for (i, col) in cols_data.iter().enumerate().skip(1) { assert!( col.len() == rows, @@ -32,8 +36,14 @@ impl Matrix { } pub fn from_vec(data: Vec, rows: usize, cols: usize) -> Self { - assert!(rows > 0, "need at least one row"); - assert!(cols > 0, "need at least one column"); + assert!( + rows > 0 || cols == 0, + "need at least one row if columns exist" + ); + assert!( + cols > 0 || rows == 0, + "need at least one column if rows exist" + ); assert_eq!(data.len(), rows * cols, "data length mismatch"); Matrix { rows, cols, data } } @@ -67,11 +77,23 @@ impl Matrix { #[inline] pub fn column(&self, c: usize) -> &[T] { + assert!( + c < self.cols, + "column index {} out of bounds for {} columns", + c, + self.cols + ); let start = c * self.rows; &self.data[start..start + self.rows] } #[inline] pub fn column_mut(&mut self, c: usize) -> &mut [T] { + assert!( + c < self.cols, + "column index {} out of bounds for {} columns", + c, + self.cols + ); let start = c * self.rows; &mut self.data[start..start + self.rows] } @@ -90,8 +112,16 @@ impl Matrix { /// Swaps two columns in the matrix. pub fn swap_columns(&mut self, c1: usize, c2: usize) { assert!( - c1 < self.cols && c2 < self.cols, - "column index out of bounds" + c1 < self.cols, + "column index c1={} out of bounds for {} columns", + c1, + self.cols + ); + assert!( + c2 < self.cols, + "column index c2={} out of bounds for {} columns", + c2, + self.cols ); if c1 == c2 { // Indices are equal; no operation required @@ -132,7 +162,7 @@ impl Matrix { impl Matrix { /// Adds a column to the matrix at the specified index. pub fn add_column(&mut self, index: usize, column: Vec) { - assert!(index <= self.cols, "column index out of bounds"); + assert!(index <= self.cols, "add_column index {} out of bounds for {} columns", index, self.cols); assert_eq!(column.len(), self.rows, "column length mismatch"); for (r, value) in column.into_iter().enumerate() { @@ -143,7 +173,7 @@ impl Matrix { /// Adds a row to the matrix at the specified index. pub fn add_row(&mut self, index: usize, row: Vec) { - assert!(index <= self.rows, "row index out of bounds"); + assert!(index <= self.rows, "add_row index {} out of bounds for {} rows", index, self.rows); assert_eq!(row.len(), self.cols, "row length mismatch"); for (c, value) in row.into_iter().enumerate() { @@ -159,7 +189,14 @@ impl Index<(usize, usize)> for Matrix { #[inline] fn index(&self, (r, c): (usize, usize)) -> &T { // Validate that the requested indices are within bounds - assert!(r < self.rows && c < self.cols, "index out of bounds"); + assert!( + r < self.rows && c < self.cols, + "index out of bounds: ({}, {}) vs {}x{}", + r, + c, + self.rows, + self.cols + ); // Compute column-major offset and return reference &self.data[c * self.rows + r] } @@ -169,7 +206,14 @@ impl IndexMut<(usize, usize)> for Matrix { #[inline] fn index_mut(&mut self, (r, c): (usize, usize)) -> &mut T { // Validate that the requested indices are within bounds - assert!(r < self.rows && c < self.cols, "index out of bounds"); + assert!( + r < self.rows && c < self.cols, + "index out of bounds: ({}, {}) vs {}x{}", + r, + c, + self.rows, + self.cols + ); // Compute column-major offset and return mutable reference &mut self.data[c * self.rows + r] } From 7cf41171a8b4a431f2f4f578729ec059c35e5140 Mon Sep 17 00:00:00 2001 From: Palash Tyagi <23239946+Magnus167@users.noreply.github.com> Date: Thu, 1 May 2025 23:12:19 +0100 Subject: [PATCH 3/6] Allow creation of 0-row matrices from empty column data and improve data flattening in from_cols method. --- src/matrix/mat.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/matrix/mat.rs b/src/matrix/mat.rs index 07a3d3d..1523bf7 100644 --- a/src/matrix/mat.rs +++ b/src/matrix/mat.rs @@ -13,13 +13,15 @@ impl Matrix { pub fn from_cols(cols_data: Vec>) -> Self { let cols = cols_data.len(); assert!(cols > 0, "need at least one column"); - let rows = cols_data[0].len(); - // Allow 0-row matrices if columns are empty, but not 0-col matrices if rows > 0 + // Handle empty cols_data + let rows = cols_data.get(0).map_or(0, |c| c.len()); + // Allow 0-row matrices if columns are empty, but not 0-col matrices if rows > 0 assert!( rows > 0 || cols == 0, "need at least one row if columns exist" ); - for (i, col) in cols_data.iter().enumerate().skip(1) { + + for (i, col) in cols_data.iter().enumerate() { assert!( col.len() == rows, "col {} has len {}, expected {}", @@ -28,10 +30,8 @@ impl Matrix { rows ); } - let mut data = Vec::with_capacity(rows * cols); - for col in cols_data { - data.extend(col); - } + // Flatten column data directly + let data = cols_data.into_iter().flatten().collect(); Matrix { rows, cols, data } } From a0a551c7d90e05e096c1c5d1d57e44a85f31969f Mon Sep 17 00:00:00 2001 From: Palash Tyagi <23239946+Magnus167@users.noreply.github.com> Date: Thu, 1 May 2025 23:13:57 +0100 Subject: [PATCH 4/6] update into_vec, from_vec --- src/matrix/mat.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/matrix/mat.rs b/src/matrix/mat.rs index 1523bf7..194bb4e 100644 --- a/src/matrix/mat.rs +++ b/src/matrix/mat.rs @@ -35,6 +35,7 @@ impl Matrix { Matrix { rows, cols, data } } + /// Build from a flat Vec, assuming column-major order. pub fn from_vec(data: Vec, rows: usize, cols: usize) -> Self { assert!( rows > 0 || cols == 0, @@ -44,7 +45,19 @@ impl Matrix { cols > 0 || rows == 0, "need at least one column if rows exist" ); - assert_eq!(data.len(), rows * cols, "data length mismatch"); + if rows * cols != 0 { + // Only assert length if matrix is non-empty + assert_eq!( + data.len(), + rows * cols, + "data length mismatch: expected {}, got {}", + rows * cols, + data.len() + ); + } else { + assert!(data.is_empty(), "data must be empty for 0-sized matrix"); + } + Matrix { rows, cols, data } } @@ -56,7 +69,13 @@ impl Matrix { &mut self.data } - pub fn as_vec(&self) -> Vec { + /// Consumes the Matrix and returns its underlying data Vec. + pub fn into_vec(self) -> Vec { + self.data + } + + /// Creates a new Vec containing the matrix data (cloned). + pub fn to_vec(&self) -> Vec { self.data.clone() } From ba1e2b3d4392db4267031aeeef4b486e835f789e Mon Sep 17 00:00:00 2001 From: Palash Tyagi <23239946+Magnus167@users.noreply.github.com> Date: Thu, 1 May 2025 23:14:37 +0100 Subject: [PATCH 5/6] update imports and module docstring --- src/matrix/mat.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/matrix/mat.rs b/src/matrix/mat.rs index 194bb4e..a951c05 100644 --- a/src/matrix/mat.rs +++ b/src/matrix/mat.rs @@ -1,4 +1,6 @@ -use std::ops::{Index, IndexMut, Not}; +//! A simple column-major Matrix implementation with element-wise operations. + +use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Index, IndexMut, Mul, Not, Sub}; /// A column‑major 2D matrix of `T` #[derive(Debug, Clone, PartialEq, Eq)] From f91ddc5c4109825e8d5d1621f5c80e2a8b735051 Mon Sep 17 00:00:00 2001 From: Palash Tyagi <23239946+Magnus167@users.noreply.github.com> Date: Thu, 1 May 2025 23:56:06 +0100 Subject: [PATCH 6/6] Improve assertions and documentation for matrix operations; allow 0-row matrices from empty columns. --- src/matrix/mat.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/matrix/mat.rs b/src/matrix/mat.rs index a951c05..82acb38 100644 --- a/src/matrix/mat.rs +++ b/src/matrix/mat.rs @@ -16,7 +16,7 @@ impl Matrix { let cols = cols_data.len(); assert!(cols > 0, "need at least one column"); // Handle empty cols_data - let rows = cols_data.get(0).map_or(0, |c| c.len()); + let rows = cols_data.get(0).map_or(0, |c| c.len()); // Allow 0-row matrices if columns are empty, but not 0-col matrices if rows > 0 assert!( rows > 0 || cols == 0, @@ -89,9 +89,12 @@ impl Matrix { self.cols } + /// Get element reference (immutable). Panics on out-of-bounds. pub fn get(&self, r: usize, c: usize) -> &T { &self[(r, c)] } + + /// Get element reference (mutable). Panics on out-of-bounds. pub fn get_mut(&mut self, r: usize, c: usize) -> &mut T { &mut self[(r, c)] } @@ -107,6 +110,7 @@ impl Matrix { let start = c * self.rows; &self.data[start..start + self.rows] } + #[inline] pub fn column_mut(&mut self, c: usize) -> &mut [T] { assert!( @@ -130,7 +134,7 @@ impl Matrix { }) } - /// Swaps two columns in the matrix. + /// Swaps two columns in the matrix. Panics on out-of-bounds. pub fn swap_columns(&mut self, c1: usize, c2: usize) { assert!( c1 < self.cols, @@ -183,7 +187,7 @@ impl Matrix { impl Matrix { /// Adds a column to the matrix at the specified index. pub fn add_column(&mut self, index: usize, column: Vec) { - assert!(index <= self.cols, "add_column index {} out of bounds for {} columns", index, self.cols); + assert!(index <= self.cols,"add_column index {} out of bounds for {} columns",index,self.cols); assert_eq!(column.len(), self.rows, "column length mismatch"); for (r, value) in column.into_iter().enumerate() { @@ -194,8 +198,8 @@ impl Matrix { /// Adds a row to the matrix at the specified index. pub fn add_row(&mut self, index: usize, row: Vec) { - assert!(index <= self.rows, "add_row index {} out of bounds for {} rows", index, self.rows); - assert_eq!(row.len(), self.cols, "row length mismatch"); + assert!(index <= self.rows,"add_row index {} out of bounds for {} rows",index,self.rows); + assert_eq!(row.len(),self.cols,"row length mismatch: expected {} (cols), got {}",self.cols,row.len()); for (c, value) in row.into_iter().enumerate() { self.data.insert(c * (self.rows + 1) + index, value); @@ -261,14 +265,16 @@ impl<'a, T> MatrixRow<'a, T> { /// Specifies the axis along which to perform a reduction operation. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Axis { - /// Apply reduction along columns (vertical axis). + /// Apply reduction along columns (vertical axis). Result has 1 row. Col, - /// Apply reduction along rows (horizontal axis). + /// Apply reduction along rows (horizontal axis). Result has 1 column. Row, } +// --- Broadcasting --- + /// A trait to turn either a `Matrix` or a scalar T into a `Vec` of -/// length `rows*cols` (broadcasting the scalar). +/// length `rows*cols` (broadcasting the scalar). Used for comparisons. pub trait Broadcastable { fn to_vec(&self, rows: usize, cols: usize) -> Vec; }