Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GoalStateMerger.cpp
Go to the documentation of this file.
2
3#include <limits>
4#include <memory>
5#include <type_traits>
6
25#include "storm/utility/graph.h"
28
29namespace storm {
30namespace transformer {
31
32namespace {
33// Whether the given operator formula asks for the minimizing (as opposed to maximizing) value.
34// Returns std::nullopt if the optimization direction cannot be derived from the formula.
35std::optional<bool> isMinimizing(storm::logic::OperatorFormula const& formula) {
36 if (formula.hasOptimalityType()) {
38 }
39 if (formula.hasBound()) {
41 }
42 return std::nullopt;
43}
44
45// Checks the given (propositional) subformula and returns the truth values vector, or std::nullopt if the subformula is not propositional.
46template<typename SparseModelType>
47std::optional<storm::storage::BitVector> checkPropositional(storm::modelchecker::SparsePropositionalModelChecker<SparseModelType>& checker,
48 storm::logic::Formula const& subformula) {
49 if (!checker.canHandle(subformula)) {
50 return std::nullopt;
51 }
52 return std::move(checker.check(subformula)->template asExplicitQualitativeCheckResult<typename SparseModelType::ValueType>().getTruthValuesVector());
53}
54
58std::optional<uint64_t> representative(storm::storage::BitVector const& b) {
59 uint64_t const result = b.getNextSetIndex(0);
60 if (result < b.size()) {
61 return result;
62 }
63 return std::nullopt;
64}
65
66} // namespace
67
68template<typename ValueType>
72 storm::exceptions::NotSupportedException, "GoalStateMerger does not support models of type " << model.getType() << ".");
73}
74
75template<typename ValueType>
77 storm::storage::BitVector const& maybeStates, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& sinkStates,
78 std::vector<std::string> const& selectedRewardModels, std::optional<storm::storage::BitVector> const& choiceFilter,
79 std::optional<uint64_t> const representativeTargetState, std::optional<uint64_t> const representativeSinkState) const {
80 STORM_LOG_THROW(maybeStates.isDisjointFrom(targetStates) && targetStates.isDisjointFrom(sinkStates) && sinkStates.isDisjointFrom(maybeStates),
81 storm::exceptions::InvalidArgumentException,
82 "Maybestates, targetstates, and sinkstates are assumed to be disjoint when creating the submodel. However, this is not the case.");
83
84 auto result = initialize(maybeStates, targetStates, sinkStates, choiceFilter);
85
86 // Initialize model components
87 auto transitionMatrix = buildTransitionMatrix(maybeStates, result.first, result.second);
88 auto labeling = buildStateLabeling(maybeStates, targetStates, sinkStates, result.first, representativeTargetState, representativeSinkState);
89 auto rewardModels = buildRewardModels(maybeStates, result.first, selectedRewardModels);
90 storm::storage::sparse::ModelComponents<ValueType> modelComponents(std::move(transitionMatrix), std::move(labeling), std::move(rewardModels));
91
92 uint64_t const newStateCount = modelComponents.transitionMatrix.getRowGroupCount();
93 uint64_t const newChoiceCount = modelComponents.transitionMatrix.getRowCount();
94
95 // Obtain model type-specific data
96 using enum storm::models::ModelType;
97 if (originalModel.isOfType(Ctmc)) {
98 // The transition matrix entries are rates (as opposed to probabilities that would need to be scaled by the exit rate).
99 modelComponents.rateTransitions = true;
100 } else if (originalModel.isOfType(MarkovAutomaton)) {
101 auto const& ma = *originalModel.template as<storm::models::sparse::MarkovAutomaton<ValueType>>();
102 modelComponents.markovianStates = ma.getMarkovianStates() % maybeStates;
103 modelComponents.markovianStates->resize(newStateCount, true);
104 modelComponents.exitRates = storm::utility::vector::filterVector(ma.getExitRates(), maybeStates);
105 modelComponents.exitRates->resize(newStateCount, storm::utility::one<ValueType>());
106 } else {
107 STORM_LOG_ASSERT(originalModel.isOfType(Dtmc) || originalModel.isOfType(Mdp), "Unhandled model type: " << originalModel.getType());
108 }
109
110 // Reapply state valuations, choice labeling, choice origins if available
111 if (originalModel.hasStateValuations()) {
112 std::vector<uint64_t> newToOldStateIndices(maybeStates.begin(), maybeStates.end());
113 if (result.first.targetState.has_value()) {
114 STORM_LOG_ASSERT(result.first.targetState.value() == newToOldStateIndices.size(), "Unexpected position of target state.");
115 newToOldStateIndices.push_back(representativeTargetState.value_or(*targetStates.begin()));
116 }
117 if (result.first.sinkState.has_value()) {
118 STORM_LOG_ASSERT(result.first.sinkState.value() == newToOldStateIndices.size(), "Unexpected position of sink state.");
119 newToOldStateIndices.push_back(representativeSinkState.value_or(*sinkStates.begin()));
120 }
121 modelComponents.stateValuations = originalModel.getStateValuations().selectEntities(newToOldStateIndices);
122 }
123 if (originalModel.hasChoiceLabeling()) {
124 modelComponents.choiceLabeling.emplace(newChoiceCount);
125 for (auto const& label : originalModel.getChoiceLabeling().getLabels()) {
126 storm::storage::BitVector choiceLabels = originalModel.getChoiceLabeling().getChoices(label) % result.first.keptChoices;
127 choiceLabels.resize(newChoiceCount, false); // no label for the choice at target/sink states
128 modelComponents.choiceLabeling->addLabel(label, std::move(choiceLabels));
129 }
130 }
131 if (originalModel.hasChoiceOrigins()) {
132 std::vector<uint64_t> newToOldChoiceIndices(result.first.keptChoices.begin(), result.first.keptChoices.end());
133 newToOldChoiceIndices.resize(newChoiceCount, std::numeric_limits<uint64_t>::max()); // target/sink choices will not have an origin
134 modelComponents.choiceOrigins = originalModel.getChoiceOrigins()->selectChoices(newToOldChoiceIndices);
135 }
136
137 // Finalize result
138 result.first.model = storm::utility::builder::buildModelFromComponents(originalModel.getType(), std::move(modelComponents));
139 return result.first;
140}
141
142template<typename ValueType>
143std::optional<typename GoalStateMerger<ValueType>::ReturnType> GoalStateMerger<ValueType>::mergeForFormula(storm::logic::Formula const& formula,
144 bool const dropUnreachableFromInit) const {
145 if (formula.isProbabilityOperatorFormula()) {
146 auto const& probabilityOperatorFormula = formula.asProbabilityOperatorFormula();
147 if (probabilityOperatorFormula.getSubformula().isUntilFormula()) {
148 return mergeForUntilProbabilities(probabilityOperatorFormula, dropUnreachableFromInit);
149 } else if (probabilityOperatorFormula.getSubformula().isEventuallyFormula()) {
150 return mergeForUntilProbabilities(probabilityOperatorFormula, dropUnreachableFromInit);
151 } else if (probabilityOperatorFormula.getSubformula().isBoundedUntilFormula()) {
152 return mergeForBoundedUntilProbabilities(probabilityOperatorFormula, dropUnreachableFromInit);
153 }
154 } else if (formula.isRewardOperatorFormula()) {
155 auto const& rewardOperatorFormula = formula.asRewardOperatorFormula();
156 if (rewardOperatorFormula.getSubformula().isReachabilityRewardFormula()) {
157 return mergeForReachabilityRewards(rewardOperatorFormula, dropUnreachableFromInit);
158 } else if (rewardOperatorFormula.getSubformula().isCumulativeRewardFormula()) {
159 return mergeForCumulativeRewards(rewardOperatorFormula, dropUnreachableFromInit);
160 }
161 }
162 return std::nullopt;
163}
164
165template<typename ValueType>
166std::optional<typename GoalStateMerger<ValueType>::ReturnType> GoalStateMerger<ValueType>::mergeForUntilProbabilities(
167 storm::logic::ProbabilityOperatorFormula const& formula, bool const dropUnreachableFromInit) const {
168 std::optional<storm::storage::BitVector> phiStates, psiStates;
169 if (formula.getSubformula().isUntilFormula()) {
170 auto const& untilFormula = formula.getSubformula().asUntilFormula();
172 phiStates = checkPropositional(propositionalChecker, untilFormula.getLeftSubformula());
173 psiStates = checkPropositional(propositionalChecker, untilFormula.getRightSubformula());
174 } else {
175 STORM_LOG_ASSERT(formula.getSubformula().isEventuallyFormula(), "Unexpected formula type: " << formula.getSubformula());
176 auto const& eventuallyFormula = formula.getSubformula().asEventuallyFormula();
177 STORM_LOG_ASSERT(eventuallyFormula.isReachabilityProbabilityFormula(), "Unexpected formula context");
179 phiStates.emplace(originalModel.getNumberOfStates(), true);
180 psiStates = checkPropositional(propositionalChecker, eventuallyFormula.getSubformula());
181 }
182 if (!phiStates || !psiStates) {
183 return std::nullopt;
184 }
185
186 auto const& transitions = originalModel.getTransitionMatrix();
187 storm::storage::SparseMatrix<ValueType> const backwardTransitions = originalModel.getBackwardTransitions();
188 std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01;
189 if (!originalModel.isNondeterministicModel()) {
190 statesWithProbability01 = storm::utility::graph::performProb01(backwardTransitions, *phiStates, *psiStates);
191 } else if (std::optional<bool> const minimizing = isMinimizing(formula); !minimizing.has_value()) {
192 return std::nullopt; // No optimization direction given.
193 } else if (*minimizing) {
194 statesWithProbability01 =
195 storm::utility::graph::performProb01Min(transitions, transitions.getRowGroupIndices(), backwardTransitions, *phiStates, *psiStates);
196 } else {
197 statesWithProbability01 =
198 storm::utility::graph::performProb01Max(transitions, transitions.getRowGroupIndices(), backwardTransitions, *phiStates, *psiStates);
199 }
200
201 storm::storage::BitVector maybeStates;
202 if (dropUnreachableFromInit) {
203 // Only consider the maybestates that are reachable from one initial state without hopping over a target (i.e., prob1) state
204 storm::storage::BitVector reachableGreater0States = storm::utility::graph::getReachableStates(
205 transitions, originalModel.getInitialStates() & ~statesWithProbability01.first, ~statesWithProbability01.first, statesWithProbability01.second);
206 maybeStates = reachableGreater0States & ~statesWithProbability01.second;
207 } else {
208 // Equivalent to the above with all states considered initial: every non-prob0, non-prob1 state is a maybestate.
209 maybeStates = ~(statesWithProbability01.first | statesWithProbability01.second);
210 }
211
212 // Ensure that we pick an actual psiState as representative for the target and an actual sink (prob0) state as representative for the sink. Note that a
213 // phi-violating state is not necessarily a sink state, as it might coincide with a psi (target) state, so we can not take a shortcut via phiStates here.
214 std::optional<uint64_t> const representativeTarget = representative(*psiStates);
215 std::optional<uint64_t> const representativeSink = representative(statesWithProbability01.first);
216 return mergeTargetAndSinkStates(maybeStates, statesWithProbability01.second, statesWithProbability01.first, std::vector<std::string>(), std::nullopt,
217 representativeTarget, representativeSink);
218}
219
220template<typename ValueType>
221std::optional<typename GoalStateMerger<ValueType>::ReturnType> GoalStateMerger<ValueType>::mergeForBoundedUntilProbabilities(
222 storm::logic::ProbabilityOperatorFormula const& formula, bool const dropUnreachableFromInit) const {
223 auto const& boundedUntilFormula = formula.getSubformula().asBoundedUntilFormula();
224 if (boundedUntilFormula.isMultiDimensional() || boundedUntilFormula.hasLowerBound()) {
225 // we can not make the psiStates absorbing or we have to deal with multi-dimensionality. We don't consider these here.
226 return std::nullopt;
227 }
228
229 std::optional<uint64_t> upperStepBound;
230 if (originalModel.isDiscreteTimeModel() && boundedUntilFormula.hasUpperBound() && !boundedUntilFormula.getTimeBoundReference().isRewardBound() &&
231 boundedUntilFormula.getUpperBound().getBaseExpression().isIntegerLiteralExpression()) {
232 upperStepBound = boundedUntilFormula.getUpperBound().evaluateAsInt();
233 if (boundedUntilFormula.isUpperBoundStrict()) {
234 if (upperStepBound.value() == 0) {
235 // The formula has the form `phi U<0 psi`. Not a case we want to treat here.
236 return std::nullopt;
237 }
238 --upperStepBound.value();
239 }
240 }
241
242 // Get the prob0, prob1 and the maybeStates
243 storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<ValueType>> propositionalChecker(originalModel);
244 auto phiStates = checkPropositional(propositionalChecker, boundedUntilFormula.getLeftSubformula());
245 auto psiStates = checkPropositional(propositionalChecker, boundedUntilFormula.getRightSubformula());
246 if (!phiStates || !psiStates) {
247 return std::nullopt;
248 }
249
250 auto const& transitions = originalModel.getTransitionMatrix();
251 storm::storage::SparseMatrix<ValueType> const backwardTransitions = originalModel.getBackwardTransitions();
252 storm::storage::BitVector probGreater0States;
253 if (!originalModel.isNondeterministicModel()) {
254 probGreater0States =
255 storm::utility::graph::performProbGreater0(backwardTransitions, *phiStates, *psiStates, upperStepBound.has_value(), upperStepBound.value_or(0));
256 } else if (std::optional<bool> const minimizing = isMinimizing(formula); !minimizing.has_value()) {
257 return std::nullopt; // No optimization direction given.
258 } else if (*minimizing) {
259 probGreater0States = storm::utility::graph::performProbGreater0A(transitions, transitions.getRowGroupIndices(), backwardTransitions, *phiStates,
260 *psiStates, upperStepBound.has_value(), upperStepBound.value_or(0));
261 } else {
262 probGreater0States =
263 storm::utility::graph::performProbGreater0E(backwardTransitions, *phiStates, *psiStates, upperStepBound.has_value(), upperStepBound.value_or(0));
264 }
265
266 storm::storage::BitVector maybeStates;
267 storm::storage::BitVector prob0States;
268 if (dropUnreachableFromInit) {
269 // Only consider the maybestates that are reachable from one initial probGreater0 state within the given amount of steps and without hopping over a
270 // target state
271 storm::storage::BitVector reachableGreater0States =
272 storm::utility::graph::getReachableStates(transitions, originalModel.getInitialStates() & probGreater0States, probGreater0States, *psiStates,
273 upperStepBound.has_value(), upperStepBound.value_or(0));
274 maybeStates = reachableGreater0States & ~*psiStates;
275 prob0States = ~reachableGreater0States & ~*psiStates;
276 } else {
277 // Equivalent to the above with all states considered initial.
278 maybeStates = probGreater0States & ~*psiStates;
279 prob0States = ~probGreater0States & ~*psiStates;
280 }
281
282 // For a reward-bounded formula we must preserve that reward model.
283 std::vector<std::string> rewardModels;
284 if (boundedUntilFormula.getTimeBoundReference().isRewardBound()) {
285 auto rewName = boundedUntilFormula.getTimeBoundReference().getOptionalRewardModelName();
286 if (!rewName) {
287 rewName = originalModel.getUniqueRewardModelName();
288 }
289 rewardModels.push_back(rewName.value());
290 }
291 // Ensure that we pick an actual psiState as representative for the target and an actual sink (prob0) state as representative for the sink. Note that a
292 // phi-violating state is not necessarily a sink state, as it might coincide with a psi (target) state, so we can not take a shortcut via phiStates here.
293 std::optional<uint64_t> const representativeTarget = representative(*psiStates);
294 std::optional<uint64_t> const representativeSink = representative(prob0States);
295 return mergeTargetAndSinkStates(maybeStates, psiStates.value(), prob0States, rewardModels, std::nullopt, representativeTarget, representativeSink);
296}
297
298template<typename ValueType>
299std::optional<typename GoalStateMerger<ValueType>::ReturnType> GoalStateMerger<ValueType>::mergeForReachabilityRewards(
300 storm::logic::RewardOperatorFormula const& formula, bool const dropUnreachableFromInit) const {
301 auto const& originalRewardModel =
302 formula.hasRewardModelName() ? originalModel.getRewardModel(formula.getRewardModelName()) : originalModel.getUniqueRewardModel();
303
304 // Get the prob1 and the maybeStates
305 storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<ValueType>> propositionalChecker(originalModel);
306 auto const& eventuallyFormula = formula.getSubformula().asEventuallyFormula();
307 auto checkedTargetStates = checkPropositional(propositionalChecker, eventuallyFormula.getSubformula());
308 if (!checkedTargetStates) {
309 return std::nullopt;
310 }
311 storm::storage::BitVector targetStates = std::move(*checkedTargetStates);
312 // we extend the target states below, so we must pick our representative here already.
313 auto representativeTargetState = representative(targetStates);
314
315 auto const& transitions = originalModel.getTransitionMatrix();
316 auto const backwardTransitions = originalModel.getBackwardTransitions();
317 storm::storage::BitVector statesWithProb1;
318 // The set of target states can be extended by the states that reach target with probability 1 without collecting any reward
319 auto const zeroRewardStates = originalRewardModel.getStatesWithZeroReward(originalModel.getTransitionMatrix());
320 storm::storage::BitVector const allStates(originalModel.getNumberOfStates(), true);
321 if (!originalModel.isNondeterministicModel()) {
322 targetStates = storm::utility::graph::performProb1(backwardTransitions, zeroRewardStates, targetStates);
323 statesWithProb1 = storm::utility::graph::performProb1(backwardTransitions, allStates, targetStates);
324 } else if (std::optional<bool> const minimizing = isMinimizing(formula); !minimizing.has_value()) {
325 return std::nullopt; // No optimization direction given.
326 } else if (*minimizing) {
327 targetStates = storm::utility::graph::performProb1E(transitions, transitions.getRowGroupIndices(), backwardTransitions, zeroRewardStates, targetStates);
328 statesWithProb1 = storm::utility::graph::performProb1E(transitions, transitions.getRowGroupIndices(), backwardTransitions, allStates, targetStates);
329 } else {
330 targetStates = storm::utility::graph::performProb1A(transitions, transitions.getRowGroupIndices(), backwardTransitions, zeroRewardStates, targetStates);
331 statesWithProb1 = storm::utility::graph::performProb1A(transitions, transitions.getRowGroupIndices(), backwardTransitions, allStates, targetStates);
332 }
333 storm::storage::BitVector infinityStates = ~statesWithProb1;
334 storm::storage::BitVector maybeStates;
335 if (dropUnreachableFromInit) {
336 // Only consider the states that are reachable from an initial state without hopping over a target state
337 storm::storage::BitVector reachableStates =
338 storm::utility::graph::getReachableStates(transitions, originalModel.getInitialStates() & statesWithProb1, statesWithProb1, targetStates);
339 maybeStates = reachableStates & ~targetStates;
340 } else {
341 // Equivalent to the above with all states considered initial.
342 maybeStates = statesWithProb1 & ~targetStates;
343 }
344
345 std::vector<std::string> rewardModelNameAsVector(
346 1, formula.hasRewardModelName() ? formula.getRewardModelName() : originalModel.getRewardModels().begin()->first);
347 return mergeTargetAndSinkStates(maybeStates, targetStates, infinityStates, rewardModelNameAsVector, std::nullopt, representativeTargetState,
348 representative(infinityStates));
349}
350
351template<typename ValueType>
352std::optional<typename GoalStateMerger<ValueType>::ReturnType> GoalStateMerger<ValueType>::mergeForCumulativeRewards(
353 storm::logic::RewardOperatorFormula const& formula, bool const dropUnreachableFromInit) const {
354 auto const& cumulativeRewardFormula = formula.getSubformula().asCumulativeRewardFormula();
355 if (cumulativeRewardFormula.isMultiDimensional()) {
356 return std::nullopt; // we don't handle those more sophisticated formulas here.
357 }
358
359 std::optional<uint64_t> stepBound;
360 if (originalModel.isDiscreteTimeModel() && !cumulativeRewardFormula.getTimeBoundReference().isRewardBound() &&
361 cumulativeRewardFormula.getBound().getBaseExpression().isIntegerLiteralExpression()) {
362 stepBound = cumulativeRewardFormula.getBound().evaluateAsInt();
363 if (cumulativeRewardFormula.isBoundStrict()) {
364 if (stepBound.value() == 0) {
365 return std::nullopt; // Don't treat formulas of the form C<0 here.
366 }
367 --stepBound.value();
368 }
369 }
370
371 auto const& originalRewardModel =
372 formula.hasRewardModelName() ? originalModel.getRewardModel(formula.getRewardModelName()) : originalModel.getUniqueRewardModel();
373
374 // Get the states for which no reward is reachable (within the step bound)
375 auto const& transitions = originalModel.getTransitionMatrix();
376 auto const backwardTransitions = originalModel.getBackwardTransitions();
377 storm::storage::BitVector maybeStates;
378 storm::storage::BitVector const statesWithNonZeroRewards = ~originalRewardModel.getStatesWithZeroReward(transitions);
379 storm::storage::BitVector const allStates(originalModel.getNumberOfStates(), true);
380 if (!originalModel.isNondeterministicModel()) {
381 maybeStates =
382 storm::utility::graph::performProbGreater0(backwardTransitions, allStates, statesWithNonZeroRewards, stepBound.has_value(), stepBound.value_or(0));
383 } else if (std::optional<bool> const minimizing = isMinimizing(formula); !minimizing.has_value()) {
384 return std::nullopt; // No optimization direction given.
385 } else if (*minimizing) {
386 maybeStates = storm::utility::graph::performProbGreater0A(transitions, transitions.getRowGroupIndices(), backwardTransitions, allStates,
387 statesWithNonZeroRewards, stepBound.has_value(), stepBound.value_or(0));
388 } else {
389 maybeStates =
390 storm::utility::graph::performProbGreater0E(backwardTransitions, allStates, statesWithNonZeroRewards, stepBound.has_value(), stepBound.value_or(0));
391 }
392 auto const noStates = ~allStates;
393 if (dropUnreachableFromInit) {
394 // Only consider the maybestates that are reachable from an initial state.
395 maybeStates = storm::utility::graph::getReachableStates(transitions, originalModel.getInitialStates() & maybeStates, maybeStates, noStates);
396 }
397 auto const zeroExpectedRewardStates = ~maybeStates;
398
399 auto rewardModels = formula.getReferencedRewardModels();
400 // An unnamed reward operator is reported as the empty name, which we resolve to the actual name of the unique reward model.
401 if (auto unnamedIt = rewardModels.find(""); unnamedIt != rewardModels.end()) {
402 if (!originalModel.hasUniqueRewardModel()) {
403 return std::nullopt;
404 }
405 rewardModels.erase(unnamedIt);
406 rewardModels.emplace(originalModel.getUniqueRewardModelName());
407 }
408 std::vector<std::string> rewardModelNameAsVector(rewardModels.begin(), rewardModels.end());
409 return mergeTargetAndSinkStates(maybeStates, noStates, zeroExpectedRewardStates, rewardModelNameAsVector, std::nullopt, std::nullopt,
410 representative(zeroExpectedRewardStates));
411}
412
413template<typename ValueType>
414std::pair<typename GoalStateMerger<ValueType>::ReturnType, uint64_t> GoalStateMerger<ValueType>::initialize(
415 storm::storage::BitVector const& maybeStates, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& sinkStates,
416 std::optional<storm::storage::BitVector> const& choiceFilter) const {
417 storm::storage::SparseMatrix<ValueType> const& origMatrix = originalModel.getTransitionMatrix();
418
419 ReturnType result;
420 result.keptChoices = storm::storage::BitVector(origMatrix.getRowCount(), false);
421 result.oldToNewStateIndexMapping = std::vector<uint64_t>(maybeStates.size(), std::numeric_limits<uint64_t>::max()); // init with some invalid state
422
423 uint64_t transitionCount(0), stateCount(0);
424 bool targetStateRequired = !originalModel.getInitialStates().isDisjointFrom(targetStates);
425 bool sinkStateRequired = !originalModel.getInitialStates().isDisjointFrom(sinkStates);
426 for (uint64_t state : maybeStates) {
427 result.oldToNewStateIndexMapping[state] = stateCount;
428
429 auto const& endOfRowGroup = origMatrix.getRowGroupIndices()[state + 1];
430 bool stateIsDeadlock = true;
431 for (uint64_t row = origMatrix.getRowGroupIndices()[state]; row < endOfRowGroup; ++row) {
432 uint64_t transitionsToMaybeStates = 0;
433 bool keepThisRow(true), hasTransitionToTarget(false), hasTransitionToSink(false);
434 if (!choiceFilter || choiceFilter->get(row)) {
435 for (auto const& entry : origMatrix.getRow(row)) {
436 if (maybeStates.get(entry.getColumn())) {
437 ++transitionsToMaybeStates;
438 } else if (targetStates.get(entry.getColumn())) {
439 hasTransitionToTarget = true;
440 } else if (sinkStates.get(entry.getColumn())) {
441 hasTransitionToSink = true;
442 } else {
443 keepThisRow = false;
444 break;
445 }
446 }
447 if (keepThisRow) {
448 stateIsDeadlock = false;
449 result.keptChoices.set(row, true);
450 transitionCount += transitionsToMaybeStates;
451 if (hasTransitionToTarget) {
452 ++transitionCount;
453 targetStateRequired = true;
454 }
455 if (hasTransitionToSink) {
456 ++transitionCount;
457 sinkStateRequired = true;
458 }
459 }
460 }
461 }
462 STORM_LOG_THROW(!stateIsDeadlock, storm::exceptions::InvalidArgumentException, "Merging goal states leads to deadlocks!");
463 ++stateCount;
464 }
465
466 // Treat the target and sink states (if these states will exist)
467 if (targetStateRequired) {
468 result.targetState = stateCount;
469 ++stateCount;
470 ++transitionCount;
471 storm::utility::vector::setVectorValues(result.oldToNewStateIndexMapping, targetStates, *result.targetState);
472 }
473
474 if (sinkStateRequired) {
475 result.sinkState = stateCount;
476 ++stateCount;
477 ++transitionCount;
478 storm::utility::vector::setVectorValues(result.oldToNewStateIndexMapping, sinkStates, *result.sinkState);
479 }
480
481 return std::make_pair(std::move(result), std::move(transitionCount));
482}
483
484template<typename ValueType>
485storm::storage::SparseMatrix<ValueType> GoalStateMerger<ValueType>::buildTransitionMatrix(storm::storage::BitVector const& maybeStates,
486 ReturnType const& resultData, uint64_t transitionCount) const {
487 storm::storage::SparseMatrix<ValueType> const& origMatrix = originalModel.getTransitionMatrix();
488
489 uint64_t rowCount = resultData.keptChoices.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
490 uint64_t maybeStateCount = maybeStates.getNumberOfSetBits();
491 uint64_t stateCount = maybeStateCount + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
492 storm::storage::SparseMatrixBuilder<ValueType> builder(rowCount, stateCount, transitionCount, true, !origMatrix.hasTrivialRowGrouping(),
493 origMatrix.hasTrivialRowGrouping() ? 0 : stateCount);
494
495 uint64_t currRow = 0;
496 for (uint64_t state : maybeStates) {
497 if (!origMatrix.hasTrivialRowGrouping()) {
498 builder.newRowGroup(currRow);
499 }
500 auto const& endOfRowGroup = origMatrix.getRowGroupIndices()[state + 1];
501 for (uint64_t row = resultData.keptChoices.getNextSetIndex(origMatrix.getRowGroupIndices()[state]); row < endOfRowGroup;
502 row = resultData.keptChoices.getNextSetIndex(row + 1)) {
503 std::optional<ValueType> targetValue, sinkValue;
504 for (auto const& entry : origMatrix.getRow(row)) {
505 uint64_t const& newColumn = resultData.oldToNewStateIndexMapping[entry.getColumn()];
506 if (newColumn < maybeStateCount) {
507 builder.addNextValue(currRow, newColumn, entry.getValue());
508 } else if (resultData.targetState && newColumn == resultData.targetState.value()) {
509 targetValue = targetValue.has_value() ? *targetValue + entry.getValue() : entry.getValue();
510 } else if (resultData.sinkState && newColumn == resultData.sinkState.value()) {
511 sinkValue = sinkValue.has_value() ? *sinkValue + entry.getValue() : entry.getValue();
512 } else {
513 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException,
514 "There is a transition originating from a maybestate that does not lead to a maybe-, target-, or sinkstate.");
515 }
516 }
517 if (targetValue) {
518 builder.addNextValue(currRow, *resultData.targetState, storm::utility::simplify(*targetValue));
519 }
520 if (sinkValue) {
521 builder.addNextValue(currRow, *resultData.sinkState, storm::utility::simplify(*sinkValue));
522 }
523 ++currRow;
524 }
525 }
526
527 // Add the selfloops at target and sink
528 if (resultData.targetState) {
529 if (!origMatrix.hasTrivialRowGrouping()) {
530 builder.newRowGroup(currRow);
531 }
532 builder.addNextValue(currRow, *resultData.targetState, storm::utility::one<ValueType>());
533 ++currRow;
534 }
535 if (resultData.sinkState) {
536 if (!origMatrix.hasTrivialRowGrouping()) {
537 builder.newRowGroup(currRow);
538 }
539 builder.addNextValue(currRow, *resultData.sinkState, storm::utility::one<ValueType>());
540 ++currRow;
541 }
542
543 return builder.build();
544}
545
546template<typename ValueType>
547storm::models::sparse::StateLabeling GoalStateMerger<ValueType>::buildStateLabeling(storm::storage::BitVector const& maybeStates,
548 storm::storage::BitVector const& targetStates,
549 storm::storage::BitVector const& sinkStates, ReturnType const& resultData,
550 std::optional<uint64_t> const representativeTargetState,
551 std::optional<uint64_t> const representativeSinkState) const {
552 uint64_t stateCount = maybeStates.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
553 storm::models::sparse::StateLabeling labeling(stateCount);
554
555 for (auto const& label : originalModel.getStateLabeling().getLabels()) {
556 storm::storage::BitVector const& oldStatesWithLabel = originalModel.getStates(label);
557 storm::storage::BitVector newStatesWithLabel = oldStatesWithLabel % maybeStates;
558 newStatesWithLabel.resize(stateCount, false);
559
560 // decide whether to mark the target/sink state with that label.
561 // There are two cases where we would do so:
562 // - either a representative target (or sink) state is given and contains label or
563 // - it is not given but one of the target (or sink) states has that label
564 // The init label is special: there we always assign the label if one of the targets/sinks is init.
565 bool const isInitialStatesLabel = label == "init";
566 bool const markTarget = (representativeTargetState && !isInitialStatesLabel) ? oldStatesWithLabel.get(representativeTargetState.value())
567 : !oldStatesWithLabel.isDisjointFrom(targetStates);
568 if (markTarget && resultData.targetState.has_value()) {
569 newStatesWithLabel.set(*resultData.targetState, true);
570 }
571 bool const markSink = (representativeSinkState && !isInitialStatesLabel) ? oldStatesWithLabel.get(representativeSinkState.value())
572 : !oldStatesWithLabel.isDisjointFrom(sinkStates);
573 if (markSink && resultData.sinkState.has_value()) {
574 newStatesWithLabel.set(*resultData.sinkState, true);
575 }
576 labeling.addLabel(label, std::move(newStatesWithLabel));
577 }
578
579 return labeling;
580}
581
582template<typename ValueType>
583std::unordered_map<std::string, models::sparse::StandardRewardModel<ValueType>> GoalStateMerger<ValueType>::buildRewardModels(
584 storm::storage::BitVector const& maybeStates, ReturnType const& resultData, std::vector<std::string> const& selectedRewardModels) const {
585 uint64_t maybeStateCount = maybeStates.getNumberOfSetBits();
586 uint64_t stateCount = maybeStateCount + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
587 uint64_t choiceCount = resultData.keptChoices.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
588
589 std::unordered_map<std::string, models::sparse::StandardRewardModel<ValueType>> rewardModels;
590 for (auto rewardModelName : selectedRewardModels) {
591 auto origRewardModel = originalModel.getRewardModel(rewardModelName);
592
593 std::optional<std::vector<ValueType>> stateRewards;
594 if (origRewardModel.hasStateRewards()) {
595 stateRewards = storm::utility::vector::filterVector(origRewardModel.getStateRewardVector(), maybeStates);
596 stateRewards->resize(stateCount, storm::utility::zero<ValueType>());
597 }
598
599 std::optional<std::vector<ValueType>> stateActionRewards;
600 if (origRewardModel.hasStateActionRewards()) {
601 stateActionRewards = storm::utility::vector::filterVector(origRewardModel.getStateActionRewardVector(), resultData.keptChoices);
602 stateActionRewards->resize(choiceCount, storm::utility::zero<ValueType>());
603 }
604
605 std::optional<storm::storage::SparseMatrix<ValueType>> transitionRewards;
606 if (origRewardModel.hasTransitionRewards()) {
607 storm::storage::SparseMatrixBuilder<ValueType> builder(choiceCount, stateCount, 0, true);
608 // The kept choices are visited in increasing order, which is exactly the order in which the rows of the new matrix are built.
609 uint64_t currRow = 0;
610 for (auto row : resultData.keptChoices) {
611 std::optional<ValueType> targetValue, sinkValue;
612 for (auto const& entry : origRewardModel.getTransitionRewardMatrix().getRow(row)) {
613 uint64_t const& newColumn = resultData.oldToNewStateIndexMapping[entry.getColumn()];
614 if (newColumn < maybeStateCount) {
615 builder.addNextValue(currRow, newColumn, entry.getValue());
616 } else if (resultData.targetState && newColumn == resultData.targetState.value()) {
617 targetValue = targetValue.has_value() ? *targetValue + entry.getValue() : entry.getValue();
618 } else if (resultData.sinkState && newColumn == resultData.sinkState.value()) {
619 sinkValue = sinkValue.has_value() ? *sinkValue + entry.getValue() : entry.getValue();
620 } else {
621 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException,
622 "There is a transition reward originating from a maybestate that does not lead to a maybe-, target-, or sinkstate.");
623 }
624 }
625 if (targetValue) {
626 builder.addNextValue(currRow, *resultData.targetState, storm::utility::simplify(*targetValue));
627 }
628 if (sinkValue) {
629 builder.addNextValue(currRow, *resultData.sinkState, storm::utility::simplify(*sinkValue));
630 }
631 ++currRow;
632 }
633 transitionRewards = builder.build();
634 }
635
636 rewardModels.emplace(rewardModelName, models::sparse::StandardRewardModel<ValueType>(std::move(stateRewards), std::move(stateActionRewards),
637 std::move(transitionRewards)));
638 }
639 return rewardModels;
640}
641
642template class GoalStateMerger<double>;
645
646} // namespace transformer
647} // namespace storm
storm::models::sparse::Dtmc< double > Dtmc
storm::models::sparse::Mdp< double > Mdp
RewardOperatorFormula & asRewardOperatorFormula()
Definition Formula.cpp:514
UntilFormula & asUntilFormula()
Definition Formula.cpp:339
BoundedUntilFormula & asBoundedUntilFormula()
Definition Formula.cpp:363
virtual bool isProbabilityOperatorFormula() const
Definition Formula.cpp:194
ProbabilityOperatorFormula & asProbabilityOperatorFormula()
Definition Formula.cpp:506
std::set< std::string > getReferencedRewardModels() const
Definition Formula.cpp:548
virtual bool isUntilFormula() const
Definition Formula.cpp:86
virtual bool isRewardOperatorFormula() const
Definition Formula.cpp:198
EventuallyFormula & asEventuallyFormula()
Definition Formula.cpp:371
virtual bool isEventuallyFormula() const
Definition Formula.cpp:102
CumulativeRewardFormula & asCumulativeRewardFormula()
Definition Formula.cpp:459
Bound const & getBound() const
storm::solver::OptimizationDirection const & getOptimalityType() const
std::string const & getRewardModelName() const
Retrieves the name of the reward model this property refers to (if any).
bool hasRewardModelName() const
Retrieves whether the reward model refers to a specific reward model.
Formula const & getSubformula() const
virtual std::unique_ptr< CheckResult > check(Environment const &env, CheckTask< storm::logic::Formula, SolutionType > const &checkTask)
Checks the provided formula.
virtual bool canHandle(CheckTask< storm::logic::Formula, SolutionType > const &checkTask) const override
virtual ModelType getType() const
Return the actual type of the model.
Definition ModelBase.cpp:7
bool isOfType(storm::models::ModelType const &modelType) const
Checks whether the model is of the given type.
Definition ModelBase.cpp:27
Base class for all sparse models.
Definition Model.h:34
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
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 isDisjointFrom(BitVector const &other) const
Checks whether none of the bits that are set in the current bit vector are also set in the given bit ...
const_iterator end() const
Returns an iterator pointing at the element past the back of the bit vector.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
void resize(uint64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
const_rows getRow(index_type row) const
Returns an object representing the given row.
bool hasTrivialRowGrouping() const
Retrieves whether the matrix has a trivial row grouping.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
GoalStateMerger(storm::models::sparse::Model< ValueType > const &model)
ReturnType mergeTargetAndSinkStates(storm::storage::BitVector const &maybeStates, storm::storage::BitVector const &targetStates, storm::storage::BitVector const &sinkStates, std::vector< std::string > const &selectedRewardModels=std::vector< std::string >(), std::optional< storm::storage::BitVector > const &choiceFilter=std::nullopt, std::optional< uint64_t > representativeTargetState=std::nullopt, std::optional< uint64_t > representativeSinkState=std::nullopt) const
Computes a submodel of the specified model that only considers the states given by maybeStates as wel...
std::optional< ReturnType > mergeForFormula(storm::logic::Formula const &formula, bool const dropUnreachableFromInit) const
Identifies the maybeStates, targetStates, sinkStates (and, if applicable, the reward model and choice...
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
bool isLowerBound(ComparisonType t)
bool constexpr minimize(OptimizationDirection d)
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > buildModelFromComponents(storm::models::ModelType modelType, storm::storage::sparse::ModelComponents< ValueType, RewardModelType > &&components)
Definition builder.cpp:20
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01(storm::models::sparse::DeterministicModel< T > const &model, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi i...
Definition graph.cpp:394
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Max(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:855
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:42
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:316
storm::storage::BitVector performProbGreater0A(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under any pos...
Definition graph.cpp:881
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:1019
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:674
storm::storage::BitVector performProb1(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &, storm::storage::BitVector const &psiStates, storm::storage::BitVector const &statesWithProbabilityGreater0)
Computes the set of states of the given model for which all paths lead to the given set of target sta...
Definition graph.cpp:377
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Min(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:1101
storm::storage::BitVector performProb1E(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability 1 of satisfying phi until psi under at least one po...
Definition graph.cpp:740
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< S > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:79
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
Definition vector.h:1069
ValueType simplify(ValueType value)
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
ComparisonType comparisonType
Definition Bound.h:17
storm::storage::SparseMatrix< ValueType > transitionMatrix
boost::optional< storm::storage::BitVector > markovianStates
std::optional< std::shared_ptr< storm::storage::sparse::ChoiceOrigins > > choiceOrigins
std::optional< storm::models::sparse::ChoiceLabeling > choiceLabeling
std::optional< storm::storage::sparse::Valuations > stateValuations
boost::optional< std::vector< ValueType > > exitRates