Skip to main content

nalgebra_sparse/ops/
mod.rs

1//! Sparse matrix arithmetic operations.
2//!
3//! This module contains a number of routines for sparse matrix arithmetic. These routines are
4//! primarily intended for "expert usage". Most users should prefer to use standard
5//! `std::ops` operations for simple and readable code when possible. The routines provided here
6//! offer more control over allocation, and allow fusing some low-level operations for higher
7//! performance.
8//!
9//! The available operations are organized by backend. Currently, only the [`serial`] backend
10//! is available. In the future, backends that expose parallel operations may become available.
11//! All `std::ops` implementations will remain single-threaded and powered by the
12//! `serial` backend.
13//!
14//! Many routines are able to implicitly transpose matrices involved in the operation.
15//! For example, the routine [`spadd_csr_prealloc`](serial::spadd_csr_prealloc) performs the
16//! operation `C <- beta * C + alpha * op(A)`. Here `op(A)` indicates that the matrix `A` can
17//! either be used as-is or transposed. The notation `op(A)` is represented in code by the
18//! [`Op`] enum.
19//!
20//! # Available `std::ops` implementations
21//!
22//! ## Binary operators
23//!
24//! The below table summarizes the currently supported binary operators between matrices.
25//! In general, binary operators between sparse matrices are only supported if both matrices
26//! are stored in the same format. All supported binary operators are implemented for
27//! all four combinations of values and references.
28//!
29//! <table>
30//!     <tr>
31//!         <th>LHS (down) \ RHS (right)</th>
32//!         <th>COO</th>
33//!         <th>CSR</th>
34//!         <th>CSC</th>
35//!         <th>Dense</th>
36//!     </tr>
37//!     <tr>
38//!         <th>COO</th>
39//!         <td></td>
40//!         <td></td>
41//!         <td></td>
42//!         <td></td>
43//!     </tr>
44//!     <tr>
45//!         <th>CSR</th>
46//!         <td></td>
47//!         <td>+ - *</td>
48//!         <td></td>
49//!         <td>*</td>
50//!     </tr>
51//!     <tr>
52//!         <th>CSC</th>
53//!         <td></td>
54//!         <td></td>
55//!         <td>+ - *</td>
56//!         <td>*</td>
57//!     </tr>
58//!     <tr>
59//!         <th>Dense</th>
60//!         <td></td>
61//!         <td></td>
62//!         <td></td>
63//!         <td>+ - *</td>
64//!     </tr>
65//! </table>
66//!
67//! As can be seen from the table, only `CSR * Dense` and `CSC * Dense` are supported.
68//! The other way around, i.e. `Dense * CSR` and `Dense * CSC` are not implemented.
69//!
70//! Additionally, [CsrMatrix](`crate::csr::CsrMatrix`) and [CscMatrix](`crate::csc::CscMatrix`)
71//! support multiplication with scalars, in addition to division by a scalar.
72//! Note that only `Matrix * Scalar` works in a generic context, although `Scalar * Matrix`
73//! has been implemented for many of the built-in arithmetic types. This is due to a fundamental
74//! restriction of the Rust type system. Therefore, in generic code you will need to always place
75//! the matrix on the left-hand side of the multiplication.
76//!
77//! ## Unary operators
78//!
79//! The following table lists currently supported unary operators.
80//!
81//! | Format   | AddAssign\<Matrix\> | MulAssign\<Matrix\> | MulAssign\<Scalar\> | Neg    |
82//! | -------- | -----------------   | -----------------   | ------------------- | ------ |
83//! | COO      |                     |                     |                     |        |
84//! | CSR      |                     |                     | x                   | x      |
85//! | CSC      |                     |                     | x                   | x      |
86//! |
87//! # Example usage
88//!
89//! For example, consider the case where you want to compute the expression
90//! `C <- 3.0 * C + 2.0 * A^T * B`, where `A`, `B`, `C` are matrices and `A^T` is the transpose
91//! of `A`. The simplest way to write this is:
92//!
93//! ```
94//! # use nalgebra_sparse::csr::CsrMatrix;
95//! # let a = CsrMatrix::identity(10); let b = CsrMatrix::identity(10);
96//! # let mut c = CsrMatrix::identity(10);
97//! c = 3.0 * c + 2.0 * a.transpose() * b;
98//! ```
99//! This is simple and straightforward to read, and therefore the recommended way to implement
100//! it. However, if you have determined that this is a performance bottleneck of your application,
101//! it may be possible to speed things up. First, let's see what's going on here. The `std`
102//! operations are evaluated eagerly. This means that the following steps take place:
103//!
104//! 1. Evaluate `let c_temp = 3.0 * c`. This requires scaling all values of the matrix.
105//! 2. Evaluate `let a_t = a.transpose()` into a new temporary matrix.
106//! 3. Evaluate `let a_t_b = a_t * b` into a new temporary matrix.
107//! 4. Evaluate `let a_t_b_scaled = 2.0 * a_t_b`. This requires scaling all values of the matrix.
108//! 5. Evaluate `c = c_temp + a_t_b_scaled`.
109//!
110//! An alternative way to implement this expression (here using CSR matrices) is:
111//!
112//! ```
113//! # use nalgebra_sparse::csr::CsrMatrix;
114//! # let a = CsrMatrix::identity(10); let b = CsrMatrix::identity(10);
115//! # let mut c = CsrMatrix::identity(10);
116//! use nalgebra_sparse::ops::{Op, serial::spmm_csr_prealloc};
117//!
118//! // Evaluate the expression `c <- 3.0 * c + 2.0 * a^T * b
119//! spmm_csr_prealloc(3.0, &mut c, 2.0, Op::Transpose(&a), Op::NoOp(&b))
120//!     .expect("We assume that the pattern of C is able to accommodate the result.");
121//! ```
122//! Compared to the simpler example, this snippet is harder to read, but it calls a single
123//! computational kernel that avoids many of the intermediate steps listed out before. Therefore
124//! directly calling kernels may sometimes lead to better performance. However, this should
125//! always be verified by performance profiling!
126
127mod impl_std_ops;
128pub mod serial;
129
130/// Determines whether a matrix should be transposed in a given operation.
131///
132/// See the [module-level documentation](crate::ops) for the purpose of this enum.
133#[derive(Debug, Copy, Clone, PartialEq, Eq)]
134pub enum Op<T> {
135    /// Indicates that the matrix should be used as-is.
136    NoOp(T),
137    /// Indicates that the matrix should be transposed.
138    Transpose(T),
139}
140
141impl<T> Op<T> {
142    /// Returns a reference to the inner value that the operation applies to.
143    #[must_use]
144    pub fn inner_ref(&self) -> &T {
145        self.as_ref().into_inner()
146    }
147
148    /// Returns an `Op` applied to a reference of the inner value.
149    #[must_use]
150    pub fn as_ref(&self) -> Op<&T> {
151        match self {
152            Op::NoOp(obj) => Op::NoOp(obj),
153            Op::Transpose(obj) => Op::Transpose(obj),
154        }
155    }
156
157    /// Converts the underlying data type.
158    pub fn convert<U>(self) -> Op<U>
159    where
160        T: Into<U>,
161    {
162        self.map_same_op(T::into)
163    }
164
165    /// Transforms the inner value with the provided function, but preserves the operation.
166    pub fn map_same_op<U, F: FnOnce(T) -> U>(self, f: F) -> Op<U> {
167        match self {
168            Op::NoOp(obj) => Op::NoOp(f(obj)),
169            Op::Transpose(obj) => Op::Transpose(f(obj)),
170        }
171    }
172
173    /// Consumes the `Op` and returns the inner value.
174    pub fn into_inner(self) -> T {
175        match self {
176            Op::NoOp(obj) | Op::Transpose(obj) => obj,
177        }
178    }
179
180    /// Applies the transpose operation.
181    ///
182    /// This operation follows the usual semantics of transposition. In particular, double
183    /// transposition is equivalent to no transposition.
184    pub fn transposed(self) -> Self {
185        match self {
186            Op::NoOp(obj) => Op::Transpose(obj),
187            Op::Transpose(obj) => Op::NoOp(obj),
188        }
189    }
190}
191
192impl<T> From<T> for Op<T> {
193    fn from(obj: T) -> Self {
194        Self::NoOp(obj)
195    }
196}