Skip to main content

numr/sparse/
format.rs

1//! Sparse format definitions and traits
2
3use crate::dtype::DType;
4
5/// Sparse matrix storage format
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub enum SparseFormat {
9    /// Coordinate format (COO)
10    ///
11    /// Stores explicit (row, col, value) triplets.
12    /// Best for: construction, format conversion, random access insertion
13    /// Storage: O(3 * nnz)
14    Coo,
15
16    /// Compressed Sparse Row (CSR)
17    ///
18    /// Row pointers + column indices + values.
19    /// Best for: row slicing, SpMV, most sparse operations
20    /// Storage: O(2 * nnz + nrows + 1)
21    Csr,
22
23    /// Compressed Sparse Column (CSC)
24    ///
25    /// Column pointers + row indices + values.
26    /// Best for: column slicing, transposed operations
27    /// Storage: O(2 * nnz + ncols + 1)
28    Csc,
29}
30
31impl SparseFormat {
32    /// Returns true if format is efficient for row operations
33    #[inline]
34    pub fn is_row_major(&self) -> bool {
35        matches!(self, SparseFormat::Csr)
36    }
37
38    /// Returns true if format is efficient for column operations
39    #[inline]
40    pub fn is_col_major(&self) -> bool {
41        matches!(self, SparseFormat::Csc)
42    }
43
44    /// Returns the format name as a string
45    pub fn name(&self) -> &'static str {
46        match self {
47            SparseFormat::Coo => "COO",
48            SparseFormat::Csr => "CSR",
49            SparseFormat::Csc => "CSC",
50        }
51    }
52}
53
54impl std::fmt::Display for SparseFormat {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}", self.name())
57    }
58}
59
60/// Trait for sparse storage backends
61///
62/// This trait defines the common interface for all sparse storage formats.
63/// Each format (COO, CSR, CSC) implements this trait.
64pub trait SparseStorage: Sized {
65    /// Returns the sparse format type
66    fn format(&self) -> SparseFormat;
67
68    /// Returns the shape as [nrows, ncols]
69    fn shape(&self) -> [usize; 2];
70
71    /// Returns the number of rows
72    #[inline]
73    fn nrows(&self) -> usize {
74        self.shape()[0]
75    }
76
77    /// Returns the number of columns
78    #[inline]
79    fn ncols(&self) -> usize {
80        self.shape()[1]
81    }
82
83    /// Returns the number of non-zero elements
84    fn nnz(&self) -> usize;
85
86    /// Returns the data type of values
87    fn dtype(&self) -> DType;
88
89    /// Returns the sparsity ratio (fraction of zeros)
90    ///
91    /// Sparsity = 1.0 - (nnz / total_elements)
92    #[inline]
93    fn sparsity(&self) -> f64 {
94        let total = (self.nrows() * self.ncols()) as f64;
95        if total == 0.0 {
96            0.0
97        } else {
98            1.0 - (self.nnz() as f64 / total)
99        }
100    }
101
102    /// Returns the density ratio (fraction of non-zeros)
103    ///
104    /// Density = nnz / total_elements = 1.0 - sparsity
105    #[inline]
106    fn density(&self) -> f64 {
107        1.0 - self.sparsity()
108    }
109
110    /// Returns true if the matrix is empty (no non-zeros)
111    #[inline]
112    fn is_empty(&self) -> bool {
113        self.nnz() == 0
114    }
115
116    /// Returns the memory usage in bytes (approximate)
117    fn memory_usage(&self) -> usize;
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_sparse_format_display() {
126        assert_eq!(SparseFormat::Coo.to_string(), "COO");
127        assert_eq!(SparseFormat::Csr.to_string(), "CSR");
128        assert_eq!(SparseFormat::Csc.to_string(), "CSC");
129    }
130
131    #[test]
132    fn test_format_properties() {
133        assert!(!SparseFormat::Coo.is_row_major());
134        assert!(SparseFormat::Csr.is_row_major());
135        assert!(!SparseFormat::Csc.is_row_major());
136
137        assert!(!SparseFormat::Coo.is_col_major());
138        assert!(!SparseFormat::Csr.is_col_major());
139        assert!(SparseFormat::Csc.is_col_major());
140    }
141}