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