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}