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.
117 lines
7.0 KiB
117 lines
7.0 KiB
#include "storm/storage/SparseMatrix.h"
|
|
#include "storm/utility/constants.h"
|
|
#include "storm/utility/vector.h"
|
|
#include "storm/exceptions/InvalidStateException.h"
|
|
#include "storm/exceptions/InvalidEnvironmentException.h"
|
|
#include "storm/exceptions/UnexpectedException.h"
|
|
#include "storm/exceptions/UnmetRequirementException.h"
|
|
|
|
namespace storm {
|
|
namespace solver {
|
|
|
|
namespace helper {
|
|
|
|
|
|
/*!
|
|
* Returns a reordering of the matrix row(groups) and columns such that we can solve the (minmax or linear) equation system in one go.
|
|
* More precisely, let x be the result and i an arbitrary rowgroup index. Solving for rowgroup x[i] only requires knowledge of the result at rowgroups x[i+1], x[i+2], ...
|
|
*/
|
|
template<typename ValueType>
|
|
boost::optional<std::vector<uint64_t>> computeTopologicalGroupOrdering(storm::storage::SparseMatrix<ValueType> const& matrix) {
|
|
uint64_t numGroups = matrix.getRowGroupCount();
|
|
bool orderedMatrixRequired = false;
|
|
std::vector<uint64_t> result;
|
|
result.reserve(numGroups);
|
|
storm::storage::BitVector processed(numGroups, false);
|
|
storm::storage::BitVector visited(numGroups, false);
|
|
std::vector<uint64_t> stack;
|
|
|
|
// It's more likely that states without a successor are located at the end (due to the way we build the model).
|
|
// We therefore process the matrix in reverse order.
|
|
uint64_t startState = numGroups;
|
|
while (startState > 0) {
|
|
--startState;
|
|
// skip already processed states
|
|
if (processed.get(startState)) continue;
|
|
|
|
// Now do a dfs from start state.
|
|
stack.push_back(startState);
|
|
while (!stack.empty()) {
|
|
uint64_t current = stack.back();
|
|
if (visited.get(current)) {
|
|
// We are backtracking, so add this state now
|
|
result.push_back(current);
|
|
processed.set(current);
|
|
stack.pop_back();
|
|
} else {
|
|
visited.set(current);
|
|
for (auto const& entry : matrix.getRowGroup(current)) {
|
|
if (!processed.get(entry.getColumn()) && !storm::utility::isZero(entry.getValue())) {
|
|
orderedMatrixRequired = true;
|
|
STORM_LOG_THROW(!visited.get(entry.getColumn()), storm::exceptions::UnmetRequirementException, "The model is not acyclic.");
|
|
stack.push_back(entry.getColumn());
|
|
}
|
|
}
|
|
// If there are no successors to process, we will add the current state to the result in the next iteration.
|
|
}
|
|
}
|
|
}
|
|
// we will do backwards iterations, so the order has to be reversed
|
|
if (orderedMatrixRequired) {
|
|
std::reverse(result.begin(), result.end());
|
|
return result;
|
|
} else {
|
|
return boost::none;
|
|
}
|
|
}
|
|
|
|
/// reorders the row group such that the i'th row of the new matrix corresponds to the order[i]'th row of the source matrix.
|
|
/// Also eliminates selfloops p>0 and inserts 1/p into the bFactors
|
|
template<typename ValueType>
|
|
storm::storage::SparseMatrix<ValueType> createReorderedMatrix(storm::storage::SparseMatrix<ValueType> const& matrix, std::vector<uint64_t> const& newToOrigIndexMap, std::vector<std::pair<uint64_t, ValueType>>& bFactors) {
|
|
std::vector<uint64_t> origToNewMap(newToOrigIndexMap.size(), std::numeric_limits<uint64>::max());
|
|
for (uint64_t i = 0; i < newToOrigIndexMap.size(); ++i) {
|
|
origToNewMap[newToOrigIndexMap[i]] = i;
|
|
}
|
|
|
|
bool hasRowGrouping = !matrix.hasTrivialRowGrouping();
|
|
storm::storage::SparseMatrixBuilder<ValueType> builder(matrix.getRowCount(), matrix.getColumnCount(), matrix.getNonzeroEntryCount(), false, hasRowGrouping, hasRowGrouping ? matrix.getRowGroupCount() : static_cast<uint64_t>(0));
|
|
uint64_t newRow = 0;
|
|
for (uint64_t newRowGroup = 0; newRowGroup < newToOrigIndexMap.size(); ++newRowGroup) {
|
|
auto const& origRowGroup = newToOrigIndexMap[newRowGroup];
|
|
if (hasRowGrouping) {
|
|
builder.newRowGroup(newRowGroup);
|
|
}
|
|
for (uint64_t origRow = matrix.getRowGroupIndices()[origRowGroup]; origRow < matrix.getRowGroupIndices()[origRowGroup + 1]; ++origRow) {
|
|
for (auto const& entry : matrix.getRow(origRow)) {
|
|
if (storm::utility::isZero(entry.getValue())) {
|
|
continue;
|
|
}
|
|
if (entry.getColumn() == origRowGroup) {
|
|
if (storm::utility::isOne(entry.getValue())) {
|
|
// a one selfloop can only mean that there is never a non-zero value at the b vector for the current row.
|
|
// Let's "assert" this by considering infinity. (This is necessary to avoid division by zero)
|
|
bFactors.emplace_back(newRow, storm::utility::infinity<ValueType>());
|
|
}
|
|
ValueType factor = storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - entry.getValue());
|
|
bFactors.emplace_back(newRow, factor);
|
|
}
|
|
builder.addNextValue(newRow, origToNewMap[entry.getColumn()], entry.getValue());
|
|
}
|
|
++newRow;
|
|
}
|
|
}
|
|
auto result = builder.build(matrix.getRowCount(), matrix.getColumnCount(), matrix.getRowGroupCount());
|
|
// apply the bFactors to the relevant rows
|
|
for (auto const& bFactor : bFactors) {
|
|
STORM_LOG_ASSERT(!storm::utility::isInfinity(bFactor.second) || storm::utility::isZero(result.getRowSum(bFactor.first)), "The input matrix does not seem to be probabilistic.");
|
|
for (auto& entry : result.getRow(bFactor.first)) {
|
|
entry.setValue(entry.getValue() * bFactor.second);
|
|
}
|
|
}
|
|
STORM_LOG_DEBUG("Reordered " << matrix.getDimensionsAsString() << " with " << bFactors.size() << " selfloop entries for acyclic solving.");
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|