Storm 1.13.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 assert(formula.isTimeOperatorFormula());
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 assert(formula.isTimeOperatorFormula());
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 (auto const& 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 (auto const& 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 (auto const& 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 assert(rew.second < storm::utility::zero<ValueType>());
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 assert(statesWithChoice.get(state));
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 assert(statesWithChoice.full());
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 assert(!upperResultBounds.has_value() && !lowerResultBounds.has_value());
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 (auto const& 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 (auto const& 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 }
546 hasThreshold() || getInfinityCase() == InfinityCase::HasNegativeInfinite, storm::exceptions::NotSupportedException,
547 "The upper bound for objective " << *objective.originalFormula << " is infinity at some state. This is only supported for thresholded objectives");
548 // Eliminate remaining mecs
549 storm::storage::MaximalEndComponentDecomposition<ValueType> remainingMecs(quotient1.matrix, backwardTransitions1, allQuotient1States,
550 subsystemChoices1);
551 STORM_LOG_ASSERT(!remainingMecs.empty(), "Incorrect infinityCase: There is no MEC with rewards.");
552 auto quotient2 =
553 storm::transformer::EndComponentEliminator<ValueType>::transform(quotient1.matrix, remainingMecs, allQuotient1States, allQuotient1States);
554 std::vector<ValueType> rewards2(quotient2.matrix.getRowCount(), storm::utility::zero<ValueType>());
555 std::vector<ValueType> exitProbs2(quotient2.matrix.getRowCount(), storm::utility::zero<ValueType>());
556 for (uint64_t choice = 0; choice < quotient2.matrix.getRowCount(); ++choice) {
557 auto const& oldChoice = quotient2.newToOldRowMapping[choice];
558 if (quotient2.sinkRows.get(choice)) {
559 exitProbs2[choice] = storm::utility::one<ValueType>();
560 } else {
561 rewards2[choice] = rewards1[oldChoice];
562 exitProbs2[choice] = exitProbs1[oldChoice];
563 }
564 }
565
566 // Add rewards within ECs
567 auto initialState2 = quotient2.oldToNewStateMapping.at(quotient1.oldToNewStateMapping.at(*model.getInitialStates().begin()));
568 ValueType rewardValueForPosInfCase;
570 rewardValueForPosInfCase = getThreshold();
571 // We need to substract a lower bound for the value at the initial state
572 std::vector<ValueType> rewards2Negative;
573 rewards2Negative.reserve(rewards2.size());
574 for (auto const& rew : rewards2) {
575 rewards2Negative.push_back(std::min(rew, storm::utility::zero<ValueType>()));
576 }
577 auto lower2 = computeValuesOfReducedSystem(env, quotient2.matrix, exitProbs2, rewards2Negative, storm::OptimizationDirection::Minimize);
578 rewardValueForPosInfCase -= lower2.at(initialState2);
579 }
580 for (auto const& mec : remainingMecs) {
581 auto mecState2 = quotient2.oldToNewStateMapping[mec.begin()->first];
585 mecReward /= VisitingTimesHelper<ValueType>::computeMecTraversalLowerBound(mec, quotient1.matrix);
586 auto groupIndices = quotient2.matrix.getRowGroupIndices(mecState2);
587 for (auto const choice2 : groupIndices) {
588 rewards2[choice2] += mecReward;
589 }
591 auto sinkChoice = quotient2.sinkRows.getNextSetIndex(*groupIndices.begin());
592 STORM_LOG_ASSERT(sinkChoice < quotient2.matrix.getRowGroupIndices()[mecState2 + 1], "EC state in quotient has no sink row.");
593 rewards2[sinkChoice] += rewardValueForPosInfCase / getLowerBoundForNonZeroReachProb(quotient2.matrix, initialState2, mecState2);
594 }
595 }
596
597 // Compute and insert missing bounds
599 auto result2 = computeValuesOfReducedSystem(env, quotient2.matrix, exitProbs2, rewards2, storm::OptimizationDirection::Minimize);
600 lowerResultBounds = std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
601 for (auto const& state : maybeStates) {
602 ValueType val = result2.at(quotient2.oldToNewStateMapping.at(quotient1.oldToNewStateMapping.at(state)));
604 (*lowerResultBounds)[state] = val;
605 }
606 } else {
608 auto result2 = computeValuesOfReducedSystem(env, quotient2.matrix, exitProbs2, rewards2, storm::OptimizationDirection::Maximize);
609 upperResultBounds = std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
610 for (auto const& state : maybeStates) {
611 ValueType val = result2.at(quotient2.oldToNewStateMapping.at(quotient1.oldToNewStateMapping.at(state)));
613 (*upperResultBounds)[state] = val;
614 }
615 }
616 }
618 std::all_of(maybeStates.begin(), maybeStates.end(), [this](auto const& s) { return this->lowerResultBounds->at(s) <= this->upperResultBounds->at(s); }),
619 storm::exceptions::UnexpectedException, "Pre-computed lower bound exceeds upper bound.");
620}
621
622template<typename ModelType>
624 Environment const& env, storm::storage::BitVector const& selectedChoices) const {
625 STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1u, "Expected a single initial state.");
626 STORM_LOG_ASSERT(model.getInitialStates().isSubsetOf(getMaybeStates()), "Expected initial state to be maybestate.");
627 STORM_LOG_ASSERT(selectedChoices.getNumberOfSetBits() == model.getNumberOfStates(), "invalid choice selection.");
628 storm::storage::BitVector allStates(model.getNumberOfStates(), true);
629 auto selectedMatrix = model.getTransitionMatrix().getSubmatrix(false, selectedChoices, allStates);
630 assert(selectedMatrix.getRowCount() == selectedMatrix.getRowGroupCount());
631 selectedMatrix.makeRowGroupingTrivial();
632 auto subMaybeStates =
633 getMaybeStates() & storm::utility::graph::getReachableStates(selectedMatrix, model.getInitialStates(), getMaybeStates(), ~getMaybeStates());
634 auto bsccCandidates = storm::utility::graph::performProbGreater0(selectedMatrix.transpose(true), subMaybeStates, ~subMaybeStates);
635 bsccCandidates.complement(); // i.e. states that can not reach non-maybe states
636 if (!bsccCandidates.empty()) {
638 bsccOptions.onlyBottomSccs(true).subsystem(bsccCandidates);
639 storm::storage::StronglyConnectedComponentDecomposition bsccs(selectedMatrix, bsccOptions);
640 for (auto const& bscc : bsccs) {
641 for (auto const& s : bscc) {
642 subMaybeStates.set(s, false);
643 }
644 }
645 }
646
647 if (subMaybeStates.get(*model.getInitialStates().begin())) {
650 auto eqSysMatrix = selectedMatrix.getSubmatrix(true, subMaybeStates, subMaybeStates, useEqSysFormat);
651 assert(eqSysMatrix.getRowCount() == eqSysMatrix.getRowGroupCount());
652 auto exitProbs = selectedMatrix.getConstrainedRowSumVector(subMaybeStates, ~subMaybeStates);
653 std::vector<ValueType> rewards;
654 rewards.reserve(exitProbs.size());
655 auto const& allRewards = getChoiceRewards();
656 for (auto const& state : getMaybeStates()) {
657 auto choice = selectedChoices.getNextSetIndex(model.getTransitionMatrix().getRowGroupIndices()[state]);
658 STORM_LOG_ASSERT(choice < model.getTransitionMatrix().getRowGroupIndices()[state + 1], " no choice selected at state" << state);
659 if (subMaybeStates.get(state)) {
660 if (auto findRes = allRewards.find(choice); findRes != allRewards.end()) {
661 rewards.push_back(findRes->second);
662 } else {
663 rewards.push_back(storm::utility::zero<ValueType>());
664 }
665 } else {
666 STORM_LOG_ASSERT(!bsccCandidates.get(state) || allRewards.count(choice) == 0, "Strategy selected a bscc with rewards");
667 }
668 }
669 if (useEqSysFormat) {
670 eqSysMatrix.convertToEquationSystem();
671 }
672 auto solver = factory.create(env, eqSysMatrix);
673 storm::storage::BitVector init = model.getInitialStates() % subMaybeStates;
674 assert(init.getNumberOfSetBits() == 1);
675 auto const initState = *init.begin();
676 solver->setRelevantValues(std::move(init));
677 auto req = solver->getRequirements(env);
678 if (!useEqSysFormat) {
679 setLowerUpperTotalRewardBoundsToSolver(*solver, eqSysMatrix, rewards, exitProbs, std::nullopt, req.lowerBounds(), req.upperBounds());
680 req.clearUpperBounds();
681 req.clearLowerBounds();
682 }
683 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
684 "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
685 std::vector<ValueType> x(rewards.size());
686 solver->solveEquations(env, x, rewards);
687
688 return x[initState];
689 } else {
690 // initial state is on a bscc
692 }
693}
694
699} // 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:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
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:39
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),...