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.
214 if (std::get<1>(*it) > std::get<1>(*current_row_start) + 1)
215 {
216 for (int i{std::get<1>(*current_row_start) + 1}; i < std::get<1>(*it); i++)
217 {
218 m_row_pointers[i] = -1;
219 }
220 }
222 }
223 if (is_last)
224 {
225 break;
226 }
227 }
228 // Now that all values are calculated, we can fill in the placeholders.
229 for (int i{static_cast<int>(m_row_pointers.size()) - 1}; i > 0; i--)
230 {
231 if (m_row_pointers[i - 1] == -1)
232 {
233 m_row_pointers[i - 1] = m_row_pointers[i];
234 }
235 }
236 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
237 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
238 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
239 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
240 m_row_pointers[this->m_rows] = this->m_matrix.size();
241
242 // After this block, the elements are all sorted by rows (ascending) and within one row by
243 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
244 // write them directly to the arrays.
245 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
246 {
247 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
248 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
249 }
250 }
251}
252
265template <typename T>
267{
268 if (m_dynamic_creation)
269 {
270 this->m_rows = rows;
271 this->m_cols = cols;
272 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
273 // their max property. Sort the temporary storage by rows (ascending) and within a row by
274 // columns (ascending).
275 std::sort(m_temporary_dynamic_create_storage.begin(),
276 m_temporary_dynamic_create_storage.end(),
277 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
278 {
279 if (std::get<1>(lhs) == std::get<1>(rhs))
280 {
281 return std::get<2>(lhs) < std::get<2>(rhs);
282 }
283 return std::get<1>(lhs) < std::get<1>(rhs);
284 });
285 // Clear out duplicate entries. They can be produced when building a global matrix from
286 // smaller ones that overlap in some entries, like when using finite elements.
287 for (auto it = m_temporary_dynamic_create_storage.begin();;)
288 {
289 if (std::get<1>(*it) == std::get<1>(*(it + 1))
290 && std::get<2>(*it) == std::get<2>(*(it + 1)))
291 {
292 it = m_temporary_dynamic_create_storage.erase(it);
293 }
294 else
295 {
296 ++it;
297 }
298 if (it + 1 == m_temporary_dynamic_create_storage.end())
299 {
300 break;
301 }
302 }
303 // All non-zero entries are known, so the valarray sizes can be finalized
304 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
305 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
306 // The last row always has entries for all FEM/FV/FD discretizations, so we can set the row
307 // pointer array size to this value + 2
308 m_row_pointers.resize(rows + 1);
309
310 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
311 bool is_last{false};
312 // We need the +1 on the end iterator here, because we need the case it = .end() within the
313 // if condition
314 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
315 {
316 is_last = (it == m_temporary_dynamic_create_storage.end());
317 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
318 {
319 // Set the row pointer for the current row to the element, which marks the start of
320 // that row.
321 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
322 current_row_start - m_temporary_dynamic_create_storage.begin());
323 // If there are empty row(s) between the entries, we need to propagate the next
324 // valid row_pointer value, so that row[i+1]-row[i] = 0 for these empty rows. As
325 // this is not calculated, we mark it with a placeholder and fill it after going
326 // through it once.
327 if (std::get<1>(*it) > std::get<1>(*current_row_start) + 1)
328 {
329 for (int i{std::get<1>(*current_row_start) + 1}; i < std::get<1>(*it); i++)
330 {
331 m_row_pointers[i] = -1;
332 }
333 }
335 }
336 if (is_last)
337 {
338 break;
339 }
340 }
341 // Now that all values are calculated, we can fill in the placeholders.
342 for (int i{static_cast<int>(m_row_pointers.size()) - 1}; i > 0; i--)
343 {
344 if (m_row_pointers[i - 1] == -1)
345 {
346 m_row_pointers[i - 1] = m_row_pointers[i];
347 }
348 }
349 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
350 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
351 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
352 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
353 m_row_pointers[rows] = this->m_matrix.size();
354
355 // After this block, the elements are all sorted by rows (ascending) and within one row by
356 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
357 // write them directly to the arrays.
358 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
359 {
360 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
361 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
362 }
363 }
364}
365
376template <typename T>
377int CsrSparseMatrix<T>::findIndex(const int rows, const int cols) const
378{
379 int lower_bounds{m_row_pointers[rows]};
380 int upper_bounds{m_row_pointers[rows + 1] - 1};
381 int test_index{-1};
382
383 // Use devide and conquer algorithm to find result in log(n) time
384 while (lower_bounds <= upper_bounds)
385 {
387 if (m_column_indices[test_index] < cols)
388 {
390 }
391 else if (m_column_indices[test_index] > cols)
392 {
394 }
395 else
396 {
397 return test_index;
398 }
399 }
400 // If no match was found, then return a negative value to show this.
401 return -1;
402}
403
413template <typename T>
415{
416 if (rows >= this->m_rows || cols >= this->m_cols)
417 {
418 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols);
419 }
420
421 int matrix_value_index{findIndex(rows, cols)};
422 // If the value is non-zero, is has to be in the given row, whose values are all between
423 // m_row_pointers[rows] and m_row_pointers[rows + 1]
424
425 // If _find_index returned -1, it has to be a zero value.
426 // This is technically not intended to access 0 elements in non-const context, as we would need
427 // to write it into the matrix (which schould never carry elements that are zero) If we want to
428 // allow element insertion in place (warning, this can be VERY slow), then we can replace that
429 // here. Right now it's just a placeholder
430 if (matrix_value_index == -1)
431 {
433 }
434 return this->m_matrix[matrix_value_index];
435}
436
445template <typename T>
446T CsrSparseMatrix<T>::operator()(const int rows, const int cols) const
447{
448 if (rows >= this->m_rows || cols >= this->m_cols || rows < 0 || cols < 0)
449 {
450 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols);
451 }
452
453 int matrix_value_index{findIndex(rows, cols)};
454
455 // If the value is a non-defined zero value, return zero.
456 if (matrix_value_index == -1)
457 {
458 throw static_cast<T>(0.0);
459 }
460 return this->m_matrix[matrix_value_index];
461}
462
467template <typename T>
468std::valarray<T> CsrSparseMatrix<T>::scalarAdd(const T value) const
469{
470 return this->m_matrix + value;
471}
472
478template <typename T>
479std::valarray<T> CsrSparseMatrix<T>::scalarSubtract(const T value) const
480{
481 return this->m_matrix - value;
482}
483
489template <typename T>
490std::valarray<T> CsrSparseMatrix<T>::scalarMultiply(const T value) const
491{
492 return this->m_matrix * value;
493}
494
500template <typename T>
501std::valarray<T> CsrSparseMatrix<T>::scalarDivide(const T value) const
502{
503 return this->m_matrix / value;
504}
505
510template <typename T>
511std::valarray<T> CsrSparseMatrix<T>::vectorElemAdd(const std::valarray<T>& value) const
512{
513 return this->m_matrix + value;
514}
515
521template <typename T>
522std::valarray<T> CsrSparseMatrix<T>::vectorElemSubtract(const std::valarray<T>& value) const
523{
524 return this->m_matrix - value;
525}
526
532template <typename T>
533std::valarray<T> CsrSparseMatrix<T>::vectorElemMultiply(const std::valarray<T>& value) const
534{
535 return this->m_matrix * value;
536}
537
543template <typename T>
544std::valarray<T> CsrSparseMatrix<T>::vectorElemDivide(const std::valarray<T>& value) const
545{
546 return this->m_matrix / value;
547}
548
556template <typename T>
557std::valarray<T> CsrSparseMatrix<T>::matrixVectorMultiplication(const std::valarray<T>& value) const
558{
559 std::valarray<T> result(this->m_rows);
560 int index_row_start;
561 int index_row_end;
562 for (int i{0}; i < this->m_rows; i++)
563 {
564 index_row_start = m_row_pointers[i];
565 index_row_end = m_row_pointers[i + 1];
566 for (int j{index_row_start}; j < index_row_end; j++)
567 {
568 result[i] += this->m_matrix[j] * value[m_column_indices[j]];
569 }
570 }
571 return result;
572}
573
577template <typename T>
579{
580 if (this->m_matrix.size() > 0)
581 {
582 for (int i{0}; i < m_row_pointers.size() - 1; i++)
583 {
584 std::valarray<int> column_values_in_row = m_column_indices[std::slice(
585 m_row_pointers[i], m_row_pointers[i + 1] - m_row_pointers[i], 1)];
586 int current_column_id{0};
587 for (int j{0}; j < this->m_cols; j++)
588 {
591 {
592 std::cout << this->m_matrix[m_row_pointers[i] + current_column_id] << ' ';
594 }
595 else
596 {
597 std::cout << "0" << ' ';
598 }
599 }
600 std::cout << "\n";
601 }
602 }
603 else
604 {
605 std::cout << "Matrix is empty\n";
606 }
607}
608
614template <typename T>
616{
617 for (const auto& position_tuple : m_temporary_dynamic_create_storage)
618 {
619 this->m_non_zero_elements.emplace_back(std::get<1>(position_tuple),
620 std::get<2>(position_tuple));
621 }
622}
623#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:414
void printMatrixInDenseFormat()
Prints the entire matrix with zero values.
Definition CsrMatrix.h:578
std::valarray< T > scalarSubtract(T value) const override
Subtraction of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:479
std::valarray< T > scalarDivide(T value) const override
Division of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:501
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:615
std::valarray< T > vectorElemMultiply(const std::valarray< T > &value) const override
Multiplication of a vector to the matrix values element-wise.
Definition CsrMatrix.h:533
std::valarray< T > scalarMultiply(T value) const override
Multiplication of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:490
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:468
std::valarray< T > vectorElemSubtract(const std::valarray< T > &value) const override
Subtraction of a vector from the matrix values element-wise.
Definition CsrMatrix.h:522
std::valarray< T > matrixVectorMultiplication(const std::valarray< T > &value) const override
Matrix-vector-multiplication for sparse matrices.
Definition CsrMatrix.h:557
std::valarray< T > vectorElemAdd(const std::valarray< T > &value) const override
Addition of a vector to the matrix values element-wise.
Definition CsrMatrix.h:511
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:544
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