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}