## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/trueno/src/matrix/ops/storage.rs

    Line
    Count
    Source
    1
    //! Matrix storage and construction operations
    2
    //!
    3
    //! This module provides storage-related operations for Matrix:
    4
    //! - Constructors: `new()`, `from_vec()`, `from_slice()`, `zeros()`, `identity()`
    5
    //! - Accessors: `rows()`, `cols()`, `shape()`, `get()`, `get_mut()`, `as_slice()`
    6
    //!
    7
    //! ## Domain Separation (PMAT-018)
    8
    //!
    9
    //! Storage is separate from Algebra (arithmetic operations).
    10
    //! A matrix's memory layout is independent of its mathematical operations.
    11
    12
    use crate::{Backend, TruenoError};
    13
    14
    use super::super::Matrix;
    15
    16
    impl std::ops::Index<(usize, usize)> for Matrix<f32> {
    17
        type Output = f32;
    18
    19
    0
        fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
    20
    0
            &self.data[row * self.cols + col]
    21
    0
        }
    22
    }
    23
    24
    impl Matrix<f32> {
    25
        // =========================================================================
    26
        // Constructors
    27
        // =========================================================================
    28
    29
        /// Creates a new matrix with uninitialized values
    30
        ///
    31
        /// # Arguments
    32
        ///
    33
        /// * `rows` - Number of rows
    34
        /// * `cols` - Number of columns
    35
        ///
    36
        /// # Returns
    37
        ///
    38
        /// A new matrix with dimensions `rows x cols` containing uninitialized values
    39
        ///
    40
        /// # Example
    41
        ///
    42
        /// ```
    43
        /// use trueno::Matrix;
    44
        ///
    45
        /// let m = Matrix::new(3, 4);
    46
        /// assert_eq!(m.rows(), 3);
    47
        /// assert_eq!(m.cols(), 4);
    48
        /// ```
    49
    0
        pub fn new(rows: usize, cols: usize) -> Self {
    50
    0
            let backend = Backend::select_best();
    51
    0
            Matrix {
    52
    0
                rows,
    53
    0
                cols,
    54
    0
                data: vec![0.0; rows * cols],
    55
    0
                backend,
    56
    0
            }
    57
    0
        }
    58
    59
        /// Creates a matrix from a vector of data
    60
        ///
    61
        /// # Arguments
    62
        ///
    63
        /// * `rows` - Number of rows
    64
        /// * `cols` - Number of columns
    65
        /// * `data` - Vector containing matrix elements in row-major order
    66
        ///
    67
        /// # Errors
    68
        ///
    69
        /// Returns `InvalidInput` if `data.len() != rows * cols`
    70
        ///
    71
        /// # Example
    72
        ///
    73
        /// ```
    74
        /// use trueno::Matrix;
    75
        ///
    76
        /// let m = Matrix::from_vec(2, 2, vec![1.0, 2.0, 3.0, 4.0]).unwrap();
    77
        /// assert_eq!(m.rows(), 2);
    78
        /// assert_eq!(m.cols(), 2);
    79
        /// ```
    80
    10
        pub fn from_vec(rows: usize, cols: usize, data: Vec<f32>) -> Result<Self, TruenoError> {
    81
    10
            if data.len() != rows * cols {
    82
    0
                return Err(TruenoError::InvalidInput(format!(
    83
    0
                    "Data length {} does not match matrix dimensions {}x{} (expected {})",
    84
    0
                    data.len(),
    85
    0
                    rows,
    86
    0
                    cols,
    87
    0
                    rows * cols
    88
    0
                )));
    89
    10
            }
    90
    91
    10
            let backend = Backend::select_best();
    92
    10
            Ok(Matrix {
    93
    10
                rows,
    94
    10
                cols,
    95
    10
                data,
    96
    10
                backend,
    97
    10
            })
    98
    10
        }
    99
    100
        /// Creates a matrix from a vector with a specific backend
    101
        ///
    102
        /// This is useful for testing specific SIMD code paths.
    103
    0
        pub fn from_vec_with_backend(
    104
    0
            rows: usize,
    105
    0
            cols: usize,
    106
    0
            data: Vec<f32>,
    107
    0
            backend: Backend,
    108
    0
        ) -> Self {
    109
    0
            assert_eq!(
    110
    0
                data.len(),
    111
    0
                rows * cols,
    112
    0
                "Data length {} does not match matrix dimensions {}x{}",
    113
    0
                data.len(),
    114
                rows,
    115
                cols
    116
            );
    117
    0
            Matrix {
    118
    0
                rows,
    119
    0
                cols,
    120
    0
                data,
    121
    0
                backend,
    122
    0
            }
    123
    0
        }
    124
    125
        /// Creates a matrix from a slice by copying the data
    126
        ///
    127
        /// This is a convenience method that copies the slice into an owned vector.
    128
        /// For zero-copy scenarios, consider using the data directly with `from_vec`
    129
        /// if you already have an owned `Vec`.
    130
        ///
    131
        /// # Arguments
    132
        ///
    133
        /// * `rows` - Number of rows
    134
        /// * `cols` - Number of columns
    135
        /// * `data` - Slice containing matrix elements in row-major order
    136
        ///
    137
        /// # Errors
    138
        ///
    139
        /// Returns `InvalidInput` if `data.len() != rows * cols`
    140
        ///
    141
        /// # Example
    142
        ///
    143
        /// ```
    144
        /// use trueno::Matrix;
    145
        ///
    146
        /// let data = [1.0, 2.0, 3.0, 4.0];
    147
        /// let m = Matrix::from_slice(2, 2, &data).unwrap();
    148
        /// assert_eq!(m.get(0, 0), Some(&1.0));
    149
        /// ```
    150
    0
        pub fn from_slice(rows: usize, cols: usize, data: &[f32]) -> Result<Self, TruenoError> {
    151
    0
            Self::from_vec(rows, cols, data.to_vec())
    152
    0
        }
    153
    154
        /// Creates a matrix filled with zeros
    155
        ///
    156
        /// # Example
    157
        ///
    158
        /// ```
    159
        /// use trueno::Matrix;
    160
        ///
    161
        /// let m = Matrix::zeros(3, 3);
    162
        /// assert_eq!(m.get(1, 1), Some(&0.0));
    163
        /// ```
    164
    0
        pub fn zeros(rows: usize, cols: usize) -> Self {
    165
    0
            Matrix::new(rows, cols)
    166
    0
        }
    167
    168
        /// Creates a matrix filled with zeros using a specific backend
    169
        /// (Internal use only - reuses backend from parent matrix)
    170
    0
        pub(crate) fn zeros_with_backend(rows: usize, cols: usize, backend: Backend) -> Self {
    171
    0
            Matrix {
    172
    0
                rows,
    173
    0
                cols,
    174
    0
                data: vec![0.0; rows * cols],
    175
    0
                backend,
    176
    0
            }
    177
    0
        }
    178
    179
        /// Creates an identity matrix (square matrix with 1s on diagonal)
    180
        ///
    181
        /// # Example
    182
        ///
    183
        /// ```
    184
        /// use trueno::Matrix;
    185
        ///
    186
        /// let m = Matrix::identity(3);
    187
        /// assert_eq!(m.get(0, 0), Some(&1.0));
    188
        /// assert_eq!(m.get(0, 1), Some(&0.0));
    189
        /// assert_eq!(m.get(1, 1), Some(&1.0));
    190
        /// ```
    191
    0
        pub fn identity(n: usize) -> Self {
    192
    0
            let mut data = vec![0.0; n * n];
    193
    0
            for i in 0..n {
    194
    0
                data[i * n + i] = 1.0;
    195
    0
            }
    196
    0
            let backend = Backend::select_best();
    197
    0
            Matrix {
    198
    0
                rows: n,
    199
    0
                cols: n,
    200
    0
                data,
    201
    0
                backend,
    202
    0
            }
    203
    0
        }
    204
    205
        // =========================================================================
    206
        // Accessors
    207
        // =========================================================================
    208
    209
        /// Returns the number of rows
    210
    0
        pub fn rows(&self) -> usize {
    211
    0
            self.rows
    212
    0
        }
    213
    214
        /// Returns the number of columns
    215
    0
        pub fn cols(&self) -> usize {
    216
    0
            self.cols
    217
    0
        }
    218
    219
        /// Returns the shape as (rows, cols)
    220
    0
        pub fn shape(&self) -> (usize, usize) {
    221
    0
            (self.rows, self.cols)
    222
    0
        }
    223
    224
        /// Gets a reference to an element at (row, col)
    225
        ///
    226
        /// Returns `None` if indices are out of bounds
    227
    0
        pub fn get(&self, row: usize, col: usize) -> Option<&f32> {
    228
    0
            if row >= self.rows || col >= self.cols {
    229
    0
                None
    230
            } else {
    231
    0
                self.data.get(row * self.cols + col)
    232
            }
    233
    0
        }
    234
    235
        /// Gets a mutable reference to an element at (row, col)
    236
        ///
    237
        /// Returns `None` if indices are out of bounds
    238
    0
        pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut f32> {
    239
    0
            if row >= self.rows || col >= self.cols {
    240
    0
                None
    241
            } else {
    242
    0
                let idx = row * self.cols + col;
    243
    0
                self.data.get_mut(idx)
    244
            }
    245
    0
        }
    246
    247
        /// Returns a reference to the underlying data
    248
    0
        pub fn as_slice(&self) -> &[f32] {
    249
    0
            &self.data
    250
    0
        }
    251
    252
        /// Returns the backend used by this matrix
    253
    0
        pub fn backend(&self) -> Backend {
    254
    0
            self.backend
    255
    0
        }
    256
    }
    257
    258
    #[cfg(test)]
    259
    mod tests {
    260
        use super::*;
    261
    262
        #[test]
    263
        fn test_new_creates_zero_matrix() {
    264
            let m = Matrix::new(3, 4);
    265
            assert_eq!(m.rows(), 3);
    266
            assert_eq!(m.cols(), 4);
    267
            assert!(m.as_slice().iter().all(|&x| x == 0.0));
    268
        }
    269
    270
        #[test]
    271
        fn test_from_vec_success() {
    272
            let m = Matrix::from_vec(2, 2, vec![1.0, 2.0, 3.0, 4.0]).unwrap();
    273
            assert_eq!(m.get(0, 0), Some(&1.0));
    274
            assert_eq!(m.get(1, 1), Some(&4.0));
    275
        }
    276
    277
        #[test]
    278
        fn test_from_vec_dimension_mismatch() {
    279
            let result = Matrix::from_vec(2, 2, vec![1.0, 2.0, 3.0]);
    280
            assert!(result.is_err());
    281
        }
    282
    283
        #[test]
    284
        fn test_identity() {
    285
            let m = Matrix::identity(3);
    286
            assert_eq!(m.get(0, 0), Some(&1.0));
    287
            assert_eq!(m.get(1, 1), Some(&1.0));
    288
            assert_eq!(m.get(2, 2), Some(&1.0));
    289
            assert_eq!(m.get(0, 1), Some(&0.0));
    290
        }
    291
    292
        #[test]
    293
        fn test_index_operator() {
    294
            let m = Matrix::from_vec(2, 2, vec![1.0, 2.0, 3.0, 4.0]).unwrap();
    295
            assert_eq!(m[(0, 0)], 1.0);
    296
            assert_eq!(m[(1, 1)], 4.0);
    297
        }
    298
    299
        #[test]
    300
        fn test_get_out_of_bounds() {
    301
            let m = Matrix::new(2, 2);
    302
            assert_eq!(m.get(2, 0), None);
    303
            assert_eq!(m.get(0, 2), None);
    304
        }
    305
    306
        #[test]
    307
        fn test_get_mut() {
    308
            let mut m = Matrix::new(2, 2);
    309
            if let Some(val) = m.get_mut(1, 1) {
    310
                *val = 42.0;
    311
            }
    312
            assert_eq!(m.get(1, 1), Some(&42.0));
    313
        }
    314
    }