Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DeterministicSchedsObjectiveHelper.cpp
Go to the documentation of this file.
2
21#include "storm/utility/graph.h"
23
26
28
29template<typename ModelType>
32 : model(model), objective(objective) {
33 initialize();
34}
35
36template<typename ModelType>
39 auto checkResult = mc.check(formula);
40 STORM_LOG_THROW(checkResult && checkResult->isExplicitQualitativeCheckResult(), storm::exceptions::UnexpectedException,
41 "Unexpected type of check result for subformula " << formula << ".");
42 return checkResult->template asExplicitQualitativeCheckResult<typename ModelType::ValueType>().getTruthValuesVector();
43}
44
45template<typename ValueType>
47 if (formula.isRewardOperatorFormula()) {
48 boost::optional<std::string> rewardModelName = formula.asRewardOperatorFormula().getOptionalRewardModelName();
50 rewardModelName.is_initialized() ? model.getRewardModel(rewardModelName.get()) : model.getUniqueRewardModel();
51
52 // Get a reward model where the state rewards are scaled accordingly
53 std::vector<ValueType> stateRewardWeights(model.getNumberOfStates(), storm::utility::zero<ValueType>());
54 for (auto const markovianState : model.getMarkovianStates()) {
55 stateRewardWeights[markovianState] = storm::utility::one<ValueType>() / model.getExitRate(markovianState);
56 }
57 return rewardModel.getTotalActionRewardVector(model.getTransitionMatrix(), stateRewardWeights);
58 } else {
59 STORM_LOG_ASSERT(formula.isTimeOperatorFormula(), "Expected time operator formula.");
60 std::vector<ValueType> result(model.getNumberOfChoices(), storm::utility::zero<ValueType>());
61 for (auto const markovianState : model.getMarkovianStates()) {
62 for (auto const& choice : model.getTransitionMatrix().getRowGroupIndices(markovianState)) {
63 result[choice] = storm::utility::one<ValueType>() / model.getExitRate(markovianState);
64 }
65 }
66 return result;
67 }
68}
69
70template<typename ValueType>
71std::vector<ValueType> getTotalRewardVector(storm::models::sparse::Mdp<ValueType> const& model, storm::logic::Formula const& formula) {
72 if (formula.isRewardOperatorFormula()) {
73 boost::optional<std::string> rewardModelName = formula.asRewardOperatorFormula().getOptionalRewardModelName();
75 rewardModelName.is_initialized() ? model.getRewardModel(rewardModelName.get()) : model.getUniqueRewardModel();
76 return rewardModel.getTotalRewardVector(model.getTransitionMatrix());
77 } else {
78 STORM_LOG_ASSERT(formula.isTimeOperatorFormula(), "Expected time operator formula.");
79 return std::vector<ValueType>(model.getNumberOfChoices(), storm::utility::one<ValueType>());
80 }
81}
82
83template<typename ModelType>
84void DeterministicSchedsObjectiveHelper<ModelType>::initialize() {
85 STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1, "Expected a single initial state.");
86 uint64_t initialState = *model.getInitialStates().begin();
87 relevantZeroRewardChoices = storm::storage::BitVector(model.getTransitionMatrix().getRowCount(), false);
88 auto const& formula = *objective.formula;
89 if (formula.isProbabilityOperatorFormula() && formula.getSubformula().isUntilFormula()) {
90 storm::storage::BitVector phiStates = evaluatePropositionalFormula(model, formula.getSubformula().asUntilFormula().getLeftSubformula());
91 storm::storage::BitVector psiStates = evaluatePropositionalFormula(model, formula.getSubformula().asUntilFormula().getRightSubformula());
92 auto backwardTransitions = model.getBackwardTransitions();
93 auto prob1States = storm::utility::graph::performProb1A(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), backwardTransitions,
94 phiStates, psiStates);
95 auto prob0States = storm::utility::graph::performProb0A(backwardTransitions, phiStates, psiStates);
96 if (prob0States.get(initialState)) {
97 constantInitialStateValue = storm::utility::zero<ValueType>();
98 } else if (prob1States.get(initialState)) {
99 constantInitialStateValue = storm::utility::one<ValueType>();
100 }
101 maybeStates = ~(prob0States | prob1States);
102 // Cut away those states that are not reachable, or only reachable via non-maybe states.
103 maybeStates &= storm::utility::graph::getReachableStates(model.getTransitionMatrix(), model.getInitialStates(), maybeStates, ~maybeStates);
104 for (uint64_t state : maybeStates) {
105 for (auto const& choice : model.getTransitionMatrix().getRowGroupIndices(state)) {
106 auto rowSum = model.getTransitionMatrix().getConstrainedRowSum(choice, prob1States);
107 if (storm::utility::isZero(rowSum)) {
108 relevantZeroRewardChoices.set(choice);
109 } else {
110 choiceRewards.emplace(choice, rowSum);
111 }
112 }
113 }
114 } else if (formula.getSubformula().isEventuallyFormula() && (formula.isRewardOperatorFormula() || formula.isTimeOperatorFormula())) {
115 storm::storage::BitVector rew0States = evaluatePropositionalFormula(model, formula.getSubformula().asEventuallyFormula().getSubformula());
116 if (formula.isRewardOperatorFormula()) {
117 auto const& baseRewardModel = formula.asRewardOperatorFormula().hasRewardModelName()
118 ? model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName())
119 : model.getUniqueRewardModel();
120 auto rewardModel =
121 storm::utility::createFilteredRewardModel(baseRewardModel, model.isDiscreteTimeModel(), formula.getSubformula().asEventuallyFormula());
122 storm::storage::BitVector statesWithoutReward = rewardModel.get().getStatesWithZeroReward(model.getTransitionMatrix());
123 rew0States = storm::utility::graph::performProb1A(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(),
124 model.getBackwardTransitions(), statesWithoutReward, rew0States);
125 }
126 if (rew0States.get(initialState)) {
127 constantInitialStateValue = storm::utility::zero<ValueType>();
128 }
129 maybeStates = ~rew0States;
130 // Cut away those states that are not reachable, or only reachable via non-maybe states.
131 maybeStates &= storm::utility::graph::getReachableStates(model.getTransitionMatrix(), model.getInitialStates(), maybeStates, ~maybeStates);
132 std::vector<ValueType> choiceBasedRewards = getTotalRewardVector(model, *objective.formula);
133 for (uint64_t state : maybeStates) {
134 for (auto const& choice : model.getTransitionMatrix().getRowGroupIndices(state)) {
135 auto const& value = choiceBasedRewards[choice];
136 if (storm::utility::isZero(value)) {
137 relevantZeroRewardChoices.set(choice);
138 } else {
139 choiceRewards.emplace(choice, value);
140 }
141 }
142 }
143 } else if (formula.isRewardOperatorFormula() && formula.getSubformula().isTotalRewardFormula()) {
144 auto const& baseRewardModel = formula.asRewardOperatorFormula().hasRewardModelName()
145 ? model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName())
146 : model.getUniqueRewardModel();
147 auto rewardModel =
148 storm::utility::createFilteredRewardModel(baseRewardModel, model.isDiscreteTimeModel(), formula.getSubformula().asTotalRewardFormula());
149 storm::storage::BitVector statesWithoutReward = rewardModel.get().getStatesWithZeroReward(model.getTransitionMatrix());
150 storm::storage::BitVector rew0States =
151 storm::utility::graph::performProbGreater0E(model.getBackwardTransitions(), statesWithoutReward, ~statesWithoutReward);
152 rew0States.complement();
153 if (rew0States.get(initialState)) {
154 constantInitialStateValue = storm::utility::zero<ValueType>();
155 }
156 maybeStates = ~rew0States;
157 // Cut away those states that are not reachable, or only reachable via non-maybe states.
158 maybeStates &= storm::utility::graph::getReachableStates(model.getTransitionMatrix(), model.getInitialStates(), maybeStates, ~maybeStates);
159 std::vector<ValueType> choiceBasedRewards = getTotalRewardVector(model, *objective.formula);
160 for (uint64_t state : maybeStates) {
161 for (auto const& choice : model.getTransitionMatrix().getRowGroupIndices(state)) {
162 auto const& value = choiceBasedRewards[choice];
163 if (storm::utility::isZero(value)) {
164 relevantZeroRewardChoices.set(choice);
165 } else {
166 choiceRewards.emplace(choice, value);
167 }
168 }
169 }
170 } else {
171 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The given formula " << formula << " is not supported.");
172 }
173
174 // negate choice rewards for minimizing objectives
175 if (storm::solver::minimize(formula.getOptimalityType())) {
176 for (auto& entry : choiceRewards) {
177 entry.second *= -storm::utility::one<ValueType>();
178 }
179 }
180
181 // Find out if we have to deal with infinite positive or negative rewards
182 storm::storage::BitVector negativeRewardChoices(model.getNumberOfChoices(), false);
183 storm::storage::BitVector positiveRewardChoices(model.getNumberOfChoices(), false);
184 for (auto const& rew : getChoiceRewards()) {
185 if (rew.second > storm::utility::zero<ValueType>()) {
186 positiveRewardChoices.set(rew.first, true);
187 } else {
188 STORM_LOG_ASSERT(rew.second < storm::utility::zero<ValueType>(), "Expected negative reward.");
189 negativeRewardChoices.set(rew.first, true);
190 }
191 }
192 auto backwardTransitions = model.getBackwardTransitions();
193 bool hasNegativeEC =
194 storm::utility::graph::checkIfECWithChoiceExists(model.getTransitionMatrix(), backwardTransitions, getMaybeStates(), negativeRewardChoices);
195 bool hasPositiveEc =
196 storm::utility::graph::checkIfECWithChoiceExists(model.getTransitionMatrix(), backwardTransitions, getMaybeStates(), positiveRewardChoices);
197 STORM_LOG_THROW(!hasNegativeEC || !hasPositiveEc, storm::exceptions::NotSupportedException,
198 "Objective is not convergent: Infinite positive and infinite negative reward is possible.");
199 infinityCase = hasNegativeEC ? InfinityCase::HasNegativeInfinite : (hasPositiveEc ? InfinityCase::HasPositiveInfinite : InfinityCase::AlwaysFinite);
200
201 if (infinityCase == InfinityCase::HasNegativeInfinite) {
202 storm::storage::BitVector negativeEcStates(maybeStates.size(), false);
203 storm::storage::MaximalEndComponentDecomposition<ValueType> mecs(model.getTransitionMatrix(), backwardTransitions, getMaybeStates());
204 for (auto const& mec : mecs) {
205 if (std::any_of(mec.begin(), mec.end(), [&negativeRewardChoices](auto const& sc) {
206 return std::any_of(sc.second.begin(), sc.second.end(), [&negativeRewardChoices](auto const& c) { return negativeRewardChoices.get(c); });
207 })) {
208 for (auto const& sc : mec) {
209 negativeEcStates.set(sc.first);
210 }
211 }
212 }
213 STORM_LOG_ASSERT(!negativeEcStates.empty(), "Expected some negative ec.");
214 rewMinusInfEStates = storm::utility::graph::performProbGreater0E(backwardTransitions, getMaybeStates(), negativeEcStates);
215 STORM_LOG_ASSERT(model.getInitialStates().isSubsetOf(rewMinusInfEStates), "Initial state does not reach all maybestates.");
216 }
217}
218
219template<typename ModelType>
223
224template<typename ModelType>
226 STORM_LOG_ASSERT(getInfinityCase() == InfinityCase::HasNegativeInfinite, "Tried to get -inf states, but there are none.");
227 return rewMinusInfEStates;
228}
229
230template<typename ModelType>
232 return constantInitialStateValue.has_value();
233}
234
235template<typename ModelType>
240
241template<typename ModelType>
242std::map<uint64_t, typename ModelType::ValueType> const& DeterministicSchedsObjectiveHelper<ModelType>::getChoiceRewards() const {
243 return choiceRewards;
244}
245
246template<typename ModelType>
250
251template<typename ModelType>
252typename ModelType::ValueType const& DeterministicSchedsObjectiveHelper<ModelType>::getUpperValueBoundAtState(uint64_t state) const {
253 STORM_LOG_ASSERT(maybeStates.get(state), "Expected a maybestate.");
254 STORM_LOG_ASSERT(upperResultBounds.has_value(), "Requested upper value bounds but they were not computed.");
255 return upperResultBounds->at(state);
256}
257
258template<typename ModelType>
259typename ModelType::ValueType const& DeterministicSchedsObjectiveHelper<ModelType>::getLowerValueBoundAtState(uint64_t state) const {
260 STORM_LOG_ASSERT(maybeStates.get(state), "Expected a maybestate.");
261 STORM_LOG_ASSERT(lowerResultBounds.has_value(), "Requested lower value bounds but they were not computed.");
262 return lowerResultBounds->at(state);
263}
264
265template<typename ModelType>
269
270template<typename ModelType>
272 return objective.formula->isRewardOperatorFormula() && objective.formula->getSubformula().isTotalRewardFormula();
273}
274
275template<typename ModelType>
277 return objective.formula->hasBound();
278}
279
280template<typename ModelType>
282 STORM_LOG_ASSERT(hasThreshold(), "Trying to get a threshold but there is none.");
283 STORM_LOG_THROW(!storm::logic::isStrict(objective.formula->getBound().comparisonType), storm::exceptions::NotSupportedException,
284 "Objective " << *objective.originalFormula << ": Strict objective thresholds are not supported.");
285 ValueType threshold = objective.formula->template getThresholdAs<ValueType>();
286 if (storm::solver::minimize(objective.formula->getOptimalityType())) {
287 threshold *= -storm::utility::one<ValueType>(); // negate minimizing thresholds
288 STORM_LOG_THROW(!storm::logic::isLowerBound(objective.formula->getBound().comparisonType), storm::exceptions::NotSupportedException,
289 "Objective " << *objective.originalFormula << ": Minimizing objective should specify an upper bound.");
290 } else {
291 STORM_LOG_THROW(storm::logic::isLowerBound(objective.formula->getBound().comparisonType), storm::exceptions::NotSupportedException,
292 "Objective " << *objective.originalFormula << ": Maximizing objective should specify a lower bound.");
293 }
294 return threshold;
295}
296
297template<typename ModelType>
299 return storm::solver::minimize(objective.formula->getOptimalityType());
300}
301
302template<typename ValueType>
304 std::vector<ValueType> const& rewards, std::vector<ValueType> const& exitProbabilities,
305 std::optional<storm::OptimizationDirection> dir, bool reqLower, bool reqUpper) {
306 if (!reqLower && !reqUpper) {
307 return; // nothing to be done!
308 }
309 STORM_LOG_ASSERT(!rewards.empty(), "Empty reward vector,.");
310
311 auto [minIt, maxIt] = std::minmax_element(rewards.begin(), rewards.end());
312 bool const hasNegativeValues = *minIt < storm::utility::zero<ValueType>();
313 bool const hasPositiveValues = *maxIt > storm::utility::zero<ValueType>();
314
315 std::optional<ValueType> lowerBound, upperBound;
316
317 // Get 0 as a trivial lower/upper bound if possible
318 if (!hasNegativeValues) {
319 lowerBound = storm::utility::zero<ValueType>();
320 }
321 if (!hasPositiveValues) {
322 upperBound = storm::utility::zero<ValueType>();
323 }
324
325 // Invoke the respective reward bound computers if needed
326 std::vector<ValueType> tmpRewards;
327 if (reqUpper && !upperBound.has_value()) {
328 if (dir.has_value() && maximize(*dir)) {
329 solver.setUpperBound(
330 storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ValueType>(matrix, exitProbabilities).computeTotalRewardBounds(rewards).upper);
331 } else {
332 if (hasNegativeValues) {
333 tmpRewards.resize(rewards.size());
334 storm::utility::vector::applyPointwise(rewards, tmpRewards, [](ValueType const& v) { return std::max(storm::utility::zero<ValueType>(), v); });
335 }
336 solver.setUpperBounds(
337 storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ValueType>(matrix, hasNegativeValues ? tmpRewards : rewards, exitProbabilities)
338 .computeUpperBounds());
339 }
340 }
341 if (reqLower && !lowerBound.has_value()) {
342 if (dir.has_value() && minimize(*dir)) {
343 solver.setLowerBound(
344 storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ValueType>(matrix, exitProbabilities).computeTotalRewardBounds(rewards).lower);
345 } else {
346 // For lower bounds we actually compute upper bounds for the negated rewards because DsMpi is not implemented for negative rewards.
347 tmpRewards.resize(rewards.size());
348 storm::utility::vector::applyPointwise(rewards, tmpRewards,
349 [](ValueType const& v) { return std::max<ValueType>(storm::utility::zero<ValueType>(), -v); });
350 auto lowerBounds =
352 storm::utility::vector::applyPointwise(lowerBounds, lowerBounds, [](ValueType const& v) { return -v; });
353 solver.setLowerBounds(std::move(lowerBounds));
354 }
355 }
356}
357
358template<typename ValueType>
359std::vector<ValueType> computeValuesOfReducedSystem(Environment const& env, storm::storage::SparseMatrix<ValueType> const& submatrix,
360 std::vector<ValueType> const& exitProbs, std::vector<ValueType> const& rewards,
361 storm::OptimizationDirection const& dir) {
363 minMaxSolver->setHasUniqueSolution(true);
364 minMaxSolver->setOptimizationDirection(dir);
365 auto req = minMaxSolver->getRequirements(env, dir);
366 setLowerUpperTotalRewardBoundsToSolver(*minMaxSolver, submatrix, rewards, exitProbs, dir, req.lowerBounds(), req.upperBounds());
367 req.clearBounds();
368 if (req.validInitialScheduler()) {
369 std::vector<uint64_t> initSched(submatrix.getRowGroupCount());
370 auto backwardsTransitions = submatrix.transpose(true);
371 storm::storage::BitVector statesWithChoice(initSched.size(), false);
372 storm::storage::BitVector foundStates(initSched.size(), false);
373 std::vector<uint64_t> stack;
374 for (uint64_t state = 0; state < initSched.size(); ++state) {
375 if (foundStates.get(state)) {
376 continue;
377 }
378 for (auto choice : submatrix.getRowGroupIndices(state)) {
379 if (!storm::utility::isZero(exitProbs[choice])) {
380 initSched[state] = choice - submatrix.getRowGroupIndices()[state];
381 statesWithChoice.set(state);
382 foundStates.set(state);
383 for (auto const& predecessor : backwardsTransitions.getRow(state)) {
384 if (!foundStates.get(predecessor.getColumn())) {
385 stack.push_back(predecessor.getColumn());
386 foundStates.set(predecessor.getColumn());
387 }
388 }
389 break;
390 }
391 }
392 }
393 while (!stack.empty()) {
394 auto state = stack.back();
395 stack.pop_back();
396 for (auto choice : submatrix.getRowGroupIndices(state)) {
397 auto row = submatrix.getRow(choice);
398 if (std::any_of(row.begin(), row.end(), [&statesWithChoice](auto const& entry) {
399 return !storm::utility::isZero(entry.getValue()) && statesWithChoice.get(entry.getColumn());
400 })) {
401 initSched[state] = choice - submatrix.getRowGroupIndices()[state];
402 statesWithChoice.set(state);
403 break;
404 }
405 }
406 STORM_LOG_ASSERT(statesWithChoice.get(state), "State does not have expected choice.");
407 for (auto const& predecessor : backwardsTransitions.getRow(state)) {
408 if (!foundStates.get(predecessor.getColumn())) {
409 stack.push_back(predecessor.getColumn());
410 foundStates.set(predecessor.getColumn());
411 }
412 }
413 }
414 STORM_LOG_ASSERT(statesWithChoice.full(), "Not all states have a choice.");
415 minMaxSolver->setInitialScheduler(std::move(initSched));
416 req.clearValidInitialScheduler();
417 }
418 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
419 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
420 minMaxSolver->setRequirementsChecked(true);
421
422 std::vector<ValueType> result(submatrix.getRowGroupCount());
423 minMaxSolver->solveEquations(env, result, rewards);
424
425 return result;
426}
427
428template<typename ValueType>
429void plusMinMaxSolverPrecision(Environment const& env, ValueType& value) {
433 value += value * eps;
434 } else {
435 value += eps;
436 }
437 }
438}
439
440template<typename ValueType>
441void minusMinMaxSolverPrecision(Environment const& env, ValueType& value) {
445 value -= value * eps;
446 } else {
447 value -= eps;
448 }
449 }
450}
451
452template<storm::OptimizationDirection Dir, typename ValueType>
453ValueType sumOfMecRewards(storm::storage::MaximalEndComponent const& mec, std::vector<ValueType> const& rewards) {
455 for (auto const& stateChoices : mec) {
457 for (auto const& choice : stateChoices.second) {
458 optimalStateValue &= rewards.at(choice);
459 }
460 sum += *optimalStateValue;
461 }
462 return sum;
463}
464
465template<typename ValueType>
466ValueType getLowerBoundForNonZeroReachProb(storm::storage::SparseMatrix<ValueType> const& transitions, uint64_t init, uint64_t target) {
467 storm::storage::BitVector allStates(transitions.getRowGroupCount(), true);
468 storm::storage::BitVector initialAsBitVector(transitions.getRowGroupCount(), false);
469 initialAsBitVector.set(init, true);
470 storm::storage::BitVector targetAsBitVector(transitions.getRowGroupCount(), false);
471 targetAsBitVector.set(target, true);
472 auto relevantStates = storm::utility::graph::getReachableStates(transitions, initialAsBitVector, allStates, targetAsBitVector);
473 auto product = storm::utility::one<ValueType>();
474 for (auto state : relevantStates) {
475 if (state == target) {
476 continue;
477 }
479 for (auto const& entry : transitions.getRowGroup(state)) {
480 if (relevantStates.get(entry.getColumn())) {
481 minProb &= entry.getValue();
482 }
483 }
484 product *= *minProb;
485 }
486 return product;
487}
488
489template<typename ModelType>
491 STORM_LOG_ASSERT(!upperResultBounds.has_value() && !lowerResultBounds.has_value(), "Bounds already computed.");
492 auto backwardTransitions = model.getBackwardTransitions();
493 auto nonMaybeStates = ~maybeStates;
494 // Eliminate problematic mecs
495 storm::storage::MaximalEndComponentDecomposition<ValueType> problMecs(model.getTransitionMatrix(), backwardTransitions, maybeStates,
497 auto quotient1 = storm::transformer::EndComponentEliminator<ValueType>::transform(model.getTransitionMatrix(), problMecs, maybeStates, maybeStates);
498 auto backwardTransitions1 = quotient1.matrix.transpose(true);
499 std::vector<ValueType> rewards1(quotient1.matrix.getRowCount(), storm::utility::zero<ValueType>());
500 std::vector<ValueType> exitProbs1(quotient1.matrix.getRowCount(), storm::utility::zero<ValueType>());
501 storm::storage::BitVector subsystemChoices1(quotient1.matrix.getRowCount(), true);
502 storm::storage::BitVector allQuotient1States(quotient1.matrix.getRowGroupCount(), true);
503 for (uint64_t choice = 0; choice < quotient1.matrix.getRowCount(); ++choice) {
504 auto const& oldChoice = quotient1.newToOldRowMapping[choice];
505 if (auto findRes = choiceRewards.find(oldChoice); findRes != choiceRewards.end()) {
506 rewards1[choice] = findRes->second;
507 }
508 if (quotient1.sinkRows.get(choice)) {
509 subsystemChoices1.set(choice, false);
510 exitProbs1[choice] = storm::utility::one<ValueType>();
511 } else if (auto prob = model.getTransitionMatrix().getConstrainedRowSum(oldChoice, nonMaybeStates); !storm::utility::isZero(prob)) {
512 exitProbs1[choice] = prob;
513 subsystemChoices1.set(choice, false);
514 }
515 }
516 // Assert that every state can eventually exit the subsystem. If that is not the case, we get infinite reward as problematic (aka zero mecs) are removed.
517 auto exitStates1 = quotient1.matrix.getRowGroupFilter(~subsystemChoices1, false);
518 STORM_LOG_THROW(storm::utility::graph::performProbGreater0E(backwardTransitions1, allQuotient1States, exitStates1).full(),
519 storm::exceptions::InvalidOperationException, "Objective " << *objective.originalFormula << " does not induce finite value.");
520
521 // Compute bounds for finite cases
523 auto result1 = computeValuesOfReducedSystem(env, quotient1.matrix, exitProbs1, rewards1, storm::OptimizationDirection::Minimize);
524 lowerResultBounds = std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
525 for (uint64_t state : maybeStates) {
526 ValueType val = result1.at(quotient1.oldToNewStateMapping.at(state));
528 (*lowerResultBounds)[state] = val;
529 }
530 }
532 auto result1 = computeValuesOfReducedSystem(env, quotient1.matrix, exitProbs1, rewards1, storm::OptimizationDirection::Maximize);
533 upperResultBounds = std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
534 for (uint64_t state : maybeStates) {
535 ValueType val = result1.at(quotient1.oldToNewStateMapping.at(state));
537 if (infinityCase == InfinityCase::HasNegativeInfinite) { // Upper bound has to be at least 0 to allow for "trivial solution" in encoding
538 val = std::max(val, storm::utility::zero<ValueType>());
539 }
540 (*upperResultBounds)[state] = val;
541 }
542 }
545 "Unexpected infinity case.");
547 hasThreshold() || getInfinityCase() == InfinityCase::HasNegativeInfinite, storm::exceptions::NotSupportedException,
548 "The upper bound for objective " << *objective.originalFormula << " is infinity at some state. This is only supported for thresholded objectives.");
549 // Eliminate remaining mecs
550 storm::storage::MaximalEndComponentDecomposition<ValueType> remainingMecs(quotient1.matrix, backwardTransitions1, allQuotient1States,
551 subsystemChoices1);
552 STORM_LOG_ASSERT(!remainingMecs.empty(), "Incorrect infinityCase: There is no MEC with rewards.");
553 auto quotient2 =
554 storm::transformer::EndComponentEliminator<ValueType>::transform(quotient1.matrix, remainingMecs, allQuotient1States, allQuotient1States);
555 std::vector<ValueType> rewards2(quotient2.matrix.getRowCount(), storm::utility::zero<ValueType>());
556 std::vector<ValueType> exitProbs2(quotient2.matrix.getRowCount(), storm::utility::zero<ValueType>());
557 for (uint64_t choice = 0; choice < quotient2.matrix.getRowCount(); ++choice) {
558 auto const& oldChoice = quotient2.newToOldRowMapping[choice];
559 if (quotient2.sinkRows.get(choice)) {
560 exitProbs2[choice] = storm::utility::one<ValueType>();
561 } else {
562 rewards2[choice] = rewards1[oldChoice];
563 exitProbs2[choice] = exitProbs1[oldChoice];
564 }
565 }
566
567 // Add rewards within ECs
568 auto initialState2 = quotient2.oldToNewStateMapping.at(quotient1.oldToNewStateMapping.at(*model.getInitialStates().begin()));
569 ValueType rewardValueForPosInfCase;
571 rewardValueForPosInfCase = getThreshold();
572 // We need to substract a lower bound for the value at the initial state
573 std::vector<ValueType> rewards2Negative;
574 rewards2Negative.reserve(rewards2.size());
575 for (auto const& rew : rewards2) {
576 rewards2Negative.push_back(std::min(rew, storm::utility::zero<ValueType>()));
577 }
578 auto lower2 = computeValuesOfReducedSystem(env, quotient2.matrix, exitProbs2, rewards2Negative, storm::OptimizationDirection::Minimize);
579 rewardValueForPosInfCase -= lower2.at(initialState2);
580 }
581 for (auto const& mec : remainingMecs) {
582 auto mecState2 = quotient2.oldToNewStateMapping[mec.begin()->first];
586 mecReward /= VisitingTimesHelper<ValueType>::computeMecTraversalLowerBound(mec, quotient1.matrix);
587 auto groupIndices = quotient2.matrix.getRowGroupIndices(mecState2);
588 for (auto const choice2 : groupIndices) {
589 rewards2[choice2] += mecReward;
590 }
592 auto sinkChoice = quotient2.sinkRows.getNextSetIndex(*groupIndices.begin());
593 STORM_LOG_ASSERT(sinkChoice < quotient2.matrix.getRowGroupIndices()[mecState2 + 1], "EC state in quotient has no sink row.");
594 rewards2[sinkChoice] += rewardValueForPosInfCase / getLowerBoundForNonZeroReachProb(quotient2.matrix, initialState2, mecState2);
595 }
596 }
597
598 // Compute and insert missing bounds
600 auto result2 = computeValuesOfReducedSystem(env, quotient2.matrix, exitProbs2, rewards2, storm::OptimizationDirection::Minimize);
601 lowerResultBounds = std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
602 for (uint64_t state : maybeStates) {
603 ValueType val = result2.at(quotient2.oldToNewStateMapping.at(quotient1.oldToNewStateMapping.at(state)));
605 (*lowerResultBounds)[state] = val;
606 }
607 } else {
608 STORM_LOG_ASSERT(getInfinityCase() == InfinityCase::HasPositiveInfinite, "Expected positive infinity case.");
609 auto result2 = computeValuesOfReducedSystem(env, quotient2.matrix, exitProbs2, rewards2, storm::OptimizationDirection::Maximize);
610 upperResultBounds = std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
611 for (uint64_t state : maybeStates) {
612 ValueType val = result2.at(quotient2.oldToNewStateMapping.at(quotient1.oldToNewStateMapping.at(state)));
614 (*upperResultBounds)[state] = val;
615 }
616 }
617 }
619 std::all_of(maybeStates.begin(), maybeStates.end(), [this](auto const& s) { return this->lowerResultBounds->at(s) <= this->upperResultBounds->at(s); }),
620 storm::exceptions::UnexpectedException, "Pre-computed lower bound exceeds upper bound.");
621}
622
623template<typename ModelType>
625 Environment const& env, storm::storage::BitVector const& selectedChoices) const {
626 STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1u, "Expected a single initial state.");
627 STORM_LOG_ASSERT(model.getInitialStates().isSubsetOf(getMaybeStates()), "Expected initial state to be maybestate.");
628 STORM_LOG_ASSERT(selectedChoices.getNumberOfSetBits() == model.getNumberOfStates(), "Invalid choice selection.");
629 storm::storage::BitVector allStates(model.getNumberOfStates(), true);
630 auto selectedMatrix = model.getTransitionMatrix().getSubmatrix(false, selectedChoices, allStates);
631 STORM_LOG_ASSERT(selectedMatrix.getRowCount() == selectedMatrix.getRowGroupCount(), "Selected matrix row/group count mismatch.");
632 selectedMatrix.makeRowGroupingTrivial();
633 auto subMaybeStates =
634 getMaybeStates() & storm::utility::graph::getReachableStates(selectedMatrix, model.getInitialStates(), getMaybeStates(), ~getMaybeStates());
635 auto bsccCandidates = storm::utility::graph::performProbGreater0(selectedMatrix.transpose(true), subMaybeStates, ~subMaybeStates);
636 bsccCandidates.complement(); // i.e. states that can not reach non-maybe states
637 if (!bsccCandidates.empty()) {
639 bsccOptions.onlyBottomSccs(true).subsystem(bsccCandidates);
641 for (auto const& bscc : bsccs) {
642 for (auto const& s : bscc) {
643 subMaybeStates.set(s, false);
644 }
645 }
646 }
647
648 if (subMaybeStates.get(*model.getInitialStates().begin())) {
651 auto eqSysMatrix = selectedMatrix.getSubmatrix(true, subMaybeStates, subMaybeStates, useEqSysFormat);
652 STORM_LOG_ASSERT(eqSysMatrix.getRowCount() == eqSysMatrix.getRowGroupCount(), "Eq sys matrix row/group count mismatch.");
653 auto exitProbs = selectedMatrix.getConstrainedRowSumVector(subMaybeStates, ~subMaybeStates);
654 std::vector<ValueType> rewards;
655 rewards.reserve(exitProbs.size());
656 auto const& allRewards = getChoiceRewards();
657 for (auto const& state : getMaybeStates()) {
658 auto choice = selectedChoices.getNextSetIndex(model.getTransitionMatrix().getRowGroupIndices()[state]);
659 STORM_LOG_ASSERT(choice < model.getTransitionMatrix().getRowGroupIndices()[state + 1], "No choice selected at state " << state << ".");
660 if (subMaybeStates.get(state)) {
661 if (auto findRes = allRewards.find(choice); findRes != allRewards.end()) {
662 rewards.push_back(findRes->second);
663 } else {
664 rewards.push_back(storm::utility::zero<ValueType>());
665 }
666 } else {
667 STORM_LOG_ASSERT(!bsccCandidates.get(state) || allRewards.count(choice) == 0, "Strategy selected a bscc with rewards.");
668 }
669 }
670 if (useEqSysFormat) {
671 eqSysMatrix.convertToEquationSystem();
672 }
673 auto solver = factory.create(env, eqSysMatrix);
674 storm::storage::BitVector init = model.getInitialStates() % subMaybeStates;
675 STORM_LOG_ASSERT(init.getNumberOfSetBits() == 1, "Expected exactly 1 initial state.");
676 auto const initState = *init.begin();
677 solver->setRelevantValues(std::move(init));
678 auto req = solver->getRequirements(env);
679 if (!useEqSysFormat) {
680 setLowerUpperTotalRewardBoundsToSolver(*solver, eqSysMatrix, rewards, exitProbs, std::nullopt, req.lowerBounds(), req.upperBounds());
681 req.clearUpperBounds();
682 req.clearLowerBounds();
683 }
684 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
685 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
686 std::vector<ValueType> x(rewards.size());
687 solver->solveEquations(env, x, rewards);
688
689 return x[initState];
690 } else {
691 // initial state is on a bscc
693 }
694}
695
700} // namespace storm::modelchecker::multiobjective
SolverEnvironment & solver()
storm::RationalNumber const & getPrecision() const
bool const & getRelativeTerminationCriterion() const
MinMaxSolverEnvironment & minMax()
RewardOperatorFormula & asRewardOperatorFormula()
Definition Formula.cpp:484
virtual bool isRewardOperatorFormula() const
Definition Formula.cpp:184
virtual bool isTimeOperatorFormula() const
Definition Formula.cpp:140
boost::optional< std::string > const & getOptionalRewardModelName() const
Retrieves the optional representing the reward model name this property refers to.
virtual std::unique_ptr< CheckResult > check(Environment const &env, CheckTask< storm::logic::Formula, SolutionType > const &checkTask)
Checks the provided formula.
std::vector< ValueType > computeUpperBounds()
Computes upper bounds on the expected rewards.
storm::storage::BitVector const & getRelevantZeroRewardChoices() const
Returns the choices that (i) originate from a maybestate and (ii) have zero value.
bool hasThreshold() const
Returns true, if this objective specifies a threshold.
storm::storage::BitVector const & getRewMinusInfEStates() const
Returns the set of states for which value -inf is possible.
storm::storage::BitVector const & getMaybeStates() const
Returns those states for which the scheduler potentially influences the value.
ValueType evaluateScheduler(Environment const &env, storm::storage::BitVector const &selectedChoices) const
Computes the value at the initial state under the given scheduler.
ValueType getConstantInitialStateValue() const
If hasConstantInitialStateValue is true, this returns that constant value.
bool isTotalRewardObjective() const
Returns true if this is a total reward objective.
DeterministicSchedsObjectiveHelper(ModelType const &model, Objective< ValueType > const &objective)
bool hasConstantInitialStateValue() const
Returns true iff the value at the (unique) initial state is a constant (that is independent of the sc...
std::map< uint64_t, ValueType > const & getChoiceRewards() const
Returns the choice rewards if non-zero.
static ValueType computeMecTraversalLowerBound(storm::storage::MaximalEndComponent const &mec, storm::storage::SparseMatrix< ValueType > const &transitions, bool assumeOptimalTransitionProbabilities=false)
Computes value l in (0,1] such that for any pair of distinct states s,s' and deterministic,...
This class represents a Markov automaton.
storm::storage::BitVector const & getMarkovianStates() const
Retrieves the set of Markovian states of the model.
ValueType const & getExitRate(storm::storage::sparse::state_type state) const
Retrieves the exit rate of the given state.
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:13
RewardModelType const & getUniqueRewardModel() const
Retrieves the unique reward model, if there exists exactly one.
Definition Model.cpp:304
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:198
CRewardModelType RewardModelType
Definition Model.h:33
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:163
RewardModelType const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name, if one exists.
Definition Model.cpp:219
uint_fast64_t getNumberOfChoices(uint_fast64_t state) 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
void complement()
Negates all bits in the bit vector.
uint64_t getNextSetIndex(uint64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to true in the bit vector.
bool full() const
Retrieves whether all bits are set 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.
bool empty() const
Checks if the decomposition is empty.
This class represents the decomposition of a nondeterministic model into its maximal end components.
This class represents a maximal end-component of a nondeterministic model.
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
const_rows getRowGroup(index_type rowGroup) const
Returns an object representing the given row group.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
std::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.
This class represents the decomposition of a graph-like structure into its strongly connected compone...
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_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
bool isLowerBound(ComparisonType t)
bool isStrict(ComparisonType t)
std::vector< ValueType > getTotalRewardVector(storm::models::sparse::MarkovAutomaton< ValueType > const &model, storm::logic::Formula const &formula)
storm::storage::BitVector evaluatePropositionalFormula(ModelType const &model, storm::logic::Formula const &formula)
void minusMinMaxSolverPrecision(Environment const &env, ValueType &value)
ValueType sumOfMecRewards(storm::storage::MaximalEndComponent const &mec, std::vector< ValueType > const &rewards)
void plusMinMaxSolverPrecision(Environment const &env, ValueType &value)
ValueType getLowerBoundForNonZeroReachProb(storm::storage::SparseMatrix< ValueType > const &transitions, uint64_t init, uint64_t target)
std::vector< ValueType > computeValuesOfReducedSystem(Environment const &env, storm::storage::SparseMatrix< ValueType > const &submatrix, std::vector< ValueType > const &exitProbs, std::vector< ValueType > const &rewards, storm::OptimizationDirection const &dir)
void setLowerUpperTotalRewardBoundsToSolver(storm::solver::AbstractEquationSolver< ValueType > &solver, storm::storage::SparseMatrix< ValueType > const &matrix, std::vector< ValueType > const &rewards, std::vector< ValueType > const &exitProbabilities, std::optional< storm::OptimizationDirection > dir, bool reqLower, bool reqUpper)
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 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 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 performProbGreater0E(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under at leas...
Definition graph.cpp:673
storm::storage::BitVector performProb0A(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:733
bool checkIfECWithChoiceExists(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &subsystem, storm::storage::BitVector const &choices)
Checks whether there is an End Component that.
Definition graph.cpp:175
void applyPointwise(std::vector< InValueType1 > const &firstOperand, std::vector< InValueType2 > const &secondOperand, std::vector< OutValueType > &target, Operation f=Operation())
Applies the given operation pointwise on the two given vectors and writes the result to the third vec...
Definition vector.h:374
Extremum< storm::OptimizationDirection::Minimize, ValueType > Minimum
Definition Extremum.h:129
FilteredRewardModel< RewardModelType > createFilteredRewardModel(RewardModelType const &baseRewardModel, storm::logic::RewardAccumulation const &acc, bool isDiscreteTimeModel)
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
solver::OptimizationDirection OptimizationDirection
static const bool IsExact
StronglyConnectedComponentDecompositionOptions & subsystem(storm::storage::BitVector const &subsystem)
Sets a bit vector indicating which subsystem to consider for the decomposition into SCCs.
StronglyConnectedComponentDecompositionOptions & onlyBottomSccs(bool value=true)
Sets if only bottom SCCs, i.e. SCCs in which all states have no way of leaving the SCC),...