From e08b61b9f7a8bf674a87b471c651bb40e5d0b6f2 Mon Sep 17 00:00:00 2001 From: dehnert Date: Mon, 9 Dec 2013 15:45:10 +0100 Subject: [PATCH] Added functional and performance tests for sparse matrix. Former-commit-id: dd9abe18269d2f0a6744383d9579301ca7773d6f --- CMakeLists.txt | 2 +- src/storage/SparseMatrix.cpp | 179 ++++-- src/storage/SparseMatrix.h | 15 +- test/functional/storage/BitVectorTest.cpp | 80 +-- test/functional/storage/SparseMatrixTest.cpp | 569 ++++++++++++++++++ test/performance/storage/BitVectorTest.cpp | 2 +- test/performance/storage/SparseMatrixTest.cpp | 43 ++ 7 files changed, 787 insertions(+), 103 deletions(-) create mode 100644 test/functional/storage/SparseMatrixTest.cpp create mode 100644 test/performance/storage/SparseMatrixTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 31d82172c..d5d8d8872 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,7 +144,7 @@ else(CLANG) # As CLANG is not set as a variable, we need to set it in case we have not matched another compiler. set (CLANG ON) # Set standard flags for clang - set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -funroll-loops -O3") + set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -funroll-loops -O4") if(UNIX AND NOT APPLE AND NOT USE_LIBCXX) set(CLANG_STDLIB libstdc++) message(STATUS "StoRM - Linking against libstdc++") diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index c4e773d34..0a10291d2 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -181,8 +181,58 @@ namespace storm { return *this; } + template + bool SparseMatrix::operator==(SparseMatrix const& other) const { + if (this == &other) { + return true; + } + + if (this->isInitialized() != other.isInitialized()) { + return false; + } + + bool equalityResult = true; + + // If the matrix is initialized, we only care about the contents and not the auxiliary members. + if (!this->isInitialized()) { + equalityResult &= rowCountSet == other.rowCountSet; + equalityResult &= columnCountSet == other.columnCountSet; + equalityResult &= internalStatus == other.internalStatus; + equalityResult &= currentEntryCount == other.currentEntryCount; + equalityResult &= lastRow == other.lastRow; + equalityResult &= lastColumn == other.lastColumn; + } + + equalityResult &= rowCount == other.rowCount; + equalityResult &= columnCount == other.columnCount; + + // For the actual contents, we need to do a little bit more work, because we want to ignore elements that + // are set to zero, please they may be represented implicitly in the other matrix. + for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { + for (uint_fast64_t elem = rowIndications[row], elem2 = other.rowIndications[row]; elem < rowIndications[row + 1] && elem2 < other.rowIndications[row + 1]; ++elem, ++elem2) { + // Skip over all zero entries in both matrices. + while (elem < rowIndications[row + 1] && valueStorage[elem] == storm::utility::constantZero()) { + ++elem; + } + while (elem2 < other.rowIndications[row + 1] && other.valueStorage[elem2] == storm::utility::constantZero()) { + ++elem2; + } + if ((elem == rowIndications[row + 1]) ^ (elem2 == other.rowIndications[row + 1]) || columnIndications[elem] != other.columnIndications[elem2] || valueStorage[elem] != other.valueStorage[elem2]) { + equalityResult = false; + break; + } + } + } + + return equalityResult; + } + template void SparseMatrix::addNextValue(uint_fast64_t row, uint_fast64_t column, T const& value) { + if (this->isInitialized()) { + throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::addNextValue: adding an entry to an already initialized matrix."; + } + // Depending on whether the internal data storage was preallocated or not, adding the value is done somewhat // differently. if (storagePreallocated) { @@ -190,13 +240,16 @@ namespace storm { if (row >= rowCount || column >= columnCount) { throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::addNextValue: adding entry at out-of-bounds position (" << row << ", " << column << ") in matrix of size (" << rowCount << ", " << columnCount << ")."; } - } else if (rowCountSet) { - if (row >= rowCount) { - throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::addNextValue: adding entry at out-of-bounds row " << row << " in matrix with " << rowCount << " rows."; + } else { + if (rowCountSet) { + if (row >= rowCount) { + throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::addNextValue: adding entry at out-of-bounds row " << row << " in matrix with " << rowCount << " rows."; + } } - } else if (columnCountSet) { - if (column >= columnCount) { - throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::addNextValue: adding entry at out-of-bounds column " << column << " in matrix with " << columnCount << " columns."; + if (columnCountSet) { + if (column >= columnCount) { + throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::addNextValue: adding entry at out-of-bounds column " << column << " in matrix with " << columnCount << " columns."; + } } } @@ -249,8 +302,8 @@ namespace storm { template void SparseMatrix::finalize(uint_fast64_t overriddenRowCount, uint_fast64_t overridenColumnCount) { // Check whether it's safe to finalize the matrix and throw error otherwise. - if (internalStatus == INITIALIZED) { - throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::finalize: finalizing an initialized matrix is forbidden."; + if (this->isInitialized()) { + throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::finalize: matrix has already been initialized."; } else if (storagePreallocated && currentEntryCount != entryCount) { throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::finalize: expected " << entryCount << " entries, but got " << currentEntryCount << " instead."; } else { @@ -287,26 +340,30 @@ namespace storm { template uint_fast64_t SparseMatrix::getRowCount() const { + checkReady("getRowCount"); return rowCount; } template uint_fast64_t SparseMatrix::getColumnCount() const { + checkReady("getColumnCount"); return columnCount; } template - bool SparseMatrix::isInitialized() { + bool SparseMatrix::isInitialized() const { return internalStatus == INITIALIZED; } template uint_fast64_t SparseMatrix::getEntryCount() const { + checkReady("getEntryCount"); return entryCount; } template void SparseMatrix::makeRowsAbsorbing(storm::storage::BitVector const& rows) { + checkReady("makeRowsAbsorbing"); for (auto row : rows) { makeRowAbsorbing(row, row); } @@ -314,6 +371,7 @@ namespace storm { template void SparseMatrix::makeRowsAbsorbing(storm::storage::BitVector const& rowGroupConstraint, std::vector const& rowGroupIndices) { + checkReady("makeRowsAbsorbing"); for (auto rowGroup : rowGroupConstraint) { for (uint_fast64_t row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) { makeRowAbsorbing(row, rowGroup); @@ -323,6 +381,7 @@ namespace storm { template void SparseMatrix::makeRowAbsorbing(const uint_fast64_t row, const uint_fast64_t column) { + checkReady("makeRowAbsorbing"); if (row > rowCount) { throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::makeRowAbsorbing: access to row " << row << " is out of bounds."; } @@ -352,6 +411,7 @@ namespace storm { template T SparseMatrix::getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const { + checkReady("getConstrainedRowSum"); T result(0); for (uint_fast64_t i = rowIndications[row]; i < rowIndications[row + 1]; ++i) { if (constraint.get(columnIndications[i])) { @@ -363,6 +423,7 @@ namespace storm { template std::vector SparseMatrix::getConstrainedRowSumVector(storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint) const { + checkReady("getConstrainedRowSumVector"); std::vector result(rowConstraint.getNumberOfSetBits()); uint_fast64_t currentRowCount = 0; for (auto row : rowConstraint) { @@ -373,6 +434,7 @@ namespace storm { template std::vector SparseMatrix::getConstrainedRowSumVector(storm::storage::BitVector const& rowGroupConstraint, std::vector const& rowGroupIndices, storm::storage::BitVector const& columnConstraint) const { + checkReady("getConstrainedRowSumVector"); std::vector result; result.reserve(rowGroupConstraint.getNumberOfSetBits()); for (auto rowGroup : rowGroupConstraint) { @@ -385,55 +447,13 @@ namespace storm { template SparseMatrix SparseMatrix::getSubmatrix(storm::storage::BitVector const& constraint) const { - // Check whether we select at least some rows and columns. - if (constraint.getNumberOfSetBits() == 0) { - throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::getSubmatrix: cannot create empty submatrix."; - } - - // First, we need to determine the number of entries of the submatrix. - uint_fast64_t subEntries = 0; - for (auto rowIndex : constraint) { - for (uint_fast64_t i = rowIndications[rowIndex]; i < rowIndications[rowIndex + 1]; ++i) { - if (constraint.get(columnIndications[i])) { - ++subEntries; - } - } + // Create a fake row grouping to reduce this to a call to a more general method. + std::vector rowGroupIndices(rowCount + 1); + uint_fast64_t i = 0; + for (std::vector::iterator it = rowGroupIndices.begin(); it != rowGroupIndices.end(); ++it, ++i) { + *it = i; } - - // Create and initialize resulting matrix. - SparseMatrix result(constraint.getNumberOfSetBits(), constraint.getNumberOfSetBits(), subEntries); - - // Create a temporary vecotr that stores for each index whose bit is set to true the number of bits that - // were set before that particular index. - std::vector bitsSetBeforeIndex; - bitsSetBeforeIndex.reserve(columnCount); - - // Compute the information to fill this vector. - uint_fast64_t lastIndex = 0; - uint_fast64_t currentNumberOfSetBits = 0; - for (auto index : constraint) { - while (lastIndex <= index) { - bitsSetBeforeIndex.push_back(currentNumberOfSetBits); - ++lastIndex; - } - ++currentNumberOfSetBits; - } - - // Copy over selected entries and use the previously computed vector to get the column offset. - uint_fast64_t rowCount = 0; - for (auto rowIndex : constraint) { - for (uint_fast64_t i = rowIndications[rowIndex]; i < rowIndications[rowIndex + 1]; ++i) { - if (constraint.get(columnIndications[i])) { - result.addNextValue(rowCount, bitsSetBeforeIndex[columnIndications[i]], valueStorage[i]); - } - } - - ++rowCount; - } - - // Finalize submatrix and return result. - result.finalize(); - return result; + return getSubmatrix(constraint, constraint, rowGroupIndices); } template @@ -443,6 +463,7 @@ namespace storm { template SparseMatrix SparseMatrix::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint, std::vector const& rowGroupIndices, bool insertDiagonalEntries) const { + checkReady("getSubmatrix"); // First, we need to determine the number of entries and the number of rows of the submatrix. uint_fast64_t subEntries = 0; uint_fast64_t subRows = 0; @@ -525,6 +546,7 @@ namespace storm { template SparseMatrix SparseMatrix::getSubmatrix(std::vector const& rowGroupToRowIndexMapping, std::vector const& rowGroupIndices, bool insertDiagonalEntries) const { + checkReady("getSubmatrix"); // First, we need to count how many non-zero entries the resulting matrix will have and reserve space for // diagonal entries if requested. uint_fast64_t subEntries = 0; @@ -577,7 +599,8 @@ namespace storm { template SparseMatrix SparseMatrix::transpose() const { - + checkReady("transpose"); + uint_fast64_t rowCount = this->columnCount; uint_fast64_t columnCount = this->rowCount; uint_fast64_t entryCount = this->entryCount; @@ -628,6 +651,8 @@ namespace storm { template void SparseMatrix::invertDiagonal() { + checkReady("invertDiagonal"); + // Check if the matrix is square, because only then it makes sense to perform this // transformation. if (this->getRowCount() != this->getColumnCount()) { @@ -660,6 +685,8 @@ namespace storm { template void SparseMatrix::negateAllNonDiagonalEntries() { + checkReady("negateAllNonDiagonalEntries"); + // Check if the matrix is square, because only then it makes sense to perform this transformation. if (this->getRowCount() != this->getColumnCount()) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is non-square."; @@ -680,6 +707,8 @@ namespace storm { template void SparseMatrix::deleteDiagonalEntries() { + checkReady("deleteDiagonalEntries"); + // Check if the matrix is square, because only then it makes sense to perform this transformation. if (this->getRowCount() != this->getColumnCount()) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::deleteDiagonalEntries: matrix is non-square."; @@ -700,6 +729,8 @@ namespace storm { template typename std::pair, storm::storage::SparseMatrix> SparseMatrix::getJacobiDecomposition() const { + checkReady("getJacobiDecomposition"); + if (rowCount != columnCount) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is non-square."; } @@ -730,6 +761,8 @@ namespace storm { template std::vector SparseMatrix::getPointwiseProductRowSumVector(storm::storage::SparseMatrix const& otherMatrix) const { + checkReady("getPointwiseProductRowSumVector"); + std::vector result(rowCount, storm::utility::constantZero()); // Iterate over all elements of the current matrix and either continue with the next element in case the @@ -755,6 +788,7 @@ namespace storm { template void SparseMatrix::multiplyWithVector(std::vector const& vector, std::vector& result) const { + checkReady("multiplyWithVector"); #ifdef STORM_HAVE_INTELTBB tbb::parallel_for(tbb::blocked_range(0, result.size()), tbbHelper_MatrixRowVectorScalarProduct, std::vector, T>(this, &vector, &result)); #else @@ -778,6 +812,8 @@ namespace storm { template uint_fast64_t SparseMatrix::getSizeInMemory() const { + checkReady("getSizeInMemory"); + uint_fast64_t size = sizeof(*this); // Add value_storage size. @@ -794,56 +830,67 @@ namespace storm { template typename SparseMatrix::const_rows SparseMatrix::getRows(uint_fast64_t startRow, uint_fast64_t endRow) const { + checkReady("getRows"); return const_rows(this->valueStorage.data() + this->rowIndications[startRow], this->columnIndications.data() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]); } template typename SparseMatrix::rows SparseMatrix::getRows(uint_fast64_t startRow, uint_fast64_t endRow) { + checkReady("getRows"); return rows(this->valueStorage.data() + this->rowIndications[startRow], this->columnIndications.data() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]); } template typename SparseMatrix::const_rows SparseMatrix::getRow(uint_fast64_t row) const { + checkReady("getRow"); return getRows(row, row); } template typename SparseMatrix::rows SparseMatrix::getRow(uint_fast64_t row) { + checkReady("getRow"); return getRows(row, row); } template typename SparseMatrix::const_iterator SparseMatrix::begin(uint_fast64_t row) const { + checkReady("begin"); return const_iterator(this->valueStorage.data() + this->rowIndications[row], this->columnIndications.data() + this->rowIndications[row]); } template typename SparseMatrix::iterator SparseMatrix::begin(uint_fast64_t row) { + checkReady("begin"); return iterator(this->valueStorage.data() + this->rowIndications[row], this->columnIndications.data() + this->rowIndications[row]); } template typename SparseMatrix::const_iterator SparseMatrix::end(uint_fast64_t row) const { + checkReady("end"); return const_iterator(this->valueStorage.data() + this->rowIndications[row + 1], this->columnIndications.data() + this->rowIndications[row + 1]); } template typename SparseMatrix::iterator SparseMatrix::end(uint_fast64_t row) { + checkReady("end"); return iterator(this->valueStorage.data() + this->rowIndications[row + 1], this->columnIndications.data() + this->rowIndications[row + 1]); } template typename SparseMatrix::const_iterator SparseMatrix::end() const { + checkReady("end"); return const_iterator(this->valueStorage.data() + this->rowIndications[rowCount], this->columnIndications.data() + this->rowIndications[rowCount]); } template typename SparseMatrix::iterator SparseMatrix::end() { + checkReady("end"); return iterator(this->valueStorage.data() + this->rowIndications[rowCount], this->columnIndications.data() + this->rowIndications[rowCount]); } template T SparseMatrix::getRowSum(uint_fast64_t row) const { + checkReady("getRowSum"); T sum = storm::utility::constantZero(); for (typename std::vector::const_iterator valueIterator = valueStorage.begin() + rowIndications[row], valueIteratorEnd = valueStorage.begin() + rowIndications[row + 1]; valueIterator != valueIteratorEnd; ++valueIterator) { sum += *valueIterator; @@ -853,13 +900,15 @@ namespace storm { template bool SparseMatrix::isSubmatrixOf(SparseMatrix const& matrix) const { + checkReady("isSubmatrixOf"); + // Check for matching sizes. if (this->getRowCount() != matrix.getRowCount()) return false; if (this->getColumnCount() != matrix.getColumnCount()) return false; // Check the subset property for all rows individually. for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { - for (uint_fast64_t elem = rowIndications[row], elem2 = matrix.rowIndications[row]; elem < rowIndications[row + 1] && elem < matrix.rowIndications[row + 1]; ++elem) { + for (uint_fast64_t elem = rowIndications[row], elem2 = matrix.rowIndications[row]; elem < rowIndications[row + 1]; ++elem) { // Skip over all entries of the other matrix that are before the current entry in the current matrix. while (elem2 < matrix.rowIndications[row + 1] && matrix.columnIndications[elem2] < columnIndications[elem]) { ++elem2; @@ -874,6 +923,8 @@ namespace storm { template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix) { + matrix.checkReady("operator<<"); + // Print column numbers in header. out << "\t\t"; for (uint_fast64_t i = 0; i < matrix.columnCount; ++i) { @@ -937,13 +988,22 @@ namespace storm { } } + template + void SparseMatrix::checkReady(std::string const& methodName) const { + if (!this->isInitialized()) { + throw storm::exceptions::InvalidStateException() << "Invalid call to SparseMatrix::" << methodName << ": matrix used for operation has not been initialized properly."; + } + } + // Explicitly instantiate the matrix and the nested classes. template class SparseMatrix::BaseIterator; template class SparseMatrix::BaseIterator; template class SparseMatrix; + template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix); template class SparseMatrix::BaseIterator; template class SparseMatrix::BaseIterator; template class SparseMatrix; + template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix); #ifdef STORM_HAVE_INTELTBB @@ -973,7 +1033,6 @@ namespace storm { #endif - } // namespace storage } // namespace storm diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index 680cee636..4c9d43627 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -295,6 +295,14 @@ namespace storm { */ SparseMatrix& operator=(SparseMatrix&& other); + /*! + * Determines whether the current and the given matrix are semantically equal. + * + * @param other The matrix with which to compare the current matrix. + * @return True iff the given matrix is semantically equal to the current one. + */ + bool operator==(SparseMatrix const& other) const; + /*! * Sets the matrix entry at the given row and column to the given value. After all entries have been added, * a call to finalize(false) is mandatory. @@ -352,7 +360,7 @@ namespace storm { * * @return True iff the matrix was initialized properly and is ready for further use. */ - bool isInitialized(); + bool isInitialized() const; /*! * This function makes the given rows absorbing. @@ -634,6 +642,11 @@ namespace storm { * will cause occasional reallocations. */ void prepareInternalStorage(); + + /*! + * Checks whether the matrix is properly initialized and throws an exception otherwise. + */ + void checkReady(std::string const& methodName) const; // A flag indicating whether the number of rows was set upon construction. bool rowCountSet; diff --git a/test/functional/storage/BitVectorTest.cpp b/test/functional/storage/BitVectorTest.cpp index 226c44421..2a3b94de5 100644 --- a/test/functional/storage/BitVectorTest.cpp +++ b/test/functional/storage/BitVectorTest.cpp @@ -3,7 +3,7 @@ #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/OutOfRangeException.h" -TEST(BitVectorTest, InitToZeroTest) { +TEST(BitVectorTest, InitToZero) { storm::storage::BitVector vector(32); for (uint_fast64_t i = 0; i < 32; ++i) { @@ -14,7 +14,7 @@ TEST(BitVectorTest, InitToZeroTest) { ASSERT_FALSE(vector.full()); } -TEST(BitVectorTest, InitToOneTest) { +TEST(BitVectorTest, InitToOne) { storm::storage::BitVector vector(32, true); for (uint_fast64_t i = 0; i < 32; ++i) { @@ -24,11 +24,11 @@ TEST(BitVectorTest, InitToOneTest) { ASSERT_TRUE(vector.full()); } -TEST(BitVectorTest, InitFromIteratorTest) { +TEST(BitVectorTest, InitFromIterator) { std::vector valueVector = {0, 4, 10}; storm::storage::BitVector vector(32, valueVector.begin(), valueVector.end()); - ASSERT_EQ(vector.size(), 32); + ASSERT_EQ(32, vector.size()); for (uint_fast64_t i = 0; i < 32; ++i) { if (i == 0 || i == 4 || i == 10) { @@ -39,7 +39,7 @@ TEST(BitVectorTest, InitFromIteratorTest) { } } -TEST(BitVectorTest, GetSetTest) { +TEST(BitVectorTest, GetSet) { storm::storage::BitVector vector(32); for (uint_fast64_t i = 0; i < 32; ++i) { @@ -47,18 +47,18 @@ TEST(BitVectorTest, GetSetTest) { } for (uint_fast64_t i = 0; i < 32; ++i) { - ASSERT_EQ(vector.get(i), i % 2 == 0); + ASSERT_EQ(i % 2 == 0, vector.get(i)); } } -TEST(BitVectorTest, GetSetExceptionTest) { +TEST(BitVectorTest, GetSetException) { storm::storage::BitVector vector(32); ASSERT_THROW(vector.get(32), storm::exceptions::OutOfRangeException); ASSERT_THROW(vector.set(32), storm::exceptions::OutOfRangeException); } -TEST(BitVectorTest, ResizeTest) { +TEST(BitVectorTest, Resize) { storm::storage::BitVector vector(32); for (uint_fast64_t i = 0; i < 32; ++i) { @@ -67,8 +67,8 @@ TEST(BitVectorTest, ResizeTest) { vector.resize(70); - ASSERT_EQ(vector.size(), 70); - ASSERT_EQ(vector.getNumberOfSetBits(), 32); + ASSERT_EQ(70, vector.size()); + ASSERT_EQ(32, vector.getNumberOfSetBits()); for (uint_fast64_t i = 0; i < 32; ++i) { ASSERT_TRUE(vector.get(i)); @@ -82,8 +82,8 @@ TEST(BitVectorTest, ResizeTest) { vector.resize(72, true); - ASSERT_EQ(vector.size(), 72); - ASSERT_EQ(vector.getNumberOfSetBits(), 34); + ASSERT_EQ(72, vector.size()); + ASSERT_EQ(34, vector.getNumberOfSetBits()); for (uint_fast64_t i = 0; i < 32; ++i) { ASSERT_TRUE(vector.get(i)); @@ -98,19 +98,19 @@ TEST(BitVectorTest, ResizeTest) { } vector.resize(16, 0); - ASSERT_EQ(vector.size(), 16); - ASSERT_EQ(vector.getNumberOfSetBits(), 16); + ASSERT_EQ(16, vector.size()); + ASSERT_EQ(16, vector.getNumberOfSetBits()); for (uint_fast64_t i = 0; i < 16; ++i) { ASSERT_TRUE(vector.get(i)); } vector.resize(65, 1); - ASSERT_EQ(vector.size(), 65); + ASSERT_EQ(65, vector.size()); ASSERT_TRUE(vector.full()); } -TEST(BitVectorTest, OperatorAndTest) { +TEST(BitVectorTest, OperatorAnd) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -129,7 +129,7 @@ TEST(BitVectorTest, OperatorAndTest) { ASSERT_TRUE(andResult.get(31)); } -TEST(BitVectorTest, OperatorAndEqualTest) { +TEST(BitVectorTest, OperatorAndEqual) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -149,7 +149,7 @@ TEST(BitVectorTest, OperatorAndEqualTest) { } -TEST(BitVectorTest, OperatorOrTest) { +TEST(BitVectorTest, OperatorOr) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -168,7 +168,7 @@ TEST(BitVectorTest, OperatorOrTest) { ASSERT_FALSE(orResult.get(31)); } -TEST(BitVectorTest, OperatorOrEqualTest) { +TEST(BitVectorTest, OperatorOrEqual) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -187,7 +187,7 @@ TEST(BitVectorTest, OperatorOrEqualTest) { ASSERT_FALSE(vector1.get(31)); } -TEST(BitVectorTest, OperatorXorTest) { +TEST(BitVectorTest, OperatorXor) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -206,7 +206,7 @@ TEST(BitVectorTest, OperatorXorTest) { } } -TEST(BitVectorTest, OperatorModuloTest) { +TEST(BitVectorTest, OperatorModulo) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -240,7 +240,7 @@ TEST(BitVectorTest, OperatorModuloTest) { ASSERT_THROW(vector1 % vector3, storm::exceptions::InvalidArgumentException); } -TEST(BitVectorTest, OperatorNotTest) { +TEST(BitVectorTest, OperatorNot) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -256,7 +256,7 @@ TEST(BitVectorTest, OperatorNotTest) { } } -TEST(BitVectorTest, ComplementTest) { +TEST(BitVectorTest, Complement) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -272,7 +272,7 @@ TEST(BitVectorTest, ComplementTest) { } } -TEST(BitVectorTest, ImpliesTest) { +TEST(BitVectorTest, Implies) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32, true); @@ -291,7 +291,7 @@ TEST(BitVectorTest, ImpliesTest) { ASSERT_TRUE(impliesResult.get(31)); } -TEST(BitVectorTest, SubsetTest) { +TEST(BitVectorTest, Subset) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32, true); @@ -306,7 +306,7 @@ TEST(BitVectorTest, SubsetTest) { ASSERT_FALSE(vector1.isSubsetOf(vector2)); } -TEST(BitVectorTest, DisjointTest) { +TEST(BitVectorTest, Disjoint) { storm::storage::BitVector vector1(32); storm::storage::BitVector vector2(32); @@ -322,7 +322,7 @@ TEST(BitVectorTest, DisjointTest) { ASSERT_FALSE(vector1.isDisjointFrom(vector2)); } -TEST(BitVectorTest, EmptyTest) { +TEST(BitVectorTest, Empty) { storm::storage::BitVector vector(32); ASSERT_TRUE(vector.empty()); @@ -337,7 +337,7 @@ TEST(BitVectorTest, EmptyTest) { ASSERT_TRUE(vector.empty()); } -TEST(BitVectorTest, FullTest) { +TEST(BitVectorTest, Full) { storm::storage::BitVector vector(32, true); ASSERT_TRUE(vector.full()); @@ -352,27 +352,27 @@ TEST(BitVectorTest, FullTest) { ASSERT_TRUE(vector.full()); } -TEST(BitVectorTest, NumberOfSetBitsTest) { +TEST(BitVectorTest, NumberOfSetBits) { storm::storage::BitVector vector(32); for (uint_fast64_t i = 0; i < 32; ++i) { vector.set(i, i % 2 == 0); } - ASSERT_EQ(vector.getNumberOfSetBits(), 16); + ASSERT_EQ(16, vector.getNumberOfSetBits()); } -TEST(BitVectorTest, NumberOfSetBitsBeforeIndexTest) { +TEST(BitVectorTest, NumberOfSetBitsBeforeIndex) { storm::storage::BitVector vector(32); for (uint_fast64_t i = 0; i < 32; ++i) { vector.set(i, i % 2 == 0); } - ASSERT_EQ(vector.getNumberOfSetBitsBeforeIndex(14), 7); + ASSERT_EQ(7, vector.getNumberOfSetBitsBeforeIndex(14)); } -TEST(BitVectorTest, BeginEndTest) { +TEST(BitVectorTest, BeginEnd) { storm::storage::BitVector vector(32); ASSERT_TRUE(vector.begin() == vector.end()); @@ -386,20 +386,20 @@ TEST(BitVectorTest, BeginEndTest) { ASSERT_TRUE(vector.begin() == vector.end()); } -TEST(BitVectorTest, NextSetIndexTest) { +TEST(BitVectorTest, NextSetIndex) { storm::storage::BitVector vector(32); vector.set(14); vector.set(17); - ASSERT_EQ(vector.getNextSetIndex(14), 14); - ASSERT_EQ(vector.getNextSetIndex(15), 17); - ASSERT_EQ(vector.getNextSetIndex(16), 17); - ASSERT_EQ(vector.getNextSetIndex(17), 17); - ASSERT_EQ(vector.getNextSetIndex(18), vector.size()); + ASSERT_EQ(14, vector.getNextSetIndex(14)); + ASSERT_EQ(17, vector.getNextSetIndex(15)); + ASSERT_EQ(17, vector.getNextSetIndex(16)); + ASSERT_EQ(17, vector.getNextSetIndex(17)); + ASSERT_EQ(vector.size(), vector.getNextSetIndex(18)); } -TEST(BitVectorTest, IteratorTest) { +TEST(BitVectorTest, Iterator) { storm::storage::BitVector vector(32); for (uint_fast64_t i = 0; i < 32; ++i) { diff --git a/test/functional/storage/SparseMatrixTest.cpp b/test/functional/storage/SparseMatrixTest.cpp new file mode 100644 index 000000000..4788e51eb --- /dev/null +++ b/test/functional/storage/SparseMatrixTest.cpp @@ -0,0 +1,569 @@ +#include "gtest/gtest.h" +#include "src/storage/SparseMatrix.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/OutOfRangeException.h" + +TEST(SparseMatrix, SimpleCreation) { + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix); + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix(3)); + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix(3, 4)); + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix(3, 4, 5)); +} + +TEST(SparseMatrix, CreationWithMovingContents) { + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix(4, {0, 2, 5, 5}, {1, 2, 0, 1, 3}, {1.0, 1.2, 0.5, 0.7, 0.2})); + storm::storage::SparseMatrix matrix(4, {0, 2, 5, 5}, {1, 2, 0, 1, 3}, {1.0, 1.2, 0.5, 0.7, 0.2}); + ASSERT_EQ(3, matrix.getRowCount()); + ASSERT_EQ(4, matrix.getColumnCount()); + ASSERT_EQ(5, matrix.getEntryCount()); +} + +TEST(SparseMatrix, CreationWithDimensions) { + storm::storage::SparseMatrix matrix(3, 4, 5); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_EQ(3, matrix.getRowCount()); + ASSERT_EQ(4, matrix.getColumnCount()); + ASSERT_EQ(5, matrix.getEntryCount()); +} + +TEST(SparseMatrix, CreationWithoutNumberOfEntries) { + storm::storage::SparseMatrix matrix(3, 4); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_EQ(3, matrix.getRowCount()); + ASSERT_EQ(4, matrix.getColumnCount()); + ASSERT_EQ(5, matrix.getEntryCount()); +} + +TEST(SparseMatrix, CreationWithNumberOfRows) { + storm::storage::SparseMatrix matrix(3); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_EQ(3, matrix.getRowCount()); + ASSERT_EQ(4, matrix.getColumnCount()); + ASSERT_EQ(5, matrix.getEntryCount()); +} + +TEST(SparseMatrix, CreationWithoutDimensions) { + storm::storage::SparseMatrix matrix; + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_EQ(2, matrix.getRowCount()); + ASSERT_EQ(4, matrix.getColumnCount()); + ASSERT_EQ(5, matrix.getEntryCount()); +} + +TEST(SparseMatrix, CopyConstruct) { + storm::storage::SparseMatrix matrix; + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_NO_THROW(storm::storage::SparseMatrix copy(matrix)); + storm::storage::SparseMatrix copy(matrix); + ASSERT_TRUE(matrix == copy); +} + +TEST(SparseMatrix, CopyAssign) { + storm::storage::SparseMatrix matrix; + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_NO_THROW(storm::storage::SparseMatrix copy = matrix); + storm::storage::SparseMatrix copy = matrix; + ASSERT_TRUE(matrix == copy); +} + +TEST(SparseMatrix, AddNextValue) { + storm::storage::SparseMatrix matrix(3, 4, 5); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_THROW(matrix.addNextValue(0, 4, 0.5), storm::exceptions::OutOfRangeException); + ASSERT_THROW(matrix.addNextValue(3, 1, 0.5), storm::exceptions::OutOfRangeException); + + storm::storage::SparseMatrix matrix2(3, 4); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 1.2)); + ASSERT_THROW(matrix2.addNextValue(0, 4, 0.5), storm::exceptions::OutOfRangeException); + ASSERT_THROW(matrix.addNextValue(3, 1, 0.5), storm::exceptions::OutOfRangeException); + + storm::storage::SparseMatrix matrix3(3); + ASSERT_NO_THROW(matrix3.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix3.addNextValue(1, 2, 1.2)); + ASSERT_NO_THROW(matrix3.addNextValue(2, 4, 0.5)); + ASSERT_THROW(matrix3.addNextValue(3, 1, 0.2), storm::exceptions::OutOfRangeException); + + storm::storage::SparseMatrix matrix4; + ASSERT_NO_THROW(matrix4.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix4.addNextValue(1, 2, 1.2)); + ASSERT_NO_THROW(matrix4.addNextValue(2, 4, 0.5)); + ASSERT_NO_THROW(matrix4.addNextValue(3, 1, 0.2)); +} + +TEST(SparseMatrix, Finalize) { + storm::storage::SparseMatrix matrix(3, 4, 5); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_FALSE(matrix.isInitialized()); + ASSERT_NO_THROW(matrix.finalize()); + ASSERT_TRUE(matrix.isInitialized()); + + storm::storage::SparseMatrix matrix2(3, 4, 5); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 0.7)); + ASSERT_THROW(matrix2.finalize(), storm::exceptions::InvalidStateException); + + storm::storage::SparseMatrix matrix3; + ASSERT_NO_THROW(matrix3.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix3.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix3.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix3.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix3.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix3.finalize()); + ASSERT_EQ(2, matrix3.getRowCount()); + ASSERT_EQ(4, matrix3.getColumnCount()); + ASSERT_EQ(5, matrix3.getEntryCount()); + + storm::storage::SparseMatrix matrix4; + ASSERT_NO_THROW(matrix4.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix4.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix4.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix4.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix4.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix4.finalize(4)); + ASSERT_EQ(4, matrix4.getRowCount()); + ASSERT_EQ(4, matrix4.getColumnCount()); + ASSERT_EQ(5, matrix4.getEntryCount()); + + storm::storage::SparseMatrix matrix5; + ASSERT_NO_THROW(matrix5.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix5.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix5.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix5.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix5.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix5.finalize(0, 6)); + ASSERT_EQ(2, matrix5.getRowCount()); + ASSERT_EQ(6, matrix5.getColumnCount()); + ASSERT_EQ(5, matrix5.getEntryCount()); +} + +TEST(SparseMatrix, MakeAbsorbing) { + storm::storage::SparseMatrix matrix(3, 4, 5); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.2)); + ASSERT_NO_THROW(matrix.finalize()); + + storm::storage::BitVector absorbingRows(3); + absorbingRows.set(1); + + ASSERT_NO_THROW(matrix.makeRowsAbsorbing(absorbingRows)); + + storm::storage::SparseMatrix matrix2(3, 4, 3); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 1)); + ASSERT_NO_THROW(matrix2.finalize()); + + ASSERT_TRUE(matrix == matrix2); +} + +TEST(SparseMatrix, MakeRowGroupAbsorbing) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + std::vector rowGroupIndices = {0, 2, 4, 5}; + + storm::storage::BitVector absorbingRowGroups(3); + absorbingRowGroups.set(1); + + ASSERT_NO_THROW(matrix.makeRowsAbsorbing(absorbingRowGroups, rowGroupIndices)); + + storm::storage::SparseMatrix matrix2; + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 1, 1)); + ASSERT_NO_THROW(matrix2.addNextValue(3, 1, 1)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix2.finalize()); + + ASSERT_TRUE(matrix == matrix2); +} + +TEST(SparseMatrix, ConstrainedRowSumVector) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + storm::storage::BitVector columnConstraint(4); + columnConstraint.set(1); + columnConstraint.set(3); + + ASSERT_NO_THROW(std::vector constrainedRowSum = matrix.getConstrainedRowSumVector(storm::storage::BitVector(5, true), columnConstraint)); + std::vector constrainedRowSum = matrix.getConstrainedRowSumVector(storm::storage::BitVector(5, true), columnConstraint); + ASSERT_TRUE(constrainedRowSum == std::vector({1.0, 0.7, 0, 0, 0.5})); + + storm::storage::SparseMatrix matrix2(5, 4, 9); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix2.addNextValue(3, 3, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix2.finalize()); + + std::vector rowGroupIndices = {0, 2, 4, 5}; + + storm::storage::BitVector rowGroupConstraint(3); + rowGroupConstraint.set(1); + + storm::storage::BitVector columnConstraint2(4); + columnConstraint2.set(2); + columnConstraint2.set(3); + + ASSERT_NO_THROW(std::vector constrainedRowSum2 = matrix2.getConstrainedRowSumVector(rowGroupConstraint, rowGroupIndices, columnConstraint2)); + std::vector constrainedRowSum2 = matrix2.getConstrainedRowSumVector(rowGroupConstraint, rowGroupIndices, columnConstraint2); + ASSERT_TRUE(constrainedRowSum2 == std::vector({0, 2.3})); +} + +TEST(SparseMatrix, Submatrix) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + std::vector rowGroupIndices = {0, 1, 2, 4, 5}; + + storm::storage::BitVector rowGroupConstraint(4); + rowGroupConstraint.set(2); + rowGroupConstraint.set(3); + + storm::storage::BitVector columnConstraint(4); + columnConstraint.set(0); + columnConstraint.set(3); + + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix2 = matrix.getSubmatrix(rowGroupConstraint, columnConstraint, rowGroupIndices, false)); + storm::storage::SparseMatrix matrix2 = matrix.getSubmatrix(rowGroupConstraint, columnConstraint, rowGroupIndices, false); + + storm::storage::SparseMatrix matrix3(3, 2, 3); + ASSERT_NO_THROW(matrix3.addNextValue(0, 0, 0.5)); + ASSERT_NO_THROW(matrix3.addNextValue(2, 0, 0.1)); + ASSERT_NO_THROW(matrix3.addNextValue(2, 1, 0.3)); + ASSERT_NO_THROW(matrix3.finalize()); + + ASSERT_TRUE(matrix2 == matrix3); + + std::vector rowGroupToIndexMapping = {0, 0, 1, 0}; + + ASSERT_NO_THROW(storm::storage::SparseMatrix matrix4 = matrix.getSubmatrix(rowGroupToIndexMapping, rowGroupIndices)); + storm::storage::SparseMatrix matrix4 = matrix.getSubmatrix(rowGroupToIndexMapping, rowGroupIndices); + + storm::storage::SparseMatrix matrix5(4, 4, 8); + ASSERT_NO_THROW(matrix5.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix5.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix5.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix5.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix5.addNextValue(2, 2, 1.1)); + ASSERT_NO_THROW(matrix5.addNextValue(3, 0, 0.1)); + ASSERT_NO_THROW(matrix5.addNextValue(3, 1, 0.2)); + ASSERT_NO_THROW(matrix5.addNextValue(3, 3, 0.3)); + ASSERT_NO_THROW(matrix5.finalize()); + + ASSERT_TRUE(matrix4 == matrix5); +} + +TEST(SparseMatrix, Transpose) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_NO_THROW(storm::storage::SparseMatrix transposeResult = matrix.transpose()); + storm::storage::SparseMatrix transposeResult = matrix.transpose(); + + storm::storage::SparseMatrix matrix2(4, 5, 9); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 4, 0.1)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 0, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 4, 0.2)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 0, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 3, 1.1)); + ASSERT_NO_THROW(matrix2.addNextValue(3, 4, 0.3)); + ASSERT_NO_THROW(matrix2.finalize()); + + ASSERT_TRUE(transposeResult == matrix2); +} + +TEST(SparseMatrix, EquationSystem) { + storm::storage::SparseMatrix matrix(4, 4, 7); + ASSERT_NO_THROW(matrix.addNextValue(0, 0, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(2, 2, 0.99)); + ASSERT_NO_THROW(matrix.addNextValue(3, 3, 0.11)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_NO_THROW(matrix.convertToEquationSystem()); + + storm::storage::SparseMatrix matrix2(4, 4, 7); + ASSERT_NO_THROW(matrix2.addNextValue(0, 0, 1 - 1.1)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, -1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 1 - 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 3, -0.7)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 0, -0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 2, 1 - 0.99)); + ASSERT_NO_THROW(matrix2.addNextValue(3, 3, 1 - 0.11)); + ASSERT_NO_THROW(matrix2.finalize()); + + ASSERT_TRUE(matrix == matrix2); +} + +TEST(SparseMatrix, JacobiDecomposition) { + storm::storage::SparseMatrix matrix(4, 4, 7); + ASSERT_NO_THROW(matrix.addNextValue(0, 0, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 3, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(2, 2, 0.99)); + ASSERT_NO_THROW(matrix.addNextValue(3, 3, 0.11)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_NO_THROW(matrix.getJacobiDecomposition()); + std::pair, storm::storage::SparseMatrix> jacobiDecomposition = matrix.getJacobiDecomposition(); + + storm::storage::SparseMatrix LU(4, 4, 3); + ASSERT_NO_THROW(LU.addNextValue(0, 1, 1.2)); + ASSERT_NO_THROW(LU.addNextValue(1, 3, 0.7)); + ASSERT_NO_THROW(LU.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(LU.finalize()); + + storm::storage::SparseMatrix Dinv(4, 4, 4); + ASSERT_NO_THROW(Dinv.addNextValue(0, 0, 1 / 1.1)); + ASSERT_NO_THROW(Dinv.addNextValue(1, 1, 1 / 0.5)); + ASSERT_NO_THROW(Dinv.addNextValue(2, 2, 1 / 0.99)); + ASSERT_NO_THROW(Dinv.addNextValue(3, 3, 1 / 0.11)); + ASSERT_NO_THROW(Dinv.finalize()); + + ASSERT_TRUE(LU == jacobiDecomposition.first); + ASSERT_TRUE(Dinv == jacobiDecomposition.second); +} + +TEST(SparseMatrix, PointwiseMultiplicationVector) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + storm::storage::SparseMatrix matrix2(5, 4, 9); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix2.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix2.finalize()); + + ASSERT_NO_THROW(std::vector pointwiseProductRowSums = matrix.getPointwiseProductRowSumVector(matrix2)); + std::vector pointwiseProductRowSums = matrix.getPointwiseProductRowSumVector(matrix2); + + std::vector correctResult = {1.0*1.0+1.2*1.2, 0.5*0.5+0.7*0.7, 0.5*0.5, 1.1*1.1, 0.1*0.1+0.2*0.2+0.3*0.3}; + ASSERT_TRUE(pointwiseProductRowSums == correctResult); +} + +TEST(SparseMatrix, MatrixVectorMultiply) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + std::vector x = {1, 0.3, 1.4, 7.1}; + std::vector result(matrix.getRowCount()); + + ASSERT_NO_THROW(matrix.multiplyWithVector(x, result)); + + std::vector correctResult = {1.0*0.3+1.2*1.4, 0.5*1+0.7*0.3, 0.5*1, 1.1*1.4, 0.1*1+0.2*0.3+0.3*7.1}; + ASSERT_TRUE(result == correctResult); +} + +TEST(SparseMatrix, Iteration) { + storm::storage::SparseMatrix matrix(5, 4, 9); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(2, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + for (auto const& entry : matrix.getRow(4)) { + if (entry.column() == 0) { + ASSERT_EQ(0.1, entry.value()); + } else if (entry.column() == 1) { + ASSERT_EQ(0.2, entry.value()); + } else if (entry.column() == 3) { + ASSERT_EQ(0.3, entry.value()); + } else { + ASSERT_TRUE(false); + } + } + + for (storm::storage::SparseMatrix::iterator it = matrix.begin(4), ite = matrix.end(4); it != ite; ++it) { + if (it.column() == 0) { + ASSERT_EQ(0.1, it.value()); + } else if (it.column() == 1) { + ASSERT_EQ(0.2, it.value()); + } else if (it.column() == 3) { + ASSERT_EQ(0.3, it.value()); + } else { + ASSERT_TRUE(false); + } + } +} + +TEST(SparseMatrix, RowSum) { + storm::storage::SparseMatrix matrix(5, 4, 8); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + ASSERT_EQ(0, matrix.getRowSum(2)); + ASSERT_EQ(0.1+0.2+0.3, matrix.getRowSum(4)); +} + +TEST(SparseMatrix, IsSubmatrix) { + storm::storage::SparseMatrix matrix(5, 4, 8); + ASSERT_NO_THROW(matrix.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix.addNextValue(0, 2, 1.2)); + ASSERT_NO_THROW(matrix.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix.addNextValue(3, 2, 1.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix.addNextValue(4, 3, 0.3)); + ASSERT_NO_THROW(matrix.finalize()); + + storm::storage::SparseMatrix matrix2(5, 4, 5); + ASSERT_NO_THROW(matrix2.addNextValue(0, 1, 1.0)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix2.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix2.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix2.finalize()); + + ASSERT_TRUE(matrix2.isSubmatrixOf(matrix)); + + storm::storage::SparseMatrix matrix3(5, 4, 5); + ASSERT_NO_THROW(matrix3.addNextValue(0, 3, 1.0)); + ASSERT_NO_THROW(matrix3.addNextValue(1, 0, 0.5)); + ASSERT_NO_THROW(matrix3.addNextValue(1, 1, 0.7)); + ASSERT_NO_THROW(matrix3.addNextValue(4, 0, 0.1)); + ASSERT_NO_THROW(matrix3.addNextValue(4, 1, 0.2)); + ASSERT_NO_THROW(matrix3.finalize()); + + ASSERT_FALSE(matrix3.isSubmatrixOf(matrix)); + ASSERT_FALSE(matrix3.isSubmatrixOf(matrix2)); +} \ No newline at end of file diff --git a/test/performance/storage/BitVectorTest.cpp b/test/performance/storage/BitVectorTest.cpp index a9486a2be..2aca461b2 100644 --- a/test/performance/storage/BitVectorTest.cpp +++ b/test/performance/storage/BitVectorTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "src/storage/BitVector.h" -TEST(BitVectorTest, IterationTest) { +TEST(BitVectorTest, Iteration) { storm::storage::BitVector vector(819200, true); for (uint_fast64_t i = 0; i < 10000; ++i) { diff --git a/test/performance/storage/SparseMatrixTest.cpp b/test/performance/storage/SparseMatrixTest.cpp new file mode 100644 index 000000000..c6c78d2b7 --- /dev/null +++ b/test/performance/storage/SparseMatrixTest.cpp @@ -0,0 +1,43 @@ +#include "gtest/gtest.h" +#include "src/storage/SparseMatrix.h" + +TEST(SparseMatrix, Iteration) { + storm::storage::SparseMatrix matrix; + for (uint_fast64_t row = 0; row < 10000; ++row) { + for (uint_fast64_t column = 0; column < row; ++column) { + ASSERT_NO_THROW(matrix.addNextValue(row, column, row+column)); + } + } + ASSERT_NO_THROW(matrix.finalize()); + + for (uint_fast64_t row = 0; row < matrix.getRowCount(); ++row) { + for (auto const& entry : matrix.getRow(row)) { + // The following can never be true, but prevents the compiler from optimizing away the loop. + if (entry.column() > matrix.getColumnCount()) { + ASSERT_TRUE(false); + } + } + } +} + +TEST(SparseMatrix, Multiplication) { + storm::storage::SparseMatrix matrix; + for (uint_fast64_t row = 0; row < 2000; ++row) { + for (uint_fast64_t column = 0; column < row; ++column) { + ASSERT_NO_THROW(matrix.addNextValue(row, column, row+column)); + } + } + ASSERT_NO_THROW(matrix.finalize()); + + std::vector x(matrix.getColumnCount(), 1.0); + std::vector result(matrix.getRowCount()); + + for (uint_fast64_t i = 0; i < 5000; ++i) { + matrix.multiplyWithVector(x, result); + + // The following can never be true, but prevents the compiler from optimizing away the loop. + if (result.size() > matrix.getRowCount()) { + ASSERT_TRUE(false); + } + } +} \ No newline at end of file