#ifndef MRMC_SPARSE_STATIC_SPARSE_MATRIX_H_ #define MRMC_SPARSE_STATIC_SPARSE_MATRIX_H_ #include #include "boost/integer/integer_mask.hpp" #include #include #include "src/exceptions/invalid_state.h" #include "src/exceptions/invalid_argument.h" #include "src/exceptions/out_of_range.h" namespace mrmc { namespace sparse { //! A sparse Matrix for DTMCs with a constant number of non-zero entries on the non-diagonal fields. /*! The sparse matrix used for calculation on the DTMCs. Adressing is NOT zero-based! The valid range for getValue and addNextValue is 1 to rows (first argument to constructor). */ template class StaticSparseMatrix { public: //! Constructor /*! \param rows Row-Count and therefor column-count of the symetric matrix \param non_zero_entries The exact count of entries that will be submitted through addNextValue *excluding* those on the diagonal (A_{i,j} with i = j) */ StaticSparseMatrix(uint_fast32_t rows, uint_fast32_t non_zero_entries) { current_size = 0; storage_size = 0; row_count = rows; non_zero_entry_count = non_zero_entries; //initialize(rows, non_zero_entries); } ~StaticSparseMatrix() { if (value_storage != NULL) { free(value_storage); } if (column_indications != NULL) { free(column_indications); } if (row_indications != NULL) { free(row_indications); } if (diagonal_storage != NULL) { free(diagonal_storage); } } //! Getter for saving matrix entry A_{row,col} to target /*! Getter function for the matrix. \param row 1-based index of the requested row \param col 1-based index of the requested column \param target pointer to where the result will be stored \return True iff the value was set, false otherwise. On false, 0 will be written to *target. */ inline bool getValue(uint_fast32_t row, uint_fast32_t col, T* const target) { if (row == col) { // storage is row_count + 1 large for direct access without the -1 *target = diagonal_storage[row]; return true; } if ((row > row_count) || (col > row_count) || (row == 0) || (col == 0)) { throw new mrmc::exceptions::out_of_range("mrmc::StaticSparseMatrix::getValue: row or col not in 1 .. rows"); } uint_fast32_t row_start = row_indications[row - 1]; uint_fast32_t row_end = row_indications[row]; while (row_start < row_end) { if (column_indications[row_start] == col) { *target = value_storage[row_start]; return true; } if (column_indications[row_start] > col) { break; } row_start++; } *target = 0; return false; } //! Mandatory initialization of the matrix /*! Mandatory initialization of the matrix, must be called before using any other member function. */ void initialize() { if (row_count == 0) { pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing invalid_argument for row_count = 0"); throw new mrmc::exceptions::invalid_argument("mrmc::StaticSparseMatrix::initialize: Matrix with 0 rows is not reasonable"); } if (((row_count * row_count) - row_count) < non_zero_entry_count) { pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing invalid_argument: More non-zero entries than entries in target matrix"); throw new mrmc::exceptions::invalid_argument("mrmc::StaticSparseMatrix::initialize: More non-zero entries than entries in target matrix"); } storage_size = non_zero_entry_count; last_row = 0; value_storage = static_cast(calloc(storage_size, sizeof(*value_storage))); column_indications = static_cast(calloc(storage_size, sizeof(*column_indications))); row_indications = static_cast(calloc(row_count + 1, sizeof(*row_indications))); // row_count + 1 so that access with 1-based indices can be direct without the overhead of a -1 each time diagonal_storage = static_cast(calloc(row_count + 1, sizeof(*diagonal_storage))); if ((value_storage == NULL) || (column_indications == NULL) || (row_indications == NULL) || (diagonal_storage == NULL)) { pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing bad_alloc: memory allocation failed"); #ifdef __linux__ throw new std::bad_alloc(); #else throw new std::bad_alloc("mrmc::StaticSparseMatrix::initialize: memory allocation failed"); #endif } } //! Linear Setter for matrix entry A_{row, col} to value /*! Linear Setter function for matrix entry A_{row, col} to value. Must be called consecutively for each element in a row in ascending order of columns AND in ascending order of rows. Diagonal entries may be set at any time. */ void addNextValue(const uint_fast32_t row, const uint_fast32_t col, const T value) { if ((row > row_count) || (col > row_count) || (row == 0) || (col == 0)) { pantheios::log_ERROR("StaticSparseMatrix::addNextValue: Throwing out_of_range: row or col not in 1 .. rows"); throw new mrmc::exceptions::out_of_range("mrmc::StaticSparseMatrix::addNextValue: row or col not in 1 .. rows"); } if (row == col) { diagonal_storage[row] = value; } else { if (row != last_row) { for (uint_fast32_t i = last_row; i < row; ++i) { row_indications[i] = current_size; } last_row = row; } value_storage[current_size] = value; column_indications[current_size] = col; // Increment counter for stored elements current_size++; } } void finalize() { if (storage_size != current_size) { pantheios::log_ERROR("StaticSparseMatrix::finalize: Throwing invalid_state: Wrong call count for addNextValue"); throw new mrmc::exceptions::invalid_state("mrmc::StaticSparseMatrix::finalize: Wrong call count for addNextValue"); } if (last_row != row_count) { for (uint_fast32_t i = last_row; i < row_count; ++i) { row_indications[i] = current_size; } } row_indications[row_count] = storage_size; } private: uint_fast32_t storage_size; uint_fast32_t current_size; uint_fast32_t row_count; uint_fast32_t non_zero_entry_count; uint_fast32_t last_row; /*! Array containing all non-zero values, apart from the diagonal entries */ T* value_storage; /*! Array containing all diagonal values */ T* diagonal_storage; /*! Array containing the column number of the corresponding value_storage entry */ uint_fast32_t* column_indications; /*! Array containing the row boundarys of valueStorage */ uint_fast32_t* row_indications; }; } // namespace sparse } // namespace mrmc #endif // MRMC_SPARSE_STATIC_SPARSE_MATRIX_H_