Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
AcyclicSolverHelper.h
Go to the documentation of this file.
1#pragma once
2
3#include <optional>
4
9
10namespace storm {
11namespace solver {
12
13namespace helper {
14
20template<typename ValueType>
21boost::optional<std::vector<uint64_t>> computeTopologicalGroupOrdering(storm::storage::SparseMatrix<ValueType> const& matrix) {
22 uint64_t numGroups = matrix.getRowGroupCount();
23 bool orderedMatrixRequired = false;
24 std::vector<uint64_t> result;
25 result.reserve(numGroups);
26 storm::storage::BitVector processed(numGroups, false);
27 storm::storage::BitVector visited(numGroups, false);
28 std::vector<uint64_t> stack;
29
30 // It's more likely that states without a successor are located at the end (due to the way we build the model).
31 // We therefore process the matrix in reverse order.
32 uint64_t startState = numGroups;
33 while (startState > 0) {
34 --startState;
35 // skip already processed states
36 if (processed.get(startState)) {
37 continue;
38 }
39
40 // Now do a dfs from start state.
41 stack.push_back(startState);
42 while (!stack.empty()) {
43 uint64_t current = stack.back();
44 if (visited.get(current)) {
45 // We are backtracking, so add this state now
46 // It might be that we already processed this state before. This can happen, e.g., if this state has two direct predecessors, A and B, and
47 // A is also a predecessor of B. Then, this node gets inserted into the stack two times: when analyzing A and B.
48 if (!processed.get(current)) {
49 result.push_back(current);
50 processed.set(current);
51 }
52 stack.pop_back();
53 } else {
54 visited.set(current);
55 for (auto const& entry : matrix.getRowGroup(current)) {
56 if (!processed.get(entry.getColumn()) && !storm::utility::isZero(entry.getValue())) {
57 orderedMatrixRequired = true;
58 STORM_LOG_THROW(!visited.get(entry.getColumn()), storm::exceptions::UnmetRequirementException, "The model is not acyclic.");
59 stack.push_back(entry.getColumn());
60 }
61 }
62 // If there are no successors to process, we will add the current state to the result in the next iteration.
63 }
64 }
65 }
66 // we will do backwards iterations, so the order has to be reversed
67 if (orderedMatrixRequired) {
68 std::reverse(result.begin(), result.end());
69 STORM_LOG_ASSERT(result.size() == matrix.getRowGroupCount(), "Result vector has an unexpected amount of entries.");
70 return result;
71 } else {
72 return boost::none;
73 }
74}
75
81template<typename ValueType>
83 std::vector<uint64_t> const& newToOrigIndexMap,
84 std::vector<std::pair<uint64_t, std::optional<ValueType>>>& bFactors) {
85 std::vector<uint64_t> origToNewMap(newToOrigIndexMap.size(), std::numeric_limits<uint64_t>::max());
86 for (uint64_t i = 0; i < newToOrigIndexMap.size(); ++i) {
87 origToNewMap[newToOrigIndexMap[i]] = i;
88 }
89
90 bool hasRowGrouping = !matrix.hasTrivialRowGrouping();
92 hasRowGrouping ? matrix.getRowGroupCount() : static_cast<uint64_t>(0));
93 uint64_t newRow = 0;
94 for (uint64_t newRowGroup = 0; newRowGroup < newToOrigIndexMap.size(); ++newRowGroup) {
95 auto const& origRowGroup = newToOrigIndexMap[newRowGroup];
96 if (hasRowGrouping) {
97 builder.newRowGroup(newRow);
98 }
99 for (uint64_t origRow = matrix.getRowGroupIndices()[origRowGroup]; origRow < matrix.getRowGroupIndices()[origRowGroup + 1]; ++origRow) {
100 for (auto const& entry : matrix.getRow(origRow)) {
101 if (storm::utility::isZero(entry.getValue())) {
102 continue;
103 }
104 if (entry.getColumn() == origRowGroup) {
105 if (storm::utility::isOne(entry.getValue())) {
106 // A one selfloop can only mean that there is never a non-zero value at the b vector for the current row.
107 // There is no factor to apply here; computing one would divide by zero.
108 bFactors.emplace_back(newRow, std::nullopt);
109 } else {
110 ValueType factor = storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - entry.getValue());
111 bFactors.emplace_back(newRow, factor);
112 }
113 }
114 builder.addNextValue(newRow, origToNewMap[entry.getColumn()], entry.getValue());
115 }
116 ++newRow;
117 }
118 }
119 auto result = builder.build(matrix.getRowCount(), matrix.getColumnCount(), matrix.getRowGroupCount());
120 // apply the bFactors to the relevant rows
121 for (auto const& bFactor : bFactors) {
122 if (!bFactor.second) {
123 // A selfloop of probability one: the row is not scaled, but it may not contribute anything either.
124 STORM_LOG_ASSERT(storm::utility::isZero(result.getRowSum(bFactor.first)), "The input matrix does not seem to be probabilistic.");
125 continue;
126 }
127 for (auto& entry : result.getRow(bFactor.first)) {
128 entry.setValue(entry.getValue() * *bFactor.second);
129 }
130 }
131 STORM_LOG_DEBUG("Reordered " << matrix.getDimensionsAsString() << " with " << bFactors.size() << " selfloop entries for acyclic solving.");
132 return result;
133}
134} // namespace helper
135} // namespace solver
136} // namespace storm
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
const_rows getRowGroup(index_type rowGroup) const
Returns an object representing the given row group.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
std::string getDimensionsAsString() const
Returns a string describing the dimensions of the matrix.
index_type getColumnCount() const
Returns the number of columns of the matrix.
bool hasTrivialRowGrouping() const
Retrieves whether the matrix has a trivial row grouping.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
index_type getNonzeroEntryCount() const
Returns the cached number of nonzero entries in the matrix.
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
boost::optional< std::vector< uint64_t > > computeTopologicalGroupOrdering(storm::storage::SparseMatrix< ValueType > const &matrix)
Returns a reordering of the matrix row(groups) and columns such that we can solve the (minmax or line...
storm::storage::SparseMatrix< ValueType > createReorderedMatrix(storm::storage::SparseMatrix< ValueType > const &matrix, std::vector< uint64_t > const &newToOrigIndexMap, std::vector< std::pair< uint64_t, std::optional< ValueType > > > &bFactors)
reorders the row group such that the i'th row of the new matrix corresponds to the order[i]'th row of...
bool isOne(ValueType const &a)
Definition constants.cpp:37
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType one()
Definition constants.cpp:19