nalgebra_sparse/lib.rs
1//! Sparse matrices and algorithms for [nalgebra](https://www.nalgebra.org).
2//!
3//! This crate extends `nalgebra` with sparse matrix formats and operations on sparse matrices.
4//!
5//! ## Goals
6//! The long-term goals for this crate are listed below.
7//!
8//! - Provide proven sparse matrix formats in an easy-to-use and idiomatic Rust API that
9//! naturally integrates with `nalgebra`.
10//! - Provide additional expert-level APIs for fine-grained control over operations.
11//! - Integrate well with external sparse matrix libraries.
12//! - Provide native Rust high-performance routines, including parallel matrix operations.
13//!
14//! ## Highlighted current features
15//!
16//! - [CSR](csr::CsrMatrix), [CSC](csc::CscMatrix) and [COO](coo::CooMatrix) formats, and
17//! [conversions](`convert`) between them.
18//! - Common arithmetic operations are implemented. See the [`ops`] module.
19//! - Sparsity patterns in CSR and CSC matrices are explicitly represented by the
20//! [SparsityPattern](pattern::SparsityPattern) type, which encodes the invariants of the
21//! associated index data structures.
22//! - [Matrix market format support](`io`) when the `io` feature is enabled.
23//! - [proptest strategies](`proptest`) for sparse matrices when the feature
24//! `proptest-support` is enabled.
25//! - [matrixcompare support](https://crates.io/crates/matrixcompare) for effortless
26//! (approximate) comparison of matrices in test code (requires the `compare` feature).
27//!
28//! ## Current state
29//!
30//! The library is in an early, but usable state. The API has been designed to be extensible,
31//! but breaking changes will be necessary to implement several planned features. While it is
32//! backed by an extensive test suite, it has yet to be thoroughly battle-tested in real
33//! applications. Moreover, the focus so far has been on correctness and API design, with little
34//! focus on performance. Future improvements will include incremental performance enhancements.
35//!
36//! Current limitations:
37//!
38//! - Limited or no availability of sparse system solvers.
39//! - Limited support for complex numbers. Currently only arithmetic operations that do not
40//! rely on particular properties of complex numbers, such as e.g. conjugation, are
41//! supported.
42//! - No integration with external libraries.
43//!
44//! # Usage
45//!
46//! Add the following to your `Cargo.toml` file:
47//!
48//! ```toml
49//! [dependencies]
50//! nalgebra_sparse = "0.1"
51//! ```
52//!
53//! # Supported matrix formats
54//!
55//! | Format | Notes |
56//! | ------------------------|--------------------------------------------- |
57//! | [COO](`coo::CooMatrix`) | Well-suited for matrix construction. <br /> Ill-suited for algebraic operations. |
58//! | [CSR](`csr::CsrMatrix`) | Immutable sparsity pattern, suitable for algebraic operations. <br /> Fast row access. |
59//! | [CSC](`csc::CscMatrix`) | Immutable sparsity pattern, suitable for algebraic operations. <br /> Fast column access. |
60//!
61//! What format is best to use depends on the application. The most common use case for sparse
62//! matrices in science is the solution of sparse linear systems. Here we can differentiate between
63//! two common cases:
64//!
65//! - Direct solvers. Typically, direct solvers take their input in CSR or CSC format.
66//! - Iterative solvers. Many iterative solvers require only matrix-vector products,
67//! for which the CSR or CSC formats are suitable.
68//!
69//! The [COO](coo::CooMatrix) format is primarily intended for matrix construction.
70//! A common pattern is to use COO for construction, before converting to CSR or CSC for use
71//! in a direct solver or for computing matrix-vector products in an iterative solver.
72//! Some high-performance applications might also directly manipulate the CSR and/or CSC
73//! formats.
74//!
75//! # Example: COO -> CSR -> matrix-vector product
76//!
77//! ```
78//! use nalgebra_sparse::{coo::CooMatrix, csr::CsrMatrix};
79//! use nalgebra::{DMatrix, DVector};
80//! use matrixcompare::assert_matrix_eq;
81//!
82//! // The dense representation of the matrix
83//! let dense = DMatrix::from_row_slice(3, 3,
84//! &[1.0, 0.0, 3.0,
85//! 2.0, 0.0, 1.3,
86//! 0.0, 0.0, 4.1]);
87//!
88//! // Build the equivalent COO representation. We only add the non-zero values
89//! let mut coo = CooMatrix::new(3, 3);
90//! // We can add elements in any order. For clarity, we do so in row-major order here.
91//! coo.push(0, 0, 1.0);
92//! coo.push(0, 2, 3.0);
93//! coo.push(1, 0, 2.0);
94//! coo.push(1, 2, 1.3);
95//! coo.push(2, 2, 4.1);
96//!
97//! // ... or add entire dense matrices like so:
98//! // coo.push_matrix(0, 0, &dense);
99//!
100//! // The simplest way to construct a CSR matrix is to first construct a COO matrix, and
101//! // then convert it to CSR. The `From` trait is implemented for conversions between different
102//! // sparse matrix types.
103//! // Alternatively, we can construct a matrix directly from the CSR data.
104//! // See the docs for CsrMatrix for how to do that.
105//! let csr = CsrMatrix::from(&coo);
106//!
107//! // Let's check that the CSR matrix and the dense matrix represent the same matrix.
108//! // We can use macros from the `matrixcompare` crate to easily do this, despite the fact that
109//! // we're comparing across two different matrix formats. Note that these macros are only really
110//! // appropriate for writing tests, however.
111//! assert_matrix_eq!(csr, dense);
112//!
113//! let x = DVector::from_column_slice(&[1.3, -4.0, 3.5]);
114//!
115//! // Compute the matrix-vector product y = A * x. We don't need to specify the type here,
116//! // but let's just do it to make sure we get what we expect
117//! let y: DVector<_> = &csr * &x;
118//!
119//! // Verify the result with a small element-wise absolute tolerance
120//! let y_expected = DVector::from_column_slice(&[11.8, 7.15, 14.35]);
121//! assert_matrix_eq!(y, y_expected, comp = abs, tol = 1e-9);
122//!
123//! // The above expression is simple, and gives easy to read code, but if we're doing this in a
124//! // loop, we'll have to keep allocating new vectors. If we determine that this is a bottleneck,
125//! // then we can resort to the lower level APIs for more control over the operations
126//! {
127//! use nalgebra_sparse::ops::{Op, serial::spmm_csr_dense};
128//! let mut y = y;
129//! // Compute y <- 0.0 * y + 1.0 * csr * dense. We store the result directly in `y`, without
130//! // any intermediate allocations
131//! spmm_csr_dense(0.0, &mut y, 1.0, Op::NoOp(&csr), Op::NoOp(&x));
132//! assert_matrix_eq!(y, y_expected, comp = abs, tol = 1e-9);
133//! }
134//! ```
135#![deny(
136 nonstandard_style,
137 unused,
138 missing_docs,
139 rust_2018_idioms,
140 rust_2018_compatibility,
141 future_incompatible,
142 missing_copy_implementations
143)]
144
145pub extern crate nalgebra as na;
146#[macro_use]
147#[cfg(feature = "io")]
148extern crate pest_derive;
149
150pub mod convert;
151pub mod coo;
152pub mod csc;
153pub mod csr;
154pub mod factorization;
155#[cfg(feature = "io")]
156pub mod io;
157pub mod ops;
158pub mod pattern;
159
160pub(crate) mod cs;
161pub(crate) mod utils;
162
163#[cfg(feature = "proptest-support")]
164pub mod proptest;
165
166#[cfg(feature = "compare")]
167mod matrixcompare;
168
169use num_traits::Zero;
170use std::error::Error;
171use std::fmt;
172
173pub use self::coo::CooMatrix;
174pub use self::csc::CscMatrix;
175pub use self::csr::CsrMatrix;
176
177/// Errors produced by functions that expect well-formed sparse format data.
178#[derive(Debug)]
179pub struct SparseFormatError {
180 kind: SparseFormatErrorKind,
181 // Currently we only use an underlying error for generating the `Display` impl
182 error: Box<dyn Error>,
183}
184
185impl SparseFormatError {
186 /// The type of error.
187 #[must_use]
188 pub fn kind(&self) -> &SparseFormatErrorKind {
189 &self.kind
190 }
191
192 pub(crate) fn from_kind_and_error(kind: SparseFormatErrorKind, error: Box<dyn Error>) -> Self {
193 Self { kind, error }
194 }
195
196 /// Helper functionality for more conveniently creating errors.
197 pub(crate) fn from_kind_and_msg(kind: SparseFormatErrorKind, msg: &'static str) -> Self {
198 Self::from_kind_and_error(kind, Box::<dyn Error>::from(msg))
199 }
200}
201
202/// The type of format error described by a [`SparseFormatError`].
203#[non_exhaustive]
204#[derive(Debug, Copy, Clone, PartialEq, Eq)]
205pub enum SparseFormatErrorKind {
206 /// Indicates that the index data associated with the format contains at least one index
207 /// out of bounds.
208 IndexOutOfBounds,
209
210 /// Indicates that the provided data contains at least one duplicate entry, and the
211 /// current format does not support duplicate entries.
212 DuplicateEntry,
213
214 /// Indicates that the provided data for the format does not conform to the high-level
215 /// structure of the format.
216 ///
217 /// For example, the arrays defining the format data might have incompatible sizes.
218 InvalidStructure,
219}
220
221impl fmt::Display for SparseFormatError {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 write!(f, "{}", self.error)
224 }
225}
226
227impl Error for SparseFormatError {}
228
229/// An entry in a sparse matrix.
230///
231/// Sparse matrices do not store all their entries explicitly. Therefore, entry (i, j) in the matrix
232/// can either be a reference to an explicitly stored element, or it is implicitly zero.
233#[derive(Debug, PartialEq, Eq)]
234pub enum SparseEntry<'a, T> {
235 /// The entry is a reference to an explicitly stored element.
236 ///
237 /// Note that the naming here is a misnomer: The element can still be zero, even though it
238 /// is explicitly stored (a so-called "explicit zero").
239 NonZero(&'a T),
240 /// The entry is implicitly zero, i.e. it is not explicitly stored.
241 Zero,
242}
243
244impl<'a, T: Clone + Zero> SparseEntry<'a, T> {
245 /// Returns the value represented by this entry.
246 ///
247 /// Either clones the underlying reference or returns zero if the entry is not explicitly
248 /// stored.
249 pub fn into_value(self) -> T {
250 match self {
251 SparseEntry::NonZero(value) => value.clone(),
252 SparseEntry::Zero => T::zero(),
253 }
254 }
255}
256
257/// A mutable entry in a sparse matrix.
258///
259/// See also `SparseEntry`.
260#[derive(Debug, PartialEq, Eq)]
261pub enum SparseEntryMut<'a, T> {
262 /// The entry is a mutable reference to an explicitly stored element.
263 ///
264 /// Note that the naming here is a misnomer: The element can still be zero, even though it
265 /// is explicitly stored (a so-called "explicit zero").
266 NonZero(&'a mut T),
267 /// The entry is implicitly zero i.e. it is not explicitly stored.
268 Zero,
269}
270
271impl<'a, T: Clone + Zero> SparseEntryMut<'a, T> {
272 /// Returns the value represented by this entry.
273 ///
274 /// Either clones the underlying reference or returns zero if the entry is not explicitly
275 /// stored.
276 pub fn into_value(self) -> T {
277 match self {
278 SparseEntryMut::NonZero(value) => value.clone(),
279 SparseEntryMut::Zero => T::zero(),
280 }
281 }
282}