diff --git a/resources/3rdparty/eigen/Eigen/src/SparseCore/SparseMatrixBase.h b/resources/3rdparty/eigen/Eigen/src/SparseCore/SparseMatrixBase.h index 9a1258097..55666fa67 100644 --- a/resources/3rdparty/eigen/Eigen/src/SparseCore/SparseMatrixBase.h +++ b/resources/3rdparty/eigen/Eigen/src/SparseCore/SparseMatrixBase.h @@ -164,7 +164,11 @@ template class SparseMatrixBase : public EigenBase /** \returns the size of the inner dimension according to the storage order, * i.e., the number of rows for a columns major matrix, and the number of cols otherwise */ Index innerSize() const { return (int(Flags)&RowMajorBit) ? this->cols() : this->rows(); } - + + bool isRowMajorMatrix() const { + return (int(Flags)&RowMajorBit); + } + bool isRValue() const { return m_isRValue; } Derived& markAsRValue() { m_isRValue = true; return derived(); } diff --git a/resources/3rdparty/stlsoft-1.9.116/include/stlsoft/stlsoft.h b/resources/3rdparty/stlsoft-1.9.116/include/stlsoft/stlsoft.h index f65ef3d18..9d959145a 100644 --- a/resources/3rdparty/stlsoft-1.9.116/include/stlsoft/stlsoft.h +++ b/resources/3rdparty/stlsoft-1.9.116/include/stlsoft/stlsoft.h @@ -554,8 +554,10 @@ # define STLSOFT_COMPILER_VERSION_STRING "Intel C/C++ 11.0" # elif (__INTEL_COMPILER >= 1200) && (__INTEL_COMPILER < 1300) # define STLSOFT_COMPILER_VERSION_STRING "Intel C/C++ 12.x" +# elif (__INTEL_COMPILER >= 1300) && (__INTEL_COMPILER < 1400) +# define STLSOFT_COMPILER_VERSION_STRING "Intel C/C++ 13.x" # else /* ? __INTEL_COMPILER */ -# error Only Intel C++ Compiler versions 6.0, 7.0(/7.1), 8.0, 9.0, 10.0, 11.0 and 12.x currently supported by the STLSoft libraries +# error Only Intel C++ Compiler versions 6.0, 7.0(/7.1), 8.0, 9.0, 10.0, 11.0, 12.x and 13.x currently supported by the STLSoft libraries # endif /* __INTEL_COMPILER */ #elif defined(__MWERKS__) diff --git a/src/parser/read_tra_file.cpp b/src/parser/read_tra_file.cpp index 72dcea77e..4a62af89c 100644 --- a/src/parser/read_tra_file.cpp +++ b/src/parser/read_tra_file.cpp @@ -135,12 +135,12 @@ sparse::StaticSparseMatrix * read_tra_file(const char * filename) { * Memory for diagonal elements is automatically allocated, hence only the number of non-diagonal * non-zero elements has to be specified (which is non_zero, computed by make_first_pass) */ - sp = new sparse::StaticSparseMatrix(rows,non_zero); + sp = new sparse::StaticSparseMatrix(rows); if ( NULL == sp ) { throw std::bad_alloc(); return NULL; } - sp->initialize(); + sp->initialize(non_zero); //Reading transitions (one per line) and saving the results in the matrix while (NULL != fgets(s, BUFFER_SIZE, p )) { diff --git a/src/sparse/static_sparse_matrix.h b/src/sparse/static_sparse_matrix.h index 4177d06ee..7a2b2ee52 100644 --- a/src/sparse/static_sparse_matrix.h +++ b/src/sparse/static_sparse_matrix.h @@ -13,6 +13,7 @@ #include "src/exceptions/out_of_range.h" #include "Eigen/Sparse" +#include "src/sparse/eigen_sparse_additions.h" namespace mrmc { @@ -46,13 +47,13 @@ class StaticSparseMatrix { //! Constructor /*! - \param rows Row-Count and therefore column-count of the symmetric 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) + \param rows Row-Count and therefore column-count of the square matrix */ - StaticSparseMatrix(uint_fast32_t rows, uint_fast32_t non_zero_entries) { - setState(MatrixStatus::UnInitialized); + StaticSparseMatrix(uint_fast32_t rows) { + // Using direct access instead of setState() because of undefined initialization value + // setState() stays in Error should Error be the current value + internal_status = MatrixStatus::UnInitialized; current_size = 0; - storage_size = 0; value_storage = NULL; diagonal_storage = NULL; @@ -60,7 +61,7 @@ class StaticSparseMatrix { row_indications = NULL; row_count = rows; - non_zero_entry_count = non_zero_entries; + non_zero_entry_count = 0; //initialize(rows, non_zero_entries); } @@ -85,98 +86,123 @@ class StaticSparseMatrix { } } - //! Getter for saving matrix entry A_{row,col} to target - /*! - Getter function for the matrix. This function does not check the internal status for errors for performance reasons. - \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 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, variant for initialize(), addNextValue() and finalize() /*! Mandatory initialization of the matrix, must be called before using any other member function. + This version is to be used together with addNextValue(). + For initialization from a Eigen SparseMatrix, use initialize(Eigen::SparseMatrix &). + \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) */ - void initialize() { + void initialize(uint_fast32_t non_zero_entries) { if (internal_status != MatrixStatus::UnInitialized) { + triggerErrorState(); pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing invalid state for status flag != 0 (is ", pantheios::integer(internal_status)," - Already initialized?"); throw mrmc::exceptions::invalid_state("StaticSparseMatrix::initialize: Invalid state for status flag != 0 - Already initialized?"); - triggerErrorState(); } else if (row_count == 0) { + triggerErrorState(); pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing invalid_argument for row_count = 0"); throw mrmc::exceptions::invalid_argument("mrmc::StaticSparseMatrix::initialize: Matrix with 0 rows is not reasonable"); + } else if (((row_count * row_count) - row_count) < non_zero_entries) { triggerErrorState(); - } else 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 mrmc::exceptions::invalid_argument("mrmc::StaticSparseMatrix::initialize: More non-zero entries than entries in target matrix"); - triggerErrorState(); } else { - storage_size = non_zero_entry_count; + non_zero_entry_count = non_zero_entries; last_row = 0; - //value_storage = static_cast(calloc(storage_size, sizeof(*value_storage))); - value_storage = new (std::nothrow) T[storage_size](); - - //column_indications = static_cast(calloc(storage_size, sizeof(*column_indications))); - column_indications = new (std::nothrow) uint_fast32_t[storage_size](); - - //row_indications = static_cast(calloc(row_count + 1, sizeof(*row_indications))); - row_indications = new (std::nothrow) uint_fast32_t[row_count + 1](); - - // 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))); - diagonal_storage = new (std::nothrow) T[row_count + 1](); - - if ((value_storage == NULL) || (column_indications == NULL) || (row_indications == NULL) || (diagonal_storage == NULL)) { + if (!prepareInternalStorage()) { + triggerErrorState(); pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing bad_alloc: memory allocation failed"); throw std::bad_alloc(); - triggerErrorState(); } else { setState(MatrixStatus::Initialized); } } } + //! Mandatory initialization of the matrix, variant for initialize(), addNextValue() and finalize() + /*! + Mandatory initialization of the matrix, must be called before using any other member function. + This version is to be used for initialization from a Eigen SparseMatrix, use initialize(uint_fast32_t) for addNextValue. + \param eigen_sparse_matrix The Eigen Sparse Matrix to be copied/ initialized from. MUST BE in compressed form! + */ + template + void initialize(const Eigen::SparseMatrix<_Scalar, _Options, _Index> &eigen_sparse_matrix) { + if (!eigen_sparse_matrix.isCompressed()) { + triggerErrorState(); + pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing invalid_argument: eigen_sparse_matrix is not in Compressed form."); + throw mrmc::exceptions::invalid_argument("StaticSparseMatrix::initialize: Throwing invalid_argument: eigen_sparse_matrix is not in Compressed form."); + } + + non_zero_entry_count = getEigenSparseMatrixCorrectNonZeroEntryCount(eigen_sparse_matrix); + last_row = 0; + + if (!prepareInternalStorage()) { + triggerErrorState(); + pantheios::log_ERROR("StaticSparseMatrix::initialize: Throwing bad_alloc: memory allocation failed"); + throw std::bad_alloc(); + } else { + // easy case, we can simply copy the data + // RowMajor: Easy, ColMajor: Hmm. But how to detect? + const T* valuePtr = eigen_sparse_matrix.valuePtr(); + const int_fast32_t* indexPtr = eigen_sparse_matrix.innerIndexPtr(); + const int_fast32_t* outerPtr = eigen_sparse_matrix.outerIndexPtr(); + + + const int_fast32_t entryCount = eigen_sparse_matrix.nonZeros(); + const int_fast32_t outerCount = eigen_sparse_matrix.outerSize(); + if (isEigenRowMajor(eigen_sparse_matrix)) { + // Easy case, all data can be copied with some minor changes. + // We have to separate diagonal entries from others + for (int row = 1; row <= outerCount; ++row) { + for (int col = outerPtr[row - 1]; col < outerPtr[row]; ++col) { + addNextValue(row, indexPtr[col] + 1, valuePtr[col]); + } + } + } else { + // temp copies, anyone? + const int eigen_col_count = eigen_sparse_matrix.cols(); + const int eigen_row_count = eigen_sparse_matrix.rows(); + + // initialise all column-start positions to known lower boundarys + int_fast32_t* positions = new int_fast32_t[eigen_col_count](); + for (int i = 0; i < eigen_col_count; ++i) { + positions[i] = outerPtr[i]; + } + + int i = 0; + int currentRow = 0; + int currentColumn = 0; + while (i < entryCount) { + if ((positions[currentColumn] < outerPtr[currentColumn + 1]) && (indexPtr[positions[currentColumn]] == currentRow)) { + addNextValue(currentRow + 1, currentColumn + 1, valuePtr[positions[currentColumn]]); + // one more found + ++i; + // mark this position as "used" + ++positions[currentColumn]; + } + // advance to next column + ++currentColumn; + if (currentColumn == eigen_col_count) { + currentColumn = 0; + ++currentRow; + } + } + } + setState(MatrixStatus::Initialized); + } + } + //! 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) { + 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 mrmc::exceptions::out_of_range("mrmc::StaticSparseMatrix::addNextValue: row or col not in 1 .. rows"); triggerErrorState(); + pantheios::log_ERROR("StaticSparseMatrix::addNextValue: Throwing out_of_range: row or col not in 1 .. rows (is ", pantheios::integer(row), " x ", pantheios::integer(col), ", max is ", pantheios::integer(row_count), " x ", pantheios::integer(row_count), ")."); + throw mrmc::exceptions::out_of_range("StaticSparseMatrix::addNextValue: row or col not in 1 .. rows"); } if (row == col) { @@ -199,13 +225,13 @@ class StaticSparseMatrix { void finalize() { if (!isInitialized()) { + triggerErrorState(); pantheios::log_ERROR("StaticSparseMatrix::finalize: Throwing invalid state for internal state not Initialized (is ", pantheios::integer(internal_status)," - Already finalized?"); throw mrmc::exceptions::invalid_state("StaticSparseMatrix::finalize: Invalid state for internal state not Initialized - Already finalized?"); + } else if (current_size != non_zero_entry_count) { triggerErrorState(); - } else if (storage_size != current_size) { pantheios::log_ERROR("StaticSparseMatrix::finalize: Throwing invalid_state: Wrong call count for addNextValue"); - throw mrmc::exceptions::invalid_state("mrmc::StaticSparseMatrix::finalize: Wrong call count for addNextValue"); - triggerErrorState(); + throw mrmc::exceptions::invalid_state("StaticSparseMatrix::finalize: Wrong call count for addNextValue"); } else { if (last_row != row_count) { for (uint_fast32_t i = last_row; i < row_count; ++i) { @@ -213,12 +239,52 @@ class StaticSparseMatrix { } } - row_indications[row_count] = storage_size; + row_indications[row_count] = non_zero_entry_count; setState(MatrixStatus::ReadReady); } } + //! Getter for saving matrix entry A_{row,col} to target + /*! + Getter function for the matrix. This function does not check the internal status for errors for performance reasons. + \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)) { + pantheios::log_ERROR("StaticSparseMatrix::getValue: row or col not in 1 .. rows (is ", pantheios::integer(row), " x ", pantheios::integer(col), ", max is ", pantheios::integer(row_count), " x ", pantheios::integer(row_count), ")."); + throw mrmc::exceptions::out_of_range("StaticSparseMatrix::getValue: row or col not in 1 .. rows"); + return false; + } + + 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; + } + uint_fast32_t getRowCount() const { return row_count; } @@ -243,16 +309,24 @@ class StaticSparseMatrix { return (internal_status == MatrixStatus::Error); } + //! Converts this matrix to an equivalent sparse matrix in Eigens format. + /*! + Exports this sparse matrix to Eigens SparseMatrix format. + Required this matrix to be in the ReadReady state. + + @return The Eigen SparseMatrix + + */ Eigen::SparseMatrix toEigenSparseMatrix() { Eigen::SparseMatrix mat(row_count, row_count); if (!isReadReady()) { + triggerErrorState(); pantheios::log_ERROR("StaticSparseMatrix::toEigenSparseMatrix: Throwing invalid state for internal state not ReadReady (is ", pantheios::integer(internal_status),")."); throw mrmc::exceptions::invalid_state("StaticSparseMatrix::toEigenSparseMatrix: Invalid state for internal state not ReadReady."); - triggerErrorState(); } else { - typedef Eigen::Triplet ETd; - std::vector tripletList; + typedef Eigen::Triplet IntTriplet; + std::vector tripletList; tripletList.reserve(non_zero_entry_count + row_count); uint_fast32_t row_start; @@ -261,12 +335,12 @@ class StaticSparseMatrix { row_start = row_indications[row - 1]; row_end = row_indications[row]; while (row_start < row_end) { - tripletList.push_back(ETd(row - 1, column_indications[row_start] - 1, value_storage[row_start])); + tripletList.push_back(IntTriplet(row - 1, column_indications[row_start] - 1, value_storage[row_start])); ++row_start; } } for (uint_fast32_t i = 1; i <= row_count; ++i) { - tripletList.push_back(ETd(i, i, diagonal_storage[i])); + tripletList.push_back(IntTriplet(i, i, diagonal_storage[i])); } mat.setFromTriplets(tripletList.begin(), tripletList.end()); @@ -276,8 +350,72 @@ class StaticSparseMatrix { return mat; } + //! Alternative way to initialize this matrix instead of using initialize(), addNextValue() and finalize(). + /*! + Initializes the matrix from the given eigen_sparse_matrix. Replaces the calls to initialize(), addNextValue() and finalize(). + Requires eigen_sparse_matrix to have at most a size of rows x rows and not more than non_zero_entries on non-diagonal fields. + To calculate the non-zero-entry count on only non-diagonal fields, you may use getEigenSparseMatrixCorrectNonZeroEntryCount(). + In most cases, it may be easier to use the alternative constructor StaticSparseMatrix(const Eigen::SparseMatrix eigen_sparse_matrix). + @param eigen_sparse_matrix the Eigen Sparse Matrix from which this matrix should be initalized. + @see getEigenSparseMatrixCorrectNonZeroEntryCount() + @see StaticSparseMatrix(const Eigen::SparseMatrix) + */ + bool fromEigenSparseMatrix(const Eigen::SparseMatrix eigen_sparse_matrix) { + if (getState() != MatrixStatus::UnInitialized) { + triggerErrorState(); + pantheios::log_ERROR("StaticSparseMatrix::fromEigenSparseMatrix: Throwing invalid state for internal state not UnInitialized (is ", pantheios::integer(internal_status),")."); + throw mrmc::exceptions::invalid_state("StaticSparseMatrix::fromEigenSparseMatrix: Invalid state for internal state not UnInitialized."); + return false; + } + + int_fast32_t eigen_row_count = eigen_sparse_matrix.rows(); + int_fast32_t eigen_col_count = eigen_sparse_matrix.cols(); + + if ((eigen_row_count > row_count) || (eigen_col_count > row_count)) { + triggerErrorState(); + pantheios::log_ERROR("StaticSparseMatrix::fromEigenSparseMatrix: Throwing invalid argument for eigenSparseMatrix is too big to fit (is ", pantheios::integer(eigen_row_count)," x ", pantheios::integer(eigen_col_count), ", max is ", pantheios::integer(row_count)," x ", pantheios::integer(row_count), ")."); + throw mrmc::exceptions::invalid_argument("StaticSparseMatrix::fromEigenSparseMatrix: Invalid argument for eigenSparseMatrix is too big to fit."); + return false; + } + + uint_fast32_t eigen_non_zero_entries = mrmc::sparse::getEigenSparseMatrixCorrectNonZeroEntryCount(eigen_sparse_matrix); + + if (eigen_non_zero_entries > non_zero_entry_count) { + triggerErrorState(); + pantheios::log_ERROR("StaticSparseMatrix::fromEigenSparseMatrix: Throwing invalid argument for eigenSparseMatrix has too many non-zero entries to fit (is ", pantheios::integer(eigen_non_zero_entries),", max is ", pantheios::integer(non_zero_entry_count),")."); + throw mrmc::exceptions::invalid_argument("StaticSparseMatrix::fromEigenSparseMatrix: Invalid argument for eigenSparseMatrix has too many non-zero entries to fit."); + return false; + } + + // make compressed + eigen_sparse_matrix.makeCompressed(); + + if (eigen_sparse_matrix.IsRowMajor()) { + // inner Index + int_fast32_t* eigenInnerIndex = eigen_sparse_matrix.innerIndexPtr(); + T* eigenValuePtr = eigen_sparse_matrix.valuePtr(); + } + + + + for (int k = 0; k < tempESM.outerSize(); ++k) { + for (SparseMatrix::InnerIterator it(tempESM, k); it; ++it) { + if (eigen_non_zero_entries >= non_zero_entry_count) { + // too many non zero entries for us. + } + addNextValue(it.row() - 1, it.col() - 1, it.value()); + if (it.row() != it.col()) { + ++eigen_non_zero_entries; + } + /*it.value(); + it.row(); // row index + it.col(); // col index (here it is equal to k) + it.index(); // inner index, here it is equal to it.row()*/ + } + } + } + private: - uint_fast32_t storage_size; uint_fast32_t current_size; uint_fast32_t row_count; @@ -309,6 +447,74 @@ class StaticSparseMatrix { void setState(const MatrixStatus new_state) { internal_status = (internal_status == MatrixStatus::Error) ? internal_status : new_state; } + + /*! + Prepares the internal CSR storage. + Requires non_zero_entry_count and row_count to be set. + @return true on success, false otherwise (allocation failed). + */ + bool prepareInternalStorage() { + value_storage = new (std::nothrow) T[non_zero_entry_count](); + + column_indications = new (std::nothrow) uint_fast32_t[non_zero_entry_count](); + + row_indications = new (std::nothrow) uint_fast32_t[row_count + 1](); + + // row_count + 1 so that access with 1-based indices can be direct without the overhead of a -1 each time + diagonal_storage = new (std::nothrow) T[row_count + 1](); + + return ((value_storage != NULL) && (column_indications != NULL) && (row_indications != NULL) && (diagonal_storage != NULL)); + } + + template + bool isEigenRowMajor(Eigen::SparseMatrix<_Scalar, Eigen::RowMajor, _Index>) { + return true; + } + + template + bool isEigenRowMajor(Eigen::SparseMatrix<_Scalar, Eigen::ColMajor, _Index>) { + return false; + } + + template + uint_fast32_t getEigenSparseMatrixCorrectNonZeroEntryCount(const Eigen::SparseMatrix<_Scalar, _Options, _Index> &eigen_sparse_matrix) { + const int_fast32_t* indexPtr = eigen_sparse_matrix.innerIndexPtr(); + const int_fast32_t* outerPtr = eigen_sparse_matrix.outerIndexPtr(); + + const int_fast32_t entryCount = eigen_sparse_matrix.nonZeros(); + const int_fast32_t outerCount = eigen_sparse_matrix.outerSize(); + + uint_fast32_t diag_non_zeros = 0; + + // for RowMajor, row is the current Row and col the column + // for ColMajor, row is the current Col and col the row + int_fast32_t innerStart = 0; + int_fast32_t innerEnd = 0; + int_fast32_t innerMid = 0; + for (int row = 0; row < outerCount; ++row) { + innerStart = outerPtr[row]; + innerEnd = outerPtr[row + 1] - 1; + + // Now with super fancy binary search, deferred equality detection + while (innerStart < innerEnd) { + innerMid = innerStart + ((innerEnd - innerStart) / 2); + + if (indexPtr[innerMid] < row) { + innerStart = innerMid + 1; + } else { + innerEnd = innerMid; + } + } + + if ((innerStart == innerEnd) && (indexPtr[innerStart] == row)) { + // found a diagonal entry + ++diag_non_zeros; + } + } + + return static_cast(entryCount - diag_non_zeros); + } + }; } // namespace sparse diff --git a/test/sparse/static_sparse_matrix_test.cpp b/test/sparse/static_sparse_matrix_test.cpp index d585eb47e..76e155261 100644 --- a/test/sparse/static_sparse_matrix_test.cpp +++ b/test/sparse/static_sparse_matrix_test.cpp @@ -3,52 +3,70 @@ #include "src/exceptions/invalid_argument.h" TEST(StaticSparseMatrixTest, ZeroRowsTest) { - mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(0, 50); + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(0); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); - ASSERT_THROW(ssm->initialize(), mrmc::exceptions::invalid_argument); + ASSERT_THROW(ssm->initialize(50), mrmc::exceptions::invalid_argument); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); delete ssm; } TEST(StaticSparseMatrixTest, TooManyEntriesTest) { - mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(2, 10); + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(2); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); - ASSERT_THROW(ssm->initialize(), mrmc::exceptions::invalid_argument); + ASSERT_THROW(ssm->initialize(10), mrmc::exceptions::invalid_argument); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); delete ssm; } TEST(StaticSparseMatrixTest, addNextValueTest) { - mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(5, 1); + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(5); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); - ASSERT_NO_THROW(ssm->initialize()); + ASSERT_NO_THROW(ssm->initialize(1)); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Initialized); ASSERT_THROW(ssm->addNextValue(0, 1, 1), mrmc::exceptions::out_of_range); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); + ASSERT_THROW(ssm->addNextValue(1, 0, 1), mrmc::exceptions::out_of_range); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); + ASSERT_THROW(ssm->addNextValue(6, 1, 1), mrmc::exceptions::out_of_range); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); + ASSERT_THROW(ssm->addNextValue(1, 6, 1), mrmc::exceptions::out_of_range); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); delete ssm; } TEST(StaticSparseMatrixTest, finalizeTest) { - mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(5, 5); + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(5); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); - ASSERT_NO_THROW(ssm->initialize()); + ASSERT_NO_THROW(ssm->initialize(5)); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Initialized); ASSERT_NO_THROW(ssm->addNextValue(1, 2, 1)); ASSERT_NO_THROW(ssm->addNextValue(1, 3, 1)); ASSERT_NO_THROW(ssm->addNextValue(1, 4, 1)); ASSERT_NO_THROW(ssm->addNextValue(1, 5, 1)); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Initialized); ASSERT_THROW(ssm->finalize(), mrmc::exceptions::invalid_state); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Error); delete ssm; } TEST(StaticSparseMatrixTest, Test) { // 25 rows, 50 non zero entries - mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(25, 50); + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(25); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); int values[50] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, @@ -76,13 +94,16 @@ TEST(StaticSparseMatrixTest, Test) { 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 /* second to last row */ }; - ASSERT_NO_THROW(ssm->initialize()); + ASSERT_NO_THROW(ssm->initialize(50)); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Initialized); for (int i = 0; i < 50; ++i) { ASSERT_NO_THROW(ssm->addNextValue(position_row[i], position_col[i], values[i])); } + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::Initialized); ASSERT_NO_THROW(ssm->finalize()); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::ReadReady); int target; for (int i = 0; i < 50; ++i) { @@ -103,6 +124,161 @@ TEST(StaticSparseMatrixTest, Test) { ASSERT_EQ(0, target); } } + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::ReadReady); delete ssm; } + +TEST(StaticSparseMatrixTest, ConversionFromDenseEigen_ColMajor_SparseMatrixTest) { + // 10 rows, 100 non zero entries + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(10); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); + + Eigen::SparseMatrix esm(10, 10); + for (int row = 0; row < 10; ++row) { + for (int col = 0; col < 10; ++col) { + esm.insert(row, col) = row * 10 + col; + } + } + + // make compressed, important for initialize() + esm.makeCompressed(); + + ASSERT_NO_THROW(ssm->initialize(esm)); + + ASSERT_NO_THROW(ssm->finalize()); + + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::ReadReady); + + int target = -1; + for (int row = 1; row <= 10; ++row) { + for (int col = 1; col <= 10; ++col) { + ASSERT_TRUE(ssm->getValue(row, col, &target)); + ASSERT_EQ(target, (row - 1) * 10 + col - 1); + } + } +} + +TEST(StaticSparseMatrixTest, ConversionFromDenseEigen_RowMajor_SparseMatrixTest) { + // 10 rows, 100 non zero entries + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(10); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); + + Eigen::SparseMatrix esm(10, 10); + for (int row = 0; row < 10; ++row) { + for (int col = 0; col < 10; ++col) { + esm.insert(row, col) = row * 10 + col; + } + } + + // make compressed, important for initialize() + esm.makeCompressed(); + + ASSERT_NO_THROW(ssm->initialize(esm)); + + ASSERT_NO_THROW(ssm->finalize()); + + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::ReadReady); + + int target = -1; + for (int row = 1; row <= 10; ++row) { + for (int col = 1; col <= 10; ++col) { + ASSERT_TRUE(ssm->getValue(row, col, &target)); + ASSERT_EQ(target, (row - 1) * 10 + col - 1); + } + } +} + +TEST(StaticSparseMatrixTest, ConversionFromSparseEigen_ColMajor_SparseMatrixTest) { + // 10 rows, 15 non zero entries + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(10); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); + + Eigen::SparseMatrix esm(10, 10); + + typedef Eigen::Triplet IntTriplet; + std::vector tripletList; + tripletList.reserve(15); + tripletList.push_back(IntTriplet(1, 0, 0)); + tripletList.push_back(IntTriplet(1, 1, 1)); + tripletList.push_back(IntTriplet(1, 2, 2)); + tripletList.push_back(IntTriplet(1, 3, 3)); + tripletList.push_back(IntTriplet(1, 4, 4)); + tripletList.push_back(IntTriplet(1, 5, 5)); + tripletList.push_back(IntTriplet(1, 6, 6)); + tripletList.push_back(IntTriplet(1, 7, 7)); + tripletList.push_back(IntTriplet(1, 8, 8)); + tripletList.push_back(IntTriplet(1, 9, 9)); + + tripletList.push_back(IntTriplet(4, 3, 10)); + tripletList.push_back(IntTriplet(4, 6, 11)); + tripletList.push_back(IntTriplet(4, 9, 12)); + + tripletList.push_back(IntTriplet(6, 0, 13)); + tripletList.push_back(IntTriplet(8, 9, 14)); + + esm.setFromTriplets(tripletList.begin(), tripletList.end()); + + // make compressed, important for initialize() + esm.makeCompressed(); + + ASSERT_NO_THROW(ssm->initialize(esm)); + + ASSERT_NO_THROW(ssm->finalize()); + + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::ReadReady); + + int target = -1; + + for (auto &coeff: tripletList) { + ASSERT_TRUE(ssm->getValue(coeff.row() + 1, coeff.col() + 1, &target)); + ASSERT_EQ(target, coeff.value()); + } +} + +TEST(StaticSparseMatrixTest, ConversionFromSparseEigen_RowMajor_SparseMatrixTest) { + // 10 rows, 15 non zero entries + mrmc::sparse::StaticSparseMatrix *ssm = new mrmc::sparse::StaticSparseMatrix(10); + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::UnInitialized); + + Eigen::SparseMatrix esm(10, 10); + + typedef Eigen::Triplet IntTriplet; + std::vector tripletList; + tripletList.reserve(15); + tripletList.push_back(IntTriplet(1, 0, 0)); + tripletList.push_back(IntTriplet(1, 1, 1)); + tripletList.push_back(IntTriplet(1, 2, 2)); + tripletList.push_back(IntTriplet(1, 3, 3)); + tripletList.push_back(IntTriplet(1, 4, 4)); + tripletList.push_back(IntTriplet(1, 5, 5)); + tripletList.push_back(IntTriplet(1, 6, 6)); + tripletList.push_back(IntTriplet(1, 7, 7)); + tripletList.push_back(IntTriplet(1, 8, 8)); + tripletList.push_back(IntTriplet(1, 9, 9)); + + tripletList.push_back(IntTriplet(4, 3, 10)); + tripletList.push_back(IntTriplet(4, 6, 11)); + tripletList.push_back(IntTriplet(4, 9, 12)); + + tripletList.push_back(IntTriplet(6, 0, 13)); + tripletList.push_back(IntTriplet(8, 9, 14)); + + esm.setFromTriplets(tripletList.begin(), tripletList.end()); + + // make compressed, important for initialize() + esm.makeCompressed(); + + ASSERT_NO_THROW(ssm->initialize(esm)); + + ASSERT_NO_THROW(ssm->finalize()); + + ASSERT_EQ(ssm->getState(), mrmc::sparse::StaticSparseMatrix::MatrixStatus::ReadReady); + + int target = -1; + + for (auto &coeff: tripletList) { + ASSERT_TRUE(ssm->getValue(coeff.row() + 1, coeff.col() + 1, &target)); + ASSERT_EQ(target, coeff.value()); + } +} \ No newline at end of file