Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparseDtmcEliminationModelChecker.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <chrono>
5#include <random>
6
23#include "storm/utility/graph.h"
26
27namespace storm {
28namespace modelchecker {
29
30template<typename SparseDtmcModelType>
35
36template<typename SparseDtmcModelType>
46
47template<typename SparseDtmcModelType>
50 storm::logic::StateFormula const& stateFormula = checkTask.getFormula();
51 std::unique_ptr<CheckResult> subResultPointer = this->check(stateFormula);
52 storm::storage::BitVector const& psiStates = subResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
53
54 storm::storage::SparseMatrix<ValueType> const& transitionMatrix = this->getModel().getTransitionMatrix();
55 uint_fast64_t numberOfStates = transitionMatrix.getRowCount();
56 if (psiStates.empty()) {
57 return std::unique_ptr<CheckResult>(
58 new ExplicitQuantitativeCheckResult<ValueType>(std::vector<ValueType>(numberOfStates, storm::utility::zero<ValueType>())));
59 }
60 if (psiStates.full()) {
61 return std::unique_ptr<CheckResult>(
62 new ExplicitQuantitativeCheckResult<ValueType>(std::vector<ValueType>(numberOfStates, storm::utility::one<ValueType>())));
63 }
64
65 storm::storage::BitVector const& initialStates = this->getModel().getInitialStates();
66 STORM_LOG_THROW(initialStates.getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException,
67 "Input model is required to have exactly one initial state.");
68 STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::IllegalArgumentException,
69 "Cannot compute long-run probabilities for all states.");
70
71 storm::storage::SparseMatrix<ValueType> backwardTransitions = this->getModel().getBackwardTransitions();
72 storm::storage::BitVector maybeStates =
73 storm::utility::graph::performProbGreater0(backwardTransitions, storm::storage::BitVector(transitionMatrix.getRowCount(), true), psiStates);
74
75 std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
76
77 // Determine whether we need to perform some further computation.
78 bool furtherComputationNeeded = true;
79 if (checkTask.isOnlyInitialStatesRelevantSet() && initialStates.isDisjointFrom(maybeStates)) {
80 STORM_LOG_DEBUG("The long-run probability for all initial states was found in a preprocessing step.");
81 furtherComputationNeeded = false;
82 }
83 if (maybeStates.empty()) {
84 STORM_LOG_DEBUG("The long-run probability for all states was found in a preprocessing step.");
85 furtherComputationNeeded = false;
86 }
87
88 if (furtherComputationNeeded) {
89 if (checkTask.isOnlyInitialStatesRelevantSet()) {
90 // Determine the set of states that is reachable from the initial state without jumping over a target state.
92 transitionMatrix, initialStates, storm::storage::BitVector(numberOfStates, true), storm::storage::BitVector(numberOfStates, false));
93
94 // Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
95 maybeStates &= reachableStates;
96 }
97
98 std::vector<ValueType> stateValues(maybeStates.size(), storm::utility::zero<ValueType>());
100 result = computeLongRunValues(env, transitionMatrix, backwardTransitions, initialStates, maybeStates, checkTask.isOnlyInitialStatesRelevantSet(),
101 stateValues);
102 }
103
104 // Construct check result based on whether we have computed values for all states or just the initial states.
105 std::unique_ptr<CheckResult> checkResult(new ExplicitQuantitativeCheckResult<ValueType>(result));
106 if (checkTask.isOnlyInitialStatesRelevantSet()) {
107 // If we computed the results for the initial states only, we need to filter the result to only
108 // communicate these results.
109 checkResult->filter(ExplicitQualitativeCheckResult<ValueType>(initialStates));
110 }
111 return checkResult;
112}
113
114template<typename SparseDtmcModelType>
117 // Do some sanity checks to establish some required properties.
118 RewardModelType const& rewardModel = this->getModel().getRewardModel(checkTask.isRewardModelSet() ? checkTask.getRewardModel() : "");
119 STORM_LOG_THROW(!rewardModel.empty(), storm::exceptions::IllegalArgumentException, "Input model does not have a reward model.");
120
121 storm::storage::BitVector const& initialStates = this->getModel().getInitialStates();
122 STORM_LOG_THROW(initialStates.getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException,
123 "Input model is required to have exactly one initial state.");
124 STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::IllegalArgumentException,
125 "Cannot compute long-run probabilities for all states.");
126
127 storm::storage::SparseMatrix<ValueType> const& transitionMatrix = this->getModel().getTransitionMatrix();
128 uint_fast64_t numberOfStates = transitionMatrix.getRowCount();
129
130 // Get the state-reward values from the reward model.
131 std::vector<ValueType> stateRewardValues = rewardModel.getTotalRewardVector(this->getModel().getTransitionMatrix());
132
133 storm::storage::BitVector maybeStates(stateRewardValues.size());
134 uint_fast64_t index = 0;
135 for (auto const& value : stateRewardValues) {
136 if (value != storm::utility::zero<ValueType>()) {
137 maybeStates.set(index, true);
138 }
139 ++index;
140 }
141
142 storm::storage::SparseMatrix<ValueType> backwardTransitions = this->getModel().getBackwardTransitions();
143
144 storm::storage::BitVector allStates(numberOfStates, true);
145 maybeStates = storm::utility::graph::performProbGreater0(backwardTransitions, allStates, maybeStates);
146
147 std::vector<ValueType> result(numberOfStates, storm::utility::zero<ValueType>());
148
149 // Determine whether we need to perform some further computation.
150 bool furtherComputationNeeded = true;
151 if (checkTask.isOnlyInitialStatesRelevantSet() && initialStates.isDisjointFrom(maybeStates)) {
152 furtherComputationNeeded = false;
153 }
154
155 if (furtherComputationNeeded) {
156 if (checkTask.isOnlyInitialStatesRelevantSet()) {
157 // Determine the set of states that is reachable from the initial state without jumping over a target state.
159 transitionMatrix, initialStates, storm::storage::BitVector(numberOfStates, true), storm::storage::BitVector(numberOfStates, false));
160
161 // Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
162 maybeStates &= reachableStates;
163 }
164
165 result = computeLongRunValues(env, transitionMatrix, backwardTransitions, initialStates, maybeStates, checkTask.isOnlyInitialStatesRelevantSet(),
166 stateRewardValues);
167 }
168
169 // Construct check result based on whether we have computed values for all states or just the initial states.
170 std::unique_ptr<CheckResult> checkResult(new ExplicitQuantitativeCheckResult<ValueType>(result));
171 if (checkTask.isOnlyInitialStatesRelevantSet()) {
172 // If we computed the results for the initial states only, we need to filter the result to only
173 // communicate these results.
174 checkResult->filter(ExplicitQualitativeCheckResult<ValueType>(initialStates));
175 }
176 return checkResult;
177}
178
179template<typename SparseDtmcModelType>
180std::vector<typename SparseDtmcEliminationModelChecker<SparseDtmcModelType>::SolutionType>
181SparseDtmcEliminationModelChecker<SparseDtmcModelType>::computeLongRunValues(Environment const& env,
182 storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
183 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
184 storm::storage::BitVector const& initialStates,
185 storm::storage::BitVector const& maybeStates,
186 bool computeResultsForInitialStatesOnly, std::vector<ValueType>& stateValues) {
187 std::chrono::high_resolution_clock::time_point totalTimeStart = std::chrono::high_resolution_clock::now();
188
189 // Start by decomposing the DTMC into its BSCCs.
190 std::chrono::high_resolution_clock::time_point sccDecompositionStart = std::chrono::high_resolution_clock::now();
192 transitionMatrix, storm::storage::StronglyConnectedComponentDecompositionOptions().onlyBottomSccs());
193 auto sccDecompositionEnd = std::chrono::high_resolution_clock::now();
194
195 std::chrono::high_resolution_clock::time_point conversionStart = std::chrono::high_resolution_clock::now();
196
197 // Then, we convert the reduced matrix to a more flexible format to be able to perform state elimination more easily.
198 storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(transitionMatrix);
199 flexibleMatrix.filterEntries(maybeStates, maybeStates);
200 storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(backwardTransitions);
201 flexibleBackwardTransitions.filterEntries(maybeStates, maybeStates);
202 auto conversionEnd = std::chrono::high_resolution_clock::now();
203
204 std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now();
205
207 boost::optional<std::vector<uint_fast64_t>> distanceBasedPriorities;
209 distanceBasedPriorities = getDistanceBasedPriorities(order, transitionMatrix, backwardTransitions, initialStates, stateValues,
211 }
212
213 uint_fast64_t numberOfStates = transitionMatrix.getRowCount();
214 storm::storage::BitVector regularStatesInBsccs(numberOfStates);
215 storm::storage::BitVector relevantBsccs(bsccDecomposition.size());
216 storm::storage::BitVector bsccRepresentativesAsBitVector(numberOfStates);
217 std::vector<storm::storage::sparse::state_type> bsccRepresentatives;
218 uint_fast64_t currentIndex = 0;
219 for (auto const& bscc : bsccDecomposition) {
220 // Since all states in an SCC can reach all other states, we only need to check whether an arbitrary
221 // state is a maybe state.
222 if (maybeStates.get(*bscc.cbegin())) {
223 relevantBsccs.set(currentIndex);
224 bsccRepresentatives.push_back(*bscc.cbegin());
225 bsccRepresentativesAsBitVector.set(*bscc.cbegin(), true);
226 for (auto const& state : bscc) {
227 regularStatesInBsccs.set(state, true);
228 }
229 }
230 ++currentIndex;
231 }
232 regularStatesInBsccs &= ~bsccRepresentativesAsBitVector;
233
234 // Compute the average time to stay in each state for all states in BSCCs.
235 std::vector<ValueType> averageTimeInStates(stateValues.size(), storm::utility::one<ValueType>());
236
237 // First, we eliminate all states in BSCCs (except for the representative states).
238 std::shared_ptr<StatePriorityQueue> priorityQueue =
239 createStatePriorityQueue(order, distanceBasedPriorities, flexibleMatrix, flexibleBackwardTransitions, stateValues, regularStatesInBsccs);
240 storm::solver::stateelimination::MultiValueStateEliminator<ValueType> stateEliminator(flexibleMatrix, flexibleBackwardTransitions, priorityQueue,
241 stateValues, averageTimeInStates);
242
243 while (priorityQueue->hasNext()) {
244 storm::storage::sparse::state_type state = priorityQueue->pop();
245 stateEliminator.eliminateState(state, true);
246 }
247
248 // Now, we set the values of all states in BSCCs to that of the representative value (and clear the
249 // transitions of the representative states while doing so).
250 auto representativeIt = bsccRepresentatives.begin();
251 for (uint64_t sccIndex : relevantBsccs) {
252 // We only need to set the values for all states of the BSCC if we are not computing the values for the
253 // initial states only.
254 ValueType bsccValue = stateValues[*representativeIt] / averageTimeInStates[*representativeIt];
255 auto const& bscc = bsccDecomposition[sccIndex];
256 if (!computeResultsForInitialStatesOnly) {
257 for (auto const& state : bscc) {
258 stateValues[state] = bsccValue;
259 }
260 } else {
261 for (auto const& state : bscc) {
262 stateValues[state] = storm::utility::zero<ValueType>();
263 }
264 stateValues[*representativeIt] = bsccValue;
265 }
266
267 FlexibleRowType& representativeForwardRow = flexibleMatrix.getRow(*representativeIt);
268 representativeForwardRow.clear();
269 representativeForwardRow.shrink_to_fit();
270
271 FlexibleRowType& representativeBackwardRow = flexibleBackwardTransitions.getRow(*representativeIt);
272 auto it = representativeBackwardRow.begin(), ite = representativeBackwardRow.end();
273 for (; it != ite; ++it) {
274 if (it->getColumn() == *representativeIt) {
275 break;
276 }
277 }
278 representativeBackwardRow.erase(it);
279
280 ++representativeIt;
281 }
282
283 // If there are states remaining that are not in BSCCs, we need to eliminate them now.
284 storm::storage::BitVector remainingStates = maybeStates & ~regularStatesInBsccs;
285
286 // Set the value initial value of all states not in a BSCC to zero, because a) any previous value would
287 // incorrectly influence the result and b) the value have been erroneously changed for the predecessors of
288 // BSCCs by the previous state elimination.
289 for (uint64_t state : remainingStates) {
290 if (!bsccRepresentativesAsBitVector.get(state)) {
291 stateValues[state] = storm::utility::zero<ValueType>();
292 }
293 }
294
295 // We only need to eliminate the remaining states if there was some BSCC that has a non-zero value, i.e.
296 // that consists of maybe states.
297 if (!relevantBsccs.empty()) {
298 performOrdinaryStateElimination(env, flexibleMatrix, flexibleBackwardTransitions, remainingStates, initialStates, computeResultsForInitialStatesOnly,
299 stateValues, distanceBasedPriorities);
300 }
301
302 std::chrono::high_resolution_clock::time_point modelCheckingEnd = std::chrono::high_resolution_clock::now();
303 std::chrono::high_resolution_clock::time_point totalTimeEnd = std::chrono::high_resolution_clock::now();
304
305 {
306 std::chrono::high_resolution_clock::duration sccDecompositionTime = sccDecompositionEnd - sccDecompositionStart;
307 std::chrono::milliseconds sccDecompositionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(sccDecompositionTime);
308 std::chrono::high_resolution_clock::duration conversionTime = conversionEnd - conversionStart;
309 std::chrono::milliseconds conversionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(conversionTime);
310 std::chrono::high_resolution_clock::duration modelCheckingTime = modelCheckingEnd - modelCheckingStart;
311 std::chrono::milliseconds modelCheckingTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(modelCheckingTime);
312 std::chrono::high_resolution_clock::duration totalTime = totalTimeEnd - totalTimeStart;
313 std::chrono::milliseconds totalTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(totalTime);
314
316 STORM_LOG_STATISTICS("Time breakdown:\n");
317 STORM_LOG_STATISTICS(" * time for SCC decomposition: " << sccDecompositionTimeInMilliseconds.count() << "ms\n");
318 STORM_LOG_STATISTICS(" * time for conversion: " << conversionTimeInMilliseconds.count() << "ms\n");
319 STORM_LOG_STATISTICS(" * time for checking: " << modelCheckingTimeInMilliseconds.count() << "ms\n");
320 STORM_LOG_STATISTICS("------------------------------------------\n");
321 STORM_LOG_STATISTICS(" * total time: " << totalTimeInMilliseconds.count() << "ms\n");
322 }
323
324 // Now, we return the value for the only initial state.
325 STORM_LOG_DEBUG("Simplifying and returning result.");
326 for (auto& value : stateValues) {
327 value = storm::utility::simplify(value);
328 }
329 return stateValues;
330}
331
332template<typename SparseDtmcModelType>
335 storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula();
336
337 STORM_LOG_THROW(!pathFormula.hasLowerBound() && pathFormula.hasUpperBound(), storm::exceptions::InvalidPropertyException,
338 "Formula needs to have single upper time bound.");
339 STORM_LOG_THROW(pathFormula.hasIntegerUpperBound(), storm::exceptions::InvalidPropertyException, "Formula needs to have discrete upper time bound.");
340
341 // Retrieve the appropriate bitvectors by model checking the subformulas.
342 std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
343 std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
344 storm::storage::BitVector const& phiStates = leftResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
345 storm::storage::BitVector const& psiStates = rightResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
346
347 // Start by determining the states that have a non-zero probability of reaching the target states within the
348 // time bound.
350 this->getModel().getBackwardTransitions(), phiStates, psiStates, true, pathFormula.getUpperBound<uint64_t>());
351 statesWithProbabilityGreater0 &= ~psiStates;
352
353 // Determine whether we need to perform some further computation.
354 bool furtherComputationNeeded = true;
355 if (checkTask.isOnlyInitialStatesRelevantSet() && this->getModel().getInitialStates().isDisjointFrom(statesWithProbabilityGreater0)) {
356 STORM_LOG_DEBUG("The probability for all initial states was found in a preprocessing step.");
357 furtherComputationNeeded = false;
358 } else if (statesWithProbabilityGreater0.empty()) {
359 STORM_LOG_DEBUG("The probability for all states was found in a preprocessing step.");
360 furtherComputationNeeded = false;
361 }
362
363 storm::storage::SparseMatrix<ValueType> const& transitionMatrix = this->getModel().getTransitionMatrix();
364 storm::storage::BitVector const& initialStates = this->getModel().getInitialStates();
365
366 std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
367
368 if (furtherComputationNeeded) {
369 uint_fast64_t timeBound = pathFormula.getUpperBound<uint64_t>();
370
371 if (checkTask.isOnlyInitialStatesRelevantSet()) {
372 // Determine the set of states that is reachable from the initial state without jumping over a target state.
373 storm::storage::BitVector reachableStates =
374 storm::utility::graph::getReachableStates(transitionMatrix, initialStates, phiStates, psiStates, true, timeBound);
375
376 // Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
377 statesWithProbabilityGreater0 &= reachableStates;
378 }
379
380 // We then build the submatrix that only has the transitions of the maybe states.
382 transitionMatrix.getSubmatrix(true, statesWithProbabilityGreater0, statesWithProbabilityGreater0, true);
383
384 std::vector<uint_fast64_t> distancesFromInitialStates;
385 storm::storage::BitVector relevantStates;
386 if (checkTask.isOnlyInitialStatesRelevantSet()) {
387 // Determine the set of initial states of the sub-model.
388 storm::storage::BitVector subInitialStates = this->getModel().getInitialStates() % statesWithProbabilityGreater0;
389
390 // Precompute the distances of the relevant states to the initial states.
391 distancesFromInitialStates = storm::utility::graph::getDistances(submatrix, subInitialStates, statesWithProbabilityGreater0);
392
393 // Set all states to be relevant for later use.
394 relevantStates = storm::storage::BitVector(statesWithProbabilityGreater0.getNumberOfSetBits(), true);
395 }
396
397 // Create the vector of one-step probabilities to go to target states.
398 std::vector<ValueType> b = transitionMatrix.getConstrainedRowSumVector(statesWithProbabilityGreater0, psiStates);
399
400 // Create the vector with which to multiply.
401 std::vector<ValueType> subresult(b);
402 std::vector<ValueType> tmp(subresult.size());
403
404 // Subtract one from the time bound because initializing the sub-result to b already accounts for one step.
405 --timeBound;
406
407 // Perform matrix-vector multiplications until the time-bound is met.
408 for (uint_fast64_t timeStep = 0; timeStep < timeBound; ++timeStep) {
409 submatrix.multiplyWithVector(subresult, tmp);
410 storm::utility::vector::addVectors(tmp, b, subresult);
411
412 // If we are computing the results for the initial states only, we can use the minimal distance from
413 // each state to the initial states to determine whether we still need to consider the values for
414 // these states. If not, we can null-out all their probabilities.
415 if (checkTask.isOnlyInitialStatesRelevantSet()) {
416 for (uint64_t state : relevantStates) {
417 if (distancesFromInitialStates[state] > (timeBound - timeStep)) {
418 for (auto& element : submatrix.getRow(state)) {
419 element.setValue(storm::utility::zero<ValueType>());
420 }
422 relevantStates.set(state, false);
423 }
424 }
425 }
426 }
427
428 // Set the values of the resulting vector accordingly.
429 storm::utility::vector::setVectorValues(result, statesWithProbabilityGreater0, subresult);
430 }
432
433 // Construct check result based on whether we have computed values for all states or just the initial states.
434 std::unique_ptr<CheckResult> checkResult(new ExplicitQuantitativeCheckResult<ValueType>(result));
435 if (checkTask.isOnlyInitialStatesRelevantSet()) {
436 // If we computed the results for the initial (and prob 0 and prob1) states only, we need to filter the
437 // result to only communicate these results.
438 checkResult->filter(ExplicitQualitativeCheckResult<ValueType>(this->getModel().getInitialStates() | psiStates));
439 }
440 return checkResult;
441}
442
443template<typename SparseDtmcModelType>
446 storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();
447
448 // Retrieve the appropriate bitvectors by model checking the subformulas.
449 std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
450 std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
451 storm::storage::BitVector const& phiStates = leftResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
452 storm::storage::BitVector const& psiStates = rightResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
453
454 return computeUntilProbabilities(env, this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(),
455 this->getModel().getInitialStates(), phiStates, psiStates, checkTask.isOnlyInitialStatesRelevantSet());
456}
457
458template<typename SparseDtmcModelType>
460 Environment const& env, storm::storage::SparseMatrix<ValueType> const& probabilityMatrix,
461 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& initialStates,
462 storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool computeForInitialStatesOnly) {
463 // Then, compute the subset of states that has a probability of 0 or 1, respectively.
464 std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 =
465 storm::utility::graph::performProb01(backwardTransitions, phiStates, psiStates);
466 storm::storage::BitVector statesWithProbability0 = statesWithProbability01.first;
467 storm::storage::BitVector statesWithProbability1 = statesWithProbability01.second;
468 storm::storage::BitVector maybeStates = ~(statesWithProbability0 | statesWithProbability1);
469
470 // Determine whether we need to perform some further computation.
471 bool furtherComputationNeeded = true;
472 if (computeForInitialStatesOnly && initialStates.isDisjointFrom(maybeStates)) {
473 STORM_LOG_DEBUG("The probability for all initial states was found in a preprocessing step.");
474 furtherComputationNeeded = false;
475 } else if (maybeStates.empty()) {
476 STORM_LOG_DEBUG("The probability for all states was found in a preprocessing step.");
477 furtherComputationNeeded = false;
478 }
479
480 std::vector<ValueType> result(maybeStates.size());
481 if (furtherComputationNeeded) {
482 // If we compute the results for the initial states only, we can cut off all maybe state that are not
483 // reachable from them.
484 if (computeForInitialStatesOnly) {
485 // Determine the set of states that is reachable from the initial state without jumping over a target state.
486 storm::storage::BitVector reachableStates =
487 storm::utility::graph::getReachableStates(probabilityMatrix, initialStates, maybeStates, statesWithProbability1);
488
489 // Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
490 maybeStates &= reachableStates;
491 }
492
493 // Create a vector for the probabilities to go to a state with probability 1 in one step.
494 std::vector<ValueType> oneStepProbabilities = probabilityMatrix.getConstrainedRowSumVector(maybeStates, statesWithProbability1);
495
496 // Determine the set of initial states of the sub-model.
497 storm::storage::BitVector newInitialStates = initialStates % maybeStates;
498
499 // We then build the submatrix that only has the transitions of the maybe states.
500 storm::storage::SparseMatrix<ValueType> submatrix = probabilityMatrix.getSubmatrix(false, maybeStates, maybeStates);
501 storm::storage::SparseMatrix<ValueType> submatrixTransposed = submatrix.transpose();
502
503 std::vector<ValueType> subresult = computeReachabilityValues(env, submatrix, oneStepProbabilities, submatrixTransposed, newInitialStates,
504 computeForInitialStatesOnly, oneStepProbabilities);
505 storm::utility::vector::setVectorValues<ValueType>(result, maybeStates, subresult);
506 }
507
508 // Construct full result.
511
512 if (computeForInitialStatesOnly) {
513 // If we computed the results for the initial (and prob 0 and prob1) states only, we need to filter the
514 // result to only communicate these results.
515 std::unique_ptr<ExplicitQuantitativeCheckResult<ValueType>> checkResult = std::make_unique<ExplicitQuantitativeCheckResult<ValueType>>();
516 for (uint64_t state : ~maybeStates | initialStates) {
517 (*checkResult)[state] = result[state];
518 }
519 return std::move(checkResult); // move() required by, e.g., clang 3.8
520 }
521 return std::make_unique<ExplicitQuantitativeCheckResult<ValueType>>(result);
522}
523
524template<typename SparseDtmcModelType>
527 storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula();
528
529 // Retrieve the appropriate bitvectors by model checking the subformulas.
530 std::unique_ptr<CheckResult> subResultPointer = this->check(eventuallyFormula.getSubformula());
531 storm::storage::BitVector trueStates(this->getModel().getNumberOfStates(), true);
532 storm::storage::BitVector const& targetStates = subResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
533
534 // Do some sanity checks to establish some required properties.
535 RewardModelType const& rewardModel = this->getModel().getRewardModel(checkTask.isRewardModelSet() ? checkTask.getRewardModel() : "");
536
537 STORM_LOG_THROW(!rewardModel.empty(), storm::exceptions::IllegalArgumentException, "Input model does not have a reward model.");
539 env, this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), this->getModel().getInitialStates(), targetStates,
540 [&](uint_fast64_t numberOfRows, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& maybeStates) {
541 return rewardModel.getTotalRewardVector(numberOfRows, transitionMatrix, maybeStates);
542 },
544}
545
546template<typename SparseDtmcModelType>
548 Environment const& env, storm::storage::SparseMatrix<ValueType> const& probabilityMatrix,
549 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& initialStates,
550 storm::storage::BitVector const& targetStates, std::vector<ValueType>& stateRewardValues, bool computeForInitialStatesOnly) {
552 env, probabilityMatrix, backwardTransitions, initialStates, targetStates,
553 [&](uint_fast64_t numberOfRows, storm::storage::SparseMatrix<ValueType> const&, storm::storage::BitVector const& maybeStates) {
554 std::vector<ValueType> result(numberOfRows);
555 storm::utility::vector::selectVectorValues(result, maybeStates, stateRewardValues);
556 return result;
557 },
558 computeForInitialStatesOnly);
559}
560
561template<typename SparseDtmcModelType>
563 Environment const& env, storm::storage::SparseMatrix<ValueType> const& probabilityMatrix,
564 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& initialStates,
565 storm::storage::BitVector const& targetStates,
566 std::function<std::vector<ValueType>(uint_fast64_t, storm::storage::SparseMatrix<ValueType> const&, storm::storage::BitVector const&)> const&
567 totalStateRewardVectorGetter,
568 bool computeForInitialStatesOnly) {
569 uint_fast64_t numberOfStates = probabilityMatrix.getRowCount();
570
571 // Compute the subset of states that has a reachability reward less than infinity.
572 storm::storage::BitVector trueStates(numberOfStates, true);
573 storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(backwardTransitions, trueStates, targetStates);
574 infinityStates.complement();
575 storm::storage::BitVector maybeStates = ~targetStates & ~infinityStates;
576
577 // Determine whether we need to perform some further computation.
578 bool furtherComputationNeeded = true;
579 if (computeForInitialStatesOnly) {
580 if (initialStates.isSubsetOf(infinityStates)) {
581 STORM_LOG_DEBUG("The reward of all initial states was found in a preprocessing step.");
582 furtherComputationNeeded = false;
583 }
584 if (initialStates.isSubsetOf(targetStates)) {
585 STORM_LOG_DEBUG("The reward of all initial states was found in a preprocessing step.");
586 furtherComputationNeeded = false;
587 }
588 }
589
590 std::vector<ValueType> result(maybeStates.size());
591 if (furtherComputationNeeded) {
592 // If we compute the results for the initial states only, we can cut off all maybe state that are not
593 // reachable from them.
594 if (computeForInitialStatesOnly) {
595 // Determine the set of states that is reachable from the initial state without jumping over a target state.
596 storm::storage::BitVector reachableStates = storm::utility::graph::getReachableStates(probabilityMatrix, initialStates, maybeStates, targetStates);
597
598 // Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
599 maybeStates &= reachableStates;
600 }
601
602 // Determine the set of initial states of the sub-model.
603 storm::storage::BitVector newInitialStates = initialStates % maybeStates;
604
605 // We then build the submatrix that only has the transitions of the maybe states.
606 storm::storage::SparseMatrix<ValueType> submatrix = probabilityMatrix.getSubmatrix(false, maybeStates, maybeStates);
607 storm::storage::SparseMatrix<ValueType> submatrixTransposed = submatrix.transpose();
608
609 // Project the state reward vector to all maybe-states.
610 std::vector<ValueType> stateRewardValues = totalStateRewardVectorGetter(submatrix.getRowCount(), probabilityMatrix, maybeStates);
611
612 std::vector<ValueType> subresult =
613 computeReachabilityValues(env, submatrix, stateRewardValues, submatrixTransposed, newInitialStates, computeForInitialStatesOnly,
614 probabilityMatrix.getConstrainedRowSumVector(maybeStates, targetStates));
615 storm::utility::vector::setVectorValues<ValueType>(result, maybeStates, subresult);
616 }
617
618 // Construct full result.
621 if (computeForInitialStatesOnly) {
622 // If we computed the results for the initial (and inf) states only, we need to filter the result to
623 // only communicate these results.
624 std::unique_ptr<ExplicitQuantitativeCheckResult<ValueType>> checkResult = std::make_unique<ExplicitQuantitativeCheckResult<ValueType>>();
625 for (uint64_t state : ~maybeStates | initialStates) {
626 (*checkResult)[state] = result[state];
627 }
628 return std::move(checkResult); // move() required by, e.g., clang 3.8
629 }
630 return std::make_unique<ExplicitQuantitativeCheckResult<ValueType>>(result);
631}
632
633template<typename SparseDtmcModelType>
636 storm::logic::ConditionalFormula const& conditionalFormula = checkTask.getFormula();
637
638 // Retrieve the appropriate bitvectors by model checking the subformulas.
639 STORM_LOG_THROW(conditionalFormula.getSubformula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException, "Expected 'eventually' formula.");
640 STORM_LOG_THROW(conditionalFormula.getConditionFormula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException,
641 "Expected 'eventually' formula.");
642
643 std::unique_ptr<CheckResult> leftResultPointer = this->check(conditionalFormula.getSubformula().asEventuallyFormula().getSubformula());
644 std::unique_ptr<CheckResult> rightResultPointer = this->check(conditionalFormula.getConditionFormula().asEventuallyFormula().getSubformula());
645 storm::storage::BitVector phiStates = leftResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
646 storm::storage::BitVector psiStates = rightResultPointer->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
647 storm::storage::BitVector trueStates(this->getModel().getNumberOfStates(), true);
648
649 STORM_LOG_THROW(this->getModel().getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException,
650 "Input model is required to have exactly one initial state.");
651 STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::IllegalArgumentException,
652 "Cannot compute conditional probabilities for all states.");
653 storm::storage::sparse::state_type initialState = *this->getModel().getInitialStates().begin();
654
655 storm::storage::SparseMatrix<ValueType> backwardTransitions = this->getModel().getBackwardTransitions();
656
657 // Compute the 'true' psi states, i.e. those psi states that can be reached without passing through another psi state first.
658 psiStates = storm::utility::graph::getReachableStates(this->getModel().getTransitionMatrix(), this->getModel().getInitialStates(), trueStates, psiStates) &
659 psiStates;
660
661 std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 =
662 storm::utility::graph::performProb01(backwardTransitions, trueStates, psiStates);
663 storm::storage::BitVector statesWithProbabilityGreater0 = ~statesWithProbability01.first;
664 storm::storage::BitVector statesWithProbability1 = std::move(statesWithProbability01.second);
665
666 STORM_LOG_THROW(this->getModel().getInitialStates().isSubsetOf(statesWithProbabilityGreater0), storm::exceptions::InvalidPropertyException,
667 "The condition of the conditional probability has zero probability.");
668
669 // If the initial state is known to have probability 1 of satisfying the condition, we can apply regular model checking.
670 if (this->getModel().getInitialStates().isSubsetOf(statesWithProbability1)) {
671 STORM_LOG_INFO("The condition holds with probability 1, so the regular reachability probability is computed.");
672 std::shared_ptr<storm::logic::BooleanLiteralFormula> trueFormula = std::make_shared<storm::logic::BooleanLiteralFormula>(true);
673 std::shared_ptr<storm::logic::UntilFormula> untilFormula =
674 std::make_shared<storm::logic::UntilFormula>(trueFormula, conditionalFormula.getSubformula().asSharedPointer());
675 return this->computeUntilProbabilities(env, *untilFormula);
676 }
677
678 // From now on, we know the condition does not have a trivial probability in the initial state.
679
680 // Compute the states that can be reached on a path that has a psi state in it.
681 storm::storage::BitVector statesWithPsiPredecessor =
682 storm::utility::graph::performProbGreater0(this->getModel().getTransitionMatrix(), trueStates, psiStates);
683 storm::storage::BitVector statesReachingPhi = storm::utility::graph::performProbGreater0(backwardTransitions, trueStates, phiStates);
684
685 // The set of states we need to consider are those that have a non-zero probability to satisfy the condition or are on some path that has a psi state in it.
686 storm::storage::BitVector maybeStates = statesWithProbabilityGreater0 | (statesWithPsiPredecessor & statesReachingPhi);
687
688 // Determine the set of initial states of the sub-DTMC.
689 storm::storage::BitVector newInitialStates = this->getModel().getInitialStates() % maybeStates;
690
691 // Create a dummy vector for the one-step probabilities.
692 std::vector<ValueType> oneStepProbabilities(maybeStates.getNumberOfSetBits(), storm::utility::zero<ValueType>());
693
694 // We then build the submatrix that only has the transitions of the maybe states.
695 storm::storage::SparseMatrix<ValueType> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(false, maybeStates, maybeStates);
696 storm::storage::SparseMatrix<ValueType> submatrixTransposed = submatrix.transpose();
697
698 // The states we want to eliminate are those that are tagged with "maybe" but are not a phi or psi state.
699 phiStates = phiStates % maybeStates;
700
701 // If there are no phi states in the reduced model, the conditional probability is trivially zero.
702 if (phiStates.empty()) {
703 return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(initialState, storm::utility::zero<ValueType>()));
704 }
705
706 psiStates = psiStates % maybeStates;
707
708 // Keep only the states that we do not eliminate in the maybe states.
709 maybeStates = phiStates | psiStates;
710
711 storm::storage::BitVector statesToEliminate = ~maybeStates & ~newInitialStates;
712
713 // Before starting the model checking process, we assign priorities to states so we can use them to
714 // impose ordering constraints later.
715 boost::optional<std::vector<uint_fast64_t>> distanceBasedPriorities;
718 distanceBasedPriorities = getDistanceBasedPriorities(order, submatrix, submatrixTransposed, newInitialStates, oneStepProbabilities,
720 }
721
722 storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(submatrix);
723 storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(submatrixTransposed, true);
724
725 std::shared_ptr<StatePriorityQueue> statePriorities =
726 createStatePriorityQueue(order, distanceBasedPriorities, flexibleMatrix, flexibleBackwardTransitions, oneStepProbabilities, statesToEliminate);
727
728 STORM_LOG_INFO("Computing conditional probilities.\n");
729 uint_fast64_t numberOfStatesToEliminate = statePriorities->size();
730 STORM_LOG_INFO("Eliminating " << numberOfStatesToEliminate << " states using the state elimination technique.\n");
731 performPrioritizedStateElimination(statePriorities, flexibleMatrix, flexibleBackwardTransitions, oneStepProbabilities, this->getModel().getInitialStates(),
732 true);
733
735 storm::solver::stateelimination::ConditionalStateEliminator<ValueType>(flexibleMatrix, flexibleBackwardTransitions, oneStepProbabilities, phiStates,
736 psiStates);
737
738 // Eliminate the transitions going into the initial state (if there are any).
739 if (!flexibleBackwardTransitions.getRow(*newInitialStates.begin()).empty()) {
740 stateEliminator.eliminateState(*newInitialStates.begin(), false);
741 }
742
743 // Now we need to basically eliminate all chains of not-psi states after phi states and chains of not-phi
744 // states after psi states.
745 for (auto const& trans1 : flexibleMatrix.getRow(*newInitialStates.begin())) {
746 auto initialStateSuccessor = trans1.getColumn();
747
748 STORM_LOG_TRACE("Exploring successor " << initialStateSuccessor << " of the initial state.");
749
750 if (phiStates.get(initialStateSuccessor)) {
751 STORM_LOG_TRACE("Is a phi state.");
752
753 // If the state is both a phi and a psi state, we do not need to eliminate chains.
754 if (psiStates.get(initialStateSuccessor)) {
755 continue;
756 }
757
758 // At this point, we know that the state satisfies phi and not psi.
759 // This means, we must compute the probability to reach psi states, which in turn means that we need
760 // to eliminate all chains of non-psi states between the current state and psi states.
761 bool hasNonPsiSuccessor = true;
762 while (hasNonPsiSuccessor) {
763 stateEliminator.setFilterPhi();
764 hasNonPsiSuccessor = false;
765
766 // Only treat the state if it has an outgoing transition other than a self-loop.
767 auto const currentRow = flexibleMatrix.getRow(initialStateSuccessor);
768 if (currentRow.size() > 1 || (!currentRow.empty() && currentRow.front().getColumn() != initialStateSuccessor)) {
769 for (auto const& element : currentRow) {
770 // If any of the successors is a phi state, we eliminate it (wrt. all its phi predecessors).
771 if (!psiStates.get(element.getColumn())) {
772 FlexibleRowType const& successorRow = flexibleMatrix.getRow(element.getColumn());
773 // Eliminate the successor only if there possibly is a psi state reachable through it.
774 if (successorRow.size() > 1 || (!successorRow.empty() && successorRow.front().getColumn() != element.getColumn())) {
775 STORM_LOG_TRACE("Found non-psi successor " << element.getColumn() << " that needs to be eliminated.");
776 stateEliminator.eliminateState(element.getColumn(), false);
777 hasNonPsiSuccessor = true;
778 }
779 }
780 }
781 STORM_LOG_ASSERT(!flexibleMatrix.getRow(initialStateSuccessor).empty(), "Expected new transitions to be non-empty (1).");
782 }
783 }
784 stateEliminator.unsetFilter();
785 } else {
786 STORM_LOG_ASSERT(psiStates.get(initialStateSuccessor), "Expected psi state.");
787 STORM_LOG_TRACE("Is a psi state.");
788
789 // At this point, we know that the state satisfies psi and not phi.
790 // This means, we must compute the probability to reach phi states, which in turn means that we need
791 // to eliminate all chains of non-phi states between the current state and phi states.
792
793 bool hasNonPhiSuccessor = true;
794 while (hasNonPhiSuccessor) {
795 stateEliminator.setFilterPsi();
796 hasNonPhiSuccessor = false;
797
798 // Only treat the state if it has an outgoing transition other than a self-loop.
799 auto const currentRow = flexibleMatrix.getRow(initialStateSuccessor);
800 if (currentRow.size() > 1 || (!currentRow.empty() && currentRow.front().getColumn() != initialStateSuccessor)) {
801 for (auto const& element : currentRow) {
802 // If any of the successors is a psi state, we eliminate it (wrt. all its psi predecessors).
803 if (!phiStates.get(element.getColumn())) {
804 FlexibleRowType const& successorRow = flexibleMatrix.getRow(element.getColumn());
805 if (successorRow.size() > 1 || (!successorRow.empty() && successorRow.front().getColumn() != element.getColumn())) {
806 STORM_LOG_TRACE("Found non-phi successor " << element.getColumn() << " that needs to be eliminated.");
807 stateEliminator.eliminateState(element.getColumn(), false);
808 hasNonPhiSuccessor = true;
809 }
810 }
811 }
812 }
813 }
814 stateEliminator.unsetFilter();
815 }
816 }
817
820
821 for (auto const& trans1 : flexibleMatrix.getRow(*newInitialStates.begin())) {
822 auto initialStateSuccessor = trans1.getColumn();
823 if (phiStates.get(initialStateSuccessor)) {
824 if (psiStates.get(initialStateSuccessor)) {
825 numerator += trans1.getValue();
826 denominator += trans1.getValue();
827 } else {
829 for (auto const& trans2 : flexibleMatrix.getRow(initialStateSuccessor)) {
830 if (psiStates.get(trans2.getColumn())) {
831 additiveTerm += trans2.getValue();
832 }
833 }
834 additiveTerm *= trans1.getValue();
835 numerator += additiveTerm;
836 denominator += additiveTerm;
837 }
838 } else {
839 STORM_LOG_ASSERT(psiStates.get(initialStateSuccessor), "Expected psi state.");
840 denominator += trans1.getValue();
842 for (auto const& trans2 : flexibleMatrix.getRow(initialStateSuccessor)) {
843 if (phiStates.get(trans2.getColumn())) {
844 additiveTerm += trans2.getValue();
845 }
846 }
847 numerator += trans1.getValue() * additiveTerm;
848 }
849 }
850
851 return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(initialState, numerator / denominator));
852}
853
854template<typename SparseDtmcModelType>
855void SparseDtmcEliminationModelChecker<SparseDtmcModelType>::performPrioritizedStateElimination(
856 std::shared_ptr<StatePriorityQueue>& priorityQueue, storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix,
857 storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions, std::vector<ValueType>& values, storm::storage::BitVector const& initialStates,
858 bool computeResultsForInitialStatesOnly) {
859 storm::solver::stateelimination::PrioritizedStateEliminator<ValueType> stateEliminator(transitionMatrix, backwardTransitions, priorityQueue, values);
860
861 while (priorityQueue->hasNext()) {
862 storm::storage::sparse::state_type state = priorityQueue->pop();
863 bool removeForwardTransitions = computeResultsForInitialStatesOnly && !initialStates.get(state);
864 stateEliminator.eliminateState(state, removeForwardTransitions);
865 if (removeForwardTransitions) {
866 values[state] = storm::utility::zero<ValueType>();
867 }
868 }
869}
870
871template<typename SparseDtmcModelType>
872void SparseDtmcEliminationModelChecker<SparseDtmcModelType>::performOrdinaryStateElimination(
873 Environment const& env, storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix,
874 storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions, storm::storage::BitVector const& subsystem,
875 storm::storage::BitVector const& initialStates, bool computeResultsForInitialStatesOnly, std::vector<ValueType>& values,
876 boost::optional<std::vector<uint_fast64_t>> const& distanceBasedPriorities) {
877 std::shared_ptr<StatePriorityQueue> statePriorities =
878 createStatePriorityQueue(env.solver().elimination().getOrder(), distanceBasedPriorities, transitionMatrix, backwardTransitions, values, subsystem);
879
880 std::size_t numberOfStatesToEliminate = statePriorities->size();
881 STORM_LOG_DEBUG("Eliminating " << numberOfStatesToEliminate << " states using the state elimination technique.\n");
882 performPrioritizedStateElimination(statePriorities, transitionMatrix, backwardTransitions, values, initialStates, computeResultsForInitialStatesOnly);
883 STORM_LOG_DEBUG("Eliminated " << numberOfStatesToEliminate << " states.\n");
884}
885
886template<typename SparseDtmcModelType>
887uint_fast64_t SparseDtmcEliminationModelChecker<SparseDtmcModelType>::performHybridStateElimination(
888 Environment const& env, storm::storage::SparseMatrix<ValueType> const& forwardTransitions,
889 storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix, storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions,
890 storm::storage::BitVector const& subsystem, storm::storage::BitVector const& initialStates, bool computeResultsForInitialStatesOnly,
891 std::vector<ValueType>& values, boost::optional<std::vector<uint_fast64_t>> const& distanceBasedPriorities) {
892 // When using the hybrid technique, we recursively treat the SCCs up to some size.
893 std::vector<storm::storage::sparse::state_type> entryStateQueue;
894 STORM_LOG_DEBUG("Eliminating " << subsystem.size() << " states using the hybrid elimination technique.\n");
895 uint_fast64_t maximalDepth =
896 treatScc(env, transitionMatrix, values, initialStates, subsystem, initialStates, forwardTransitions, backwardTransitions, false, 0,
897 env.solver().elimination().getMaximalSccSize(), entryStateQueue, computeResultsForInitialStatesOnly, distanceBasedPriorities);
898
899 // If the entry states were to be eliminated last, we need to do so now.
900 if (env.solver().elimination().isEliminateEntryStatesLastSet()) {
901 STORM_LOG_DEBUG("Eliminating " << entryStateQueue.size() << " entry states as a last step.");
902 std::vector<storm::storage::sparse::state_type> sortedStates(entryStateQueue.begin(), entryStateQueue.end());
903 std::shared_ptr<StatePriorityQueue> queuePriorities = std::make_shared<StaticStatePriorityQueue>(sortedStates);
904 performPrioritizedStateElimination(queuePriorities, transitionMatrix, backwardTransitions, values, initialStates, computeResultsForInitialStatesOnly);
905 }
906 STORM_LOG_DEBUG("Eliminated " << subsystem.size() << " states.\n");
907 return maximalDepth;
908}
909
910template<typename SparseDtmcModelType>
911std::vector<typename SparseDtmcEliminationModelChecker<SparseDtmcModelType>::ValueType>
912SparseDtmcEliminationModelChecker<SparseDtmcModelType>::computeReachabilityValues(
913 Environment const& env, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType>& values,
914 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& initialStates, bool computeResultsForInitialStatesOnly,
915 std::vector<ValueType> const& oneStepProbabilitiesToTarget) {
916 // Then, we convert the reduced matrix to a more flexible format to be able to perform state elimination more easily.
917 storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(transitionMatrix);
918 storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(backwardTransitions);
919
920 EliminationOrder order = env.solver().elimination().getOrder();
921 boost::optional<std::vector<uint_fast64_t>> distanceBasedPriorities;
923 distanceBasedPriorities = getDistanceBasedPriorities(order, transitionMatrix, backwardTransitions, initialStates, oneStepProbabilitiesToTarget,
925 }
926
927 // Create a bit vector that represents the subsystem of states we still have to eliminate.
928 storm::storage::BitVector subsystem = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
929
930 if (env.solver().elimination().getMethod() == EliminationMethod::State) {
931 performOrdinaryStateElimination(env, flexibleMatrix, flexibleBackwardTransitions, subsystem, initialStates, computeResultsForInitialStatesOnly, values,
932 distanceBasedPriorities);
933 } else if (env.solver().elimination().getMethod() == EliminationMethod::Hybrid) {
934 uint64_t maximalDepth = performHybridStateElimination(env, transitionMatrix, flexibleMatrix, flexibleBackwardTransitions, subsystem, initialStates,
935 computeResultsForInitialStatesOnly, values, distanceBasedPriorities);
936 STORM_LOG_TRACE("Maximal depth of decomposition was " << maximalDepth << ".");
937 }
938
939 STORM_LOG_ASSERT(flexibleMatrix.empty(), "Not all transitions were eliminated.");
940 STORM_LOG_ASSERT(flexibleBackwardTransitions.empty(), "Not all transitions were eliminated.");
941
942 // Now, we return the value for the only initial state.
943 STORM_LOG_DEBUG("Simplifying and returning result.");
944 for (auto& value : values) {
945 value = storm::utility::simplify(value);
946 }
947 return values;
948}
949
950template<typename SparseDtmcModelType>
951uint_fast64_t SparseDtmcEliminationModelChecker<SparseDtmcModelType>::treatScc(
952 Environment const& env, storm::storage::FlexibleSparseMatrix<ValueType>& matrix, std::vector<ValueType>& values,
953 storm::storage::BitVector const& entryStates, storm::storage::BitVector const& scc, storm::storage::BitVector const& initialStates,
954 storm::storage::SparseMatrix<ValueType> const& forwardTransitions, storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions,
955 bool eliminateEntryStates, uint_fast64_t level, uint_fast64_t maximalSccSize, std::vector<storm::storage::sparse::state_type>& entryStateQueue,
956 bool computeResultsForInitialStatesOnly, boost::optional<std::vector<uint_fast64_t>> const& distanceBasedPriorities) {
957 uint_fast64_t maximalDepth = level;
958
959 // If the SCCs are large enough, we try to split them further.
960 if (scc.getNumberOfSetBits() > maximalSccSize) {
961 STORM_LOG_TRACE("SCC is large enough (" << scc.getNumberOfSetBits() << " states) to be decomposed further.");
962
963 // Here, we further decompose the SCC into sub-SCCs.
964 storm::storage::BitVector nonEntrySccStates = scc & ~entryStates;
965 storm::storage::StronglyConnectedComponentDecomposition<ValueType> decomposition(
966 forwardTransitions, storm::storage::StronglyConnectedComponentDecompositionOptions().subsystem(nonEntrySccStates));
967 STORM_LOG_TRACE("Decomposed SCC into " << decomposition.size() << " sub-SCCs.");
968
969 // Store a bit vector of remaining SCCs so we can be flexible when it comes to the order in which
970 // we eliminate the SCCs.
971 storm::storage::BitVector remainingSccs(decomposition.size(), true);
972
973 // First, get rid of the trivial SCCs.
974 storm::storage::BitVector statesInTrivialSccs(matrix.getRowCount());
975 for (uint_fast64_t sccIndex = 0; sccIndex < decomposition.size(); ++sccIndex) {
976 storm::storage::StronglyConnectedComponent const& scc = decomposition.getBlock(sccIndex);
977 if (scc.isTrivial()) {
978 // Put the only state of the trivial SCC into the set of states to eliminate.
979 statesInTrivialSccs.set(*scc.begin(), true);
980 remainingSccs.set(sccIndex, false);
981 }
982 }
983
984 std::shared_ptr<StatePriorityQueue> statePriorities =
985 createStatePriorityQueue(env.solver().elimination().getOrder(), distanceBasedPriorities, matrix, backwardTransitions, values, statesInTrivialSccs);
986 STORM_LOG_TRACE("Eliminating " << statePriorities->size() << " trivial SCCs.");
987 performPrioritizedStateElimination(statePriorities, matrix, backwardTransitions, values, initialStates, computeResultsForInitialStatesOnly);
988 STORM_LOG_TRACE("Eliminated all trivial SCCs.");
989
990 // And then recursively treat the remaining sub-SCCs.
991 STORM_LOG_TRACE("Eliminating " << remainingSccs.getNumberOfSetBits() << " remaining SCCs on level " << level << ".");
992 for (uint64_t sccIndex : remainingSccs) {
993 storm::storage::StronglyConnectedComponent const& newScc = decomposition.getBlock(sccIndex);
994
995 // Rewrite SCC into bit vector and subtract it from the remaining states.
996 storm::storage::BitVector newSccAsBitVector(forwardTransitions.getRowCount(), newScc.begin(), newScc.end());
997
998 // Determine the set of entry states of the SCC.
999 storm::storage::BitVector entryStates(forwardTransitions.getRowCount());
1000 for (auto const& state : newScc) {
1001 for (auto const& predecessor : backwardTransitions.getRow(state)) {
1002 if (predecessor.getValue() != storm::utility::zero<ValueType>() && !newSccAsBitVector.get(predecessor.getColumn())) {
1003 entryStates.set(state);
1004 }
1005 }
1006 }
1007
1008 // Recursively descend in SCC-hierarchy.
1009 uint_fast64_t depth = treatScc(env, matrix, values, entryStates, newSccAsBitVector, initialStates, forwardTransitions, backwardTransitions,
1010 eliminateEntryStates || !env.solver().elimination().isEliminateEntryStatesLastSet(), level + 1, maximalSccSize,
1011 entryStateQueue, computeResultsForInitialStatesOnly, distanceBasedPriorities);
1012 maximalDepth = std::max(maximalDepth, depth);
1013 }
1014 } else {
1015 // In this case, we perform simple state elimination in the current SCC.
1016 STORM_LOG_TRACE("SCC of size " << scc.getNumberOfSetBits() << " is small enough to be eliminated directly.");
1017 std::shared_ptr<StatePriorityQueue> statePriorities =
1018 createStatePriorityQueue(env.solver().elimination().getOrder(), distanceBasedPriorities, matrix, backwardTransitions, values, scc & ~entryStates);
1019 performPrioritizedStateElimination(statePriorities, matrix, backwardTransitions, values, initialStates, computeResultsForInitialStatesOnly);
1020 STORM_LOG_TRACE("Eliminated all states of SCC.");
1021 }
1022
1023 // Finally, eliminate the entry states (if we are required to do so).
1024 if (eliminateEntryStates) {
1025 STORM_LOG_TRACE("Finally, eliminating entry states.");
1026 std::shared_ptr<StatePriorityQueue> naivePriorities = createStatePriorityQueue(entryStates);
1027 performPrioritizedStateElimination(naivePriorities, matrix, backwardTransitions, values, initialStates, computeResultsForInitialStatesOnly);
1028 STORM_LOG_TRACE("Eliminated/added entry states.");
1029 } else {
1030 STORM_LOG_TRACE("Finally, adding entry states to queue.");
1031 for (uint64_t state : entryStates) {
1032 entryStateQueue.push_back(state);
1033 }
1034 }
1035
1036 return maximalDepth;
1037}
1038
1040
1043} // namespace modelchecker
1044} // namespace storm
storm::solver::stateelimination::EliminationOrder const & getOrder() const
SolverEnvironment & solver()
EliminationSolverEnvironment & elimination()
Formula const & getRightSubformula() const
Formula const & getLeftSubformula() const
storm::expressions::Expression const & getUpperBound(unsigned i=0) const
bool hasIntegerUpperBound(unsigned i=0) const
Formula const & getConditionFormula() const
Formula const & getSubformula() const
EventuallyFormula & asEventuallyFormula()
Definition Formula.cpp:341
bool isInFragment(FragmentSpecification const &fragment) const
Definition Formula.cpp:204
virtual bool isEventuallyFormula() const
Definition Formula.cpp:88
std::shared_ptr< Formula const > asSharedPointer()
Definition Formula.cpp:571
FragmentSpecification & setOnlyEventuallyFormuluasInConditionalFormulasAllowed(bool newValue)
FragmentSpecification & setCumulativeRewardFormulasAllowed(bool newValue)
FragmentSpecification & setConditionalProbabilityFormulasAllowed(bool newValue)
FragmentSpecification & setLongRunAverageProbabilitiesAllowed(bool newValue)
FragmentSpecification & setInstantaneousFormulasAllowed(bool newValue)
FragmentSpecification & setNestedOperatorsAllowed(bool newValue)
Formula const & getSubformula() const
virtual std::unique_ptr< CheckResult > check(Environment const &env, CheckTask< storm::logic::Formula, SolutionType > const &checkTask)
bool isRewardModelSet() const
Retrieves whether a reward model was set.
Definition CheckTask.h:191
std::string const & getRewardModel() const
Retrieves the reward model over which to perform the checking (if set).
Definition CheckTask.h:198
FormulaType const & getFormula() const
Retrieves the formula from this task.
Definition CheckTask.h:141
bool isOnlyInitialStatesRelevantSet() const
Retrieves whether only the initial states are relevant in the computation.
Definition CheckTask.h:205
virtual bool canHandle(CheckTask< storm::logic::Formula, SolutionType > const &checkTask) const override
SparseDtmcEliminationModelChecker(storm::models::sparse::Dtmc< ValueType > const &model)
Creates an elimination-based model checker for the given model.
virtual std::unique_ptr< CheckResult > computeReachabilityRewards(Environment const &env, CheckTask< storm::logic::EventuallyFormula, SolutionType > const &checkTask) override
virtual std::unique_ptr< CheckResult > computeUntilProbabilities(Environment const &env, CheckTask< storm::logic::UntilFormula, SolutionType > const &checkTask) override
virtual std::unique_ptr< CheckResult > computeLongRunAverageProbabilities(Environment const &env, CheckTask< storm::logic::StateFormula, SolutionType > const &checkTask) override
storm::storage::FlexibleSparseMatrix< ValueType >::row_type FlexibleRowType
virtual std::unique_ptr< CheckResult > computeBoundedUntilProbabilities(Environment const &env, CheckTask< storm::logic::BoundedUntilFormula, SolutionType > const &checkTask) override
virtual std::unique_ptr< CheckResult > computeLongRunAverageRewards(Environment const &env, CheckTask< storm::logic::LongRunAverageRewardFormula, SolutionType > const &checkTask) override
virtual std::unique_ptr< CheckResult > computeConditionalProbabilities(Environment const &env, CheckTask< storm::logic::ConditionalFormula, SolutionType > const &checkTask) override
This class represents a discrete-time Markov chain.
Definition Dtmc.h:13
void eliminateState(storm::storage::sparse::state_type state, bool removeForwardTransitions)
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void complement()
Negates all bits in the bit vector.
bool isDisjointFrom(BitVector const &other) const
Checks whether none of the bits that are set in the current bit vector are also set in the given bit ...
bool full() const
Retrieves whether all bits are set in this bit vector.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
bool isSubsetOf(BitVector const &other) const
Checks whether all bits that are set in the current bit vector are also set in the given bit vector.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
The flexible sparse matrix is used during state elimination.
row_type & getRow(index_type)
Returns an object representing the given row.
index_type getRowCount() const
Returns the number of rows of the matrix.
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.
void multiplyWithVector(std::vector< value_type > const &vector, std::vector< value_type > &result, std::vector< value_type > const *summand=nullptr) const
Multiplies the matrix with the given vector and writes the result to the given result vector.
SparseMatrix getSubmatrix(bool useGroups, storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint, bool insertDiagonalEntries=false, storm::storage::BitVector const &makeZeroColumns=storm::storage::BitVector()) const
Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not set to ...
std::vector< value_type > getConstrainedRowSumVector(storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint) const
Computes a vector whose i-th entry is the sum of the entries in the i-th selected row where only thos...
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
iterator begin()
Returns an iterator to the states in this SCC.
Definition StateBlock.cpp:5
iterator end()
Returns an iterator that points one past the end of the states in this SCC.
This class represents the decomposition of a graph-like structure into its strongly connected compone...
bool isTrivial() const
Retrieves whether this SCC is trivial.
#define STORM_LOG_INFO(message)
Definition logging.h:27
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_STATISTICS(message)
Definition logging.h:41
#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 ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
FragmentSpecification prctl()
bool eliminationOrderNeedsReversedDistances(EliminationOrder const &order)
bool eliminationOrderNeedsForwardDistances(EliminationOrder const &order)
std::shared_ptr< StatePriorityQueue > createStatePriorityQueue(EliminationOrder const &order, boost::optional< std::vector< uint_fast64_t > > const &distanceBasedStatePriorities, storm::storage::FlexibleSparseMatrix< ValueType > const &transitionMatrix, storm::storage::FlexibleSparseMatrix< ValueType > const &backwardTransitions, std::vector< ValueType > const &oneStepProbabilities, storm::storage::BitVector const &states)
bool eliminationOrderNeedsDistances(EliminationOrder const &order)
EliminationOrder
An enum that contains all available state elimination orders.
std::vector< uint_fast64_t > getDistanceBasedPriorities(EliminationOrder const &order, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &transitionMatrixTransposed, storm::storage::BitVector const &initialStates, std::vector< ValueType > const &oneStepProbabilities, bool forward, bool reverse)
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01(storm::models::sparse::DeterministicModel< T > const &model, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi i...
Definition graph.cpp:393
storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::BitVector const &initialStates, storm::storage::BitVector const &constraintStates, storm::storage::BitVector const &targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceFilter)
Performs a forward depth-first search through the underlying graph structure to identify the states t...
Definition graph.cpp:41
storm::storage::BitVector performProbGreater0(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Performs a backward depth-first search trough the underlying graph structure of the given model to de...
Definition graph.cpp:315
storm::storage::BitVector performProb1(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &, storm::storage::BitVector const &psiStates, storm::storage::BitVector const &statesWithProbabilityGreater0)
Computes the set of states of the given model for which all paths lead to the given set of target sta...
Definition graph.cpp:376
std::vector< uint_fast64_t > getDistances(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::BitVector const &initialStates, boost::optional< storm::storage::BitVector > const &subsystem)
Performs a breadth-first search through the underlying graph structure to compute the distance from a...
Definition graph.cpp:281
void addVectors(std::vector< InValueType1 > const &firstOperand, std::vector< InValueType2 > const &secondOperand, std::vector< OutValueType > &target)
Adds the two given vectors and writes the result to the target vector.
Definition vector.h:399
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:78
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
Definition vector.h:184
ValueType simplify(ValueType value)
ValueType zero()
Definition constants.cpp:24
ValueType infinity()
Definition constants.cpp:29
ValueType one()
Definition constants.cpp:19