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
78 int getNonZeroElementNumber() const { return this->m_matrix.size(); }
79 const std::valarray<int>& getColumnIndices() { return this->m_column_indices; }
80 const std::valarray<int>& getRowPointers() { return this->m_row_pointers; }
81 const std::valarray<T>& getMatrixElements() { return this->m_matrix; }
82 void updateNonZeroElements() override;
83
84 ~CsrSparseMatrix() override = default;
85};
86
91template <typename T>
93{
94}
95
107template <typename T>
109 const std::valarray<int>& column_indices,
110 const std::valarray<int>& row_pointers, int rows, int cols)
111 : GenericMatrix<T>{rows, cols, values, true},
112 m_column_indices{column_indices},
113 m_row_pointers{row_pointers}
114{
115}
116
125template <typename T>
126void CsrSparseMatrix<T>::registerElement(const T value, const int rows, const int cols)
127{
128 if (m_dynamic_creation)
129 {
130 m_temporary_dynamic_create_storage.emplace_back(value, rows, cols);
131 }
132}
133
144template <typename T>
146{
147 if (m_dynamic_creation)
148 {
149 const auto& max_row_ptr = std::max_element(
150 m_temporary_dynamic_create_storage.begin(), m_temporary_dynamic_create_storage.end(),
151 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
152 { return std::get<1>(lhs) < std::get<1>(rhs); });
153 const auto& max_col_ptr = std::max_element(
154 m_temporary_dynamic_create_storage.begin(), m_temporary_dynamic_create_storage.end(),
155 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
156 { return std::get<2>(lhs) < std::get<2>(rhs); });
157 this->m_rows = std::get<1>(*max_row_ptr) + 1;
158 this->m_cols = std::get<2>(*max_col_ptr) + 1;
159 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
160 // their max property. Sort the temporary storage by rows (ascending) and within a row by
161 // columns (ascending).
162 std::sort(m_temporary_dynamic_create_storage.begin(),
163 m_temporary_dynamic_create_storage.end(),
164 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
165 {
166 if (std::get<1>(lhs) == std::get<1>(rhs))
167 {
168 return std::get<2>(lhs) < std::get<2>(rhs);
169 }
170 return std::get<1>(lhs) < std::get<1>(rhs);
171 });
172 // Clear out duplicate entries. They can be produced when building a global matrix from
173 // smaller ones that overlap in some entries, like when using finite elements.
174 for (auto it = m_temporary_dynamic_create_storage.begin();;)
175 {
176 if (std::get<1>(*it) == std::get<1>(*(it + 1))
177 && std::get<2>(*it) == std::get<2>(*(it + 1)))
178 {
179 it = m_temporary_dynamic_create_storage.erase(it);
180 }
181 else
182 {
183 ++it;
184 }
185 if (it + 1 == m_temporary_dynamic_create_storage.end())
186 {
187 break;
188 }
189 }
190 // All non-zero entries are known, so the valarray sizes can be finalized
191 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
192 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
193 // The row pointer always has one element more that points to n+1, as to always define
194 // row_pointer[i+1].
195 m_row_pointers.resize(this->m_rows + 1);
196
197 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
198 bool is_last{false};
199 // We need the +1 on the end iterator here, because we need the case it = .end() within the
200 // if condition
201 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
202 {
203 is_last = (it == m_temporary_dynamic_create_storage.end());
204 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
205 {
206 // Set the row pointer for the current row to the element, which marks the start of
207 // that row.
208 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
209 current_row_start - m_temporary_dynamic_create_storage.begin());
210 // If there are empty row(s) between the entries, we need to propagate the next
211 // valid row_pointer value, so that row[i+1]-row[i] = 0 for these empty rows. As
212 // this is not calculated, we mark it with a placeholder and fill it after going
213 // through it once. Not is_last is checked first in order to not deref the end()
214 // pointer.
215 if (!is_last && std::get<1>(*it) > std::get<1>(*current_row_start) + 1)
216 {
217 for (int i{std::get<1>(*current_row_start) + 1}; i < std::get<1>(*it); i++)
218 {
219 m_row_pointers[i] = -1;
220 }
221 }
223 }
224 if (is_last)
225 {
226 break;
227 }
228 }
229 // Now that all values are calculated, we can fill in the placeholders.
230 for (int i{static_cast<int>(m_row_pointers.size()) - 1}; i > 0; i--)
231 {
232 if (m_row_pointers[i - 1] == -1)
233 {
234 m_row_pointers[i - 1] = m_row_pointers[i];
235 }
236 }
237 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
238 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
239 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
240 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
241 m_row_pointers[this->m_rows] = this->m_matrix.size();
242
243 // After this block, the elements are all sorted by rows (ascending) and within one row by
244 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
245 // write them directly to the arrays.
246 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
247 {
248 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
249 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
250 }
251 }
252}
253
266template <typename T>
268{
269 if (m_dynamic_creation)
270 {
271 this->m_rows = rows;
272 this->m_cols = cols;
273 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
274 // their max property. Sort the temporary storage by rows (ascending) and within a row by
275 // columns (ascending).
276 std::sort(m_temporary_dynamic_create_storage.begin(),
277 m_temporary_dynamic_create_storage.end(),
278 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
279 {
280 if (std::get<1>(lhs) == std::get<1>(rhs))
281 {
282 return std::get<2>(lhs) < std::get<2>(rhs);
283 }
284 return std::get<1>(lhs) < std::get<1>(rhs);
285 });
286 // Clear out duplicate entries. They can be produced when building a global matrix from
287 // smaller ones that overlap in some entries, like when using finite elements.
288 for (auto it = m_temporary_dynamic_create_storage.begin();;)
289 {
290 if (std::get<1>(*it) == std::get<1>(*(it + 1))
291 && std::get<2>(*it) == std::get<2>(*(it + 1)))
292 {
293 it = m_temporary_dynamic_create_storage.erase(it);
294 }
295 else
296 {
297 ++it;
298 }
299 if (it + 1 == m_temporary_dynamic_create_storage.end())
300 {
301 break;
302 }
303 }
304 // All non-zero entries are known, so the valarray sizes can be finalized
305 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
306 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
307 // The last row always has entries for all FEM/FV/FD discretizations, so we can set the row
308 // pointer array size to this value + 2
309 m_row_pointers.resize(rows + 1);
310
311 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
312 bool is_last{false};
313 bool last_is_empty{false};
314 // We need the +1 on the end iterator here, because we need the case it = .end() within the
315 // if condition
316 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
317 {
318 is_last = (it == m_temporary_dynamic_create_storage.end());
319 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
320 {
321 // Set the row pointer for the current row to the element, which marks the start of
322 // that row.
323 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
324 current_row_start - m_temporary_dynamic_create_storage.begin());
325 // If there are empty row(s) between the entries, we need to propagate the next
326 // valid row_pointer value, so that row[i+1]-row[i] = 0 for these empty rows. As
327 // this is not calculated, we mark it with a placeholder and fill it after going
328 // through it once. Not is_last is checked first in order to not deref the end()
329 // pointer.
330 if (!is_last && std::get<1>(*it) > std::get<1>(*current_row_start) + 1)
331 {
332 for (int i{std::get<1>(*current_row_start) + 1}; i < std::get<1>(*it); i++)
333 {
334 m_row_pointers[i] = -1;
335 }
336 }
337 // If the last row was not the nomially last one, it is empty and needs to be
338 // treated differently.
339 if(is_last && std::get<1>(*current_row_start) != rows-1)
340 {
341 last_is_empty = true;
342 }
344 }
345 if (is_last)
346 {
347 break;
348 }
349 }
350 // Now that all values are calculated, we can fill in the placeholders.
351 if(last_is_empty)
352 {
353 m_row_pointers[rows-1] = this->m_matrix.size();
354 }
355 for (int i{static_cast<int>(m_row_pointers.size()) - 1}; i > 0; i--)
356 {
357 if (m_row_pointers[i - 1] == -1)
358 {
359 m_row_pointers[i - 1] = m_row_pointers[i];
360 }
361 }
362 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
363 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
364 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
365 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
366 m_row_pointers[rows] = this->m_matrix.size();
367
368 // After this block, the elements are all sorted by rows (ascending) and within one row by
369 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
370 // write them directly to the arrays.
371 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
372 {
373 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
374 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
375 }
376 }
377}
378
389template <typename T>
390int CsrSparseMatrix<T>::findIndex(const int rows, const int cols) const
391{
392 int lower_bounds{m_row_pointers[rows]};
393 int upper_bounds{m_row_pointers[rows + 1] - 1};
394 int test_index{-1};
395
396 // Use devide and conquer algorithm to find result in log(n) time
397 while (lower_bounds <= upper_bounds)
398 {
400 if (m_column_indices[test_index] < cols)
401 {
403 }
404 else if (m_column_indices[test_index] > cols)
405 {
407 }
408 else
409 {
410 return test_index;
411 }
412 }
413 // If no match was found, then return a negative value to show this.
414 return -1;
415}
416
426template <typename T>
428{
429 if (rows >= this->m_rows || cols >= this->m_cols)
430 {
431 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols);
432 }
433
434 int matrix_value_index{findIndex(rows, cols)};
435 // If the value is non-zero, is has to be in the given row, whose values are all between
436 // m_row_pointers[rows] and m_row_pointers[rows + 1]
437
438 // If _find_index returned -1, it has to be a zero value.
439 // This is technically not intended to access 0 elements in non-const context, as we would need
440 // to write it into the matrix (which schould never carry elements that are zero) If we want to
441 // allow element insertion in place (warning, this can be VERY slow), then we can replace that
442 // here. Right now it's just a placeholder
443 if (matrix_value_index == -1)
444 {
446 }
447 return this->m_matrix[matrix_value_index];
448}
449
458template <typename T>
459T CsrSparseMatrix<T>::operator()(const int rows, const int cols) const
460{
461 if (rows >= this->m_rows || cols >= this->m_cols || rows < 0 || cols < 0)
462 {
463 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols);
464 }
465
466 int matrix_value_index{findIndex(rows, cols)};
467
468 // If the value is a non-defined zero value, return zero.
469 if (matrix_value_index == -1)
470 {
471 throw static_cast<T>(0.0);
472 }
473 return this->m_matrix[matrix_value_index];
474}
475
480template <typename T>
481std::valarray<T> CsrSparseMatrix<T>::scalarAdd(const T value) const
482{
483 return this->m_matrix + value;
484}
485
491template <typename T>
492std::valarray<T> CsrSparseMatrix<T>::scalarSubtract(const T value) const
493{
494 return this->m_matrix - value;
495}
496
502template <typename T>
503std::valarray<T> CsrSparseMatrix<T>::scalarMultiply(const T value) const
504{
505 return this->m_matrix * value;
506}
507
513template <typename T>
514std::valarray<T> CsrSparseMatrix<T>::scalarDivide(const T value) const
515{
516 return this->m_matrix / value;
517}
518
523template <typename T>
524std::valarray<T> CsrSparseMatrix<T>::vectorElemAdd(const std::valarray<T>& value) const
525{
526 return this->m_matrix + value;
527}
528
534template <typename T>
535std::valarray<T> CsrSparseMatrix<T>::vectorElemSubtract(const std::valarray<T>& value) const
536{
537 return this->m_matrix - value;
538}
539
545template <typename T>
546std::valarray<T> CsrSparseMatrix<T>::vectorElemMultiply(const std::valarray<T>& value) const
547{
548 return this->m_matrix * value;
549}
550
556template <typename T>
557std::valarray<T> CsrSparseMatrix<T>::vectorElemDivide(const std::valarray<T>& value) const
558{
559 return this->m_matrix / value;
560}
561
569template <typename T>
570std::valarray<T> CsrSparseMatrix<T>::matrixVectorMultiplication(const std::valarray<T>& value) const
571{
572 std::valarray<T> result(this->m_rows);
573 int index_row_start;
574 int index_row_end;
575 for (int i{0}; i < this->m_rows; i++)
576 {
577 index_row_start = m_row_pointers[i];
578 index_row_end = m_row_pointers[i + 1];
579 for (int j{index_row_start}; j < index_row_end; j++)
580 {
581 result[i] += this->m_matrix[j] * value[m_column_indices[j]];
582 }
583 }
584 return result;
585}
586
590template <typename T>
592{
593 if (this->m_matrix.size() > 0)
594 {
595 for (int i{0}; i < m_row_pointers.size() - 1; i++)
596 {
597 std::valarray<int> column_values_in_row = m_column_indices[std::slice(
598 m_row_pointers[i], m_row_pointers[i + 1] - m_row_pointers[i], 1)];
599 int current_column_id{0};
600 for (int j{0}; j < this->m_cols; j++)
601 {
604 {
605 std::cout << this->m_matrix[m_row_pointers[i] + current_column_id] << ' ';
607 }
608 else
609 {
610 std::cout << "0" << ' ';
611 }
612 }
613 std::cout << "\n";
614 }
615 }
616 else
617 {
618 std::cout << "Matrix is empty\n";
619 }
620}
621
627template <typename T>
629{
630 for (const auto& position_tuple : m_temporary_dynamic_create_storage)
631 {
632 this->m_non_zero_elements.emplace_back(std::get<1>(position_tuple),
633 std::get<2>(position_tuple));
634 }
635}
636#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:126
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:427
void printMatrixInDenseFormat()
Prints the entire matrix with zero values.
Definition CsrMatrix.h:591
std::valarray< T > scalarSubtract(T value) const override
Subtraction of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:492
std::valarray< T > scalarDivide(T value) const override
Division of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:514
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:628
std::valarray< T > vectorElemMultiply(const std::valarray< T > &value) const override
Multiplication of a vector to the matrix values element-wise.
Definition CsrMatrix.h:546
std::valarray< T > scalarMultiply(T value) const override
Multiplication of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:503
const std::valarray< int > & getColumnIndices()
Definition CsrMatrix.h:79
int getNonZeroElementNumber() const
Definition CsrMatrix.h:78
CsrSparseMatrix()
Constructor for an empty CsrSparseMatrix object. Leaves all storage arrays empty but sets the flag to...
Definition CsrMatrix.h:92
~CsrSparseMatrix() override=default
const std::valarray< int > & getRowPointers()
Definition CsrMatrix.h:80
std::valarray< T > scalarAdd(T value) const override
Addition of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:481
std::valarray< T > vectorElemSubtract(const std::valarray< T > &value) const override
Subtraction of a vector from the matrix values element-wise.
Definition CsrMatrix.h:535
std::valarray< T > matrixVectorMultiplication(const std::valarray< T > &value) const override
Matrix-vector-multiplication for sparse matrices.
Definition CsrMatrix.h:570
std::valarray< T > vectorElemAdd(const std::valarray< T > &value) const override
Addition of a vector to the matrix values element-wise.
Definition CsrMatrix.h:524
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:557
void buildAtRuntime()
Builds the CSR matrix from the registered elements. It sorts all elements of the COO format vector by...
Definition CsrMatrix.h:145
const std::valarray< T > & getMatrixElements()
Definition CsrMatrix.h:81
Template generalized Matrix object that should be the base for all further implementations of a matri...
Definition GenericMatrix.h:19
std::valarray< T > m_matrix
Definition GenericMatrix.h:24
std::vector< std::pair< int, int > > m_non_zero_elements
Definition GenericMatrix.h:25
std::pair< int, int > size() const
Function that returns the size of the matrix as a pair in (rows, columns) format.
Definition GenericMatrix.h:118
int m_rows
Definition GenericMatrix.h:21
int m_cols
Definition GenericMatrix.h:22
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