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 { row, rows, position: i });
56            }
57        }
58
59        for (i, &col) in col_indices.iter().enumerate() {
60            if col as usize >= cols {
61                return Err(SparseError::ColumnOutOfBounds { col, cols, position: i });
62            }
63        }
64
65        Ok(Self { rows, cols, row_indices, col_indices, values })
66    }
67
68    /// Number of stored nonzero entries.
69    #[must_use]
70    pub fn nnz(&self) -> usize {
71        self.values.len()
72    }
73
74    /// Create an empty COO matrix.
75    #[must_use]
76    pub fn empty(rows: usize, cols: usize) -> Self {
77        Self { rows, cols, row_indices: Vec::new(), col_indices: Vec::new(), values: Vec::new() }
78    }
79}