#include // To detect whether the usage of TBB is possible, this include is neccessary #include "storm-config.h" #ifdef STORM_HAVE_INTELTBB #include "tbb/tbb.h" #endif #include "src/storage/SparseMatrix.h" #include "src/exceptions/InvalidStateException.h" #include "log4cplus/logger.h" #include "log4cplus/loggingmacros.h" extern log4cplus::Logger logger; namespace storm { namespace storage { template MatrixEntry::MatrixEntry(IndexType column, ValueType value) : entry(column, value) { // Intentionally left empty. } template MatrixEntry::MatrixEntry(std::pair&& pair) : entry(std::move(pair)) { // Intentionally left empty. } template IndexType const& MatrixEntry::getColumn() const { return this->entry.first; } template void MatrixEntry::setColumn(IndexType const& column) { this->entry.first = column; } template ValueType const& MatrixEntry::getValue() const { return this->entry.second; } template void MatrixEntry::setValue(ValueType const& value) { this->entry.second = value; } template std::pair const& MatrixEntry::getColumnValuePair() const { return this->entry; } template SparseMatrixBuilder::SparseMatrixBuilder(IndexType rows, IndexType columns, IndexType entries, bool hasCustomRowGrouping, IndexType rowGroups) : rowCountSet(rows != 0), rowCount(rows), columnCountSet(columns != 0), columnCount(columns), entryCount(entries), hasCustomRowGrouping(hasCustomRowGrouping), rowGroupCountSet(rowGroups != 0), rowGroupCount(rowGroups), rowGroupIndices(), storagePreallocated(rows != 0 && columns != 0 && entries != 0), columnsAndValues(), rowIndications(), currentEntryCount(0), lastRow(0), lastColumn(0), currentRowGroup(0) { this->prepareInternalStorage(); } template void SparseMatrixBuilder::addNextValue(IndexType row, IndexType column, ValueType const& value) { // Depending on whether the internal data storage was preallocated or not, adding the value is done somewhat // differently. if (storagePreallocated) { // Check whether the given row and column positions are valid and throw error otherwise. if (row >= rowCount || column >= columnCount) { throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrixBuilder::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 SparseMatrixBuilder::addNextValue: adding entry at out-of-bounds row " << row << " in matrix with " << rowCount << " rows."; } } if (columnCountSet) { if (column >= columnCount) { throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding entry at out-of-bounds column " << column << " in matrix with " << columnCount << " columns."; } } } // Check that we did not move backwards wrt. the row. if (row < lastRow) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in row " << row << ", but an element in row " << lastRow << " has already been added."; } // Check that we did not move backwards wrt. to column. if (row == lastRow && column < lastColumn) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in column " << column << " in row " << row << ", but an element in column " << lastColumn << " has already been added in that row."; } // If we switched to another row, we have to adjust the missing entries in the row indices vector. if (row != lastRow) { if (storagePreallocated) { // If the storage was preallocated, we can access the elements in the vectors with the subscript // operator. for (IndexType i = lastRow + 1; i <= row; ++i) { rowIndications[i] = currentEntryCount; } } else { // Otherwise, we need to push the correct values to the vectors, which might trigger reallocations. for (IndexType i = lastRow + 1; i <= row; ++i) { rowIndications.push_back(currentEntryCount); } } if (!hasCustomRowGrouping) { for (IndexType i = lastRow + 1; i <= row; ++i) { rowGroupIndices.push_back(i); ++currentRowGroup; } } lastRow = row; } lastColumn = column; // Finally, set the element and increase the current size. if (storagePreallocated) { columnsAndValues[currentEntryCount] = std::make_pair(column, value); } else { columnsAndValues.emplace_back(column, value); if (!columnCountSet) { columnCount = std::max(columnCount, column + 1); } if (!rowCountSet) { rowCount = row + 1; } } ++currentEntryCount; } template void SparseMatrixBuilder::newRowGroup(IndexType startingRow) { if (!hasCustomRowGrouping) { throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::newRowGroup: matrix was not created to have a custom row grouping."; } else if (rowGroupIndices.size() > 0 && startingRow < rowGroupIndices.back()) { throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::newRowGroup: illegal row group with negative size."; } if (rowGroupCountSet) { rowGroupIndices[currentRowGroup] = startingRow; ++currentRowGroup; } else { rowGroupIndices.push_back(startingRow); } } template SparseMatrix SparseMatrixBuilder::build(IndexType overriddenRowCount, IndexType overriddenColumnCount, IndexType overriddenRowGroupCount) { // Check whether it's safe to finalize the matrix and throw error otherwise. if (storagePreallocated && currentEntryCount != entryCount) { throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::build: expected " << entryCount << " entries, but got " << currentEntryCount << " instead."; } else { // Fill in the missing entries in the row indices array, as there may be empty rows at the end. if (storagePreallocated) { for (IndexType i = lastRow + 1; i < rowCount; ++i) { rowIndications[i] = currentEntryCount; } } else { if (!rowCountSet) { rowCount = std::max(overriddenRowCount, rowCount); } for (IndexType i = lastRow + 1; i < rowCount; ++i) { rowIndications.push_back(currentEntryCount); } } // We put a sentinel element at the last position of the row indices array. This eases iteration work, // as now the indices of row i are always between rowIndications[i] and rowIndications[i + 1], also for // the first and last row. if (storagePreallocated) { rowIndications[rowCount] = currentEntryCount; } else { rowIndications.push_back(currentEntryCount); if (!columnCountSet) { columnCount = std::max(columnCount, overriddenColumnCount); } } entryCount = currentEntryCount; if (hasCustomRowGrouping && rowGroupCountSet) { rowGroupIndices[rowGroupCount] = rowCount; } else { if (!hasCustomRowGrouping) { for (IndexType i = currentRowGroup; i < rowCount; ++i) { rowGroupIndices.push_back(i + 1); } } else { overriddenRowGroupCount = std::max(overriddenRowGroupCount, currentRowGroup + 1); for (IndexType i = currentRowGroup; i < overriddenRowGroupCount; ++i) { rowGroupIndices.push_back(rowCount); } } } } return SparseMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); } template void SparseMatrixBuilder::prepareInternalStorage() { // Only allocate the memory for the matrix contents if the dimensions of the matrix are already known. if (storagePreallocated) { columnsAndValues = std::vector>(entryCount, MatrixEntry(0, storm::utility::constantZero())); rowIndications = std::vector(rowCount + 1, 0); } else { rowIndications.push_back(0); } // Only allocate the memory for the row grouping of the matrix contents if the number of groups is already // known. if (hasCustomRowGrouping && rowGroupCountSet) { rowGroupIndices = std::vector(rowGroupCount + 1, 0); } else { if (hasCustomRowGrouping) { // Nothing to do in this case } else { rowGroupIndices.push_back(0); } } } template SparseMatrix::rows::rows(iterator begin, IndexType entryCount) : beginIterator(begin), entryCount(entryCount) { // Intentionally left empty. } template typename SparseMatrix::iterator SparseMatrix::rows::begin() { return beginIterator; } template typename SparseMatrix::iterator SparseMatrix::rows::end() { return beginIterator + entryCount; } template typename SparseMatrix::IndexType SparseMatrix::rows::getNumberOfEntries() const { return this->entryCount; } template SparseMatrix::const_rows::const_rows(const_iterator begin, IndexType entryCount) : beginIterator(begin), entryCount(entryCount) { // Intentionally left empty. } template typename SparseMatrix::const_iterator SparseMatrix::const_rows::begin() const { return beginIterator; } template typename SparseMatrix::const_iterator SparseMatrix::const_rows::end() const { return beginIterator + entryCount; } template typename SparseMatrix::IndexType SparseMatrix::const_rows::getNumberOfEntries() const { return this->entryCount; } template SparseMatrix::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { // Intentionally left empty. } template SparseMatrix::SparseMatrix(SparseMatrix const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { // Intentionally left empty. } template SparseMatrix::SparseMatrix(SparseMatrix&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { // Now update the source matrix other.rowCount = 0; other.columnCount = 0; other.entryCount = 0; } template SparseMatrix::SparseMatrix(IndexType columnCount, std::vector const& rowIndications, std::vector> const& columnsAndValues, std::vector const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { for (auto const& element : *this) { if (element.getValue() != storm::utility::constantZero()) { ++this->nonzeroEntryCount; } } } template SparseMatrix::SparseMatrix(IndexType columnCount, std::vector&& rowIndications, std::vector>&& columnsAndValues, std::vector&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { for (auto const& element : *this) { if (element.getValue() != storm::utility::constantZero()) { ++this->nonzeroEntryCount; } } } template SparseMatrix& SparseMatrix::operator=(SparseMatrix const& other) { // Only perform assignment if source and target are not the same. if (this != &other) { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = other.columnsAndValues; rowIndications = other.rowIndications; rowGroupIndices = other.rowGroupIndices; } return *this; } template SparseMatrix& SparseMatrix::operator=(SparseMatrix&& other) { // Only perform assignment if source and target are not the same. if (this != &other) { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = std::move(other.columnsAndValues); rowIndications = std::move(other.rowIndications); rowGroupIndices = std::move(other.rowGroupIndices); } return *this; } template bool SparseMatrix::operator==(SparseMatrix const& other) const { if (this == &other) { return true; } bool equalityResult = true; equalityResult &= this->getRowCount() == other.getRowCount(); equalityResult &= this->getColumnCount() == other.getColumnCount(); equalityResult &= this->getRowGroupIndices() == other.getRowGroupIndices(); // 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 (IndexType row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2; ++it1, ++it2) { // Skip over all zero entries in both matrices. while (it1 != ite1 && it1->getValue() == storm::utility::constantZero()) { ++it1; } while (it2 != ite2 && it2->getValue() == storm::utility::constantZero()) { ++it2; } if ((it1 == ite1) || (it2 == ite2)) { equalityResult = (it1 == ite1) ^ (it2 == ite2); break; } else { if (it1->getColumn() != it2->getColumn() || it1->getValue() != it2->getValue()) { equalityResult = false; break; } } } } return equalityResult; } template typename SparseMatrix::IndexType SparseMatrix::getRowCount() const { return rowCount; } template typename SparseMatrix::IndexType SparseMatrix::getColumnCount() const { return columnCount; } template typename SparseMatrix::IndexType SparseMatrix::getEntryCount() const { return entryCount; } template typename SparseMatrix::IndexType SparseMatrix::getNonzeroEntryCount() const { return nonzeroEntryCount; } template typename SparseMatrix::IndexType SparseMatrix::getRowGroupCount() const { return rowGroupIndices.size() - 1; } template typename SparseMatrix::IndexType SparseMatrix::getRowGroupSize(IndexType group) const { return this->getRowGroupIndices()[group + 1] - this->getRowGroupIndices()[group]; } template std::vector::IndexType> const& SparseMatrix::getRowGroupIndices() const { return rowGroupIndices; } template void SparseMatrix::makeRowsAbsorbing(storm::storage::BitVector const& rows) { for (auto row : rows) { makeRowDirac(row, row); } } template void SparseMatrix::makeRowGroupsAbsorbing(storm::storage::BitVector const& rowGroupConstraint) { for (auto rowGroup : rowGroupConstraint) { for (IndexType row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) { makeRowDirac(row, rowGroup); } } } template void SparseMatrix::makeRowDirac(IndexType row, IndexType column) { iterator columnValuePtr = this->begin(row); iterator columnValuePtrEnd = this->end(row); // If the row has no elements in it, we cannot make it absorbing, because we would need to move all elements // in the vector of nonzeros otherwise. if (columnValuePtr >= columnValuePtrEnd) { throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::makeRowDirac: cannot make row " << row << " absorbing, but there is no entry in this row."; } // If there is at least one entry in this row, we can just set it to one, modify its column value to the // one given by the parameter and set all subsequent elements of this row to zero. columnValuePtr->setColumn(column); columnValuePtr->setValue(storm::utility::constantOne()); ++columnValuePtr; for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) { columnValuePtr->setColumn(0); columnValuePtr->setValue(storm::utility::constantZero()); } } template ValueType SparseMatrix::getConstrainedRowSum(IndexType row, storm::storage::BitVector const& constraint) const { ValueType result = storm::utility::constantZero(); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { if (constraint.get(it->getColumn())) { result += it->getValue(); } } return result; } template std::vector SparseMatrix::getConstrainedRowSumVector(storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint) const { std::vector result(rowConstraint.getNumberOfSetBits()); IndexType currentRowCount = 0; for (auto row : rowConstraint) { result[currentRowCount++] = getConstrainedRowSum(row, columnConstraint); } return result; } template std::vector SparseMatrix::getConstrainedRowGroupSumVector(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint) const { std::vector result; result.reserve(rowGroupConstraint.getNumberOfSetBits()); for (auto rowGroup : rowGroupConstraint) { for (IndexType row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) { result.push_back(getConstrainedRowSum(row, columnConstraint)); } } return result; } template SparseMatrix SparseMatrix::getSubmatrix(bool useGroups, storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint, bool insertDiagonalElements) const { if (useGroups) { return getSubmatrix(rowConstraint, columnConstraint, this->getRowGroupIndices(), insertDiagonalElements); } else { // Create a fake row grouping to reduce this to a call to a more general method. std::vector fakeRowGroupIndices(rowCount + 1); IndexType i = 0; for (std::vector::iterator it = fakeRowGroupIndices.begin(); it != fakeRowGroupIndices.end(); ++it, ++i) { *it = i; } return getSubmatrix(rowConstraint, columnConstraint, fakeRowGroupIndices, insertDiagonalElements); } } template SparseMatrix SparseMatrix::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint, std::vector const& rowGroupIndices, bool insertDiagonalEntries) const { // First, we need to determine the number of entries and the number of rows of the submatrix. IndexType subEntries = 0; IndexType subRows = 0; for (auto index : rowGroupConstraint) { subRows += rowGroupIndices[index + 1] - rowGroupIndices[index]; for (IndexType i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) { bool foundDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { if (columnConstraint.get(it->getColumn())) { ++subEntries; if (index == it->getColumn()) { foundDiagonalElement = true; } } } // If requested, we need to reserve one entry more for inserting the diagonal zero entry. if (insertDiagonalEntries && !foundDiagonalElement) { ++subEntries; } } } // Create and initialize resulting matrix. SparseMatrixBuilder matrixBuilder(subRows, columnConstraint.getNumberOfSetBits(), subEntries, true); // Create a temporary vector 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. IndexType lastIndex = 0; IndexType currentNumberOfSetBits = 0; // If we are requested to add missing diagonal entries, we need to make sure the corresponding rows are also // taken. storm::storage::BitVector columnBitCountConstraint = columnConstraint; if (insertDiagonalEntries) { columnBitCountConstraint |= rowGroupConstraint; } for (auto index : columnBitCountConstraint) { while (lastIndex <= index) { bitsSetBeforeIndex.push_back(currentNumberOfSetBits); ++lastIndex; } ++currentNumberOfSetBits; } // Copy over selected entries. IndexType rowCount = 0; for (auto index : rowGroupConstraint) { matrixBuilder.newRowGroup(rowCount); for (IndexType i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) { bool insertedDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { if (columnConstraint.get(it->getColumn())) { if (index == it->getColumn()) { insertedDiagonalElement = true; } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > index) { matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero()); insertedDiagonalElement = true; } matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->getColumn()], it->getValue()); } } if (insertDiagonalEntries && !insertedDiagonalElement) { matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero()); } ++rowCount; } } return matrixBuilder.build(); } template SparseMatrix SparseMatrix::selectRowsFromRowGroups(std::vector const& rowGroupToRowIndexMapping, bool insertDiagonalEntries) const { // First, we need to count how many non-zero entries the resulting matrix will have and reserve space for // diagonal entries if requested. IndexType subEntries = 0; for (IndexType rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) { // Determine which row we need to select from the current row group. IndexType rowToCopy = rowGroupIndices[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex]; // Iterate through that row and count the number of slots we have to reserve for copying. bool foundDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { if (it->getColumn() == rowGroupIndex) { foundDiagonalElement = true; } ++subEntries; } if (insertDiagonalEntries && !foundDiagonalElement) { ++subEntries; } } // Now create the matrix to be returned with the appropriate size. SparseMatrixBuilder matrixBuilder(rowGroupIndices.size() - 1, columnCount, subEntries); // Copy over the selected lines from the source matrix. for (IndexType rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) { // Determine which row we need to select from the current row group. IndexType rowToCopy = rowGroupIndices[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex]; // Iterate through that row and copy the entries. This also inserts a zero element on the diagonal if // there is no entry yet. bool insertedDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { if (it->getColumn() == rowGroupIndex) { insertedDiagonalElement = true; } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > rowGroupIndex) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero()); insertedDiagonalElement = true; } matrixBuilder.addNextValue(rowGroupIndex, it->getColumn(), it->getValue()); } if (insertDiagonalEntries && !insertedDiagonalElement) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero()); } } // Finalize created matrix and return result. return matrixBuilder.build(); } template SparseMatrix SparseMatrix::transpose(bool joinGroups) const { IndexType rowCount = this->getColumnCount(); IndexType columnCount = joinGroups ? this->getRowGroupCount() : this->getRowCount(); IndexType entryCount = this->getEntryCount(); std::vector rowIndications(rowCount + 1); std::vector> columnsAndValues(entryCount); // First, we need to count how many entries each column has. for (IndexType group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { if (transition.getValue() != storm::utility::constantZero()) { ++rowIndications[transition.getColumn() + 1]; } } } // Now compute the accumulated offsets. for (IndexType i = 1; i < rowCount + 1; ++i) { rowIndications[i] = rowIndications[i - 1] + rowIndications[i]; } // Create an array that stores the index for the next value to be added for // each row in the transposed matrix. Initially this corresponds to the previously // computed accumulated offsets. std::vector nextIndices = rowIndications; // Now we are ready to actually fill in the values of the transposed matrix. for (IndexType group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { if (transition.getValue() != storm::utility::constantZero()) { columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue()); nextIndices[transition.getColumn()]++; } } } std::vector rowGroupIndices(rowCount + 1); for (IndexType i = 0; i <= rowCount; ++i) { rowGroupIndices[i] = i; } storm::storage::SparseMatrix transposedMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); return transposedMatrix; } template void SparseMatrix::convertToEquationSystem() { invertDiagonal(); negateAllNonDiagonalEntries(); } template void SparseMatrix::invertDiagonal() { // Now iterate over all row groups and set the diagonal elements to the inverted value. // If there is a row without the diagonal element, an exception is thrown. ValueType one = storm::utility::constantOne(); bool foundDiagonalElement = false; for (IndexType group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { if (entry.getColumn() == group) { entry.setValue(one - entry.getValue()); foundDiagonalElement = true; } } // Throw an exception if a row did not have an element on the diagonal. if (!foundDiagonalElement) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is missing diagonal entries."; } } } template void SparseMatrix::negateAllNonDiagonalEntries() { // Iterate over all row groups and negate all the elements that are not on the diagonal. for (IndexType group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { if (entry.getColumn() != group) { entry.setValue(-entry.getValue()); } } } } template void SparseMatrix::deleteDiagonalEntries() { // Iterate over all rows and negate all the elements that are not on the diagonal. for (IndexType group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { if (entry.getColumn() == group) { entry.setValue(storm::utility::constantZero()); } } } } template typename std::pair, storm::storage::SparseMatrix> SparseMatrix::getJacobiDecomposition() const { if (rowCount != columnCount) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is non-square."; } storm::storage::SparseMatrix resultLU(*this); resultLU.deleteDiagonalEntries(); SparseMatrixBuilder dInvBuilder(rowCount, columnCount, rowCount); // Copy entries to the appropriate matrices. for (IndexType rowNumber = 0; rowNumber < rowCount; ++rowNumber) { // Because the matrix may have several entries on the diagonal, we need to sum them before we are able // to invert the entry. ValueType diagonalValue = storm::utility::constantZero(); for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) { if (it->getColumn() == rowNumber) { diagonalValue += it->getValue(); } else if (it->getColumn() > rowNumber) { break; } } dInvBuilder.addNextValue(rowNumber, rowNumber, storm::utility::constantOne() / diagonalValue); } return std::make_pair(std::move(resultLU), dInvBuilder.build()); } template std::vector SparseMatrix::getPointwiseProductRowSumVector(storm::storage::SparseMatrix const& otherMatrix) const { 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 // given matrix does not have a non-zero element at this column position, or multiply the two entries and // add the result to the corresponding position in the vector. for (IndexType row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = otherMatrix.begin(row), ite2 = otherMatrix.end(row); it1 != ite1 && it2 != ite2; ++it1) { if (it1->getColumn() < it2->getColumn()) { continue; } else { // If the precondition of this method (i.e. that the given matrix is a submatrix // of the current one) was fulfilled, we know now that the two elements are in // the same column, so we can multiply and add them to the row sum vector. result[row] += it2->getValue() * it1->getValue(); ++it2; } } } return result; } template void SparseMatrix::multiplyWithVector(std::vector const& vector, std::vector& result) const { #ifdef STORM_HAVE_INTELTBB if (this->getNonzeroEntryCount() > 10000) { return this->multiplyWithVectorParallel(vector, result); } else { return this->multiplyWithVectorSequential(vector, result); } #else return multiplyWithVectorSequential(vector, result); #endif } template void SparseMatrix::multiplyWithVectorSequential(std::vector const& vector, std::vector& result) const { const_iterator it = this->begin(); const_iterator ite; typename std::vector::const_iterator rowIterator = rowIndications.begin(); typename std::vector::iterator resultIterator = result.begin(); typename std::vector::iterator resultIteratorEnd = result.end(); for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) { *resultIterator = storm::utility::constantZero(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { *resultIterator += it->getValue() * vector[it->getColumn()]; } } } #ifdef STORM_HAVE_INTELTBB template void SparseMatrix::multiplyWithVectorParallel(std::vector const& vector, std::vector& result) const { tbb::parallel_for(tbb::blocked_range(0, result.size(), 10), [&] (tbb::blocked_range const& range) { IndexType startRow = range.begin(); IndexType endRow = range.end(); const_iterator it = this->begin(startRow); const_iterator ite; std::vector::const_iterator rowIterator = this->rowIndications.begin() + startRow; std::vector::const_iterator rowIteratorEnd = this->rowIndications.begin() + endRow; typename std::vector::iterator resultIterator = result.begin() + startRow; typename std::vector::iterator resultIteratorEnd = result.begin() + endRow; for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) { *resultIterator = storm::utility::constantZero(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { *resultIterator += it->getValue() * vector[it->getColumn()]; } } }); } #endif template uint_fast64_t SparseMatrix::getSizeInMemory() const { uint_fast64_t size = sizeof(*this); // Add size of columns and values. size += sizeof(MatrixEntry) * columnsAndValues.capacity(); // Add row_indications size. size += sizeof(uint_fast64_t) * rowIndications.capacity(); return size; } template typename SparseMatrix::const_rows SparseMatrix::getRows(IndexType startRow, IndexType endRow) const { return const_rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]); } template typename SparseMatrix::rows SparseMatrix::getRows(IndexType startRow, IndexType endRow) { return rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]); } template typename SparseMatrix::const_rows SparseMatrix::getRow(IndexType row) const { return getRows(row, row); } template typename SparseMatrix::rows SparseMatrix::getRow(IndexType row) { return getRows(row, row); } template typename SparseMatrix::const_rows SparseMatrix::getRowGroup(IndexType rowGroup) const { return getRows(rowGroupIndices[rowGroup], rowGroupIndices[rowGroup + 1] - 1); } template typename SparseMatrix::rows SparseMatrix::getRowGroup(IndexType rowGroup) { return getRows(rowGroupIndices[rowGroup], rowGroupIndices[rowGroup + 1] - 1); } template typename SparseMatrix::const_iterator SparseMatrix::begin(IndexType row) const { return this->columnsAndValues.begin() + this->rowIndications[row]; } template typename SparseMatrix::iterator SparseMatrix::begin(IndexType row) { return this->columnsAndValues.begin() + this->rowIndications[row]; } template typename SparseMatrix::const_iterator SparseMatrix::end(IndexType row) const { return this->columnsAndValues.begin() + this->rowIndications[row + 1]; } template typename SparseMatrix::iterator SparseMatrix::end(IndexType row) { return this->columnsAndValues.begin() + this->rowIndications[row + 1]; } template typename SparseMatrix::const_iterator SparseMatrix::end() const { return this->columnsAndValues.begin() + this->rowIndications[rowCount]; } template typename SparseMatrix::iterator SparseMatrix::end() { return this->columnsAndValues.begin() + this->rowIndications[rowCount]; } template ValueType SparseMatrix::getRowSum(IndexType row) const { ValueType sum = storm::utility::constantZero(); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { sum += it->getValue(); } return sum; } template bool SparseMatrix::isSubmatrixOf(SparseMatrix const& matrix) const { // Check for matching sizes. if (this->getRowCount() != matrix.getRowCount()) return false; if (this->getColumnCount() != matrix.getColumnCount()) return false; if (this->getRowGroupIndices() != matrix.getRowGroupIndices()) return false; // Check the subset property for all rows individually. for (IndexType row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) { // Skip over all entries of the other matrix that are before the current entry in the current matrix. while (it2 != ite2 && it2->getColumn() < it1->getColumn()) { ++it2; } if (it2 == ite2 || it1->getColumn() != it2->getColumn()) { return false; } } } return true; } template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix) { // Print column numbers in header. out << "\t\t"; for (typename SparseMatrix::IndexType i = 0; i < matrix.getColumnCount(); ++i) { out << i << "\t"; } out << std::endl; // Iterate over all row groups. for (typename SparseMatrix::IndexType group = 0; group < matrix.getRowGroupCount(); ++group) { out << "\t---- group " << group << "/" << (matrix.getRowGroupCount() - 1) << " ---- " << std::endl; for (typename SparseMatrix::IndexType i = matrix.getRowGroupIndices()[group]; i < matrix.getRowGroupIndices()[group + 1]; ++i) { typename SparseMatrix::IndexType nextIndex = matrix.rowIndications[i]; // Print the actual row. out << i << "\t(\t"; typename SparseMatrix::IndexType currentRealIndex = 0; while (currentRealIndex < matrix.columnCount) { if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].getColumn()) { out << matrix.columnsAndValues[nextIndex].getValue() << "\t"; ++nextIndex; } else { out << "0\t"; } ++currentRealIndex; } out << "\t)\t" << i << std::endl; } } // Print column numbers in footer. out << "\t\t"; for (typename SparseMatrix::IndexType i = 0; i < matrix.getColumnCount(); ++i) { out << i << "\t"; } out << std::endl; return out; } template std::size_t SparseMatrix::hash() const { std::size_t result = 0; boost::hash_combine(result, this->getRowCount()); boost::hash_combine(result, this->getColumnCount()); boost::hash_combine(result, this->getEntryCount()); boost::hash_combine(result, boost::hash_range(columnsAndValues.begin(), columnsAndValues.end())); boost::hash_combine(result, boost::hash_range(rowIndications.begin(), rowIndications.end())); boost::hash_combine(result, boost::hash_range(rowGroupIndices.begin(), rowGroupIndices.end())); return result; } // Explicitly instantiate the entry, builder and the matrix. template class MatrixEntry::IndexType, double>; template class SparseMatrixBuilder; template class SparseMatrix; template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix); template class MatrixEntry::IndexType, int>; template class SparseMatrixBuilder; template class SparseMatrix; template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix); } // namespace storage } // namespace storm