MoCSI API Reference
Loading...
Searching...
No Matches
CsrMatrix.h
Go to the documentation of this file.
1#ifndef CSR_MATRIX_H
2#define CSR_MATRIX_H
3
4#include <algorithm>
5#include <iostream>
6#include <tuple>
7#include <valarray>
8#include <vector>
9
10#include "CoreEnums.h"
11#include "GenericMatrix.h"
12
16class StaticSparseMatrixAccessError : public std::runtime_error
17{
18 public:
20 : std::runtime_error("Accessing zero element at (" + std::to_string(row) + ","
21 + std::to_string(col) + ") in non-const context.")
22 {
23 }
24};
25
33template <typename T>
35{
36 private:
37 std::valarray<int> m_column_indices;
38 std::valarray<int> m_row_pointers;
39 int m_number_non_zeros{};
40 bool m_dynamic_creation{};
41 std::vector<std::tuple<T, int, int>> m_temporary_dynamic_create_storage{};
42
43 int findIndex(int rows, int cols) const;
44
45 public:
47 CsrSparseMatrix(const std::valarray<T>& values, const std::valarray<int>& column_indices,
48 const std::valarray<int>& row_pointers, int rows, int cols);
49
50 void registerElement(T value, int rows, int cols) override;
51 void buildAtRuntime();
52 void buildAtRuntime(int rows, int cols) override;
53
54 T& operator()(int rows, int cols) override;
55 T operator()(int rows, int cols) const override;
56
57 std::valarray<T> scalarAdd(T value) const override;
58 std::valarray<T> scalarSubtract(T value) const override;
59 std::valarray<T> scalarMultiply(T value) const override;
60 std::valarray<T> scalarDivide(T value) const override;
61
62 std::valarray<T> vectorElemAdd(const std::valarray<T>& value) const override;
63 std::valarray<T> vectorElemSubtract(const std::valarray<T>& value) const override;
64 std::valarray<T> vectorElemMultiply(const std::valarray<T>& value) const override;
65 std::valarray<T> vectorElemDivide(const std::valarray<T>& value) const override;
66
67 std::valarray<T> matrixVectorMultiplication(const std::valarray<T>& value) const override;
68
69 const MatrixType getMatrixType() override { return MatrixType::Csr; }
70
72
73 const std::vector<std::pair<int, int>>& getNonZeroElements() override
74 {
75 return this->m_non_zero_elements;
76 };
77 void updateNonZeroElements() override;
78
79 ~CsrSparseMatrix() override = default;
80};
81
86template <typename T>
88{
89}
90
102template <typename T>
104 const std::valarray<int>& column_indices,
105 const std::valarray<int>& row_pointers, int rows, int cols)
106 : GenericMatrix<T>{rows, cols, values, true},
107 m_column_indices{column_indices},
108 m_row_pointers{row_pointers}
109{
110}
111
120template <typename T>
121void CsrSparseMatrix<T>::registerElement(const T value, const int rows, const int cols)
122{
123 if (m_dynamic_creation)
124 {
125 m_temporary_dynamic_create_storage.emplace_back(value, rows, cols);
126 }
127}
128
139template <typename T>
141{
142 if (m_dynamic_creation)
143 {
144 const auto& max_row_ptr = std::max_element(
145 m_temporary_dynamic_create_storage.begin(), m_temporary_dynamic_create_storage.end(),
146 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
147 { return std::get<1>(lhs) < std::get<1>(rhs); });
148 const auto& max_col_ptr = std::max_element(
149 m_temporary_dynamic_create_storage.begin(), m_temporary_dynamic_create_storage.end(),
150 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
151 { return std::get<2>(lhs) < std::get<2>(rhs); });
152 this->m_rows = std::get<1>(*max_row_ptr) + 1;
153 this->m_cols = std::get<2>(*max_col_ptr) + 1;
154 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
155 // their max property. Sort the temporary storage by rows (ascending) and within a row by
156 // columns (ascending).
157 std::sort(m_temporary_dynamic_create_storage.begin(),
158 m_temporary_dynamic_create_storage.end(),
159 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
160 {
161 if (std::get<1>(lhs) == std::get<1>(rhs))
162 {
163 return std::get<2>(lhs) < std::get<2>(rhs);
164 }
165 return std::get<1>(lhs) < std::get<1>(rhs);
166 });
167 // Clear out duplicate entries. They can be produced when building a global matrix from
168 // smaller ones that overlap in some entries, like when using finite elements.
169 for (auto it = m_temporary_dynamic_create_storage.begin();;)
170 {
171 if (std::get<1>(*it) == std::get<1>(*(it + 1))
172 && std::get<2>(*it) == std::get<2>(*(it + 1)))
173 {
174 it = m_temporary_dynamic_create_storage.erase(it);
175 }
176 else
177 {
178 ++it;
179 }
180 if (it + 1 == m_temporary_dynamic_create_storage.end())
181 {
182 break;
183 }
184 }
185 // All non-zero entries are known, so the valarray sizes can be finalized
186 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
187 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
188 // The row pointer always has one element more that points to n+1, as to always define
189 // row_pointer[i+1].
190 m_row_pointers.resize(this->m_rows + 1);
191
192 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
193 bool is_last{false};
194 // We need the +1 on the end iterator here, because we need the case it = .end() within the
195 // if condition
196 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
197 {
198 is_last = (it == m_temporary_dynamic_create_storage.end());
199 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
200 {
201 // Set the row pointer for the current row to the element, which marks the start of
202 // that row.
203 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
204 current_row_start - m_temporary_dynamic_create_storage.begin());
206 }
207 if (is_last)
208 {
209 break;
210 }
211 }
212 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
213 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
214 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
215 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
216 m_row_pointers[this->m_rows] = this->m_matrix.size();
217
218 // After this block, the elements are all sorted by rows (ascending) and within one row by
219 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
220 // write them directly to the arrays.
221 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
222 {
223 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
224 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
225 }
226 }
227}
228
241template <typename T>
243{
244 if (m_dynamic_creation)
245 {
246 this->m_rows = rows;
247 this->m_cols = cols;
248 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
249 // their max property. Sort the temporary storage by rows (ascending) and within a row by
250 // columns (ascending).
251 std::sort(m_temporary_dynamic_create_storage.begin(),
252 m_temporary_dynamic_create_storage.end(),
253 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
254 {
255 if (std::get<1>(lhs) == std::get<1>(rhs))
256 {
257 return std::get<2>(lhs) < std::get<2>(rhs);
258 }
259 return std::get<1>(lhs) < std::get<1>(rhs);
260 });
261 // Clear out duplicate entries. They can be produced when building a global matrix from
262 // smaller ones that overlap in some entries, like when using finite elements.
263 for (auto it = m_temporary_dynamic_create_storage.begin();;)
264 {
265 if (std::get<1>(*it) == std::get<1>(*(it + 1))
266 && std::get<2>(*it) == std::get<2>(*(it + 1)))
267 {
268 it = m_temporary_dynamic_create_storage.erase(it);
269 }
270 else
271 {
272 ++it;
273 }
274 if (it + 1 == m_temporary_dynamic_create_storage.end())
275 {
276 break;
277 }
278 }
279 // All non-zero entries are known, so the valarray sizes can be finalized
280 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
281 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
282 // The last row always has entries for all FEM/FV/FD discretizations, so we can set the row
283 // pointer array size to this value + 2
284 m_row_pointers.resize(rows + 1);
285
286 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
287 bool is_last{false};
288 // We need the +1 on the end iterator here, because we need the case it = .end() within the
289 // if condition
290 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
291 {
292 is_last = (it == m_temporary_dynamic_create_storage.end());
293 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
294 {
295 // Set the row pointer for the current row to the element, which marks the start of
296 // that row.
297 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
298 current_row_start - m_temporary_dynamic_create_storage.begin());
300 }
301 if (is_last)
302 {
303 break;
304 }
305 }
306 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
307 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
308 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
309 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
310 m_row_pointers[rows] = this->m_matrix.size();
311
312 // After this block, the elements are all sorted by rows (ascending) and within one row by
313 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
314 // write them directly to the arrays.
315 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
316 {
317 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
318 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
319 }
320 }
321}
322
333template <typename T>
334int CsrSparseMatrix<T>::findIndex(const int rows, const int cols) const
335{
336 int lower_bounds{m_row_pointers[rows]};
337 int upper_bounds{m_row_pointers[rows + 1] - 1};
338 int test_index{-1};
339
340 // Use devide and conquer algorithm to find result in log(n) time
341 while (lower_bounds <= upper_bounds)
342 {
344 if (m_column_indices[test_index] < cols)
345 {
347 }
348 else if (m_column_indices[test_index] > cols)
349 {
351 }
352 else
353 {
354 return test_index;
355 }
356 }
357 // If no match was found, then return a negative value to show this.
358 return -1;
359}
360
370template <typename T>
372{
373 if (rows >= this->m_rows || cols >= this->m_cols)
374 {
375 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols);
376 }
377
378 int matrix_value_index{findIndex(rows, cols)};
379 // If the value is non-zero, is has to be in the given row, whose values are all between
380 // m_row_pointers[rows] and m_row_pointers[rows + 1]
381
382 // If _find_index returned -1, it has to be a zero value.
383 // This is technically not intended to access 0 elements in non-const context, as we would need
384 // to write it into the matrix (which schould never carry elements that are zero) If we want to
385 // allow element insertion in place (warning, this can be VERY slow), then we can replace that
386 // here. Right now it's just a placeholder
387 if (matrix_value_index == -1)
388 {
390 }
391 return this->m_matrix[matrix_value_index];
392}
393
402template <typename T>
403T CsrSparseMatrix<T>::operator()(const int rows, const int cols) const
404{
405 if (rows >= this->m_rows || cols >= this->m_cols || rows < 0 || cols < 0)
406 {
407 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols);
408 }
409
410 int matrix_value_index{findIndex(rows, cols)};
411
412 // If the value is a non-defined zero value, return zero.
413 if (matrix_value_index == -1)
414 {
415 throw static_cast<T>(0.0);
416 }
417 return this->m_matrix[matrix_value_index];
418}
419
424template <typename T>
425std::valarray<T> CsrSparseMatrix<T>::scalarAdd(const T value) const
426{
427 return this->m_matrix + value;
428}
429
435template <typename T>
436std::valarray<T> CsrSparseMatrix<T>::scalarSubtract(const T value) const
437{
438 return this->m_matrix - value;
439}
440
446template <typename T>
447std::valarray<T> CsrSparseMatrix<T>::scalarMultiply(const T value) const
448{
449 return this->m_matrix * value;
450}
451
457template <typename T>
458std::valarray<T> CsrSparseMatrix<T>::scalarDivide(const T value) const
459{
460 return this->m_matrix / value;
461}
462
467template <typename T>
468std::valarray<T> CsrSparseMatrix<T>::vectorElemAdd(const std::valarray<T>& value) const
469{
470 return this->m_matrix + value;
471}
472
478template <typename T>
479std::valarray<T> CsrSparseMatrix<T>::vectorElemSubtract(const std::valarray<T>& value) const
480{
481 return this->m_matrix - value;
482}
483
489template <typename T>
490std::valarray<T> CsrSparseMatrix<T>::vectorElemMultiply(const std::valarray<T>& value) const
491{
492 return this->m_matrix * value;
493}
494
500template <typename T>
501std::valarray<T> CsrSparseMatrix<T>::vectorElemDivide(const std::valarray<T>& value) const
502{
503 return this->m_matrix / value;
504}
505
513template <typename T>
514std::valarray<T> CsrSparseMatrix<T>::matrixVectorMultiplication(const std::valarray<T>& value) const
515{
516 std::valarray<T> result(this->m_rows);
517 int index_row_start;
518 int index_row_end;
519 for (int i{0}; i < this->m_rows; i++)
520 {
521 index_row_start = m_row_pointers[i];
522 index_row_end = m_row_pointers[i + 1];
523 for (int j{index_row_start}; j < index_row_end; j++)
524 {
525 result[i] += this->m_matrix[j] * value[m_column_indices[j]];
526 }
527 }
528 return result;
529}
530
534template <typename T>
536{
537 if (this->m_matrix.size() > 0)
538 {
539 for (int i{0}; i < m_row_pointers.size() - 1; i++)
540 {
541 std::valarray<int> column_values_in_row = m_column_indices[std::slice(
542 m_row_pointers[i], m_row_pointers[i + 1] - m_row_pointers[i], 1)];
543 int current_column_id{0};
544 for (int j{0}; j < this->m_cols; j++)
545 {
548 {
549 std::cout << this->m_matrix[m_row_pointers[i] + current_column_id] << ' ';
551 }
552 else
553 {
554 std::cout << "0" << ' ';
555 }
556 }
557 std::cout << "\n";
558 }
559 }
560 else
561 {
562 std::cout << "Matrix is empty\n";
563 }
564}
565
571template <typename T>
573{
574 for (const auto& position_tuple : m_temporary_dynamic_create_storage)
575 {
576 this->m_non_zero_elements.emplace_back(std::get<1>(position_tuple),
577 std::get<2>(position_tuple));
578 }
579}
580#endif
MatrixType
Definition CoreEnums.h:33
Concrete implementation of a matrix class representing a Compressed Sparse Rows (CSR) matrix....
Definition CsrMatrix.h:35
void registerElement(T value, int rows, int cols) override
Registers the element and it's initial value within a COO style vector for dynamic creation.
Definition CsrMatrix.h:121
T & operator()(int rows, int cols) override
Parentheses operator to allow access to the non-zero matrix values as Matrix(row,column)....
Definition CsrMatrix.h:371
void printMatrixInDenseFormat()
Prints the entire matrix with zero values.
Definition CsrMatrix.h:535
std::valarray< T > scalarSubtract(T value) const override
Subtraction of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:436
std::valarray< T > scalarDivide(T value) const override
Division of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:458
const std::vector< std::pair< int, int > > & getNonZeroElements() override
Definition CsrMatrix.h:73
void updateNonZeroElements() override
Sets the m_non_zero_elememts parameter of the parent class.
Definition CsrMatrix.h:572
std::valarray< T > vectorElemMultiply(const std::valarray< T > &value) const override
Multiplication of a vector to the matrix values element-wise.
Definition CsrMatrix.h:490
std::valarray< T > scalarMultiply(T value) const override
Multiplication of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:447
CsrSparseMatrix()
Constructor for an empty CsrSparseMatrix object. Leaves all storage arrays empty but sets the flag to...
Definition CsrMatrix.h:87
~CsrSparseMatrix() override=default
std::valarray< T > scalarAdd(T value) const override
Addition of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:425
std::valarray< T > vectorElemSubtract(const std::valarray< T > &value) const override
Subtraction of a vector from the matrix values element-wise.
Definition CsrMatrix.h:479
std::valarray< T > matrixVectorMultiplication(const std::valarray< T > &value) const override
Matrix-vector-multiplication for sparse matrices.
Definition CsrMatrix.h:514
std::valarray< T > vectorElemAdd(const std::valarray< T > &value) const override
Addition of a vector to the matrix values element-wise.
Definition CsrMatrix.h:468
const MatrixType getMatrixType() override
Definition CsrMatrix.h:69
std::valarray< T > vectorElemDivide(const std::valarray< T > &value) const override
Division of a vector from the matrix values element-wise.
Definition CsrMatrix.h:501
void buildAtRuntime()
Builds the CSR matrix from the registered elements. It sorts all elements of the COO format vector by...
Definition CsrMatrix.h:140
Error class to throw when a module chain has circular or missing dependencies. Inherits from std::exc...
Definition ChainManager.h:28
Template generalized Matrix object that should be the base for all further implementations of a matri...
Definition GenericMatrix.h:19
std::vector< std::pair< int, int > > m_non_zero_elements
Definition GenericMatrix.h:25
Error class to throw when the matrix is accessed out of bounds. Inherits from std::exception.
Definition GenericMatrix.h:150
Error class to be thrown when a zero element is tried to be accessed as a reference.
Definition CsrMatrix.h:17
StaticSparseMatrixAccessError(const int row, const int col)
Definition CsrMatrix.h:19