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}