You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1232 lines
44 KiB
1232 lines
44 KiB
#ifndef STORM_STORAGE_SPARSEMATRIX_H_
|
|
#define STORM_STORAGE_SPARSEMATRIX_H_
|
|
|
|
#include <exception>
|
|
#include <new>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include "boost/integer/integer_mask.hpp"
|
|
|
|
#include "src/exceptions/InvalidStateException.h"
|
|
#include "src/exceptions/InvalidArgumentException.h"
|
|
#include "src/exceptions/OutOfRangeException.h"
|
|
#include "src/exceptions/FileIoException.h"
|
|
#include "src/storage/BitVector.h"
|
|
#include "src/storage/JacobiDecomposition.h"
|
|
|
|
#include "src/utility/ConstTemplates.h"
|
|
#include "Eigen/Sparse"
|
|
#include "gmm/gmm_matrix.h"
|
|
|
|
#include "log4cplus/logger.h"
|
|
#include "log4cplus/loggingmacros.h"
|
|
|
|
extern log4cplus::Logger logger;
|
|
|
|
// Forward declaration for adapter classes.
|
|
namespace storm {
|
|
namespace adapters{
|
|
class GmmxxAdapter;
|
|
class EigenAdapter;
|
|
class StormAdapter;
|
|
}
|
|
}
|
|
|
|
namespace storm {
|
|
namespace storage {
|
|
|
|
/*!
|
|
* A sparse matrix class with a constant number of non-zero entries.
|
|
* NOTE: Addressing *is* zero-based, so the valid range for getValue and addNextValue is 0..(rows - 1) where rows is the
|
|
* first argument to the constructor.
|
|
*/
|
|
template<class T>
|
|
class SparseMatrix {
|
|
public:
|
|
/*!
|
|
* Declare adapter classes as friends to use internal data.
|
|
*/
|
|
friend class storm::adapters::GmmxxAdapter;
|
|
friend class storm::adapters::EigenAdapter;
|
|
friend class storm::adapters::StormAdapter;
|
|
|
|
/*!
|
|
* If we only want to iterate over the columns or values of the non-zero entries of
|
|
* a row, we can simply iterate over the array (part) itself.
|
|
*/
|
|
typedef const uint_fast64_t* const ConstIndexIterator;
|
|
typedef const T* const ConstValueIterator;
|
|
|
|
/*!
|
|
* Iterator class that is able to iterate over the non-zero elements of a matrix and return the position
|
|
* (row, column) in addition to the value itself. This is a const iterator in the sense that it is not possible to
|
|
* modify the matrix with it.
|
|
*/
|
|
class ConstRowsIterator {
|
|
public:
|
|
/*!
|
|
* Constructs an iterator over the elements of the given matrix.
|
|
*
|
|
* @param matrix The matrix on which this iterator operates.
|
|
*/
|
|
ConstRowsIterator(SparseMatrix<T> const& matrix, uint_fast64_t rowIndex = 0) : matrix(matrix), posIndex(0), rowIndex(0) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
/*!
|
|
* Moves the iterator the next non-zero element of the matrix.
|
|
*
|
|
* @returns A reference to itself.
|
|
*/
|
|
ConstRowsIterator& operator++() {
|
|
++posIndex;
|
|
if (posIndex >= matrix.rowIndications[rowIndex + 1]) {
|
|
++rowIndex;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* Dereferencing operator for this iterator. Actually returns a reference to itself. This is
|
|
* needed, because the range-based for-loop in C++11 dereferences the iterator automatically.
|
|
*
|
|
* @returns A reference to itself.
|
|
*/
|
|
ConstRowsIterator& operator*() {
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* Comparison operator that compares the current iterator with the given one in terms of
|
|
* the indices they point to. Note that this does not check whether the iterators are
|
|
* interpreted over the same matrix.
|
|
*
|
|
* @return True iff the given iterator points to the same index as the current iterator.
|
|
*/
|
|
bool operator==(ConstRowsIterator const& other) const {
|
|
return this->posIndex == other.posIndex;
|
|
}
|
|
|
|
/*!
|
|
* Comparison operator that compares the current iterator with the given one in terms of
|
|
* the indices they point to. Note that this does not check whether the iterators are
|
|
* interpreted over the same matrix.
|
|
*
|
|
* @return True iff the given iterator points to a differnent index as the current iterator.
|
|
*/
|
|
bool operator!=(ConstRowsIterator const& other) const {
|
|
return this->posIndex != other.posIndex;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves the row that is associated with the current non-zero element this iterator
|
|
* points to.
|
|
*
|
|
* @returns The row of the current non-zero element this iterator points to.
|
|
*/
|
|
uint_fast64_t row() const {
|
|
return this->rowIndex;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves the column that is associated with the current non-zero element this iterator
|
|
* points to.
|
|
*
|
|
* @returns The column of the current non-zero element this iterator points to.
|
|
*/
|
|
uint_fast64_t column() const {
|
|
return matrix.columnIndications[posIndex];
|
|
}
|
|
|
|
/*!
|
|
* Retrieves the internal index of this iterator. This index corresponds to the position of
|
|
* the current element in the vector of non-zero values of the matrix.
|
|
*/
|
|
uint_fast64_t index() const {
|
|
return this->posIndex;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves the value of the current non-zero element this iterator points to.
|
|
*
|
|
* @returns The value of the current non-zero element this iterator points to.
|
|
*/
|
|
T const& value() const {
|
|
return matrix.valueStorage[posIndex];
|
|
}
|
|
|
|
/*!
|
|
* Moves the iterator to the beginning of the given row.
|
|
*
|
|
* @param row The row this iterator is to be moved to.
|
|
*/
|
|
void moveToRow(uint_fast64_t row) {
|
|
this->rowIndex = row;
|
|
this->posIndex = matrix.rowIndications[row];
|
|
}
|
|
|
|
/*!
|
|
* Moves the iterator to the beginning of the next row.
|
|
*/
|
|
void moveToNextRow() {
|
|
moveToRow(rowIndex + 1);
|
|
}
|
|
|
|
private:
|
|
// A constant reference to the matrix this iterator is associated with.
|
|
SparseMatrix<T> const& matrix;
|
|
|
|
// The current index in the list of all non-zero elements of the matrix this iterator points to.
|
|
uint_fast64_t posIndex;
|
|
|
|
// The row of the element this iterator currently points to.
|
|
uint_fast64_t rowIndex;
|
|
};
|
|
|
|
// Declare the iterator as a friend class to grant access to private data members of the matrix.
|
|
friend class ConstRowsIterator;
|
|
|
|
/*!
|
|
* An enum representing the internal state of the Matrix
|
|
* After creating the Matrix using the Constructor, the Object is in state UnInitialized. After
|
|
* calling initialize(), that state changes to Initialized and after all entries have been
|
|
* entered and finalize() has been called, to ReadReady.
|
|
* Should a critical error occur in any of the former functions, the state will change to Error.
|
|
*
|
|
* @see getState()
|
|
* @see isReadReady()
|
|
* @see isInitialized()
|
|
* @see hasError()
|
|
*/
|
|
enum MatrixStatus {
|
|
Error = -1, UnInitialized = 0, Initialized = 1, ReadReady = 2
|
|
};
|
|
|
|
/*!
|
|
* Constructs a sparse matrix object with the given number of rows.
|
|
*
|
|
* @param rows The number of rows of the matrix
|
|
*/
|
|
SparseMatrix(uint_fast64_t rows, uint_fast64_t cols) : rowCount(rows), colCount(cols),
|
|
nonZeroEntryCount(0), internalStatus(MatrixStatus::UnInitialized), currentSize(0), lastRow(0) {
|
|
|
|
}
|
|
|
|
/*!
|
|
* Constructs a square sparse matrix object with the given number rows.
|
|
*
|
|
* @param size The number of rows and columns of the matrix.
|
|
*/
|
|
SparseMatrix(uint_fast64_t size)
|
|
: rowCount(size), colCount(size), nonZeroEntryCount(0),
|
|
internalStatus(MatrixStatus::UnInitialized), currentSize(0), lastRow(0) {
|
|
|
|
}
|
|
|
|
/*!
|
|
* Initializes the sparse matrix with the given number of non-zero entries
|
|
* and prepares it for use with addNextValue() and finalize().
|
|
* Note: Calling this method before any other member function is mandatory.
|
|
* This version is to be used together with addNextValue().
|
|
*
|
|
* @param nonZeroEntries The number of non-zero entries this matrix is going to hold.
|
|
*/
|
|
void initialize(uint_fast64_t nonZeroEntries) {
|
|
// Check whether initializing the matrix is safe.
|
|
if (internalStatus != MatrixStatus::UnInitialized) {
|
|
triggerErrorState();
|
|
LOG4CPLUS_ERROR(logger, "Trying to initialize matrix that is not uninitialized.");
|
|
throw storm::exceptions::InvalidStateException("Trying to initialize matrix that is not uninitialized.");
|
|
} else if ((rowCount == 0) || (colCount == 0)) {
|
|
triggerErrorState();
|
|
LOG4CPLUS_ERROR(logger, "Trying to create initialize a matrix with 0 rows or 0 columns.");
|
|
throw storm::exceptions::InvalidArgumentException("Trying to create initialize a matrix with 0 rows or 0 columns.");
|
|
} else if ((rowCount * colCount) < nonZeroEntries) {
|
|
triggerErrorState();
|
|
LOG4CPLUS_ERROR(logger, "Trying to initialize a matrix with more non-zero entries than there can be.");
|
|
throw storm::exceptions::InvalidArgumentException("Trying to initialize a matrix with more non-zero entries than there can be.");
|
|
} else {
|
|
// If it is safe, initialize necessary members and prepare the
|
|
// internal storage.
|
|
nonZeroEntryCount = nonZeroEntries;
|
|
lastRow = 0;
|
|
|
|
if (!prepareInternalStorage()) {
|
|
triggerErrorState();
|
|
throw std::bad_alloc();
|
|
} else {
|
|
setState(MatrixStatus::Initialized);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Sets the matrix element at the given row and column to the given value.
|
|
* NOTE: This is a linear setter. It must be called consecutively for each element,
|
|
* row by row *and* column by column.
|
|
*
|
|
* @param row The row in which the matrix element is to be set.
|
|
* @param col The column in which the matrix element is to be set.
|
|
* @param value The value that is to be set.
|
|
*/
|
|
void addNextValue(const uint_fast64_t row, const uint_fast64_t col, T const& value) {
|
|
// Check whether the given row and column positions are valid and throw
|
|
// error otherwise.
|
|
if ((row > rowCount) || (col > colCount)) {
|
|
triggerErrorState();
|
|
LOG4CPLUS_ERROR(logger, "Trying to add a value at illegal position (" << row << ", " << col << ") in matrix of size (" << rowCount << ", " << colCount << ").");
|
|
throw storm::exceptions::OutOfRangeException("Trying to add a value at illegal position.");
|
|
}
|
|
|
|
// If we switched to another row, we have to adjust the missing
|
|
// entries in the row_indications array.
|
|
if (row != lastRow) {
|
|
for (uint_fast64_t i = lastRow + 1; i <= row; ++i) {
|
|
rowIndications[i] = currentSize;
|
|
}
|
|
lastRow = row;
|
|
}
|
|
|
|
// Finally, set the element and increase the current size.
|
|
valueStorage[currentSize] = value;
|
|
columnIndications[currentSize] = col;
|
|
|
|
++currentSize;
|
|
}
|
|
|
|
/*
|
|
* Finalizes the sparse matrix to indicate that initialization has been
|
|
* completed and the matrix may now be used.
|
|
*/
|
|
void finalize() {
|
|
// Check whether it's safe to finalize the matrix and throw error
|
|
// otherwise.
|
|
if (!isInitialized()) {
|
|
triggerErrorState();
|
|
LOG4CPLUS_ERROR(logger, "Trying to finalize an uninitialized matrix.");
|
|
throw storm::exceptions::InvalidStateException("Trying to finalize an uninitialized matrix.");
|
|
} else if (currentSize != nonZeroEntryCount) {
|
|
triggerErrorState();
|
|
LOG4CPLUS_ERROR(logger, "Trying to finalize a matrix that was initialized with more non-zero entries than given (expected " << nonZeroEntryCount << " but got " << currentSize << " instead)");
|
|
throw storm::exceptions::InvalidStateException() << "Trying to finalize a matrix that was initialized with more non-zero entries than given (expected " << nonZeroEntryCount << " but got " << currentSize << " instead).";
|
|
} else {
|
|
// Fill in the missing entries in the row_indications array.
|
|
// (Can happen because of empty rows at the end.)
|
|
if (lastRow != rowCount) {
|
|
for (uint_fast64_t i = lastRow + 1; i < rowCount; ++i) {
|
|
rowIndications[i] = currentSize;
|
|
}
|
|
}
|
|
|
|
// Set a sentinel element at the last position of the row_indications
|
|
// array. This eases iteration work, as now the indices of row i
|
|
// are always between row_indications[i] and row_indications[i + 1],
|
|
// also for the first and last row.
|
|
rowIndications[rowCount] = nonZeroEntryCount;
|
|
|
|
setState(MatrixStatus::ReadReady);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Gets the matrix element at the given row and column to the given value.
|
|
* Note: This function does not check the internal status for errors for performance reasons.
|
|
*
|
|
* @param row The row in which the element is to be read.
|
|
* @param col The column in which the element is to be read.
|
|
* @param target A pointer to the memory location where the read content is
|
|
* to be put.
|
|
* @returns True iff the value is set in the matrix, false otherwise.
|
|
* On false, 0 will be written to *target.
|
|
*/
|
|
inline bool getValue(uint_fast64_t row, uint_fast64_t col, T* const target) const {
|
|
// Check for illegal access indices.
|
|
if ((row > rowCount) || (col > colCount)) {
|
|
LOG4CPLUS_ERROR(logger, "Trying to read a value from illegal position (" << row << ", " << col << ").");
|
|
throw storm::exceptions::OutOfRangeException("Trying to read a value from illegal position.");
|
|
return false;
|
|
}
|
|
|
|
// In case the element is not on the diagonal, we have to iterate
|
|
// over the accessed row to find the element.
|
|
uint_fast64_t rowStart = rowIndications[row];
|
|
uint_fast64_t rowEnd = rowIndications[row + 1];
|
|
while (rowStart < rowEnd) {
|
|
// If the lement is found, write the content to the specified
|
|
// position and return true.
|
|
if (columnIndications[rowStart] == col) {
|
|
*target = valueStorage[rowStart];
|
|
return true;
|
|
}
|
|
|
|
// If the column of the current element is already larger than the
|
|
// requested column, the requested element cannot be contained
|
|
// in the matrix and we may therefore stop searching.
|
|
if (columnIndications[rowStart] > col) {
|
|
break;
|
|
}
|
|
++rowStart;
|
|
}
|
|
|
|
// Set 0 as the content and return false in case the element was not found.
|
|
*target = 0;
|
|
return false;
|
|
}
|
|
|
|
/*!
|
|
* Gets the matrix element at the given row and column in the form of a reference to it.
|
|
* Note: This function does not check the internal status for errors for performance reasons.
|
|
* Warning: It is possible to modify the matrix through this function. It may only be used
|
|
* for elements that exist in the sparse matrix. If the value at the requested position does not exist,
|
|
* an exception will be thrown.
|
|
|
|
* @param row The row in which the element is to be read.
|
|
* @param col The column in which the element is to be read.
|
|
*
|
|
* @return A reference to the value at the given position.
|
|
*/
|
|
inline T& getValue(uint_fast64_t row, uint_fast64_t col) {
|
|
// Check for illegal access indices.
|
|
if ((row > rowCount) || (col > colCount)) {
|
|
LOG4CPLUS_ERROR(logger, "Trying to read a value from illegal position (" << row << ", " << col << ").");
|
|
throw storm::exceptions::OutOfRangeException("Trying to read a value from illegal position.");
|
|
}
|
|
|
|
// we have to iterate
|
|
// over the accessed row to find the element.
|
|
uint_fast64_t rowStart = rowIndications[row];
|
|
uint_fast64_t rowEnd = rowIndications[row + 1];
|
|
while (rowStart < rowEnd) {
|
|
// If the lement is found, return it.
|
|
if (columnIndications[rowStart] == col) {
|
|
return valueStorage[rowStart];
|
|
}
|
|
|
|
// If the column of the current element is already larger than the
|
|
// requested column, the requested element cannot be contained
|
|
// in the matrix and we may therefore stop searching.
|
|
if (columnIndications[rowStart] > col) {
|
|
break;
|
|
}
|
|
++rowStart;
|
|
}
|
|
|
|
throw storm::exceptions::InvalidArgumentException("Trying to get a reference to a non-existant value.");
|
|
}
|
|
|
|
/*!
|
|
* Returns the number of rows of the matrix.
|
|
*
|
|
* @returns The number of rows of the matrix.
|
|
*/
|
|
uint_fast64_t getRowCount() const {
|
|
return rowCount;
|
|
}
|
|
|
|
/*!
|
|
* Returns the number of columns of the matrix.
|
|
*
|
|
* @returns The number of columns of the matrix.
|
|
*/
|
|
uint_fast64_t getColumnCount() const {
|
|
return colCount;
|
|
}
|
|
|
|
/*!
|
|
* Checks whether the internal status of the matrix makes it ready for
|
|
* reading access.
|
|
*
|
|
* @returns True iff the internal status of the matrix makes it ready for
|
|
* reading access.
|
|
*/
|
|
bool isReadReady() {
|
|
return (internalStatus == MatrixStatus::ReadReady);
|
|
}
|
|
|
|
/*!
|
|
* Checks whether the matrix was initialized previously. The matrix may
|
|
* still require to be finalized, even if this check returns true.
|
|
*
|
|
* @returns True iff the matrix was initialized previously.
|
|
*/
|
|
bool isInitialized() {
|
|
return (internalStatus == MatrixStatus::Initialized || internalStatus == MatrixStatus::ReadReady);
|
|
}
|
|
|
|
/*!
|
|
* Returns the internal state of the matrix.
|
|
*
|
|
* @returns The internal state of the matrix.
|
|
*/
|
|
MatrixStatus getState() {
|
|
return internalStatus;
|
|
}
|
|
|
|
/*!
|
|
* Checks whether the internal state of the matrix signals an error.
|
|
*
|
|
* @returns True iff the internal state of the matrix signals an error.
|
|
*/
|
|
bool hasError() const {
|
|
return (internalStatus == MatrixStatus::Error);
|
|
}
|
|
|
|
/*!
|
|
* Returns the number of non-zero entries in the matrix.
|
|
*
|
|
* @returns The number of non-zero entries in the matrix.
|
|
*/
|
|
uint_fast64_t getNonZeroEntryCount() const {
|
|
return nonZeroEntryCount;
|
|
}
|
|
|
|
/*!
|
|
* This function makes the rows given by the bit vector absorbing.
|
|
*
|
|
* @param rows A bit vector indicating which rows to make absorbing.
|
|
* @returns True iff the operation was successful.
|
|
*/
|
|
bool makeRowsAbsorbing(storm::storage::BitVector const& rows) {
|
|
bool result = true;
|
|
for (auto row : rows) {
|
|
result &= makeRowAbsorbing(row, row);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* This function makes the groups of rows given by the bit vector absorbing.
|
|
*
|
|
* @param rowGroupConstraint A bit vector indicating which row groups to make absorbing.
|
|
* @param rowGroupIndices A vector indicating which rows belong to a given row group.
|
|
* @return True iff the operation was successful.
|
|
*/
|
|
bool makeRowsAbsorbing(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices) {
|
|
bool result = true;
|
|
for (auto rowGroup : rowGroupConstraint) {
|
|
for (uint_fast64_t row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) {
|
|
result &= makeRowAbsorbing(row, rowGroup);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*!
|
|
* This function makes the given row absorbing. This means that all entries will be set to 0
|
|
* except the one at the specified column, which is set to 1 instead.
|
|
*
|
|
* @param row The row to be made absorbing.
|
|
* @param column The index of the column whose value is to be set to 1.
|
|
* @returns True iff the operation was successful.
|
|
*/
|
|
bool makeRowAbsorbing(const uint_fast64_t row, const uint_fast64_t column) {
|
|
// Check whether the accessed state exists.
|
|
if (row > rowCount) {
|
|
LOG4CPLUS_ERROR(logger, "Trying to make an illegal row " << row << " absorbing.");
|
|
throw storm::exceptions::OutOfRangeException("Trying to make an illegal row absorbing.");
|
|
return false;
|
|
}
|
|
|
|
// Iterate over the elements in the row that are not on the diagonal
|
|
// and set them to zero.
|
|
uint_fast64_t rowStart = rowIndications[row];
|
|
uint_fast64_t rowEnd = rowIndications[row + 1];
|
|
|
|
// 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 (rowStart >= rowEnd) {
|
|
LOG4CPLUS_ERROR(logger, "Cannot make row " << row << " absorbing, because there is no entry in this row.");
|
|
throw storm::exceptions::InvalidStateException() << "Cannot make row " << row << " absorbing, because there is no entry in this row.";
|
|
}
|
|
|
|
// If there is at least one nonzero entry in this row, we can just set it to one, modify its
|
|
// column indication to the one given by the parameter and set all subsequent elements of this
|
|
// row to zero.
|
|
valueStorage[rowStart] = storm::utility::constGetOne<T>();
|
|
columnIndications[rowStart] = column;
|
|
for (uint_fast64_t index = rowStart + 1; index < rowEnd; ++index) {
|
|
valueStorage[index] = storm::utility::constGetZero<T>();
|
|
columnIndications[index] = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Computes the sum of the elements in the given row whose column bits are set to one on the
|
|
* given constraint.
|
|
*
|
|
* @param row The row whose elements to add.
|
|
* @param constraint A bit vector that indicates which columns to add.
|
|
* @returns The sum of the elements in the given row whose column bits
|
|
* are set to one on the given constraint.
|
|
*/
|
|
T getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const {
|
|
T result(0);
|
|
for (uint_fast64_t i = rowIndications[row]; i < rowIndications[row + 1]; ++i) {
|
|
if (constraint.get(columnIndications[i])) {
|
|
result += valueStorage[i];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Computes a vector whose ith element is the sum of the elements in the ith row where only
|
|
* those elements are added whose bits are set to true in the given column constraint and only
|
|
* those rows are treated whose bits are set to true in the given row constraint.
|
|
*
|
|
* @param rowConstraint A bit vector that indicates for which rows to perform summation.
|
|
* @param columnConstraint A bit vector that indicates which columns to add.
|
|
*
|
|
* @returns A vector whose ith element is the sum of the elements in the ith row where only
|
|
* those elements are added whose bits are set to true in the given column constraint and only
|
|
* those rows are treated whose bits are set to true in the given row constraint.
|
|
*/
|
|
std::vector<T> getConstrainedRowSumVector(storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint) const {
|
|
std::vector<T> result(rowConstraint.getNumberOfSetBits());
|
|
uint_fast64_t currentRowCount = 0;
|
|
for (auto row : rowConstraint) {
|
|
result[currentRowCount++] = getConstrainedRowSum(row, columnConstraint);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Computes a vector whose elements represent the sums of selected (given by the column
|
|
* constraint) entries for all rows in selected row groups given by the row group constraint.
|
|
*
|
|
* @param rowGroupConstraint A bit vector that indicates which row groups are to be considered.
|
|
* @param rowGroupIndices A vector indicating which rows belong to a given row group.
|
|
* @param columnConstraint A bit vector that indicates which columns to add.
|
|
* @returns
|
|
*/
|
|
std::vector<T> getConstrainedRowSumVector(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, storm::storage::BitVector const& columnConstraint, uint_fast64_t numberOfRows) const {
|
|
std::vector<T> result(numberOfRows);
|
|
uint_fast64_t currentRowCount = 0;
|
|
for (auto rowGroup : rowGroupConstraint) {
|
|
for (uint_fast64_t row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) {
|
|
result[currentRowCount++] = getConstrainedRowSum(row, columnConstraint);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not
|
|
* set to one in the given bit vector.
|
|
*
|
|
* @param constraint A bit vector indicating which rows and columns to keep.
|
|
* @returns A matrix corresponding to a submatrix of the current matrix in which only rows and
|
|
* columns given by the constraint are kept and all others are dropped.
|
|
*/
|
|
SparseMatrix getSubmatrix(storm::storage::BitVector const& constraint) const {
|
|
LOG4CPLUS_DEBUG(logger, "Creating a sub-matrix with " << constraint.getNumberOfSetBits() << " rows.");
|
|
|
|
// Check for valid constraint.
|
|
if (constraint.getNumberOfSetBits() == 0) {
|
|
LOG4CPLUS_ERROR(logger, "Trying to create a sub-matrix of size 0.");
|
|
throw storm::exceptions::InvalidArgumentException("Trying to create a sub-matrix of size 0.");
|
|
}
|
|
|
|
// First, we need to determine the number of non-zero entries of the
|
|
// sub-matrix.
|
|
uint_fast64_t subNonZeroEntries = 0;
|
|
for (auto rowIndex : constraint) {
|
|
for (uint_fast64_t i = rowIndications[rowIndex]; i < rowIndications[rowIndex + 1]; ++i) {
|
|
if (constraint.get(columnIndications[i])) {
|
|
++subNonZeroEntries;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create and initialize resulting matrix.
|
|
SparseMatrix result(constraint.getNumberOfSetBits());
|
|
result.initialize(subNonZeroEntries);
|
|
|
|
// Create a temporary array that stores for each index whose bit is set
|
|
// to true the number of bits that were set before that particular index.
|
|
uint_fast64_t* bitsSetBeforeIndex = new uint_fast64_t[colCount];
|
|
uint_fast64_t lastIndex = 0;
|
|
uint_fast64_t currentNumberOfSetBits = 0;
|
|
for (auto index : constraint) {
|
|
while (lastIndex <= index) {
|
|
bitsSetBeforeIndex[lastIndex++] = currentNumberOfSetBits;
|
|
}
|
|
++currentNumberOfSetBits;
|
|
}
|
|
|
|
// Copy over selected entries.
|
|
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;
|
|
}
|
|
|
|
// Dispose of the temporary array.
|
|
delete[] bitsSetBeforeIndex;
|
|
|
|
// Finalize sub-matrix and return result.
|
|
result.finalize();
|
|
LOG4CPLUS_DEBUG(logger, "Done creating sub-matrix.");
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Creates a submatrix of the current matrix by keeping only row groups and columns in the given
|
|
* row group constraint.
|
|
*
|
|
* @param rowGroupConstraint A bit vector indicating which row groups and columns to keep.
|
|
* @param rowGroupIndices A vector indicating which rows belong to a given row group.
|
|
* @returns A matrix corresponding to a submatrix of the current matrix in which only row groups
|
|
* and columns given by the row group constraint are kept and all others are dropped.
|
|
*/
|
|
SparseMatrix getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices) const {
|
|
LOG4CPLUS_DEBUG(logger, "Creating a sub-matrix (of unknown size).");
|
|
|
|
// First, we need to determine the number of non-zero entries and the number of rows of the
|
|
// sub-matrix.
|
|
uint_fast64_t subNonZeroEntries = 0;
|
|
uint_fast64_t subRowCount = 0;
|
|
for (auto index : rowGroupConstraint) {
|
|
subRowCount += rowGroupIndices[index + 1] - rowGroupIndices[index];
|
|
for (uint_fast64_t i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
|
|
for (uint_fast64_t j = rowIndications[i]; j < rowIndications[i + 1]; ++j) {
|
|
if (rowGroupConstraint.get(columnIndications[j])) {
|
|
++subNonZeroEntries;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG4CPLUS_DEBUG(logger, "Determined size of submatrix to be " << subRowCount << "x" << rowGroupConstraint.getNumberOfSetBits() << ".");
|
|
|
|
// Create and initialize resulting matrix.
|
|
SparseMatrix result(subRowCount, rowGroupConstraint.getNumberOfSetBits());
|
|
result.initialize(subNonZeroEntries);
|
|
|
|
// Create a temporary array that stores for each index whose bit is set
|
|
// to true the number of bits that were set before that particular index.
|
|
uint_fast64_t* bitsSetBeforeIndex = new uint_fast64_t[colCount];
|
|
uint_fast64_t lastIndex = 0;
|
|
uint_fast64_t currentNumberOfSetBits = 0;
|
|
for (auto index : rowGroupConstraint) {
|
|
while (lastIndex <= index) {
|
|
bitsSetBeforeIndex[lastIndex++] = currentNumberOfSetBits;
|
|
}
|
|
++currentNumberOfSetBits;
|
|
}
|
|
|
|
// Copy over selected entries.
|
|
uint_fast64_t rowCount = 0;
|
|
for (auto index : rowGroupConstraint) {
|
|
for (uint_fast64_t i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
|
|
for (uint_fast64_t j = rowIndications[i]; j < rowIndications[i + 1]; ++j) {
|
|
if (rowGroupConstraint.get(columnIndications[j])) {
|
|
result.addNextValue(rowCount, bitsSetBeforeIndex[columnIndications[j]], valueStorage[j]);
|
|
}
|
|
}
|
|
++rowCount;
|
|
}
|
|
}
|
|
|
|
// Dispose of the temporary array.
|
|
delete[] bitsSetBeforeIndex;
|
|
|
|
// Finalize sub-matrix and return result.
|
|
result.finalize();
|
|
LOG4CPLUS_DEBUG(logger, "Done creating sub-matrix.");
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Performs a change to the matrix that is needed if this matrix is to be interpreted as a
|
|
* set of linear equations. In particular, it transforms A to (1-A), meaning that the elements
|
|
* on the diagonal are inverted with respect to addition and the other elements are negated.
|
|
*/
|
|
void convertToEquationSystem() {
|
|
invertDiagonal();
|
|
negateAllNonDiagonalElements();
|
|
}
|
|
|
|
/*!
|
|
* Inverts all elements on the diagonal, i.e. sets the diagonal values to 1 minus their previous
|
|
* value. Requires the matrix to contain each diagonal element and to be square.
|
|
*/
|
|
void invertDiagonal() {
|
|
// 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() << "SparseMatrix::invertDiagonal requires the Matrix to be square!";
|
|
}
|
|
|
|
// Now iterate over all rows and set the diagonal elements to the inverted value.
|
|
// If there is a row without the diagonal element, an exception is thrown.
|
|
T one = storm::utility::constGetOne<T>();
|
|
bool foundDiagonalElement = false;
|
|
for (uint_fast64_t row = 0; row < rowCount; ++row) {
|
|
uint_fast64_t rowStart = rowIndications[row];
|
|
uint_fast64_t rowEnd = rowIndications[row + 1];
|
|
foundDiagonalElement = false;
|
|
while (rowStart < rowEnd) {
|
|
if (columnIndications[rowStart] == row) {
|
|
valueStorage[rowStart] = one - valueStorage[rowStart];
|
|
foundDiagonalElement = true;
|
|
break;
|
|
}
|
|
++rowStart;
|
|
}
|
|
|
|
// Throw an exception if a row did not have an element on the diagonal.
|
|
if (!foundDiagonalElement) {
|
|
throw storm::exceptions::InvalidArgumentException() << "SparseMatrix::invertDiagonal requires the Matrix to contain all diagonal entries!";
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Negates all non-zero elements that are not on the diagonal.
|
|
*/
|
|
void negateAllNonDiagonalElements() {
|
|
// 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() << "SparseMatrix::invertDiagonal requires the Matrix to be square!";
|
|
}
|
|
|
|
// Iterate over all rows and negate all the elements that are not on the diagonal.
|
|
for (uint_fast64_t row = 0; row < rowCount; ++row) {
|
|
uint_fast64_t rowStart = rowIndications[row];
|
|
uint_fast64_t rowEnd = rowIndications[row + 1];
|
|
while (rowStart < rowEnd) {
|
|
if (columnIndications[rowStart] != row) {
|
|
valueStorage[rowStart] = - valueStorage[rowStart];
|
|
}
|
|
++rowStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Calculates the Jacobi-Decomposition of this sparse matrix.
|
|
* The source Sparse Matrix must be square.
|
|
* @return A pointer to a class containing the matrix L+U and the inverted diagonal matrix D^-1
|
|
*/
|
|
storm::storage::JacobiDecomposition<T>* getJacobiDecomposition() const {
|
|
uint_fast64_t rowCount = this->getRowCount();
|
|
uint_fast64_t colCount = this->getColumnCount();
|
|
if (rowCount != colCount) {
|
|
throw storm::exceptions::InvalidArgumentException() << "SparseMatrix::getJacobiDecomposition requires the Matrix to be square.";
|
|
}
|
|
storm::storage::SparseMatrix<T> *resultLU = new storm::storage::SparseMatrix<T>(*this);
|
|
storm::storage::SparseMatrix<T> *resultDinv = new storm::storage::SparseMatrix<T>(rowCount, colCount);
|
|
// no entries apart from those on the diagonal (rowCount)
|
|
resultDinv->initialize(rowCount);
|
|
|
|
// constant 1 for diagonal inversion
|
|
T constOne = storm::utility::constGetOne<T>();
|
|
|
|
// copy diagonal entries to other matrix
|
|
for (unsigned int i = 0; i < rowCount; ++i) {
|
|
resultDinv->addNextValue(i, i, constOne / resultLU->getValue(i, i));
|
|
resultLU->getValue(i, i) = storm::utility::constGetZero<T>();
|
|
}
|
|
|
|
return new storm::storage::JacobiDecomposition<T>(resultLU, resultDinv);
|
|
}
|
|
|
|
/*!
|
|
* Performs a pointwise matrix multiplication of the matrix with the given matrix and returns a
|
|
* vector containing the sum of the elements in each row of the resulting matrix.
|
|
*
|
|
* @param otherMatrix A reference to the matrix with which to perform the pointwise multiplication.
|
|
* This matrix must be a submatrix of the current matrix in the sense that it may not have
|
|
* non-zero entries at indices where there is a zero in the current matrix.
|
|
* @returns A vector containing the sum of the elements in each row of the matrix resulting from
|
|
* pointwise multiplication of the current matrix with the given matrix.
|
|
*/
|
|
std::vector<T> getPointwiseProductRowSumVector(storm::storage::SparseMatrix<T> const& otherMatrix) {
|
|
// Prepare result.
|
|
std::vector<T> result(rowCount, storm::utility::constGetZero<T>());
|
|
|
|
// 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 (uint_fast64_t row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) {
|
|
for (uint_fast64_t element = rowIndications[row], nextOtherElement = otherMatrix.rowIndications[row]; element < rowIndications[row + 1] && nextOtherElement < otherMatrix.rowIndications[row + 1]; ++element) {
|
|
if (columnIndications[element] < otherMatrix.columnIndications[nextOtherElement]) {
|
|
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] += otherMatrix.valueStorage[nextOtherElement] * valueStorage[element];
|
|
++nextOtherElement;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Multiplies the matrix with the given vector and writes the result to given result vector.
|
|
*
|
|
* @param vector The vector with which to multiply the matrix.
|
|
* @param result The vector that is supposed to hold the result of the multiplication after the
|
|
* operation.
|
|
* @returns The product of the matrix and the given vector as the content of the given result
|
|
* vector.
|
|
*/
|
|
void multiplyWithVector(std::vector<T> const& vector, std::vector<T>& result) const {
|
|
// Initialize two iterators that
|
|
ConstRowsIterator elementIt(*this);
|
|
ConstRowsIterator elementIte(*this, 1);
|
|
|
|
// Iterate over all positions of the result vector and compute its value as the scalar
|
|
// product of the corresponding row with the input vector.
|
|
// Note that only the end iterator has to be moved by one row, because the other iterator
|
|
// is automatically moved forward one row by the inner loop.
|
|
for (uint_fast64_t i = 0; i < result.size(); ++i, elementIte.moveToNextRow()) {
|
|
result[i] = storm::utility::constGetZero<T>();
|
|
|
|
// Perform the scalar product.
|
|
for (; elementIt != elementIte; ++elementIt) {
|
|
result[i] += elementIt.value() * vector[elementIt.column()];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Returns the size of the matrix in memory measured in bytes.
|
|
*
|
|
* @returns The size of the matrix in memory measured in bytes.
|
|
*/
|
|
uint_fast64_t getSizeInMemory() const {
|
|
uint_fast64_t size = sizeof(*this);
|
|
// Add value_storage size.
|
|
size += sizeof(T) * valueStorage.capacity();
|
|
// Add column_indications size.
|
|
size += sizeof(uint_fast64_t) * columnIndications.capacity();
|
|
// Add row_indications size.
|
|
size += sizeof(uint_fast64_t) * rowIndications.capacity();
|
|
return size;
|
|
}
|
|
|
|
/*!
|
|
* Returns a const iterator to the elements of the matrix.
|
|
* @param row If given, this specifies the starting row of the iterator.
|
|
*
|
|
* @returns An iterator to the elements of the matrix that cannot be used for modifying the
|
|
* contents.
|
|
*/
|
|
ConstRowsIterator begin(uint_fast64_t row = 0) const {
|
|
return ConstRowsIterator(*this, row);
|
|
}
|
|
|
|
/*!
|
|
* Returns a const iterator that points to the first element after the matrix.
|
|
*
|
|
* @returns A const iterator that points past the last element of the matrix.
|
|
*/
|
|
ConstRowsIterator end() const {
|
|
return ConstRowsIterator(this->rowCount);
|
|
}
|
|
|
|
/*!
|
|
* Returns a const iterator that points to the first element after the given row.
|
|
*
|
|
* @returns row The row past which this iterator has to point.
|
|
* @returns A const iterator that points to the first element after the given row.
|
|
*/
|
|
ConstRowsIterator end(uint_fast64_t row) const {
|
|
return ConstRowsIterator(row + 1);
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator to the columns of the non-zero entries of the matrix.
|
|
*
|
|
* @param row If given, the iterator will start at the specified row.
|
|
* @returns An iterator to the columns of the non-zero entries of the matrix.
|
|
*/
|
|
ConstIndexIterator constColumnIteratorBegin(uint_fast64_t row = 0) const {
|
|
return &(this->columnIndications[0]) + this->rowIndications[row];
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator that points to the first element after the matrix.
|
|
*
|
|
* @returns An iterator that points to the first element after the matrix.
|
|
*/
|
|
ConstIndexIterator constColumnIteratorEnd() const {
|
|
return &(this->columnIndications[0]) + this->rowIndications[rowCount];
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator that points to the first element after the given row.
|
|
*
|
|
* @param row The row past which this iterator has to point.
|
|
* @returns An iterator that points to the first element after the matrix.
|
|
*/
|
|
ConstIndexIterator constColumnIteratorEnd(uint_fast64_t row) const {
|
|
return &(this->columnIndications[0]) + this->rowIndications[row + 1];
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator to the values of the non-zero entries of the matrix.
|
|
*
|
|
* @param row If given, the iterator will start at the specified row.
|
|
* @returns An iterator to the values of the non-zero entries of the matrix.
|
|
*/
|
|
ConstValueIterator constValueIteratorBegin(uint_fast64_t row = 0) const {
|
|
return &(this->valueStorage[0]) + this->rowIndications[row];
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator that points to the first element after the matrix.
|
|
*
|
|
* @returns An iterator that points to the first element after the matrix.
|
|
*/
|
|
ConstValueIterator constValueIteratorEnd() const {
|
|
return &(this->valueStorage[0]) + this->rowIndications[rowCount];
|
|
}
|
|
|
|
/*!
|
|
* Returns an iterator that points to the first element after the given row.
|
|
*
|
|
* @param row The row past which this iterator has to point.
|
|
* @returns An iterator that points to the first element after the matrix.
|
|
*/
|
|
ConstValueIterator constValueIteratorEnd(uint_fast64_t row) const {
|
|
return &(this->valueStorage[0]) + this->rowIndications[row + 1];
|
|
}
|
|
|
|
/*!
|
|
* Computes the sum of the elements in a given row.
|
|
*
|
|
* @param row The row that should be summed.
|
|
* @return Sum of the row.
|
|
*/
|
|
T getRowSum(uint_fast64_t row) const {
|
|
T sum = storm::utility::constGetZero<T>();
|
|
for (auto it = this->constValueIteratorBegin(row), ite = this->constValueIteratorEnd(row); it != ite; ++it) {
|
|
sum += *it;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
/*!
|
|
* Checks if the given matrix is a submatrix of the current matrix, where A matrix A is called a
|
|
* submatrix of B if a value in A is only nonzero, if the value in B at the same position is
|
|
* also nonzero. Furthermore, A and B have to have the same size.
|
|
*
|
|
* @param matrix The matrix that is possibly a submatrix of the current matrix.
|
|
* @returns True iff the given matrix is a submatrix of the current matrix.
|
|
*/
|
|
bool containsAllPositionsOf(SparseMatrix<T> const& matrix) const {
|
|
// Check for mismatching 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) {
|
|
// 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;
|
|
}
|
|
if (!(elem2 < matrix.rowIndications[row + 1]) || columnIndications[elem] != matrix.columnIndications[elem2]) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves a compressed string representation of the matrix.
|
|
*
|
|
* @returns a compressed string representation of the matrix.
|
|
*/
|
|
std::string toStringCompressed() const {
|
|
std::stringstream result;
|
|
result << rowIndications << std::endl;
|
|
result << columnIndications << std::endl;
|
|
result << valueStorage << std::endl;
|
|
return result.str();
|
|
}
|
|
|
|
/*!
|
|
* Retrieves a (non-compressed) string representation of the matrix.
|
|
* Note: the matrix is presented densely. That is, all zeros are filled in and are part of the
|
|
* string representation, so calling this method on big matrices should be done with care.
|
|
*
|
|
* @param rowGroupIndices A vector indicating to which group of rows a given row belongs. If
|
|
* given, rows of different groups will be separated by a dashed line.
|
|
* @returns A (non-compressed) string representation of the matrix.
|
|
*/
|
|
std::string toString(std::vector<uint_fast64_t> const* rowGroupIndices = nullptr) const {
|
|
std::stringstream result;
|
|
uint_fast64_t currentNondeterministicChoiceIndex = 0;
|
|
|
|
// Print column numbers in header.
|
|
result << "\t\t";
|
|
for (uint_fast64_t i = 0; i < colCount; ++i) {
|
|
result << i << "\t";
|
|
}
|
|
result << std::endl;
|
|
|
|
// Iterate over all rows.
|
|
for (uint_fast64_t i = 0; i < rowCount; ++i) {
|
|
uint_fast64_t nextIndex = rowIndications[i];
|
|
|
|
// If we need to group rows, print a dashed line in case we have moved to the next group of rows.
|
|
if (rowGroupIndices != nullptr) {
|
|
if (i == (*rowGroupIndices)[currentNondeterministicChoiceIndex]) {
|
|
if (i != 0) {
|
|
result << "\t(\t";
|
|
for (uint_fast64_t j = 0; j < colCount - 2; ++j) {
|
|
result << "----";
|
|
if (j == 1) {
|
|
result << "\t" << currentNondeterministicChoiceIndex << "\t";
|
|
}
|
|
}
|
|
result << "\t)" << std::endl;
|
|
}
|
|
++currentNondeterministicChoiceIndex;
|
|
}
|
|
}
|
|
|
|
// Print the actual row.
|
|
result << i << "\t(\t";
|
|
uint_fast64_t currentRealIndex = 0;
|
|
while (currentRealIndex < colCount) {
|
|
if (nextIndex < rowIndications[i + 1] && currentRealIndex == columnIndications[nextIndex]) {
|
|
result << valueStorage[nextIndex] << "\t";
|
|
++nextIndex;
|
|
} else {
|
|
result << "0\t";
|
|
}
|
|
++currentRealIndex;
|
|
}
|
|
result << "\t)\t" << i << std::endl;
|
|
}
|
|
|
|
// Print column numbers in footer.
|
|
result << "\t\t";
|
|
for (uint_fast64_t i = 0; i < colCount; ++i) {
|
|
result << i << "\t";
|
|
}
|
|
result << std::endl;
|
|
|
|
// Return final result.
|
|
return result.str();
|
|
}
|
|
|
|
private:
|
|
|
|
/*!
|
|
* The number of rows of the matrix.
|
|
*/
|
|
uint_fast64_t rowCount;
|
|
|
|
/*!
|
|
* The number of columns of the matrix.
|
|
*/
|
|
uint_fast64_t colCount;
|
|
|
|
/*!
|
|
* The number of non-zero elements.
|
|
*/
|
|
uint_fast64_t nonZeroEntryCount;
|
|
|
|
/*!
|
|
* Stores all non-zero values.
|
|
*/
|
|
std::vector<T> valueStorage;
|
|
|
|
/*!
|
|
* Stores the column for each non-zero element.
|
|
*/
|
|
std::vector<uint_fast64_t> columnIndications;
|
|
|
|
/*!
|
|
* Vector containing the boundaries (indices) in the value_storage array
|
|
* for each row. All elements of value_storage with indices between the
|
|
* i-th and the (i+1)-st element of this array belong to row i.
|
|
*/
|
|
std::vector<uint_fast64_t> rowIndications;
|
|
|
|
/*!
|
|
* The internal status of the matrix.
|
|
*/
|
|
MatrixStatus internalStatus;
|
|
|
|
/*!
|
|
* Stores the current number of non-zero elements that have been added to
|
|
* the matrix. Used for correctly inserting elements in the matrix.
|
|
*/
|
|
uint_fast64_t currentSize;
|
|
|
|
/*!
|
|
* Stores the row in which the last element was inserted. Used for correctly
|
|
* inserting elements in the matrix .
|
|
*/
|
|
uint_fast64_t lastRow;
|
|
|
|
/*!
|
|
* Sets the internal status to signal an error.
|
|
*/
|
|
void triggerErrorState() {
|
|
setState(MatrixStatus::Error);
|
|
}
|
|
|
|
/*!
|
|
* Sets the internal status to the given state if the current state is not
|
|
* the error state.
|
|
* @param new_state The new state to be switched to.
|
|
*/
|
|
void setState(const MatrixStatus new_state) {
|
|
internalStatus = (internalStatus == MatrixStatus::Error) ? internalStatus : new_state;
|
|
}
|
|
|
|
/*!
|
|
* Prepares the internal storage. For this, it requires the number of non-zero entries and the
|
|
* amount of rows to be set correctly.
|
|
*
|
|
* @param initializeElements If set to true, all entries are initialized.
|
|
* @return True on success, false otherwise (allocation failed).
|
|
*/
|
|
bool prepareInternalStorage(bool initializeElements = true) {
|
|
if (initializeElements) {
|
|
// Set up the arrays for the elements that are not on the diagonal.
|
|
valueStorage.resize(nonZeroEntryCount, storm::utility::constGetZero<T>());
|
|
columnIndications.resize(nonZeroEntryCount, 0);
|
|
|
|
// Set up the rowIndications vector and reserve one element more than there are rows in
|
|
// order to put a sentinel element at the end, which eases iteration process.
|
|
rowIndications.resize(rowCount + 1, 0);
|
|
|
|
// Return whether all the allocations could be made without error.
|
|
return ((valueStorage.capacity() >= nonZeroEntryCount) && (columnIndications.capacity() >= nonZeroEntryCount)
|
|
&& (rowIndications.capacity() >= (rowCount + 1)));
|
|
} else {
|
|
// If it was not requested to initialize the elements, we simply reserve the space.
|
|
valueStorage.reserve(nonZeroEntryCount);
|
|
columnIndications.reserve(nonZeroEntryCount);
|
|
rowIndications.reserve(rowCount + 1);
|
|
return true;
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace storage
|
|
} // namespace storm
|
|
|
|
#endif // STORM_STORAGE_SPARSEMATRIX_H_
|