Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ConditionalHelper.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4
20#include "storm/utility/graph.h"
22
23namespace storm::modelchecker {
24
25namespace internal {
26
27template<typename ValueType>
28void eliminateEndComponents(storm::storage::BitVector possibleEcStates, bool addRowAtRepresentativeState, std::optional<uint64_t> representativeRowEntry,
29 storm::storage::SparseMatrix<ValueType>& matrix, uint64_t& initialState, storm::storage::BitVector& rowsWithSum1,
30 std::vector<ValueType>& rowValues1, storm::OptionalRef<std::vector<ValueType>> rowValues2 = {}) {
31 storm::storage::MaximalEndComponentDecomposition<ValueType> ecs(matrix, matrix.transpose(true), possibleEcStates, rowsWithSum1);
32 if (ecs.empty()) {
33 return; // nothing to do
34 }
35
36 storm::storage::BitVector allRowGroups(matrix.getRowGroupCount(), true);
38 matrix, ecs, allRowGroups, addRowAtRepresentativeState ? allRowGroups : ~allRowGroups, representativeRowEntry.has_value());
39
40 // Update matrix
41 matrix = std::move(ecElimResult.matrix);
42 if (addRowAtRepresentativeState && representativeRowEntry) {
43 auto const columnIndex = ecElimResult.oldToNewStateMapping[*representativeRowEntry];
44 for (auto representativeRowIndex : ecElimResult.sinkRows) {
45 auto row = matrix.getRow(representativeRowIndex);
46 STORM_LOG_ASSERT(row.getNumberOfEntries() == 1, "unexpected number of entries in representative row.");
47 auto& entry = *row.begin();
48 entry.setColumn(columnIndex);
49 }
50 }
51
52 // update vectors
53 auto updateRowValue = [&ecElimResult](std::vector<ValueType>& rowValues) {
54 std::vector<ValueType> newRowValues;
55 newRowValues.reserve(ecElimResult.newToOldRowMapping.size());
56 for (auto oldRowIndex : ecElimResult.newToOldRowMapping) {
57 newRowValues.push_back(rowValues[oldRowIndex]);
58 }
59 rowValues = std::move(newRowValues);
61 std::all_of(ecElimResult.sinkRows.begin(), ecElimResult.sinkRows.end(), [&rowValues](auto i) { return storm::utility::isZero(rowValues[i]); }),
62 "Sink rows are expected to have zero value");
63 };
64 updateRowValue(rowValues1);
65 if (rowValues2) {
66 updateRowValue(*rowValues2);
67 }
68
69 // update initial state
70 initialState = ecElimResult.oldToNewStateMapping[initialState];
71
72 // update bitvector
73 storm::storage::BitVector newRowsWithSum1(ecElimResult.newToOldRowMapping.size(), true);
74 uint64_t newRowIndex = 0;
75 for (auto oldRowIndex : ecElimResult.newToOldRowMapping) {
76 if ((addRowAtRepresentativeState && !representativeRowEntry.has_value() && ecElimResult.sinkRows.get(newRowIndex)) || !rowsWithSum1.get(oldRowIndex)) {
77 newRowsWithSum1.set(newRowIndex, false);
78 }
79 ++newRowIndex;
80 }
81 rowsWithSum1 = std::move(newRowsWithSum1);
82}
83
84template<typename ValueType, typename SolutionType = ValueType>
86 std::vector<ValueType> const& rowValues, storm::storage::BitVector const& rowsWithSum1,
87 storm::solver::OptimizationDirection const dir, uint64_t const initialState) {
88 // Initialize the solution vector.
89 std::vector<SolutionType> x(matrix.getRowGroupCount(), storm::utility::zero<ValueType>());
90
91 // Set up the solver.
93 solver->setOptimizationDirection(dir);
94 solver->setRequirementsChecked();
95 solver->setHasUniqueSolution(true);
96 solver->setHasNoEndComponents(true);
97 solver->setLowerBound(storm::utility::zero<ValueType>());
98 solver->setUpperBound(storm::utility::one<ValueType>());
99
100 // Solve the corresponding system of equations.
101 solver->solveEquations(env, x, rowValues);
102 return x[initialState];
103}
104
109template<typename ValueType>
110void computeReachabilityProbabilities(Environment const& env, std::map<uint64_t, ValueType>& nonZeroResults, storm::solver::OptimizationDirection const dir,
111 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& initialStates,
112 storm::storage::BitVector const& allowedStates, storm::storage::BitVector const& targetStates) {
113 if (initialStates.empty()) { // nothing to do
114 return;
115 }
116 auto const reachableStates = storm::utility::graph::getReachableStates(transitionMatrix, initialStates, allowedStates, targetStates);
117 auto const subTargets = targetStates % reachableStates;
118 // Catch the case where no target is reachable from an initial state. In this case, there is nothing to do since all probabilities are zero.
119 if (subTargets.empty()) {
120 return;
121 }
122 auto const subInits = initialStates % reachableStates;
123 auto const submatrix = transitionMatrix.getSubmatrix(true, reachableStates, reachableStates);
125 env, storm::solver::SolveGoal<ValueType>(dir, subInits), submatrix, submatrix.transpose(true), storm::storage::BitVector(subTargets.size(), true),
126 subTargets, false, false);
127 auto origInitIt = initialStates.begin();
128 for (auto subInit : subInits) {
129 auto const& val = subResult.values[subInit];
130 if (!storm::utility::isZero(val)) {
131 nonZeroResults.emplace(*origInitIt, val);
132 }
133 ++origInitIt;
134 }
135}
136
137template<typename ValueType>
139 storm::storage::BitVector const maybeStates; // Those states that can be reached from initial without reaching a terminal state
140 storm::storage::BitVector const terminalStates; // Those states where we already know the probability to reach the condition and the target value
141 storm::storage::BitVector const conditionStates; // Those states where the condition holds almost surely (under all schedulers)
142 storm::storage::BitVector const universalObservationFailureStates; // Those states where the condition is not reachable (under all schedulers)
143 storm::storage::BitVector const existentialObservationFailureStates; // Those states s where a scheduler exists that (i) does not reach the condition from
144 // s and (ii) acts optimal in all terminal states
145 std::map<uint64_t, ValueType> const nonZeroTargetStateValues; // The known non-zero target values. (default is zero)
146 // There are three cases of terminal states:
147 // 1. conditionStates: The condition holds, so the target value is the optimal probability to reach target from there
148 // 2. targetStates: The target is reached, so the target value is the optimal probability to reach a condition from there.
149 // The remaining probability mass is the probability of an observation failure
150 // 3. states that can not reach the condition under any scheduler. The target value is zero.
151
152 // TerminalStates is a superset of conditionStates and dom(nonZeroTargetStateValues).
153 // For a terminalState that is not a conditionState, it is impossible to (reach the condition and not reach the target).
154
155 ValueType getTargetValue(uint64_t state) const {
156 STORM_LOG_ASSERT(terminalStates.get(state), "Tried to get target value for non-terminal state");
157 auto const it = nonZeroTargetStateValues.find(state);
158 return it == nonZeroTargetStateValues.end() ? storm::utility::zero<ValueType>() : it->second;
159 }
160
161 ValueType failProbability(uint64_t state) const {
162 STORM_LOG_ASSERT(terminalStates.get(state), "Tried to get fail probability for non-terminal state");
163 STORM_LOG_ASSERT(!conditionStates.get(state), "Tried to get fail probability for a condition state");
164 // condition states have fail probability zero
165 return storm::utility::one<ValueType>() - getTargetValue(state);
166 }
167};
168
169template<typename ValueType>
171 storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
172 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& relevantStates,
173 storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates) {
174 storm::storage::BitVector const allStates(transitionMatrix.getRowGroupCount(), true);
175 auto extendedConditionStates =
176 storm::utility::graph::performProb1A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, allStates, conditionStates);
177 auto universalObservationFailureStates = storm::utility::graph::performProb0A(backwardTransitions, allStates, extendedConditionStates);
178 std::map<uint64_t, ValueType> nonZeroTargetStateValues;
179 auto const extendedTargetStates =
180 storm::utility::graph::performProb1A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, allStates, targetStates);
181 computeReachabilityProbabilities(env, nonZeroTargetStateValues, dir, transitionMatrix, extendedConditionStates, allStates, extendedTargetStates);
182 auto const targetAndNotCondFailStates = extendedTargetStates & ~(extendedConditionStates | universalObservationFailureStates);
183 computeReachabilityProbabilities(env, nonZeroTargetStateValues, dir, transitionMatrix, targetAndNotCondFailStates, allStates, extendedConditionStates);
184
185 // get states where the optimal policy reaches the condition with positive probability
186 auto terminalStatesThatReachCondition = extendedConditionStates;
187 for (auto state : targetAndNotCondFailStates) {
188 if (nonZeroTargetStateValues.contains(state)) {
189 terminalStatesThatReachCondition.set(state, true);
190 }
191 }
192
193 // get the terminal states following the three cases described above
194 auto terminalStates = extendedConditionStates | extendedTargetStates | universalObservationFailureStates;
195 if (storm::solver::minimize(dir)) {
196 // There can be target states from which (only) the *minimal* probability to reach a condition is zero.
197 // For those states, the optimal policy is to enforce observation failure.
198 // States that can only reach (target states with almost sure observation failure) or observation failure will be treated as terminal states with
199 // targetValue zero and failProbability one.
200 terminalStates |= storm::utility::graph::performProb0A(backwardTransitions, ~terminalStates, terminalStatesThatReachCondition);
201 }
202
203 auto nonTerminalStates = ~terminalStates;
204
205 auto existentialObservationFailureStates = storm::utility::graph::performProb0E(transitionMatrix, transitionMatrix.getRowGroupIndices(),
206 backwardTransitions, nonTerminalStates, terminalStatesThatReachCondition);
207
208 // Restrict non-terminal states to those that are still relevant
209 nonTerminalStates &= storm::utility::graph::getReachableStates(transitionMatrix, relevantStates, nonTerminalStates, terminalStates);
210
211 return NormalFormData<ValueType>{.maybeStates = std::move(nonTerminalStates),
212 .terminalStates = std::move(terminalStates),
213 .conditionStates = std::move(extendedConditionStates),
214 .universalObservationFailureStates = std::move(universalObservationFailureStates),
215 .existentialObservationFailureStates = std::move(existentialObservationFailureStates),
216 .nonZeroTargetStateValues = std::move(nonZeroTargetStateValues)};
217}
218
223template<typename ValueType, typename SolutionType = ValueType>
224SolutionType computeViaRestartMethod(Environment const& env, uint64_t const initialState, storm::solver::OptimizationDirection const dir,
225 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, NormalFormData<ValueType> const& normalForm) {
226 auto const& maybeStates = normalForm.maybeStates;
227 auto const stateToMatrixIndexMap = maybeStates.getNumberOfSetBitsBeforeIndices();
228 auto const numMaybeStates = maybeStates.getNumberOfSetBits();
229 auto const numMaybeChoices = transitionMatrix.getNumRowsInRowGroups(maybeStates);
230
231 // Build the transitions that include a backwards loop to the initial state
232 storm::storage::SparseMatrixBuilder<ValueType> matrixBuilder(numMaybeChoices, numMaybeStates, 0, true, true, numMaybeStates);
233 std::vector<ValueType> rowValues;
234 storm::storage::BitVector rowsWithSum1(numMaybeChoices, true);
235 rowValues.reserve(numMaybeChoices);
236 uint64_t currentRow = 0;
237 for (auto state : maybeStates) {
238 matrixBuilder.newRowGroup(currentRow);
239 for (auto origRowIndex : transitionMatrix.getRowGroupIndices(state)) {
240 // We make two passes over the successors. First, we find out the reset probabilities and target probabilities
241 // Then, we insert the matrix entries in the correct order
242 // This two-phase approach is to avoid a costly out-of-order insertion into the matrix
243 ValueType targetProbability = storm::utility::zero<ValueType>();
244 ValueType restartProbability = storm::utility::zero<ValueType>();
245 bool rowSumIsLess1 = false;
246 for (auto const& entry : transitionMatrix.getRow(origRowIndex)) {
247 if (normalForm.terminalStates.get(entry.getColumn())) {
248 ValueType const targetValue = normalForm.getTargetValue(entry.getColumn());
249 targetProbability += targetValue * entry.getValue();
250 if (normalForm.conditionStates.get(entry.getColumn())) {
251 rowSumIsLess1 = true;
252 } else {
253 if (!storm::utility::isZero(targetValue)) {
254 rowSumIsLess1 = true;
255 }
256 restartProbability += entry.getValue() * normalForm.failProbability(entry.getColumn());
257 }
258 }
259 }
260 if (rowSumIsLess1) {
261 rowsWithSum1.set(currentRow, false);
262 }
263 rowValues.push_back(targetProbability);
264 bool addRestartTransition = !storm::utility::isZero(restartProbability);
265 for (auto const& entry : transitionMatrix.getRow(origRowIndex)) {
266 // Insert backloop probability if we haven't done so yet and are past the initial state index
267 // This is to avoid a costly out-of-order insertion into the matrix
268 if (addRestartTransition && entry.getColumn() > initialState) {
269 matrixBuilder.addNextValue(currentRow, stateToMatrixIndexMap[initialState], restartProbability);
270 addRestartTransition = false;
271 }
272 if (maybeStates.get(entry.getColumn())) {
273 matrixBuilder.addNextValue(currentRow, stateToMatrixIndexMap[entry.getColumn()], entry.getValue());
274 }
275 }
276 // Add the backloop if we haven't done this already
277 if (addRestartTransition) {
278 matrixBuilder.addNextValue(currentRow, stateToMatrixIndexMap[initialState], restartProbability);
279 }
280 ++currentRow;
281 }
282 }
283
284 auto matrix = matrixBuilder.build();
285 auto initStateInMatrix = stateToMatrixIndexMap[initialState];
286
287 // Eliminate end components in two phases
288 // First, we catch all end components that do not contain the initial state. It is possible to stay in those ECs forever
289 // without reaching the condition. This is reflected by a backloop to the initial state.
290 storm::storage::BitVector selectedStatesInMatrix(numMaybeStates, true);
291 selectedStatesInMatrix.set(initStateInMatrix, false);
292 eliminateEndComponents(selectedStatesInMatrix, true, initStateInMatrix, matrix, initStateInMatrix, rowsWithSum1, rowValues);
293 // Second, eliminate the remaining ECs. These must involve the initial state and might have been introduced in the previous step.
294 // A policy selecting such an EC must reach the condition with probability zero and is thus invalid.
295 selectedStatesInMatrix.set(initStateInMatrix, true);
296 eliminateEndComponents(selectedStatesInMatrix, false, std::nullopt, matrix, initStateInMatrix, rowsWithSum1, rowValues);
297
298 STORM_LOG_INFO("Processed model has " << matrix.getRowGroupCount() << " states and " << matrix.getRowGroupCount() << " choices and "
299 << matrix.getEntryCount() << " transitions.");
300 // Finally, solve the equation system
301 return solveMinMaxEquationSystem(env, matrix, rowValues, rowsWithSum1, dir, initStateInMatrix);
302}
303
309template<typename ValueType, typename SolutionType = ValueType>
311 public:
312 WeightedReachabilityHelper(uint64_t const initialState, storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
313 NormalFormData<ValueType> const& normalForm) {
314 // Determine rowgroups (states) and rows (choices) of the submatrix
315 auto subMatrixRowGroups = normalForm.maybeStates;
316 // Identify and eliminate the initial component to enforce that it is eventually exited
317 // The initial component is the largest subset of maybestates C such that
318 // (i) the initial state is contained in C
319 // (ii) each state in C can be reached from the initial state while only playing actions that stay inside C or observation failure and
320 // (iii) for each state in C except the initial state there is a policy that almost surely reaches an observation failure
321 // An optimal scheduler can intuitively pick the best exiting action of C and enforce that all paths that satisfy the condition exit C through that
322 // action. By eliminating the initial component, we ensure that only policies that actually exit C are considered. The remaining policies have
323 // probability zero of satisfying the condition.
324 storm::storage::BitVector initialComponentExitRows(transitionMatrix.getRowCount(), false);
325 subMatrixRowGroups.set(initialState, false); // temporarily unset initial state
326 std::vector<uint64_t> dfsStack = {initialState};
327 while (!dfsStack.empty()) {
328 auto const state = dfsStack.back();
329 dfsStack.pop_back();
330 for (auto rowIndex : transitionMatrix.getRowGroupIndices(state)) {
331 auto const row = transitionMatrix.getRow(rowIndex);
332 if (std::all_of(row.begin(), row.end(),
333 [&normalForm](auto const& entry) { return normalForm.existentialObservationFailureStates.get(entry.getColumn()); })) {
334 for (auto const& entry : row) {
335 auto const& successor = entry.getColumn();
336 if (subMatrixRowGroups.get(successor)) {
337 subMatrixRowGroups.set(successor, false);
338 dfsStack.push_back(successor);
339 }
340 }
341 } else {
342 initialComponentExitRows.set(rowIndex, true);
343 }
344 }
345 }
346 auto const numSubmatrixRows = transitionMatrix.getNumRowsInRowGroups(subMatrixRowGroups) + initialComponentExitRows.getNumberOfSetBits();
347 subMatrixRowGroups.set(initialState, true); // set initial state again, as single representative state for the initial component
348 auto const numSubmatrixRowGroups = subMatrixRowGroups.getNumberOfSetBits();
349
350 // state index mapping and initial state
351 auto stateToMatrixIndexMap = subMatrixRowGroups.getNumberOfSetBitsBeforeIndices();
352 initialStateInSubmatrix = stateToMatrixIndexMap[initialState];
353 auto const eliminatedInitialComponentStates = normalForm.maybeStates & ~subMatrixRowGroups;
354 for (auto state : eliminatedInitialComponentStates) {
355 stateToMatrixIndexMap[state] = initialStateInSubmatrix; // map all eliminated states to the initial state
356 }
357
358 // build matrix, rows that sum up to 1, target values, condition values
359 storm::storage::SparseMatrixBuilder<ValueType> matrixBuilder(numSubmatrixRows, numSubmatrixRowGroups, 0, true, true, numSubmatrixRowGroups);
360 rowsWithSum1 = storm::storage::BitVector(numSubmatrixRows, true);
361 targetRowValues.reserve(numSubmatrixRows);
362 conditionRowValues.reserve(numSubmatrixRows);
363 uint64_t currentRow = 0;
364 for (auto state : subMatrixRowGroups) {
365 matrixBuilder.newRowGroup(currentRow);
366
367 // Put the row processing into a lambda for avoiding code duplications
368 auto processRow = [&](uint64_t origRowIndex) {
369 // We make two passes. First, we find out the probability to reach an eliminated initial component state
370 ValueType const eliminatedInitialComponentProbability = transitionMatrix.getConstrainedRowSum(origRowIndex, eliminatedInitialComponentStates);
371 // Second, we insert the submatrix entries and find out the target and condition probabilities for this row
372 ValueType targetProbability = storm::utility::zero<ValueType>();
373 ValueType conditionProbability = storm::utility::zero<ValueType>();
374 bool rowSumIsLess1 = false;
375 bool initialStateEntryInserted = false;
376 for (auto const& entry : transitionMatrix.getRow(origRowIndex)) {
377 if (normalForm.terminalStates.get(entry.getColumn())) {
378 STORM_LOG_ASSERT(!storm::utility::isZero(entry.getValue()), "Transition probability must be non-zero");
379 rowSumIsLess1 = true;
380 ValueType const scaledTargetValue = normalForm.getTargetValue(entry.getColumn()) * entry.getValue();
381 targetProbability += scaledTargetValue;
382 if (normalForm.conditionStates.get(entry.getColumn())) {
383 conditionProbability += entry.getValue(); // conditionValue of successor is 1
384 } else {
385 conditionProbability += scaledTargetValue; // for terminal, non-condition states, the condition value equals the target value
386 }
387 } else if (!eliminatedInitialComponentStates.get(entry.getColumn())) {
388 auto const columnIndex = stateToMatrixIndexMap[entry.getColumn()];
389 if (!initialStateEntryInserted && columnIndex >= initialStateInSubmatrix) {
390 if (columnIndex == initialStateInSubmatrix) {
391 matrixBuilder.addNextValue(currentRow, initialStateInSubmatrix, eliminatedInitialComponentProbability + entry.getValue());
392 } else {
393 matrixBuilder.addNextValue(currentRow, initialStateInSubmatrix, eliminatedInitialComponentProbability);
394 matrixBuilder.addNextValue(currentRow, columnIndex, entry.getValue());
395 }
396 initialStateEntryInserted = true;
397 } else {
398 matrixBuilder.addNextValue(currentRow, columnIndex, entry.getValue());
399 }
400 }
401 }
402 if (rowSumIsLess1) {
403 rowsWithSum1.set(currentRow, false);
404 }
405 targetRowValues.push_back(targetProbability);
406 conditionRowValues.push_back(conditionProbability);
407 ++currentRow;
408 };
409 // invoke the lambda
410 if (state == initialState) {
411 for (auto origRowIndex : initialComponentExitRows) {
412 processRow(origRowIndex);
413 }
414 } else {
415 for (auto origRowIndex : transitionMatrix.getRowGroupIndices(state)) {
416 processRow(origRowIndex);
417 }
418 }
419 }
420 submatrix = matrixBuilder.build();
421
422 // eliminate ECs if present. We already checked that the initial state can not yield observation failure, so it cannot be part of an EC.
423 // For all remaining ECs, staying in an EC forever is reflected by collecting a value of zero for both, target and condition
424 storm::storage::BitVector allExceptInit(numSubmatrixRowGroups, true);
425 allExceptInit.set(initialStateInSubmatrix, false);
426 eliminateEndComponents<ValueType>(allExceptInit, true, std::nullopt, submatrix, initialStateInSubmatrix, rowsWithSum1, targetRowValues,
427 conditionRowValues);
428 STORM_LOG_INFO("Processed model has " << submatrix.getRowGroupCount() << " states and " << submatrix.getRowGroupCount() << " choices and "
429 << submatrix.getEntryCount() << " transitions.");
430 }
431
432 SolutionType computeWeightedDiff(storm::Environment const& env, storm::OptimizationDirection const dir, ValueType const& targetWeight,
433 ValueType const& conditionWeight) const {
434 auto rowValues = createScaledVector(targetWeight, targetRowValues, conditionWeight, conditionRowValues);
435
436 // Initialize the solution vector.
437 std::vector<SolutionType> x(submatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
438
439 // Set up the solver.
441 solver->setOptimizationDirection(dir);
442 solver->setRequirementsChecked();
443 solver->setHasUniqueSolution(true);
444 solver->setHasNoEndComponents(true);
445 solver->setLowerBound(-storm::utility::one<ValueType>());
446 solver->setUpperBound(storm::utility::one<ValueType>());
447
448 solver->solveEquations(env, x, rowValues);
449 return x[initialStateInSubmatrix];
450 }
451
453 return initialStateInSubmatrix;
454 }
455
456 void evaluateScheduler(storm::Environment const& env, std::vector<uint64_t>& scheduler, std::vector<SolutionType>& targetResults,
457 std::vector<SolutionType>& conditionResults) const {
458 if (scheduler.empty()) {
459 scheduler.resize(submatrix.getRowGroupCount(), 0);
460 }
461 if (targetResults.empty()) {
462 targetResults.resize(submatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
463 }
464 if (conditionResults.empty()) {
465 conditionResults.resize(submatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
466 }
467 // apply the scheduler
469 bool const convertToEquationSystem = factory.getEquationProblemFormat(env) == storm::solver::LinearEquationSolverProblemFormat::EquationSystem;
470 auto scheduledMatrix = submatrix.selectRowsFromRowGroups(scheduler, convertToEquationSystem);
471 if (convertToEquationSystem) {
472 scheduledMatrix.convertToEquationSystem();
473 }
474 auto solver = factory.create(env, std::move(scheduledMatrix));
475 solver->setBounds(storm::utility::zero<ValueType>(), storm::utility::one<ValueType>());
476 solver->setCachingEnabled(true);
477
478 std::vector<ValueType> subB(submatrix.getRowGroupCount());
479 storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, submatrix.getRowGroupIndices(), targetRowValues);
480 solver->solveEquations(env, targetResults, subB);
481
482 storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, submatrix.getRowGroupIndices(), conditionRowValues);
483 solver->solveEquations(env, conditionResults, subB);
484 }
485
486 template<OptimizationDirection Dir>
487 bool improveScheduler(std::vector<uint64_t>& scheduler, ValueType const& lambda, std::vector<SolutionType> const& targetResults,
488 std::vector<SolutionType> const& conditionResults) {
489 bool improved = false;
490 for (uint64_t rowGroupIndex = 0; rowGroupIndex < scheduler.size(); ++rowGroupIndex) {
492 uint64_t optimalRowIndex{0};
493 ValueType scheduledValue;
494 for (auto rowIndex : submatrix.getRowGroupIndices(rowGroupIndex)) {
495 ValueType rowValue = targetRowValues[rowIndex] - lambda * conditionRowValues[rowIndex];
496 for (auto const& entry : submatrix.getRow(rowIndex)) {
497 rowValue += entry.getValue() * (targetResults[entry.getColumn()] - lambda * conditionResults[entry.getColumn()]);
498 }
499 if (rowIndex == scheduler[rowGroupIndex] + submatrix.getRowGroupIndices()[rowGroupIndex]) {
500 scheduledValue = rowValue;
501 }
502 if (groupValue &= rowValue) {
503 optimalRowIndex = rowIndex;
504 }
505 }
506 if (scheduledValue != *groupValue) {
507 scheduler[rowGroupIndex] = optimalRowIndex - submatrix.getRowGroupIndices()[rowGroupIndex];
508 improved = true;
509 }
510 }
511 return improved;
512 }
513
514 private:
515 std::vector<ValueType> createScaledVector(ValueType const& w1, std::vector<ValueType> const& v1, ValueType const& w2,
516 std::vector<ValueType> const& v2) const {
517 STORM_LOG_ASSERT(v1.size() == v2.size(), "Vector sizes must match");
518 std::vector<ValueType> result;
519 result.reserve(v1.size());
520 for (size_t i = 0; i < v1.size(); ++i) {
521 result.push_back(w1 * v1[i] + w2 * v2[i]);
522 }
523 return result;
524 }
525
527 storm::storage::BitVector rowsWithSum1;
528 std::vector<ValueType> targetRowValues;
529 std::vector<ValueType> conditionRowValues;
530 uint64_t initialStateInSubmatrix;
531};
532
534template<typename ValueType, typename SolutionType = ValueType>
535SolutionType computeViaBisection(Environment const& env, BisectionMethodBounds boundOption, uint64_t const initialState,
537 NormalFormData<ValueType> const& normalForm) {
538 // We currently handle sound model checking incorrectly: we would need the actual lower/upper bounds of the weightedReachabilityHelper
540 "Bisection method does not adequately handle propagation of errors. Result is not necessarily sound.");
541 SolutionType const precision = [&env, boundOption]() {
544 "Selected bisection method with exact precision in a setting that might not terminate.");
545 return storm::utility::zero<SolutionType>();
546 } else {
547 return storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision());
548 }
549 }();
550 bool const relative = env.solver().minMax().getRelativeTerminationCriterion();
551
552 WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm);
553 SolutionType pMin{storm::utility::zero<SolutionType>()};
554 SolutionType pMax{storm::utility::one<SolutionType>()};
555
556 if (boundOption == BisectionMethodBounds::Advanced) {
557 pMin = wrh.computeWeightedDiff(env, storm::OptimizationDirection::Minimize, storm::utility::zero<ValueType>(), storm::utility::one<ValueType>());
558 pMax = wrh.computeWeightedDiff(env, storm::OptimizationDirection::Maximize, storm::utility::zero<ValueType>(), storm::utility::one<ValueType>());
559 STORM_LOG_TRACE("Conditioning event bounds:\n\t Lower bound: " << storm::utility::convertNumber<double>(pMin)
560 << ",\n\t Upper bound: " << storm::utility::convertNumber<double>(pMax));
561 }
562 storm::utility::Extremum<storm::OptimizationDirection::Maximize, SolutionType> lowerBound = storm::utility::zero<ValueType>();
563 storm::utility::Extremum<storm::OptimizationDirection::Minimize, SolutionType> upperBound = storm::utility::one<ValueType>();
564 SolutionType middle = (*lowerBound + *upperBound) / 2;
565 for (uint64_t iterationCount = 1; true; ++iterationCount) {
566 // evaluate the current middle
567 SolutionType const middleValue = wrh.computeWeightedDiff(env, dir, storm::utility::one<ValueType>(), -middle);
568 // update the bounds and new middle value according to the bisection method
569 if (boundOption == BisectionMethodBounds::Simple) {
570 if (middleValue >= storm::utility::zero<ValueType>()) {
571 lowerBound &= middle;
572 }
573 if (middleValue <= storm::utility::zero<ValueType>()) {
574 upperBound &= middle;
575 }
576 middle = (*lowerBound + *upperBound) / 2; // update middle to the average of the bounds
577 } else {
578 STORM_LOG_ASSERT(boundOption == BisectionMethodBounds::Advanced, "Unknown bisection method bounds");
579 if (middleValue >= storm::utility::zero<ValueType>()) {
580 lowerBound &= middle + (middleValue / pMax);
581 upperBound &= middle + (middleValue / pMin);
582 }
583 if (middleValue <= storm::utility::zero<ValueType>()) {
584 lowerBound &= middle + (middleValue / pMin);
585 upperBound &= middle + (middleValue / pMax);
586 }
587 // update middle to the average of the bounds, but scale it according to the middle value (which is in [-1,1])
588 middle = *lowerBound + (storm::utility::one<SolutionType>() + middleValue) * (*upperBound - *lowerBound) / 2;
589
591 if (*lowerBound > *upperBound) {
592 std::swap(*lowerBound, *upperBound);
593 }
594 STORM_LOG_WARN("Precision of non-exact type reached during bisection method. Result might be inaccurate.");
595 } else {
596 STORM_LOG_ASSERT(middle >= *lowerBound && middle <= *upperBound, "Bisection method bounds are inconsistent.");
597 }
598 }
599 // check for convergence
600 SolutionType const boundDiff = *upperBound - *lowerBound;
601 STORM_LOG_TRACE("Iteration #" << iterationCount << ":\n\t Lower bound: " << storm::utility::convertNumber<double>(*lowerBound)
602 << ",\n\t Upper bound: " << storm::utility::convertNumber<double>(*upperBound)
603 << ",\n\t Difference: " << storm::utility::convertNumber<double>(boundDiff)
604 << ",\n\t Middle val: " << storm::utility::convertNumber<double>(middleValue) << ",\n\t Difference bound: "
605 << storm::utility::convertNumber<double>((relative ? (precision * *lowerBound) : precision)) << ".");
606 if (boundDiff <= (relative ? (precision * *lowerBound) : precision)) {
607 STORM_LOG_INFO("Bisection method converged after " << iterationCount << " iterations. Difference is "
608 << std::setprecision(std::numeric_limits<double>::digits10)
609 << storm::utility::convertNumber<double>(boundDiff) << ".");
610 break;
611 }
612 // check for early termination
614 STORM_LOG_WARN("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is "
615 << storm::utility::convertNumber<double>(boundDiff) << ".");
616 break;
617 }
618 // process the middle value for the next iteration
620 // find a rational number with a concise representation close to middle and within the bounds
621 auto const exactMiddle = middle;
622
623 // Find number of digits - 1. Method using log10 does not work since that uses doubles internally.
624 auto numDigits = storm::utility::numDigits<SolutionType>(*upperBound - *lowerBound) - 1;
625
626 do {
627 ++numDigits;
628 middle = storm::utility::kwek_mehlhorn::sharpen<SolutionType, SolutionType>(numDigits, exactMiddle);
629 } while (middle <= *lowerBound || middle >= *upperBound);
630 }
631 // Since above code never sets 'middle' to exactly zero or one, we check if that could be necessary after a couple of iterations
632 if (iterationCount == 8) { // 8 is just a heuristic value, it could be any number
633 if (storm::utility::isZero(*lowerBound)) {
634 middle = storm::utility::zero<SolutionType>();
635 } else if (storm::utility::isOne(*upperBound)) {
636 middle = storm::utility::one<SolutionType>();
637 }
638 }
639 }
640 return (*lowerBound + *upperBound) / 2;
641}
642
643template<typename ValueType, typename SolutionType = ValueType>
644SolutionType computeViaPolicyIteration(Environment const& env, uint64_t const initialState, storm::solver::OptimizationDirection const dir,
645 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, NormalFormData<ValueType> const& normalForm) {
646 WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm);
647
648 std::vector<uint64_t> scheduler;
649 std::vector<SolutionType> targetResults, conditionResults;
650 for (uint64_t iterationCount = 1; true; ++iterationCount) {
651 wrh.evaluateScheduler(env, scheduler, targetResults, conditionResults);
653 targetResults[wrh.getInternalInitialState()] <= conditionResults[wrh.getInternalInitialState()],
654 "Potential numerical issues: the probability to reach the target is greater than the probability to reach the condition. Difference is "
655 << (storm::utility::convertNumber<double, ValueType>(targetResults[wrh.getInternalInitialState()] -
656 conditionResults[wrh.getInternalInitialState()]))
657 << ".");
658 ValueType const lambda = storm::utility::isZero(conditionResults[wrh.getInternalInitialState()])
659 ? storm::utility::zero<ValueType>()
660 : ValueType(targetResults[wrh.getInternalInitialState()] / conditionResults[wrh.getInternalInitialState()]);
661 bool schedulerChanged{false};
662 if (storm::solver::minimize(dir)) {
663 schedulerChanged = wrh.template improveScheduler<storm::OptimizationDirection::Minimize>(scheduler, lambda, targetResults, conditionResults);
664 } else {
665 schedulerChanged = wrh.template improveScheduler<storm::OptimizationDirection::Maximize>(scheduler, lambda, targetResults, conditionResults);
666 }
667 if (!schedulerChanged) {
668 STORM_LOG_INFO("Policy iteration for conditional probabilities converged after " << iterationCount << " iterations.");
669 return lambda;
670 }
672 STORM_LOG_WARN("Policy iteration for conditional probabilities converged aborted after " << iterationCount << "iterations.");
673 return lambda;
674 }
675 }
676}
677
678template<typename ValueType, typename SolutionType = ValueType>
679std::optional<SolutionType> handleTrivialCases(uint64_t const initialState, NormalFormData<ValueType> const& normalForm) {
680 if (normalForm.terminalStates.get(initialState)) {
681 STORM_LOG_DEBUG("Initial state is terminal.");
682 if (normalForm.conditionStates.get(initialState)) {
683 return normalForm.getTargetValue(initialState); // The value is already known, nothing to do.
684 } else {
685 STORM_LOG_THROW(!normalForm.universalObservationFailureStates.get(initialState), storm::exceptions::NotSupportedException,
686 "Trying to compute undefined conditional probability: the condition has probability 0 under all policies.");
687 // The last case for a terminal initial state is that it is already target and the condition is reachable with non-zero probability.
688 // In this case, all schedulers induce a conditional probability of 1 (or do not reach the condition, i.e., have undefined value)
689 return storm::utility::one<SolutionType>();
690 }
691 } else {
692 // Catch the case where all terminal states have value zero
693 if (normalForm.nonZeroTargetStateValues.empty()) {
694 return storm::utility::zero<SolutionType>();
695 };
696 }
697 return std::nullopt; // No trivial case applies, we need to compute the value.
698}
699
700} // namespace internal
701
702template<typename ValueType, typename SolutionType>
704 storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
705 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
706 storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates) {
707 // We might require adapting the precision of the solver to counter error propagation (e.g. when computing the normal form).
708 auto normalFormConstructionEnv = env;
709 auto analysisEnv = env;
710 if (env.solver().isForceSoundness()) {
711 // We intuitively have to divide the precision into two parts, one for computations when constructing the normal form and one for the actual analysis.
712 // As the former is usually less numerically challenging, we use a factor of 1/10 for the normal form construction and 9/10 for the analysis.
713 auto const normalFormPrecisionFactor = storm::utility::convertNumber<storm::RationalNumber, std::string>("1/10");
714 normalFormConstructionEnv.solver().minMax().setPrecision(env.solver().minMax().getPrecision() * normalFormPrecisionFactor);
715 analysisEnv.solver().minMax().setPrecision(env.solver().minMax().getPrecision() *
716 (storm::utility::one<storm::RationalNumber>() - normalFormPrecisionFactor));
717 }
718
719 // We first translate the problem into a normal form.
720 // @see doi.org/10.1007/978-3-642-54862-8_43
721 STORM_LOG_THROW(goal.hasRelevantValues(), storm::exceptions::NotSupportedException,
722 "No initial state given. Conditional probabilities can only be computed for models with a single initial state.");
723 STORM_LOG_THROW(goal.relevantValues().hasUniqueSetBit(), storm::exceptions::NotSupportedException,
724 "Only one initial state is supported for conditional probabilities");
725 STORM_LOG_TRACE("Computing conditional probabilities for a model with " << transitionMatrix.getRowGroupCount() << " states and "
726 << transitionMatrix.getEntryCount() << " transitions.");
727 // storm::utility::Stopwatch sw(true);
728 auto normalFormData = internal::obtainNormalForm(normalFormConstructionEnv, goal.direction(), transitionMatrix, backwardTransitions, goal.relevantValues(),
729 targetStates, conditionStates);
730 // sw.stop();
731 // STORM_PRINT_AND_LOG("Time for obtaining the normal form: " << sw << ".\n");
732 // Then, we solve the induced problem using the selected algorithm
733 auto const initialState = *goal.relevantValues().begin();
734 ValueType initialStateValue = -storm::utility::one<ValueType>();
735 if (auto trivialValue = internal::handleTrivialCases<ValueType, SolutionType>(initialState, normalFormData); trivialValue.has_value()) {
736 initialStateValue = *trivialValue;
737 STORM_LOG_DEBUG("Initial state has trivial value " << initialStateValue);
738 } else {
739 STORM_LOG_ASSERT(normalFormData.maybeStates.get(initialState), "Initial state must be a maybe state if it is not a terminal state");
740 auto alg = analysisEnv.modelchecker().getConditionalAlgorithmSetting();
743 }
744 STORM_LOG_INFO("Analyzing normal form with " << normalFormData.maybeStates.getNumberOfSetBits() << " maybe states using algorithm '" << alg << ".");
745 // sw.restart();
746 switch (alg) {
748 initialStateValue = internal::computeViaRestartMethod(analysisEnv, initialState, goal.direction(), transitionMatrix, normalFormData);
749 break;
751 initialStateValue = internal::computeViaBisection(analysisEnv, internal::BisectionMethodBounds::Simple, initialState, goal.direction(),
752 transitionMatrix, normalFormData);
753 break;
755 initialStateValue = internal::computeViaBisection(analysisEnv, internal::BisectionMethodBounds::Advanced, initialState, goal.direction(),
756 transitionMatrix, normalFormData);
757 break;
759 initialStateValue = internal::computeViaPolicyIteration(analysisEnv, initialState, goal.direction(), transitionMatrix, normalFormData);
760 break;
761 default:
762 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unknown conditional probability algorithm: " << alg);
763 }
764 // sw.stop();
765 // STORM_PRINT_AND_LOG("Time for analyzing the normal form: " << sw << ".\n");
766 }
767 return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(initialState, initialStateValue));
768}
769
770template std::unique_ptr<CheckResult> computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal<double>&& goal,
771 storm::storage::SparseMatrix<double> const& transitionMatrix,
772 storm::storage::SparseMatrix<double> const& backwardTransitions,
773 storm::storage::BitVector const& targetStates,
774 storm::storage::BitVector const& conditionStates);
775
779 storm::storage::BitVector const& targetStates,
780 storm::storage::BitVector const& conditionStates);
781
782} // namespace storm::modelchecker
SolverEnvironment & solver()
storm::RationalNumber const & getPrecision() const
bool const & getRelativeTerminationCriterion() const
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
MinMaxSolverEnvironment & minMax()
static MDPSparseModelCheckingHelperReturnType< SolutionType > computeUntilProbabilities(Environment const &env, storm::solver::SolveGoal< ValueType, SolutionType > &&goal, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool qualitative, bool produceScheduler, ModelCheckerHint const &hint=ModelCheckerHint())
A helper class that computes (weighted) reachability probabilities for a given MDP in normal form.
bool improveScheduler(std::vector< uint64_t > &scheduler, ValueType const &lambda, std::vector< SolutionType > const &targetResults, std::vector< SolutionType > const &conditionResults)
SolutionType computeWeightedDiff(storm::Environment const &env, storm::OptimizationDirection const dir, ValueType const &targetWeight, ValueType const &conditionWeight) const
WeightedReachabilityHelper(uint64_t const initialState, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, NormalFormData< ValueType > const &normalForm)
void evaluateScheduler(storm::Environment const &env, std::vector< uint64_t > &scheduler, std::vector< SolutionType > &targetResults, std::vector< SolutionType > &conditionResults) const
virtual std::unique_ptr< LinearEquationSolver< ValueType > > create(Environment const &env) const override
Creates an equation solver with the current settings, but without a matrix.
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
virtual LinearEquationSolverProblemFormat getEquationProblemFormat(Environment const &env) const
Retrieves the problem format that the solver expects if it was created with the current settings.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
std::vector< uint64_t > getNumberOfSetBitsBeforeIndices() const
Retrieves a vector that holds at position i the number of bits set before index i.
bool empty() const
Retrieves whether no bits are set to true in this 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.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
This class represents the decomposition of a nondeterministic model into its maximal end components.
A class that can be used to build a sparse matrix by adding value by value.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
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.
index_type getEntryCount() const
Returns the number of entries in the matrix.
index_type getNumRowsInRowGroups(storm::storage::BitVector const &groupConstraint) const
Returns the total number of rows that are in one of the specified row groups.
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 ...
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
value_type getConstrainedRowSum(index_type row, storm::storage::BitVector const &columns) const
Sums the entries in the given row and columns.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
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.
static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix< ValueType > const &originalMatrix, storm::storage::MaximalEndComponentDecomposition< ValueType > ecs, storm::storage::BitVector const &subsystemStates, storm::storage::BitVector const &addSinkRowStates, bool addSelfLoopAtSinkStates=false)
Stores and manages an extremal (maximal or minimal) value.
Definition Extremum.h:15
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_DEBUG(message)
Definition logging.h:18
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
void computeReachabilityProbabilities(Environment const &env, std::map< uint64_t, ValueType > &nonZeroResults, storm::solver::OptimizationDirection const dir, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::BitVector const &initialStates, storm::storage::BitVector const &allowedStates, storm::storage::BitVector const &targetStates)
Computes the reachability probabilities for the given target states and inserts all non-zero values i...
void eliminateEndComponents(storm::storage::BitVector possibleEcStates, bool addRowAtRepresentativeState, std::optional< uint64_t > representativeRowEntry, storm::storage::SparseMatrix< ValueType > &matrix, uint64_t &initialState, storm::storage::BitVector &rowsWithSum1, std::vector< ValueType > &rowValues1, storm::OptionalRef< std::vector< ValueType > > rowValues2={})
SolutionType computeViaPolicyIteration(Environment const &env, uint64_t const initialState, storm::solver::OptimizationDirection const dir, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, NormalFormData< ValueType > const &normalForm)
NormalFormData< ValueType > obtainNormalForm(Environment const &env, storm::solver::OptimizationDirection const dir, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &relevantStates, storm::storage::BitVector const &targetStates, storm::storage::BitVector const &conditionStates)
std::optional< SolutionType > handleTrivialCases(uint64_t const initialState, NormalFormData< ValueType > const &normalForm)
SolutionType solveMinMaxEquationSystem(storm::Environment const &env, storm::storage::SparseMatrix< ValueType > const &matrix, std::vector< ValueType > const &rowValues, storm::storage::BitVector const &rowsWithSum1, storm::solver::OptimizationDirection const dir, uint64_t const initialState)
SolutionType computeViaBisection(Environment const &env, BisectionMethodBounds boundOption, uint64_t const initialState, storm::solver::OptimizationDirection const dir, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, NormalFormData< ValueType > const &normalForm)
SolutionType computeViaRestartMethod(Environment const &env, uint64_t const initialState, storm::solver::OptimizationDirection const dir, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, NormalFormData< ValueType > const &normalForm)
Uses the restart method by Baier et al.
std::unique_ptr< CheckResult > computeConditionalProbabilities(Environment const &env, storm::solver::SolveGoal< ValueType, SolutionType > &&goal, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &targetStates, storm::storage::BitVector const &conditionStates)
bool constexpr minimize(OptimizationDirection d)
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 performProb1A(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 1 of satisfying phi until psi under all possible re...
Definition graph.cpp:981
storm::storage::BitVector performProb0A(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:733
storm::storage::BitVector performProb0E(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 of satisfying phi until psi under at least one po...
Definition graph.cpp:960
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
bool isOne(ValueType const &a)
Definition constants.cpp:34
bool isAlmostZero(ValueType const &a)
Definition constants.cpp:93
bool isZero(ValueType const &a)
Definition constants.cpp:39
storm::storage::BitVector const existentialObservationFailureStates
ValueType failProbability(uint64_t state) const
std::map< uint64_t, ValueType > const nonZeroTargetStateValues
storm::storage::BitVector const conditionStates
storm::storage::BitVector const universalObservationFailureStates