Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
EliminatorBase.cpp
Go to the documentation of this file.
2
8
9namespace storm {
10namespace solver {
11namespace stateelimination {
12
13template<typename ValueType, ScalingMode Mode>
20template<typename ValueType, ScalingMode Mode>
21void EliminatorBase<ValueType, Mode>::eliminate(uint64_t row, uint64_t column, bool clearRow) {
25 // Start by finding the entry in the given column.
26 bool hasEntryInColumn = false;
27 ValueType columnValue = storm::utility::zero<ValueType>();
28 FlexibleRowType& entriesInRow = matrix.getRow(row);
29 for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); entryIt != entryIte; ++entryIt) {
30 if (entryIt->getColumn() >= column) {
31 if (entryIt->getColumn() == column) {
32 columnValue = entryIt->getValue();
33 hasEntryInColumn = true;
34
35 // If we do not clear the row completely, we need to remove the entry in the requested column.
36 // All other elements are scaled with the entry anyway.
37 if (!clearRow) {
38 entriesInRow.erase(entryIt);
39 }
40 }
41 break;
42 }
43 }
44
45 // Scale all entries in this row.
46 // Depending on the scaling mode, we scale the other entries of the row.
47 STORM_LOG_TRACE((hasEntryInColumn ? "State has entry in column." : "State does not have entry in column."));
48 if (Mode == ScalingMode::Divide) {
49 STORM_LOG_ASSERT(hasEntryInColumn, "The scaling mode 'divide' requires an element in the given column.");
50 STORM_LOG_ASSERT(storm::utility::isZero(columnValue), "The scaling mode 'divide' requires a non-zero element in the given column.");
51 columnValue = storm::utility::one<ValueType>() / columnValue;
52 } else if (Mode == ScalingMode::DivideOneMinus) {
53 if (hasEntryInColumn) {
54 if (storm::utility::isOne(columnValue)) {
55 // The state is absorbing, i.e., it has a self-loop with probability one. In this case, the
56 // solution for this state is zero (least fixed point), and it does not contribute anything to
57 // its predecessors. Record this by setting the scaling factor to zero.
58 STORM_LOG_TRACE("State is absorbing, its value will be zero.");
59 columnValue = storm::utility::zero<ValueType>();
60 } else {
61 columnValue = storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - columnValue);
62 columnValue = storm::utility::simplify(columnValue);
63 }
64 }
65 }
66
67 if (hasEntryInColumn) {
68 for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); entryIt != entryIte; ++entryIt) {
69 // Only scale the entries in a different column.
70 if (entryIt->getColumn() != column) {
71 entryIt->setValue(storm::utility::simplify((ValueType)(entryIt->getValue() * columnValue)));
72 }
73 }
74 updateValue(row, columnValue);
75 }
76
77 // Now substitute the row entries in all other rows that contain an element whose column is the current row.
78 FlexibleRowType& elementsWithEntryInColumnEqualRow = transposedMatrix.getRow(column);
79
80 // In case we have a constrained elimination, we need to keep track of the rows that keep their value
81 // in the column equal to the current row.
82 FlexibleRowType rowsKeepingEntryInColumnEqualRow;
83
84 // For each entry in the row d, we need to build a list of other rows that will contain an element in the
85 // column d.
86 std::vector<FlexibleRowType> newBackwardEntries(entriesInRow.size());
87 for (auto& backwardEntry : newBackwardEntries) {
88 backwardEntry.reserve(elementsWithEntryInColumnEqualRow.size());
89 }
90
91 // Now go through the rows with an entry in the column corresponding to the current row and substitute
92 // the elements of this row unless the elimination is filtered.
93 for (auto const& predecessorEntry : elementsWithEntryInColumnEqualRow) {
94 uint_fast64_t predecessor = predecessorEntry.getColumn();
95 STORM_LOG_TRACE("Found predecessor " << predecessor << ".");
96
97 // Skip the row itself.
98 if (predecessor == row) {
99 STORM_LOG_ASSERT(hasEntryInColumn, "Expected entry in column.");
100 continue;
101 }
102
103 // Skip the state if the elimination is constrained, but the predecessor is not in the constraint.
104 if (isFilterPredecessor() && !filterPredecessor(predecessor)) {
105 rowsKeepingEntryInColumnEqualRow.emplace_back(predecessorEntry);
106 STORM_LOG_TRACE("Not eliminating predecessor " << predecessor << ", because it does not fit the filter.");
107 continue;
108 }
109 STORM_LOG_TRACE("Eliminating predecessor " << predecessor << ".");
110
111 // First, find the probability with which the predecessor can move to the current state, because
112 // the forward probabilities of the state to be eliminated need to be scaled with this factor.
113 FlexibleRowType& predecessorForwardTransitions = matrix.getRow(predecessor);
114 FlexibleRowIterator multiplyElement = std::find_if(predecessorForwardTransitions.begin(), predecessorForwardTransitions.end(),
115 [&](MatrixEntry const& a) { return a.getColumn() == column; });
116
117 // Make sure we have found the probability and set it to zero.
118 STORM_LOG_THROW(multiplyElement != predecessorForwardTransitions.end(), storm::exceptions::InvalidStateException,
119 "No probability for successor found.");
120 ValueType multiplyFactor = multiplyElement->getValue();
121 multiplyElement->setValue(storm::utility::zero<ValueType>());
122
123 // At this point, we need to update the (forward) transitions of the predecessor.
124 FlexibleRowIterator first1 = predecessorForwardTransitions.begin();
125 FlexibleRowIterator last1 = predecessorForwardTransitions.end();
126 FlexibleRowIterator first2 = entriesInRow.begin();
127 FlexibleRowIterator last2 = entriesInRow.end();
128
129 FlexibleRowType newSuccessors;
130 newSuccessors.reserve((last1 - first1) + (last2 - first2));
131 std::insert_iterator<FlexibleRowType> result(newSuccessors, newSuccessors.end());
132
133 uint_fast64_t successorOffsetInNewBackwardTransitions = 0;
134 // Now we merge the two successor lists. (Code taken from std::set_union and modified to suit our needs).
135 for (; first1 != last1; ++result) {
136 // Skip the transitions to the state that is currently being eliminated.
137 if (first1->getColumn() == column || (first2 != last2 && first2->getColumn() == column)) {
138 if (first1->getColumn() == column) {
139 ++first1;
140 }
141 if (first2 != last2 && first2->getColumn() == column) {
142 ++first2;
143 }
144 continue;
145 }
146
147 if (first2 == last2) {
148 std::copy_if(first1, last1, result, [&](MatrixEntry const& a) { return a.getColumn() != column; });
149 break;
150 }
151 if (first2->getColumn() < first1->getColumn()) {
152 ValueType successorValue = storm::utility::simplify<ValueType>((first2->getValue() * multiplyFactor));
153 *result = MatrixEntry(first2->getColumn(), successorValue);
154 newBackwardEntries[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, successorValue);
155 ++first2;
156 ++successorOffsetInNewBackwardTransitions;
157 } else if (first1->getColumn() < first2->getColumn()) {
158 *result = *first1;
159 ++first1;
160 } else {
161 ValueType sprod = multiplyFactor * first2->getValue();
162 ValueType sum = first1->getValue() + storm::utility::simplify(sprod);
163 auto probability = storm::utility::simplify(sum);
164 *result = MatrixEntry(first1->getColumn(), probability);
165 newBackwardEntries[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, probability);
166 ++first1;
167 ++first2;
168 ++successorOffsetInNewBackwardTransitions;
169 }
170 }
171 for (; first2 != last2; ++first2) {
172 if (first2->getColumn() != column) {
173 ValueType probability = storm::utility::simplify<ValueType>(first2->getValue() * multiplyFactor);
174 *result = MatrixEntry(first2->getColumn(), probability);
175 newBackwardEntries[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, probability);
176 ++successorOffsetInNewBackwardTransitions;
177 }
178 }
179
180 // Now move the new transitions in place.
181 predecessorForwardTransitions = std::move(newSuccessors);
182 STORM_LOG_TRACE("Fixed new next-state probabilities of predecessor state " << predecessor << ".");
183
184 updatePredecessor(predecessor, multiplyFactor, row);
185
186 STORM_LOG_TRACE("Updating priority of predecessor.");
187 updatePriority(predecessor);
188 }
189
190 // Finally, we need to add the predecessor to the set of predecessors of every successor.
191 uint_fast64_t successorOffsetInNewBackwardTransitions = 0;
192 for (auto const& successorEntry : entriesInRow) {
193 if (successorEntry.getColumn() == column) {
194 continue;
195 }
196
197 FlexibleRowType& successorBackwardTransitions = transposedMatrix.getRow(successorEntry.getColumn());
198
199 // Delete the current state as a predecessor of the successor state only if we are going to remove the
200 // current state's forward transitions.
201 if (clearRow) {
202 FlexibleRowIterator elimIt = std::find_if(successorBackwardTransitions.begin(), successorBackwardTransitions.end(),
203 [&](MatrixEntry const& a) { return a.getColumn() == row; });
204 STORM_LOG_ASSERT(elimIt != successorBackwardTransitions.end(),
205 "Expected a proper backward transition from " << successorEntry.getColumn() << " to " << column << ", but found none.");
206 successorBackwardTransitions.erase(elimIt);
207 }
208
209 FlexibleRowIterator first1 = successorBackwardTransitions.begin();
210 FlexibleRowIterator last1 = successorBackwardTransitions.end();
211 FlexibleRowIterator first2 = newBackwardEntries[successorOffsetInNewBackwardTransitions].begin();
212 FlexibleRowIterator last2 = newBackwardEntries[successorOffsetInNewBackwardTransitions].end();
213
214 FlexibleRowType newPredecessors;
215 newPredecessors.reserve((last1 - first1) + (last2 - first2));
216 std::insert_iterator<FlexibleRowType> result(newPredecessors, newPredecessors.end());
217
218 for (; first1 != last1; ++result) {
219 if (first2 == last2) {
220 std::copy(first1, last1, result);
221 break;
222 }
223 if (first2->getColumn() < first1->getColumn()) {
224 if (first2->getColumn() != row) {
225 *result = *first2;
226 }
227 ++first2;
228 } else if (first1->getColumn() == first2->getColumn()) {
229 if (estimateComplexity(first1->getValue()) > estimateComplexity(first2->getValue())) {
230 *result = *first1;
231 } else {
232 *result = *first2;
233 }
234 ++first1;
235 ++first2;
236 } else {
237 *result = *first1;
238 ++first1;
239 }
240 }
241 if (isFilterPredecessor()) {
242 std::copy_if(first2, last2, result, [&](MatrixEntry const& a) { return a.getColumn() != row && filterPredecessor(a.getColumn()); });
243 } else {
244 std::copy_if(first2, last2, result, [&](MatrixEntry const& a) { return a.getColumn() != row; });
245 }
246 // Now move the new predecessors in place.
247 successorBackwardTransitions = std::move(newPredecessors);
248 ++successorOffsetInNewBackwardTransitions;
249 }
250 STORM_LOG_TRACE("Fixed predecessor lists of successor states.");
251
252 // Clear the row if requested.
253 if (clearRow) {
254 entriesInRow.clear();
255 entriesInRow.shrink_to_fit();
256 }
257
258 // If the substitution was filtered, we need to store the new rows that have an entry in column equal to this row.
259 if (isFilterPredecessor()) {
260 elementsWithEntryInColumnEqualRow = std::move(rowsKeepingEntryInColumnEqualRow);
261 } else {
262 elementsWithEntryInColumnEqualRow.clear();
263 elementsWithEntryInColumnEqualRow.shrink_to_fit();
264 }
265}
266
267template<typename ValueType, ScalingMode Mode>
269 // Start by finding value of the selfloop.
270 bool hasEntryInColumn = false;
271 ValueType columnValue = storm::utility::zero<ValueType>();
272 FlexibleRowType& entriesInRow = matrix.getRow(state);
273 for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); entryIt != entryIte; ++entryIt) {
274 if (entryIt->getColumn() == state) {
275 columnValue = entryIt->getValue();
276 hasEntryInColumn = true;
277 }
278 }
279
280 // Scale all entries in this row.
281 // Depending on the scaling mode, we scale the other entries of the row.
282 STORM_LOG_TRACE((hasEntryInColumn ? "State has entry in column." : "State does not have entry in column."));
283 if (Mode == ScalingMode::Divide) {
284 STORM_LOG_ASSERT(hasEntryInColumn, "The scaling mode 'divide' requires an element in the given column.");
285 STORM_LOG_ASSERT(storm::utility::isZero(columnValue), "The scaling mode 'divide' requires a non-zero element in the given column.");
286 columnValue = storm::utility::one<ValueType>() / columnValue;
287 } else if (Mode == ScalingMode::DivideOneMinus) {
288 if (hasEntryInColumn) {
289 if (storm::utility::isOne(columnValue)) {
290 // The state is absorbing, i.e., it has a self-loop with probability one. Its value is zero
291 // (least fixed point) and it does not contribute anything to its predecessors.
292 STORM_LOG_TRACE("State is absorbing, its value will be zero.");
293 columnValue = storm::utility::zero<ValueType>();
294 } else {
295 columnValue = storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - columnValue);
296 columnValue = storm::utility::simplify(columnValue);
297 }
298 }
299 }
300
301 if (hasEntryInColumn) {
302 for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); entryIt != entryIte; ++entryIt) {
303 // Scale the entries in a different column, set state transition probability to 0.
304 if (entryIt->getColumn() != state) {
305 entryIt->setValue(storm::utility::simplify((ValueType)(entryIt->getValue() * columnValue)));
306 } else {
307 entryIt->setValue(storm::utility::zero<ValueType>());
308 }
309 }
310 }
311}
312
313template<typename ValueType, ScalingMode Mode>
315 // Intentionally left empty.
316}
317
318template<typename ValueType, ScalingMode Mode>
321 // Intentionally left empty.
322}
323
324template<typename ValueType, ScalingMode Mode>
328
329template<typename ValueType, ScalingMode Mode>
331 STORM_LOG_ASSERT(false, "Must not filter predecessors.");
332 return false;
333}
334
335template<typename ValueType, ScalingMode Mode>
337 return false;
338}
339
342
345
348} // namespace stateelimination
349} // namespace solver
350} // namespace storm
void eliminate(uint64_t row, uint64_t column, bool clearRow)
virtual void updateValue(storm::storage::sparse::state_type const &state, ValueType const &loopProbability)
storm::storage::FlexibleSparseMatrix< ValueType > & transposedMatrix
virtual void updatePriority(storm::storage::sparse::state_type const &state)
storm::storage::FlexibleSparseMatrix< ValueType >::row_type FlexibleRowType
storm::storage::FlexibleSparseMatrix< ValueType > & matrix
EliminatorBase(storm::storage::FlexibleSparseMatrix< ValueType > &matrix, storm::storage::FlexibleSparseMatrix< ValueType > &transposedMatrix)
virtual void updatePredecessor(storm::storage::sparse::state_type const &predecessor, ValueType const &probability, storm::storage::sparse::state_type const &state)
virtual bool filterPredecessor(storm::storage::sparse::state_type const &state)
The flexible sparse matrix is used during state elimination.
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
uint_fast64_t estimateComplexity(ValueType const &)
bool isOne(ValueType const &a)
Definition constants.cpp:37
ValueType simplify(ValueType value)
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19