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}
OSZAR »