nalgebra_sparse/coo.rs
1//! An implementation of the COO sparse matrix format.
2
3#[cfg(feature = "serde-serialize")]
4mod coo_serde;
5
6use crate::SparseFormatError;
7
8/// A COO representation of a sparse matrix.
9///
10/// A COO matrix stores entries in coordinate-form, that is triplets `(i, j, v)`, where `i` and `j`
11/// correspond to row and column indices of the entry, and `v` to the value of the entry.
12/// The format is of limited use for standard matrix operations. Its main purpose is to facilitate
13/// easy construction of other, more efficient matrix formats (such as CSR/COO), and the
14/// conversion between different formats.
15///
16/// # Format
17///
18/// For given dimensions `nrows` and `ncols`, the matrix is represented by three same-length
19/// arrays `row_indices`, `col_indices` and `values` that constitute the coordinate triplets
20/// of the matrix. The indices must be in bounds, but *duplicate entries are explicitly allowed*.
21/// Upon conversion to other formats, the duplicate entries may be summed together. See the
22/// documentation for the respective conversion functions.
23///
24/// # Examples
25///
26/// ```
27/// use nalgebra_sparse::{coo::CooMatrix, csr::CsrMatrix, csc::CscMatrix};
28///
29/// // Initialize a matrix with all zeros (no explicitly stored entries).
30/// let mut coo = CooMatrix::new(4, 4);
31/// // Or initialize it with a set of triplets
32/// coo = CooMatrix::try_from_triplets(4, 4, vec![1, 2], vec![0, 1], vec![3.0, 4.0]).unwrap();
33///
34/// // Push a few triplets
35/// coo.push(2, 0, 1.0);
36/// coo.push(0, 1, 2.0);
37///
38/// // Convert to other matrix formats
39/// let csr = CsrMatrix::from(&coo);
40/// let csc = CscMatrix::from(&coo);
41/// ```
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct CooMatrix<T> {
44 nrows: usize,
45 ncols: usize,
46 row_indices: Vec<usize>,
47 col_indices: Vec<usize>,
48 values: Vec<T>,
49}
50
51impl<T: na::Scalar> CooMatrix<T> {
52 /// Pushes a dense matrix into the sparse one.
53 ///
54 /// This adds the dense matrix `m` starting at the `r`th row and `c`th column
55 /// to the matrix.
56 ///
57 /// Panics
58 /// ------
59 ///
60 /// Panics if any part of the dense matrix is out of bounds of the sparse matrix
61 /// when inserted at `(r, c)`.
62 #[inline]
63 pub fn push_matrix<R: na::Dim, C: na::Dim, S: nalgebra::storage::RawStorage<T, R, C>>(
64 &mut self,
65 r: usize,
66 c: usize,
67 m: &na::Matrix<T, R, C, S>,
68 ) {
69 let block_nrows = m.nrows();
70 let block_ncols = m.ncols();
71 let max_row_with_block = r + block_nrows - 1;
72 let max_col_with_block = c + block_ncols - 1;
73 assert!(max_row_with_block < self.nrows);
74 assert!(max_col_with_block < self.ncols);
75
76 self.reserve(block_ncols * block_nrows);
77
78 for (col_idx, col) in m.column_iter().enumerate() {
79 for (row_idx, v) in col.iter().enumerate() {
80 self.row_indices.push(r + row_idx);
81 self.col_indices.push(c + col_idx);
82 self.values.push(v.clone());
83 }
84 }
85 }
86}
87
88impl<T> CooMatrix<T> {
89 /// Construct a zero COO matrix of the given dimensions.
90 ///
91 /// Specifically, the collection of triplets - corresponding to explicitly stored entries -
92 /// is empty, so that the matrix (implicitly) represented by the COO matrix consists of all
93 /// zero entries.
94 pub fn new(nrows: usize, ncols: usize) -> Self {
95 Self {
96 nrows,
97 ncols,
98 row_indices: Vec::new(),
99 col_indices: Vec::new(),
100 values: Vec::new(),
101 }
102 }
103
104 /// Construct a zero COO matrix of the given dimensions.
105 ///
106 /// Specifically, the collection of triplets - corresponding to explicitly stored entries -
107 /// is empty, so that the matrix (implicitly) represented by the COO matrix consists of all
108 /// zero entries.
109 pub fn zeros(nrows: usize, ncols: usize) -> Self {
110 Self::new(nrows, ncols)
111 }
112
113 /// Try to construct a COO matrix from the given dimensions and a collection of
114 /// (i, j, v) triplets.
115 ///
116 /// Returns an error if either row or column indices contain indices out of bounds,
117 /// or if the data arrays do not all have the same length. Note that the COO format
118 /// inherently supports duplicate entries.
119 pub fn try_from_triplets(
120 nrows: usize,
121 ncols: usize,
122 row_indices: Vec<usize>,
123 col_indices: Vec<usize>,
124 values: Vec<T>,
125 ) -> Result<Self, SparseFormatError> {
126 use crate::SparseFormatErrorKind::*;
127 if row_indices.len() != col_indices.len() {
128 return Err(SparseFormatError::from_kind_and_msg(
129 InvalidStructure,
130 "Number of row and col indices must be the same.",
131 ));
132 } else if col_indices.len() != values.len() {
133 return Err(SparseFormatError::from_kind_and_msg(
134 InvalidStructure,
135 "Number of col indices and values must be the same.",
136 ));
137 }
138
139 let row_indices_in_bounds = row_indices.iter().all(|i| *i < nrows);
140 let col_indices_in_bounds = col_indices.iter().all(|j| *j < ncols);
141
142 if !row_indices_in_bounds {
143 Err(SparseFormatError::from_kind_and_msg(
144 IndexOutOfBounds,
145 "Row index out of bounds.",
146 ))
147 } else if !col_indices_in_bounds {
148 Err(SparseFormatError::from_kind_and_msg(
149 IndexOutOfBounds,
150 "Col index out of bounds.",
151 ))
152 } else {
153 Ok(Self {
154 nrows,
155 ncols,
156 row_indices,
157 col_indices,
158 values,
159 })
160 }
161 }
162
163 /// Try to construct a COO matrix from the given dimensions and a finite iterator of
164 /// (i, j, v) triplets.
165 ///
166 /// Returns an error if either row or column indices contain indices out of bounds.
167 /// Note that the COO format inherently supports duplicate entries, but they are not
168 /// eagerly summed.
169 ///
170 /// Implementation note:
171 /// Calls try_from_triplets so each value is scanned twice.
172 pub fn try_from_triplets_iter(
173 nrows: usize,
174 ncols: usize,
175 triplets: impl IntoIterator<Item = (usize, usize, T)>,
176 ) -> Result<Self, SparseFormatError> {
177 let (row_indices, (col_indices, values)) =
178 triplets.into_iter().map(|(r, c, v)| (r, (c, v))).unzip();
179 Self::try_from_triplets(nrows, ncols, row_indices, col_indices, values)
180 }
181
182 /// An iterator over triplets (i, j, v).
183 // TODO: Consider giving the iterator a concrete type instead of impl trait...?
184 pub fn triplet_iter(&self) -> impl Iterator<Item = (usize, usize, &T)> {
185 self.row_indices
186 .iter()
187 .zip(&self.col_indices)
188 .zip(&self.values)
189 .map(|((i, j), v)| (*i, *j, v))
190 }
191
192 /// A mutable iterator over triplets (i, j, v).
193 // TODO: Consider giving the iterator a concrete type instead of impl trait...?
194 pub fn triplet_iter_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
195 self.row_indices
196 .iter()
197 .zip(&self.col_indices)
198 .zip(self.values.iter_mut())
199 .map(|((i, j), v)| (*i, *j, v))
200 }
201
202 /// Reserves capacity for COO matrix by at least `additional` elements.
203 ///
204 /// This increase the capacities of triplet holding arrays by reserving more space to avoid
205 /// frequent reallocations in `push` operations.
206 ///
207 /// ## Panics
208 ///
209 /// Panics if any of the individual allocation of triplet arrays fails.
210 ///
211 /// ## Example
212 ///
213 /// ```
214 /// # use nalgebra_sparse::coo::CooMatrix;
215 /// let mut coo = CooMatrix::new(4, 4);
216 /// // Reserve capacity in advance
217 /// coo.reserve(10);
218 /// coo.push(1, 0, 3.0);
219 /// ```
220 pub fn reserve(&mut self, additional: usize) {
221 self.row_indices.reserve(additional);
222 self.col_indices.reserve(additional);
223 self.values.reserve(additional);
224 }
225
226 /// Push a single triplet to the matrix.
227 ///
228 /// This adds the value `v` to the `i`th row and `j`th column in the matrix.
229 ///
230 /// Panics
231 /// ------
232 ///
233 /// Panics if `i` or `j` is out of bounds.
234 #[inline]
235 pub fn push(&mut self, i: usize, j: usize, v: T) {
236 assert!(i < self.nrows);
237 assert!(j < self.ncols);
238 self.row_indices.push(i);
239 self.col_indices.push(j);
240 self.values.push(v);
241 }
242
243 /// Clear all triplets from the matrix.
244 pub fn clear_triplets(&mut self) {
245 self.col_indices.clear();
246 self.row_indices.clear();
247 self.values.clear();
248 }
249
250 /// The number of rows in the matrix.
251 #[inline]
252 #[must_use]
253 pub fn nrows(&self) -> usize {
254 self.nrows
255 }
256
257 /// The number of columns in the matrix.
258 #[inline]
259 #[must_use]
260 pub fn ncols(&self) -> usize {
261 self.ncols
262 }
263
264 /// The number of explicitly stored entries in the matrix.
265 ///
266 /// This number *includes* duplicate entries. For example, if the `CooMatrix` contains duplicate
267 /// entries, then it may have a different number of non-zeros as reported by `nnz()` compared
268 /// to its CSR representation.
269 #[inline]
270 #[must_use]
271 pub fn nnz(&self) -> usize {
272 self.values.len()
273 }
274
275 /// The row indices of the explicitly stored entries.
276 #[must_use]
277 pub fn row_indices(&self) -> &[usize] {
278 &self.row_indices
279 }
280
281 /// The column indices of the explicitly stored entries.
282 #[must_use]
283 pub fn col_indices(&self) -> &[usize] {
284 &self.col_indices
285 }
286
287 /// The values of the explicitly stored entries.
288 #[must_use]
289 pub fn values(&self) -> &[T] {
290 &self.values
291 }
292
293 /// Disassembles the matrix into individual triplet arrays.
294 ///
295 /// Examples
296 /// --------
297 ///
298 /// ```
299 /// # use nalgebra_sparse::coo::CooMatrix;
300 /// let row_indices = vec![0, 1];
301 /// let col_indices = vec![1, 2];
302 /// let values = vec![1.0, 2.0];
303 /// let coo = CooMatrix::try_from_triplets(2, 3, row_indices, col_indices, values)
304 /// .unwrap();
305 ///
306 /// let (row_idx, col_idx, val) = coo.disassemble();
307 /// assert_eq!(row_idx, vec![0, 1]);
308 /// assert_eq!(col_idx, vec![1, 2]);
309 /// assert_eq!(val, vec![1.0, 2.0]);
310 /// ```
311 pub fn disassemble(self) -> (Vec<usize>, Vec<usize>, Vec<T>) {
312 (self.row_indices, self.col_indices, self.values)
313 }
314}