Skip to main content

trueno_sparse/
coo.rs

1//! Coordinate (COO) sparse matrix format.
2//!
3//! COO is the natural construction format: store (row, col, value) triplets.
4//! Convert to CSR for efficient arithmetic operations.
5
6use crate::SparseError;
7
8/// Coordinate sparse matrix format.
9///
10/// Stores explicit (row, col, value) triplets. Ideal for incremental
11/// construction; convert to [`CsrMatrix`](crate::CsrMatrix) for arithmetic.
12///
13/// # Invariants (provable contract)
14///
15/// - All arrays have equal length
16/// - `row_indices[i] < rows` for all i
17/// - `col_indices[i] < cols` for all i
18#[derive(Debug, Clone)]
19pub struct CooMatrix<T> {
20    /// Number of rows.
21    pub rows: usize,
22    /// Number of columns.
23    pub cols: usize,
24    /// Row index for each nonzero.
25    pub row_indices: Vec<u32>,
26    /// Column index for each nonzero.
27    pub col_indices: Vec<u32>,
28    /// Value for each nonzero.
29    pub values: Vec<T>,
30}
31
32impl<T: Clone> CooMatrix<T> {
33    /// Create a new COO matrix with validation.
34    ///
35    /// # Errors
36    ///
37    /// Returns error if arrays have mismatched lengths or indices are out of bounds.
38    pub fn new(
39        rows: usize,
40        cols: usize,
41        row_indices: Vec<u32>,
42        col_indices: Vec<u32>,
43        values: Vec<T>,
44    ) -> Result<Self, SparseError> {
45        if row_indices.len() != col_indices.len() || col_indices.len() != values.len() {
46            return Err(SparseError::CooLengthMismatch {
47                row_len: row_indices.len(),
48                col_len: col_indices.len(),
49                val_len: values.len(),
50            });
51        }
52
53        for (i, &row) in row_indices.iter().enumerate() {
54            if row as usize >= rows {
55                return Err(SparseError::RowOutOfBounds {
56                    row,
57                    rows,
58                    position: i,
59                });
60            }
61        }
62
63        for (i, &col) in col_indices.iter().enumerate() {
64            if col as usize >= cols {
65                return Err(SparseError::ColumnOutOfBounds {
66                    col,
67                    cols,
68                    position: i,
69                });
70            }
71        }
72
73        Ok(Self {
74            rows,
75            cols,
76            row_indices,
77            col_indices,
78            values,
79        })
80    }
81
82    /// Number of stored nonzero entries.
83    #[must_use]
84    pub fn nnz(&self) -> usize {
85        self.values.len()
86    }
87
88    /// Create an empty COO matrix.
89    #[must_use]
90    pub fn empty(rows: usize, cols: usize) -> Self {
91        Self {
92            rows,
93            cols,
94            row_indices: Vec::new(),
95            col_indices: Vec::new(),
96            values: Vec::new(),
97        }
98    }
99}