1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//! Sparse matrix operations
//!
//! RLST contains a complete sparse matrix algebra. Sparse matrices
//! can defined either on a single process or via MPI distributed across
//! multiple processes. Typical matrix operations such as addition, componentwise
//! operations and matrix vector products are supported for sparse matrices.
//!
//! # The default sparse matrix format
//!
//! RLST uses the CSR format for sparse matrices with `nnz` nonzero entries. A sparse matrix in this format is defined
//! by three arrays.
//!
//! - `indices`: This array contains the column indices of all nonzero elements. It is of length `nnz`.
//! - `indptr`: The entries belonging to row `i` occupy the half-open range
//! `indptr[i]..indptr[i + 1]` in `indices` and `data`. It has `1 + m`
//! elements for an `m`-row matrix, starts at zero, is nondecreasing, and ends at `nnz`.
//! - `data`: The data entries of the sparse matrix.
//!
//! CSR constructors produce a canonical representation: column indices are sorted within each
//! row, duplicate coordinates are summed, and explicit zeros are removed. Both raw
//! [`CsrMatrix::new`](crate::sparse::csr_mat::CsrMatrix::new) construction and AIJ construction
//! enforce this construction-time invariant. [`CsrMatrix::new`](crate::sparse::csr_mat::CsrMatrix::new)
//! panics if the pointer length, pointer endpoints, pointer ordering, data/index lengths, or column
//! bounds are invalid. AIJ construction requires equally long slices and in-bounds row and column
//! indices. Mutable entry access can subsequently introduce a stored zero; `map_stored` applies its
//! function to every currently stored entry and filters zero results when the mapped matrix is assembled.
//!
//! ```
//! use rlst::{FromAij, Nonzeros};
//! use rlst::sparse::csr_mat::CsrMatrix;
//!
//! // Duplicates are summed, rows are sorted, and explicit zeros are discarded.
//! let matrix = CsrMatrix::from_aij(
//! [2, 3],
//! &[1, 0, 0, 0],
//! &[2, 1, 1, 2],
//! &[4.0, 2.0, 3.0, 0.0],
//! );
//! assert_eq!(matrix.nnz(), 2);
//! assert_eq!(matrix.indptr().data().unwrap(), &[0, 1, 2]);
//! assert_eq!(matrix.indices().data().unwrap(), &[1, 2]);
//! assert_eq!(matrix.data().data().unwrap(), &[5.0, 4.0]);
//! ```
//!
//! On a single node the [CsrMatrix](crate::sparse::csr_mat::CsrMatrix) defines this structure. To initialize a new sparse
//! matrix the easiest is to use the [CsrMatrix::from_aij](crate::traits::FromAij::from_aij) method. It creates a sparse
//! matrix from a `rows`, `cols`, and `data` array. Repeated entries for the same row/col index are summed up in the sparse matrix assembly.
//! Zero entries are filtered out. The following example creates a sparse matrix `sparse_mat` and multiplies it with a vector `x`.
//!
//! ```
//! # use rlst;
//! # use rlst::sparse::csr_mat::CsrMatrix;
//! # extern crate blas_src;
//! use rlst::FromAij;
//! let rows: Vec<usize> = vec![1, 4, 4];
//! let cols: Vec<usize> = vec![2, 5, 6];
//! let data: Vec<f64> = vec![1.0, 2.0, 3.0];
//!
//! let shape = [8, 13];
//! let sparse_mat = CsrMatrix::from_aij(shape, &rows, &cols, &data);
//!
//! let mut x = rlst::DynArray::<f64, 1>::from_shape([shape[1]]);
//! x.fill_from_seed_equally_distributed(0);
//!
//! let y = rlst::dot!(sparse_mat, x);
//! # let expected = rlst::dot_blas!(sparse_mat.todense(), x);
//!
//! # rlst::assert_array_relative_eq!(y, expected, 1E-10);
//! ```
//!
//! # Matrix-vector products
//!
//! The `CsrMatrix` type in RLST supports the [`AsMatrixApply`](crate::AsMatrixApply) trait for
//! multiplication with one- and two-dimensional dense arrays. The [`dot!`](crate::dot) macro
//! allocates the result of a sparse matrix-vector or matrix-matrix product. Its inner dimension
//! must equal the sparse matrix column count.
//!
//! ```
//! # use rlst;
//! # use rlst::sparse::csr_mat::CsrMatrix;
//! # use rlst::FromAij;
//! # extern crate blas_src;
//! # let rows: Vec<usize> = vec![1, 4, 4];
//! # let cols: Vec<usize> = vec![2, 5, 6];
//! # let data: Vec<f64> = vec![1.0, 2.0, 3.0];
//! # let shape = [8, 13];
//! # let sparse_mat = CsrMatrix::from_aij(shape, &rows, &cols, &data);
//! # let mut x = rlst::DynArray::<f64, 1>::from_shape([shape[1]]);
//! # x.fill_from_seed_equally_distributed(0);
//! let y = rlst::dot!(sparse_mat, x);
//! # let expected = rlst::dot_blas!(sparse_mat.todense(), x);
//! # rlst::assert_array_relative_eq!(y, expected, 1E-10);
//! ```
//!
//! # Componentwise operations on sparse matrices
//!
//! Sparse matrices support addition, subtraction, scalar multiplication,
//! componentwise matrix multiplication, and zero-preserving unary operations
//! such as [Abs](crate::Abs), [Square](crate::Square), [Sqrt](crate::Sqrt), and
//! [Sin](crate::Sin). Binary matrix operations require equal shapes.
//!
//! Functions that do not preserve implicit zero (for example `exp`, `cos`, and
//! `recip`) deliberately do not implement the ordinary numerical traits for
//! sparse matrices. Use
//! [`SparseMatOpIterator::map_stored`](crate::sparse::mat_operations::SparseMatOpIterator::map_stored)
//! when applying such a function only to explicitly stored entries is intended.
//! Sparse matrix/matrix division is also unsupported because division involving
//! implicit zeros generally cannot be represented by a sparse result.
//!
//! To access componentwise operations each sparse matrix supports the [op](crate::sparse::csr_mat::CsrMatrix::op). This returns a
//! [SparseMatOpIterator](crate::sparse::mat_operations::SparseMatOpIterator). Componentwise sparse matrix operations are defined
//! through this iterator. Operations such as adding two sparse matrices or componentwise multiplication of two sparse matrices
//! are executed by merging the corresponding iterators. The following code gives an example.
//!
//!
//! ```
//! # use rlst;
//! # use rlst::sparse::csr_mat::CsrMatrix;
//! use rlst::FromAij;
//! use std::ops::Mul;
//! let rows1: Vec<usize> = vec![1, 4, 4];
//! let cols1: Vec<usize> = vec![2, 5, 6];
//! let data1: Vec<f64> = vec![1.0, 2.0, 3.0];
//!
//! let rows2: Vec<usize> = vec![2, ];
//! let cols2: Vec<usize> = vec![2, ];
//! let data2: Vec<f64> = vec![1.0];
//!
//!
//! let shape = [8, 13];
//! let a = CsrMatrix::from_aij(shape, &rows1, &cols1, &data1);
//! let b = CsrMatrix::from_aij(shape, &rows2, &cols2, &data2);
//!
//! let c = (5.0 * a.op().map_stored(f64::cosh) + b.op()).into_csr();
//!
//! ```
//! In the above example we apply `cosh` to each stored element in `a`, multiply the result by `5.0` and add `b`.
//! The result is then turned back into a sparse matrix. The implementation uses lazy evalution. The operations are only executed
//! when `into_csr` is called.
//!
//! # Direct sparse solves with UMFPACK
//!
//! The companion `rlst-suitesparse` crate, added in RLST 0.7, factorizes real `f64`
//! and complex `c64` CSR matrices with SuiteSparse UMFPACK. Call `factorize()` once and reuse the
//! returned numeric factorization for multiple contiguous vector right-hand sides. Matrices must
//! be nonempty and square; singularity is exposed through the factorization status.
//!
//! This integration requires a native SuiteSparse installation. UMFPACK is GPL-2.0-or-later, so
//! linking it can affect distribution licensing even though the Rust crates are dual-licensed.
//! See the companion crate's [real and complex solve tutorial](https://codeberg.org/rlst/rlst/src/branch/main/rlst-suitesparse/README.md#real-and-complex-solves)
//! for setup, factorization reuse, status handling, and `SUITESPARSE_PATH` discovery.
//!
//! # Matrix Market files
//!
//! [`write_coordinate_mm`](crate::io::matrix_market::write_coordinate_mm) writes stored entries
//! using Matrix Market's one-based coordinate indices. [`read_coordinate_mm`](crate::io::matrix_market::read_coordinate_mm)
//! accepts general real or complex coordinate files and assembles duplicates with the same CSR
//! canonicalization described above. Dense [`write_array_mm`](crate::io::matrix_market::write_array_mm)
//! and [`read_array_mm`](crate::io::matrix_market::read_array_mm) use Matrix Market column-major
//! record order, regardless of the source array's internal layout. Readers reject unsupported
//! integer and symmetric variants, malformed records, count mismatches, and out-of-range indices
//! with an [`RlstError`](crate::RlstError).
//!
//! # Distributed multiplication
//!
//! With the `mpi` feature, `dot!(a, x)` also accepts a distributed CSR matrix and
//! a distributed vector or matrix. The input must share `a.domain_layout()`;
//! the result shares `a.range_layout()` and owns its local dense data. Matrix
//! inputs retain their number of RHS columns. Borrowed views and lazy distributed
//! expressions are supported. The equivalent method call is `a.dot(&x)`.
//!
//! Every rank in the matrix communicator must call the operation, even with no
//! local rows or a globally empty input. Input layouts, local row counts, and
//! agreement on RHS dimensions are checked collectively before ghost exchange.
//!
//! ```no_run
//! # #[cfg(feature = "mpi")]
//! # {
//! use std::rc::Rc;
//! use rlst::{FromAijDistributed, distributed_tools::IndexLayout};
//! use rlst::sparse::distributed_csr_mat::DistributedCsrMatrix;
//! let universe = mpi::initialize().unwrap();
//! let world = universe.world();
//! let layout = Rc::new(IndexLayout::from_local_counts(1, &world));
//! let row = layout.local_range().0;
//! let a = DistributedCsrMatrix::from_aij(
//! layout.clone(), layout.clone(), &[row], &[row], &[2.0_f64],
//! );
//! let mut x = rlst::dist_vec!(f64, layout.clone());
//! x.local.fill_with_value(3.0);
//! let y = rlst::dot!(a, x.r());
//! assert_eq!(y.local[[0]], 6.0);
//! # }
//! ```
//!
//! # Matrix Market round trip
//!
//! A coordinate-file round trip is:
//!
//! ```no_run
//! use rlst::{FromAij, Nonzeros};
//! use rlst::io::matrix_market::{read_coordinate_mm, write_coordinate_mm};
//! use rlst::sparse::csr_mat::CsrMatrix;
//!
//! let matrix = CsrMatrix::from_aij([2, 2], &[0, 1], &[1, 0], &[2.0_f64, 3.0]);
//! let path = std::env::temp_dir().join("rlst-matrix-market-example.mtx");
//! write_coordinate_mm(&matrix, path.to_str().unwrap())?;
//! let restored = read_coordinate_mm::<f64>(path.to_str().unwrap())?;
//! assert_eq!(restored.nnz(), 2);
//! std::fs::remove_file(path)?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```