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(
21 static_cast<std::string>("[CsrMatrix]: Accessing zero element at (")
22 + std::to_string(row) + static_cast<std::string>(",") + std::to_string(col)
23 + static_cast<std::string>(") in non-const context."))
24 {
25 }
26};
27
35template <typename T>
37{
38 private:
39 std::valarray<int> m_column_indices;
40 std::valarray<int> m_row_pointers;
41 int m_number_non_zeros{};
42 bool m_dynamic_creation{};
43 std::vector<std::tuple<T, int, int>> m_temporary_dynamic_create_storage{};
44
45 int findIndex(int rows, int cols) const;
46
47 public:
49 CsrSparseMatrix(const std::valarray<T>& values, const std::valarray<int>& column_indices,
50 const std::valarray<int>& row_pointers, int rows, int cols);
51
52 void registerElement(T value, int rows, int cols) override;
53 void buildAtRuntime();
54 void buildAtRuntime(int rows, int cols) override;
55
56 T& operator()(int rows, int cols) override;
57 T operator()(int rows, int cols) const override;
58
59 std::valarray<T> scalarAdd(T value) const override;
60 std::valarray<T> scalarSubtract(T value) const override;
61 std::valarray<T> scalarMultiply(T value) const override;
62 std::valarray<T> scalarDivide(T value) const override;
63
64 std::valarray<T> vectorElemAdd(const std::valarray<T>& value) const override;
65 std::valarray<T> vectorElemSubtract(const std::valarray<T>& value) const override;
66 std::valarray<T> vectorElemMultiply(const std::valarray<T>& value) const override;
67 std::valarray<T> vectorElemDivide(const std::valarray<T>& value) const override;
68
69 std::valarray<T> matrixVectorMultiplication(const std::valarray<T>& value) const override;
70
71 const MatrixType getMatrixType() override { return MatrixType::Csr; }
72
74
75 const std::vector<std::pair<int, int>>& getNonZeroElements() override
76 {
77 return this->m_non_zero_elements;
78 };
79
80 int getNonZeroElementNumber() const { return this->m_matrix.size(); }
81 const std::valarray<int>& getColumnIndices() { return this->m_column_indices; }
82 const std::valarray<int>& getRowPointers() { return this->m_row_pointers; }
83 const std::valarray<T>& getMatrixElements() { return this->m_matrix; }
84 void updateNonZeroElements() override;
85
86 ~CsrSparseMatrix() override = default;
87};
88
93template <typename T>
95{
96}
97
109template <typename T>
111 const std::valarray<int>& column_indices,
112 const std::valarray<int>& row_pointers, int rows, int cols)
113 : GenericMatrix<T>{rows, cols, values, true},
114 m_column_indices{column_indices},
115 m_row_pointers{row_pointers}
116{
117}
118
127template <typename T>
128void CsrSparseMatrix<T>::registerElement(const T value, const int rows, const int cols)
129{
130 if (m_dynamic_creation)
131 {
132 m_temporary_dynamic_create_storage.emplace_back(value, rows, cols);
133 }
134}
135
146template <typename T>
148{
149 if (m_dynamic_creation)
150 {
151 const auto& max_row_ptr = std::max_element(
152 m_temporary_dynamic_create_storage.begin(), m_temporary_dynamic_create_storage.end(),
153 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
154 { return std::get<1>(lhs) < std::get<1>(rhs); });
155 const auto& max_col_ptr = std::max_element(
156 m_temporary_dynamic_create_storage.begin(), m_temporary_dynamic_create_storage.end(),
157 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
158 { return std::get<2>(lhs) < std::get<2>(rhs); });
159 this->m_rows = std::get<1>(*max_row_ptr) + 1;
160 this->m_cols = std::get<2>(*max_col_ptr) + 1;
161 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
162 // their max property. Sort the temporary storage by rows (ascending) and within a row by
163 // columns (ascending).
164 std::sort(m_temporary_dynamic_create_storage.begin(),
165 m_temporary_dynamic_create_storage.end(),
166 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
167 {
168 if (std::get<1>(lhs) == std::get<1>(rhs))
169 {
170 return std::get<2>(lhs) < std::get<2>(rhs);
171 }
172 return std::get<1>(lhs) < std::get<1>(rhs);
173 });
174 // Clear out duplicate entries. They can be produced when building a global matrix from
175 // smaller ones that overlap in some entries, like when using finite elements.
176 for (auto it = m_temporary_dynamic_create_storage.begin();;)
177 {
178 if (std::get<1>(*it) == std::get<1>(*(it + 1))
179 && std::get<2>(*it) == std::get<2>(*(it + 1)))
180 {
181 it = m_temporary_dynamic_create_storage.erase(it);
182 }
183 else
184 {
185 ++it;
186 }
187 if (it + 1 == m_temporary_dynamic_create_storage.end())
188 {
189 break;
190 }
191 }
192 // All non-zero entries are known, so the valarray sizes can be finalized
193 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
194 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
195 // The row pointer always has one element more that points to n+1, as to always define
196 // row_pointer[i+1].
197 m_row_pointers.resize(this->m_rows + 1);
198
199 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
200 bool is_last{false};
201 // We need the +1 on the end iterator here, because we need the case it = .end() within the
202 // if condition
203 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
204 {
205 is_last = (it == m_temporary_dynamic_create_storage.end());
206 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
207 {
208 // Set the row pointer for the current row to the element, which marks the start of
209 // that row.
210 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
211 current_row_start - m_temporary_dynamic_create_storage.begin());
212 // If there are empty row(s) between the entries, we need to propagate the next
213 // valid row_pointer value, so that row[i+1]-row[i] = 0 for these empty rows. As
214 // this is not calculated, we mark it with a placeholder and fill it after going
215 // through it once. Not is_last is checked first in order to not deref the end()
216 // pointer.
217 if (!is_last && std::get<1>(*it) > std::get<1>(*current_row_start) + 1)
218 {
219 for (int i{std::get<1>(*current_row_start) + 1}; i < std::get<1>(*it); i++)
220 {
221 m_row_pointers[i] = -1;
222 }
223 }
225 }
226 if (is_last)
227 {
228 break;
229 }
230 }
231 // Now that all values are calculated, we can fill in the placeholders.
232 for (int i{static_cast<int>(m_row_pointers.size()) - 1}; i > 0; i--)
233 {
234 if (m_row_pointers[i - 1] == -1)
235 {
236 m_row_pointers[i - 1] = m_row_pointers[i];
237 }
238 }
239 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
240 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
241 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
242 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
243 m_row_pointers[this->m_rows] = this->m_matrix.size();
244
245 // After this block, the elements are all sorted by rows (ascending) and within one row by
246 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
247 // write them directly to the arrays.
248 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
249 {
250 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
251 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
252 }
253 }
254}
255
268template <typename T>
270{
271 if (m_dynamic_creation)
272 {
273 this->m_rows = rows;
274 this->m_cols = cols;
275 // Do not use the max_ptrs afterwards, as we sort the elements and the pointers will lose
276 // their max property. Sort the temporary storage by rows (ascending) and within a row by
277 // columns (ascending).
278 std::sort(m_temporary_dynamic_create_storage.begin(),
279 m_temporary_dynamic_create_storage.end(),
280 [](std::tuple<T, int, int> lhs, std::tuple<T, int, int> rhs)
281 {
282 if (std::get<1>(lhs) == std::get<1>(rhs))
283 {
284 return std::get<2>(lhs) < std::get<2>(rhs);
285 }
286 return std::get<1>(lhs) < std::get<1>(rhs);
287 });
288 // Clear out duplicate entries. They can be produced when building a global matrix from
289 // smaller ones that overlap in some entries, like when using finite elements.
290 for (auto it = m_temporary_dynamic_create_storage.begin();;)
291 {
292 if (std::get<1>(*it) == std::get<1>(*(it + 1))
293 && std::get<2>(*it) == std::get<2>(*(it + 1)))
294 {
295 it = m_temporary_dynamic_create_storage.erase(it);
296 }
297 else
298 {
299 ++it;
300 }
301 if (it + 1 == m_temporary_dynamic_create_storage.end())
302 {
303 break;
304 }
305 }
306 // All non-zero entries are known, so the valarray sizes can be finalized
307 this->m_matrix.resize(m_temporary_dynamic_create_storage.size());
308 m_column_indices.resize(m_temporary_dynamic_create_storage.size());
309 // The last row always has entries for all FEM/FV/FD discretizations, so we can set the row
310 // pointer array size to this value + 2
311 m_row_pointers.resize(rows + 1);
312
313 auto current_row_start{m_temporary_dynamic_create_storage.begin()};
314 bool is_last{false};
315 bool last_is_empty{false};
316 // We need the +1 on the end iterator here, because we need the case it = .end() within the
317 // if condition
318 for (auto it{m_temporary_dynamic_create_storage.begin()};; it++)
319 {
320 is_last = (it == m_temporary_dynamic_create_storage.end());
321 if (is_last || std::get<1>(*it) != std::get<1>(*current_row_start))
322 {
323 // Set the row pointer for the current row to the element, which marks the start of
324 // that row.
325 m_row_pointers[std::get<1>(*current_row_start)] = static_cast<int>(
326 current_row_start - m_temporary_dynamic_create_storage.begin());
327 // If there are empty row(s) between the entries, we need to propagate the next
328 // valid row_pointer value, so that row[i+1]-row[i] = 0 for these empty rows. As
329 // this is not calculated, we mark it with a placeholder and fill it after going
330 // through it once. Not is_last is checked first in order to not deref the end()
331 // pointer.
332 if (!is_last && std::get<1>(*it) > std::get<1>(*current_row_start) + 1)
333 {
334 for (int i{std::get<1>(*current_row_start) + 1}; i < std::get<1>(*it); i++)
335 {
336 m_row_pointers[i] = -1;
337 }
338 }
339 // If the last row was not the nomially last one, it is empty and needs to be
340 // treated differently.
341 if (is_last && std::get<1>(*current_row_start) != rows - 1)
342 {
343 last_is_empty = true;
344 }
346 }
347 if (is_last)
348 {
349 break;
350 }
351 }
352 // Now that all values are calculated, we can fill in the placeholders.
353 if (last_is_empty)
354 {
355 m_row_pointers[rows - 1] = this->m_matrix.size();
356 }
357 for (int i{static_cast<int>(m_row_pointers.size()) - 1}; i > 0; i--)
358 {
359 if (m_row_pointers[i - 1] == -1)
360 {
361 m_row_pointers[i - 1] = m_row_pointers[i];
362 }
363 }
364 // This element is not a part of the matrix, but it makes the expresion m_row_pointers[rows]
365 // and m_row_pointers[rows + 1] valid for all rows <= nr_of_rows!
366 // m_row_pointers[current_row_int] = static_cast<int>( current_row_start -
367 // m_temporary_dynamic_create_storage.begin() ); current_row_int++;
368 m_row_pointers[rows] = this->m_matrix.size();
369
370 // After this block, the elements are all sorted by rows (ascending) and within one row by
371 // columns (ascending) and the beginnings of the rows are already marked, so we can simply
372 // write them directly to the arrays.
373 for (int i{0}; i < m_temporary_dynamic_create_storage.size(); i++)
374 {
375 this->m_matrix[i] = std::get<0>(m_temporary_dynamic_create_storage[i]);
376 m_column_indices[i] = std::get<2>(m_temporary_dynamic_create_storage[i]);
377 }
378 }
379}
380
391template <typename T>
392int CsrSparseMatrix<T>::findIndex(const int rows, const int cols) const
393{
394 int lower_bounds{m_row_pointers[rows]};
395 int upper_bounds{m_row_pointers[rows + 1] - 1};
396 int test_index{-1};
397
398 // Use devide and conquer algorithm to find result in log(n) time
399 while (lower_bounds <= upper_bounds)
400 {
402 if (m_column_indices[test_index] < cols)
403 {
405 }
406 else if (m_column_indices[test_index] > cols)
407 {
409 }
410 else
411 {
412 return test_index;
413 }
414 }
415 // If no match was found, then return a negative value to show this.
416 return -1;
417}
418
428template <typename T>
430{
431 if (rows >= this->m_rows || cols >= this->m_cols)
432 {
433 std::cerr << "[CsrSparseMatrix]: Trying to access a matrix out of bounds!\n";
434 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols,
435 static_cast<std::string>("[CsrSparseMatrix]:"));
436 }
437
438 int matrix_value_index{findIndex(rows, cols)};
439 // If the value is non-zero, is has to be in the given row, whose values are all between
440 // m_row_pointers[rows] and m_row_pointers[rows + 1]
441
442 // If _find_index returned -1, it has to be a zero value.
443 // This is technically not intended to access 0 elements in non-const context, as we would need
444 // to write it into the matrix (which schould never carry elements that are zero) If we want to
445 // allow element insertion in place (warning, this can be VERY slow), then we can replace that
446 // here. Right now it's just a placeholder
447 if (matrix_value_index == -1)
448 {
449 // I don't write to cerr here because there are cases where this resolves non-fatally.
451 }
452 return this->m_matrix[matrix_value_index];
453}
454
463template <typename T>
464T CsrSparseMatrix<T>::operator()(const int rows, const int cols) const
465{
466 if (rows >= this->m_rows || cols >= this->m_cols || rows < 0 || cols < 0)
467 {
468 std::cerr << "[CsrSparseMatrix]: Trying to access a matrix out of bounds!\n";
469 throw MatrixOutOfBoundsError(rows, cols, this->m_rows, this->m_cols,
470 static_cast<std::string>("[CsrSparseMatrix]:"));
471 }
472
473 int matrix_value_index{findIndex(rows, cols)};
474
475 // If the value is a non-defined zero value, return zero.
476 if (matrix_value_index == -1)
477 {
478 throw static_cast<T>(0.0);
479 }
480 return this->m_matrix[matrix_value_index];
481}
482
487template <typename T>
488std::valarray<T> CsrSparseMatrix<T>::scalarAdd(const T value) const
489{
490 return this->m_matrix + value;
491}
492
498template <typename T>
499std::valarray<T> CsrSparseMatrix<T>::scalarSubtract(const T value) const
500{
501 return this->m_matrix - value;
502}
503
509template <typename T>
510std::valarray<T> CsrSparseMatrix<T>::scalarMultiply(const T value) const
511{
512 return this->m_matrix * value;
513}
514
520template <typename T>
521std::valarray<T> CsrSparseMatrix<T>::scalarDivide(const T value) const
522{
523 return this->m_matrix / value;
524}
525
530template <typename T>
531std::valarray<T> CsrSparseMatrix<T>::vectorElemAdd(const std::valarray<T>& value) const
532{
533 return this->m_matrix + value;
534}
535
541template <typename T>
542std::valarray<T> CsrSparseMatrix<T>::vectorElemSubtract(const std::valarray<T>& value) const
543{
544 return this->m_matrix - value;
545}
546
552template <typename T>
553std::valarray<T> CsrSparseMatrix<T>::vectorElemMultiply(const std::valarray<T>& value) const
554{
555 return this->m_matrix * value;
556}
557
563template <typename T>
564std::valarray<T> CsrSparseMatrix<T>::vectorElemDivide(const std::valarray<T>& value) const
565{
566 return this->m_matrix / value;
567}
568
576template <typename T>
577std::valarray<T> CsrSparseMatrix<T>::matrixVectorMultiplication(const std::valarray<T>& value) const
578{
579 std::valarray<T> result(this->m_rows);
580 int index_row_start;
581 int index_row_end;
582 for (int i{0}; i < this->m_rows; i++)
583 {
584 index_row_start = m_row_pointers[i];
585 index_row_end = m_row_pointers[i + 1];
586 for (int j{index_row_start}; j < index_row_end; j++)
587 {
588 result[i] += this->m_matrix[j] * value[m_column_indices[j]];
589 }
590 }
591 return result;
592}
593
597template <typename T>
599{
600 if (this->m_matrix.size() > 0)
601 {
602 for (int i{0}; i < m_row_pointers.size() - 1; i++)
603 {
604 std::valarray<int> column_values_in_row = m_column_indices[std::slice(
605 m_row_pointers[i], m_row_pointers[i + 1] - m_row_pointers[i], 1)];
606 int current_column_id{0};
607 for (int j{0}; j < this->m_cols; j++)
608 {
611 {
612 std::cout << this->m_matrix[m_row_pointers[i] + current_column_id] << ' ';
614 }
615 else
616 {
617 std::cout << "0" << ' ';
618 }
619 }
620 std::cout << "\n";
621 }
622 }
623 else
624 {
625 std::cout << "Matrix is empty\n";
626 }
627}
628
634template <typename T>
636{
637 for (const auto& position_tuple : m_temporary_dynamic_create_storage)
638 {
639 this->m_non_zero_elements.emplace_back(std::get<1>(position_tuple),
640 std::get<2>(position_tuple));
641 }
642}
643#endif
MatrixType
Definition CoreEnums.h:36
Concrete implementation of a matrix class representing a Compressed Sparse Rows (CSR) matrix....
Definition CsrMatrix.h:37
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:128
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:429
void printMatrixInDenseFormat()
Prints the entire matrix with zero values.
Definition CsrMatrix.h:598
std::valarray< T > scalarSubtract(T value) const override
Subtraction of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:499
std::valarray< T > scalarDivide(T value) const override
Division of a scalar from the matrix values element-wise.
Definition CsrMatrix.h:521
const std::vector< std::pair< int, int > > & getNonZeroElements() override
Definition CsrMatrix.h:75
void updateNonZeroElements() override
Sets the m_non_zero_elememts parameter of the parent class.
Definition CsrMatrix.h:635
std::valarray< T > vectorElemMultiply(const std::valarray< T > &value) const override
Multiplication of a vector to the matrix values element-wise.
Definition CsrMatrix.h:553
std::valarray< T > scalarMultiply(T value) const override
Multiplication of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:510
const std::valarray< int > & getColumnIndices()
Definition CsrMatrix.h:81
int getNonZeroElementNumber() const
Definition CsrMatrix.h:80
CsrSparseMatrix()
Constructor for an empty CsrSparseMatrix object. Leaves all storage arrays empty but sets the flag to...
Definition CsrMatrix.h:94
~CsrSparseMatrix() override=default
const std::valarray< int > & getRowPointers()
Definition CsrMatrix.h:82
std::valarray< T > scalarAdd(T value) const override
Addition of a scalar to the matrix values element-wise.
Definition CsrMatrix.h:488
std::valarray< T > vectorElemSubtract(const std::valarray< T > &value) const override
Subtraction of a vector from the matrix values element-wise.
Definition CsrMatrix.h:542
std::valarray< T > matrixVectorMultiplication(const std::valarray< T > &value) const override
Matrix-vector-multiplication for sparse matrices.
Definition CsrMatrix.h:577
std::valarray< T > vectorElemAdd(const std::valarray< T > &value) const override
Addition of a vector to the matrix values element-wise.
Definition CsrMatrix.h:531
const MatrixType getMatrixType() override
Definition CsrMatrix.h:71
std::valarray< T > vectorElemDivide(const std::valarray< T > &value) const override
Division of a vector from the matrix values element-wise.
Definition CsrMatrix.h:564
void buildAtRuntime()
Builds the CSR matrix from the registered elements. It sorts all elements of the COO format vector by...
Definition CsrMatrix.h:147
const std::valarray< T > & getMatrixElements()
Definition CsrMatrix.h:83
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:120
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:152
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