Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ProductModel.cpp
Go to the documentation of this file.
2
8
13
16
17namespace storm {
18namespace modelchecker {
19namespace helper {
20namespace rewardbounded {
21
22template<typename ValueType>
25 std::vector<Dimension<ValueType>> const& dimensions, std::vector<storm::storage::BitVector> const& objectiveDimensions,
26 EpochManager const& epochManager, std::vector<Epoch> const& originalModelSteps)
27 : dimensions(dimensions),
28 objectiveDimensions(objectiveDimensions),
29 epochManager(epochManager),
30 memoryStateManager(dimensions.size()),
31 prob1InitialStates(objectives.size(), boost::none) {
32 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
33 if (!dimensions[dim].memoryLabel) {
34 memoryStateManager.setDimensionWithoutMemory(dim);
35 }
36 }
37
38 storm::storage::MemoryStructure memory = computeMemoryStructure(model, objectives);
39 assert(memoryStateManager.getMemoryStateCount() == memory.getNumberOfStates());
40 std::vector<MemoryState> memoryStateMap = computeMemoryStateMap(memory);
41
43
44 setReachableProductStates(productBuilder, originalModelSteps, memoryStateMap);
45 product = productBuilder.build();
46
47 uint64_t numModelStates = productBuilder.getOriginalModel().getNumberOfStates();
48 MemoryState upperMemStateBound = memoryStateManager.getUpperMemoryStateBound();
49 uint64_t numMemoryStates = memoryStateManager.getMemoryStateCount();
50 uint64_t numProductStates = getProduct().getNumberOfStates();
51
52 // Compute a mappings from product states to model/memory states and back
53 modelMemoryToProductStateMap.resize(upperMemStateBound * numModelStates, std::numeric_limits<uint64_t>::max());
54 productToModelStateMap.resize(numProductStates, std::numeric_limits<uint64_t>::max());
55 productToMemoryStateMap.resize(numProductStates, std::numeric_limits<uint64_t>::max());
56 for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) {
57 for (uint64_t memoryStateIndex = 0; memoryStateIndex < numMemoryStates; ++memoryStateIndex) {
58 if (productBuilder.isStateReachable(modelState, memoryStateIndex)) {
59 uint64_t productState = productBuilder.getResultState(modelState, memoryStateIndex);
60 modelMemoryToProductStateMap[modelState * upperMemStateBound + memoryStateMap[memoryStateIndex]] = productState;
61 productToModelStateMap[productState] = modelState;
62 productToMemoryStateMap[productState] = memoryStateMap[memoryStateIndex];
63 }
64 }
65 }
66
67 // Map choice indices of the product to the state where it origins
68 choiceToStateMap.reserve(getProduct().getTransitionMatrix().getRowCount());
69 for (uint64_t productState = 0; productState < numProductStates; ++productState) {
70 uint64_t groupSize = getProduct().getTransitionMatrix().getRowGroupSize(productState);
71 for (uint64_t i = 0; i < groupSize; ++i) {
72 choiceToStateMap.push_back(productState);
73 }
74 }
75
76 // Compute the epoch steps for the product
77 steps.resize(getProduct().getTransitionMatrix().getRowCount(), 0);
78 for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) {
79 uint64_t numChoices = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupSize(modelState);
80 uint64_t firstChoice = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupIndices()[modelState];
81 for (uint64_t choiceOffset = 0; choiceOffset < numChoices; ++choiceOffset) {
82 Epoch const& step = originalModelSteps[firstChoice + choiceOffset];
83 if (step != 0) {
84 for (MemoryState const& memoryState : memoryStateMap) {
85 if (productStateExists(modelState, memoryState)) {
86 uint64_t productState = getProductState(modelState, memoryState);
87 uint64_t productChoice = getProduct().getTransitionMatrix().getRowGroupIndices()[productState] + choiceOffset;
88 assert(productChoice < getProduct().getTransitionMatrix().getRowGroupIndices()[productState + 1]);
89 steps[productChoice] = step;
90 }
91 }
92 }
93 }
94 }
95
96 // getProduct().writeDotToStream(std::cout);
97
98 computeReachableStatesInEpochClasses();
99}
100
101template<typename ValueType>
105
106 // Create a memory structure that remembers whether (sub)objectives are satisfied
108 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
109 if (!objectives[objIndex].formula->isProbabilityOperatorFormula()) {
110 continue;
111 }
112
113 std::vector<uint64_t> dimensionIndexMap;
114 for (auto globalDimensionIndex : objectiveDimensions[objIndex]) {
115 dimensionIndexMap.push_back(globalDimensionIndex);
116 }
117
118 // collect the memory states for this objective
119 std::vector<storm::storage::BitVector> objMemStates;
120 storm::storage::BitVector m(dimensionIndexMap.size(), false);
121 for (; !m.full(); m.increment()) {
122 objMemStates.push_back(~m);
123 }
124 objMemStates.push_back(~m);
125 assert(objMemStates.size() == 1ull << dimensionIndexMap.size());
126
127 // build objective memory
128 auto objMemoryBuilder = storm::storage::MemoryStructureBuilder<ValueType>(objMemStates.size(), model);
129
130 // Get the set of states that for all subobjectives satisfy either the left or the right subformula
131 storm::storage::BitVector constraintStates(model.getNumberOfStates(), true);
132 for (auto dim : objectiveDimensions[objIndex]) {
133 auto const& dimension = dimensions[dim];
134 STORM_LOG_ASSERT(dimension.formula->isBoundedUntilFormula(), "Unexpected Formula type");
135 constraintStates &=
136 (mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector() |
137 mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
138 }
139
140 // Build the transitions between the memory states
141 for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) {
142 auto const& memStateBV = objMemStates[memState];
143 for (uint64_t memStatePrime = 0; memStatePrime < objMemStates.size(); ++memStatePrime) {
144 auto const& memStatePrimeBV = objMemStates[memStatePrime];
145 if (memStatePrimeBV.isSubsetOf(memStateBV)) {
146 std::shared_ptr<storm::logic::Formula const> transitionFormula = storm::logic::Formula::getTrueFormula();
147 for (auto subObjIndex : memStateBV) {
148 std::shared_ptr<storm::logic::Formula const> subObjFormula =
149 dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer();
150 if (memStatePrimeBV.get(subObjIndex)) {
151 subObjFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not,
152 subObjFormula);
153 }
154 transitionFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(
155 storm::logic::BinaryBooleanStateFormula::OperatorType::And, transitionFormula, subObjFormula);
156 }
157
158 storm::storage::BitVector transitionStates = mc.check(*transitionFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
159 if (memStatePrimeBV.empty()) {
160 transitionStates |= ~constraintStates;
161 } else {
162 transitionStates &= constraintStates;
163 }
164 objMemoryBuilder.setTransition(memState, memStatePrime, transitionStates);
165
166 // Set the initial states
167 if (memStateBV.full()) {
168 storm::storage::BitVector initialTransitionStates = model.getInitialStates() & transitionStates;
169 // At this point we can check whether there is an initial state that already satisfies all subObjectives.
170 // Such a situation can not be reduced (easily) to an expected reward computation and thus requires special treatment
171 if (memStatePrimeBV.empty() && !initialTransitionStates.empty()) {
172 prob1InitialStates[objIndex] = initialTransitionStates;
173 }
174
175 for (auto initState : initialTransitionStates) {
176 objMemoryBuilder.setInitialMemoryState(initState, memStatePrime);
177 }
178 }
179 }
180 }
181 }
182
183 // Build the memory labels
184 for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) {
185 auto const& memStateBV = objMemStates[memState];
186 for (auto subObjIndex : memStateBV) {
187 objMemoryBuilder.setLabel(memState, dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get());
188 }
189 }
190 auto objMemory = objMemoryBuilder.build();
191 memory = memory.product(objMemory);
192 }
193 return memory;
194}
195
196template<typename ValueType>
197std::vector<typename ProductModel<ValueType>::MemoryState> ProductModel<ValueType>::computeMemoryStateMap(storm::storage::MemoryStructure const& memory) const {
198 // Compute a mapping between the different representations of memory states
199 std::vector<MemoryState> result;
200 result.reserve(memory.getNumberOfStates());
201 for (uint64_t memStateIndex = 0; memStateIndex < memory.getNumberOfStates(); ++memStateIndex) {
202 MemoryState memState = memoryStateManager.getInitialMemoryState();
203 std::set<std::string> stateLabels = memory.getStateLabeling().getLabelsOfState(memStateIndex);
204 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
205 if (dimensions[dim].memoryLabel) {
206 if (stateLabels.find(dimensions[dim].memoryLabel.get()) != stateLabels.end()) {
207 memoryStateManager.setRelevantDimension(memState, dim, true);
208 } else {
209 memoryStateManager.setRelevantDimension(memState, dim, false);
210 }
211 }
212 }
213 result.push_back(std::move(memState));
214 }
215 return result;
216}
217
218template<typename ValueType>
219void ProductModel<ValueType>::setReachableProductStates(storm::storage::SparseModelMemoryProduct<ValueType>& productBuilder,
220 std::vector<Epoch> const& originalModelSteps, std::vector<MemoryState> const& memoryStateMap) const {
221 std::vector<uint64_t> inverseMemoryStateMap(memoryStateManager.getUpperMemoryStateBound(), std::numeric_limits<uint64_t>::max());
222 for (uint64_t memStateIndex = 0; memStateIndex < memoryStateMap.size(); ++memStateIndex) {
223 inverseMemoryStateMap[memoryStateMap[memStateIndex]] = memStateIndex;
224 }
225
226 auto const& memory = productBuilder.getMemory();
227 auto const& model = productBuilder.getOriginalModel();
228 auto const& modelTransitions = model.getTransitionMatrix();
229
230 std::vector<storm::storage::BitVector> reachableProductStates(memoryStateManager.getUpperMemoryStateBound());
231 for (auto const& memState : memoryStateMap) {
232 reachableProductStates[memState] = storm::storage::BitVector(model.getNumberOfStates(), false);
233 }
234
235 // Initialize the reachable states with the initial states
236 // If the bound is not known (e.g., when computing quantiles) we don't know the initial epoch class. Hence, we consider all possibilities.
237 std::vector<EpochClass> initEpochClasses;
238 initEpochClasses.push_back(epochManager.getEpochClass(epochManager.getZeroEpoch()));
239 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
240 Dimension<ValueType> const& dimension = dimensions[dim];
241 if (dimension.boundType == DimensionBoundType::Unbounded) {
242 // For unbounded dimensions we are only interested in the bottom class.
243 for (auto& ec : initEpochClasses) {
244 epochManager.setDimensionOfEpochClass(ec, dim, true);
245 }
246 } else if (!dimension.maxValue) {
247 // If no max value is known, we have to consider all possibilities
248 std::vector<EpochClass> newEcs = initEpochClasses;
249 for (auto& ec : newEcs) {
250 epochManager.setDimensionOfEpochClass(ec, dim, true);
251 }
252 initEpochClasses.insert(initEpochClasses.end(), newEcs.begin(), newEcs.end());
253 }
254 }
255 for (auto const& initEpochClass : initEpochClasses) {
256 auto memStateIt = memory.getInitialMemoryStates().begin();
257 for (auto initState : model.getInitialStates()) {
258 uint64_t transformedMemoryState = transformMemoryState(memoryStateMap[*memStateIt], initEpochClass, memoryStateManager.getInitialMemoryState());
259 reachableProductStates[transformedMemoryState].set(initState, true);
260 ++memStateIt;
261 }
262 assert(memStateIt == memory.getInitialMemoryStates().end());
263 }
264
265 // Find the reachable epoch classes
266 std::set<Epoch> possibleSteps(originalModelSteps.begin(), originalModelSteps.end());
267 std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(
268 std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
269 collectReachableEpochClasses(reachableEpochClasses, possibleSteps);
270
271 // Iterate over all epoch classes starting from the initial one (i.e., no bottom dimension).
272 for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) {
273 auto const& epochClass = *epochClassIt;
274
275 // Find the remaining set of reachable states via DFS.
276 std::vector<std::pair<uint64_t, MemoryState>> dfsStack;
277 for (MemoryState const& memState : memoryStateMap) {
278 for (auto modelState : reachableProductStates[memState]) {
279 dfsStack.emplace_back(modelState, memState);
280 }
281 }
282
283 while (!dfsStack.empty()) {
284 uint64_t currentModelState = dfsStack.back().first;
285 MemoryState currentMemoryState = dfsStack.back().second;
286 uint64_t currentMemoryStateIndex = inverseMemoryStateMap[currentMemoryState];
287 dfsStack.pop_back();
288
289 for (uint64_t choice = modelTransitions.getRowGroupIndices()[currentModelState];
290 choice != modelTransitions.getRowGroupIndices()[currentModelState + 1]; ++choice) {
291 for (auto transitionIt = modelTransitions.getRow(choice).begin(); transitionIt < modelTransitions.getRow(choice).end(); ++transitionIt) {
292 MemoryState successorMemoryState =
293 memoryStateMap[memory.getSuccessorMemoryState(currentMemoryStateIndex, transitionIt - modelTransitions.begin())];
294 successorMemoryState = transformMemoryState(successorMemoryState, epochClass, currentMemoryState);
295 if (!reachableProductStates[successorMemoryState].get(transitionIt->getColumn())) {
296 reachableProductStates[successorMemoryState].set(transitionIt->getColumn(), true);
297 dfsStack.emplace_back(transitionIt->getColumn(), successorMemoryState);
298 }
299 }
300 }
301 }
302 }
303
304 for (uint64_t memStateIndex = 0; memStateIndex < memoryStateManager.getMemoryStateCount(); ++memStateIndex) {
305 for (auto modelState : reachableProductStates[memoryStateMap[memStateIndex]]) {
306 productBuilder.addReachableState(modelState, memStateIndex);
307 }
308 }
309}
310
311template<typename ValueType>
315
316template<typename ValueType>
317std::vector<typename ProductModel<ValueType>::Epoch> const& ProductModel<ValueType>::getSteps() const {
318 return steps;
319}
320
321template<typename ValueType>
322bool ProductModel<ValueType>::productStateExists(uint64_t const& modelState, MemoryState const& memoryState) const {
323 return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState] < getProduct().getNumberOfStates();
324}
325
326template<typename ValueType>
327uint64_t ProductModel<ValueType>::getProductState(uint64_t const& modelState, MemoryState const& memoryState) const {
328 STORM_LOG_ASSERT(productStateExists(modelState, memoryState), "Tried to obtain state (" << modelState << ", " << memoryStateManager.toString(memoryState)
329 << ") in the model-memory-product which does not exist");
330 return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState];
331}
332
333template<typename ValueType>
334uint64_t ProductModel<ValueType>::getInitialProductState(uint64_t const& initialModelState, storm::storage::BitVector const& initialModelStates,
335 EpochClass const& epochClass) const {
336 auto productInitStateIt = getProduct().getInitialStates().begin();
337 productInitStateIt += initialModelStates.getNumberOfSetBitsBeforeIndex(initialModelState);
338 STORM_LOG_ASSERT(getModelState(*productInitStateIt) == initialModelState, "Could not find the corresponding initial state in the product model.");
339 return transformProductState(*productInitStateIt, epochClass, memoryStateManager.getInitialMemoryState());
340}
341
342template<typename ValueType>
343uint64_t ProductModel<ValueType>::getModelState(uint64_t const& productState) const {
344 return productToModelStateMap[productState];
345}
346
347template<typename ValueType>
349 return productToMemoryStateMap[productState];
350}
351
352template<typename ValueType>
354 return memoryStateManager;
355}
356
357template<typename ValueType>
358uint64_t ProductModel<ValueType>::getProductStateFromChoice(uint64_t const& productChoice) const {
359 return choiceToStateMap[productChoice];
360}
361
362template<typename ValueType>
363std::vector<std::vector<ValueType>> ProductModel<ValueType>::computeObjectiveRewards(
364 EpochClass const& epochClass, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const {
365 std::vector<std::vector<ValueType>> objectiveRewards;
366 objectiveRewards.reserve(objectives.size());
367
368 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
369 auto const& formula = *objectives[objIndex].formula;
370 if (formula.isProbabilityOperatorFormula()) {
372 std::vector<uint64_t> dimensionIndexMap;
373 for (auto globalDimensionIndex : objectiveDimensions[objIndex]) {
374 dimensionIndexMap.push_back(globalDimensionIndex);
375 }
376
377 std::shared_ptr<storm::logic::Formula const> sinkStatesFormula;
378 for (auto dim : objectiveDimensions[objIndex]) {
379 auto memLabelFormula = std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dim].memoryLabel.get());
380 if (sinkStatesFormula) {
381 sinkStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or,
382 sinkStatesFormula, memLabelFormula);
383 } else {
384 sinkStatesFormula = memLabelFormula;
385 }
386 }
387 sinkStatesFormula =
388 std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, sinkStatesFormula);
389
390 std::vector<ValueType> objRew(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
391 storm::storage::BitVector relevantObjectives(objectiveDimensions[objIndex].getNumberOfSetBits());
392
393 while (!relevantObjectives.full()) {
394 relevantObjectives.increment();
395
396 // find out whether objective reward should be earned within this epoch class
397 bool collectRewardInEpoch = true;
398 for (auto subObjIndex : relevantObjectives) {
399 if (dimensions[dimensionIndexMap[subObjIndex]].boundType == DimensionBoundType::UpperBound &&
400 epochManager.isBottomDimensionEpochClass(epochClass, dimensionIndexMap[subObjIndex])) {
401 collectRewardInEpoch = false;
402 break;
403 }
404 }
405
406 if (collectRewardInEpoch) {
407 std::shared_ptr<storm::logic::Formula const> relevantStatesFormula;
408 std::shared_ptr<storm::logic::Formula const> goalStatesFormula = storm::logic::CloneVisitor().clone(*sinkStatesFormula);
409 for (uint64_t subObjIndex = 0; subObjIndex < dimensionIndexMap.size(); ++subObjIndex) {
410 std::shared_ptr<storm::logic::Formula> memLabelFormula =
411 std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get());
412 if (relevantObjectives.get(subObjIndex)) {
413 auto rightSubFormula =
414 dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer();
415 goalStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(
416 storm::logic::BinaryBooleanStateFormula::OperatorType::And, goalStatesFormula, rightSubFormula);
417 } else {
418 memLabelFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(
419 storm::logic::UnaryBooleanStateFormula::OperatorType::Not, memLabelFormula);
420 }
421 if (relevantStatesFormula) {
422 relevantStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(
423 storm::logic::BinaryBooleanStateFormula::OperatorType::And, relevantStatesFormula, memLabelFormula);
424 } else {
425 relevantStatesFormula = memLabelFormula;
426 }
427 }
428
429 storm::storage::BitVector relevantStates = mc.check(*relevantStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
430 storm::storage::BitVector relevantChoices = getProduct().getTransitionMatrix().getRowFilter(relevantStates);
431 storm::storage::BitVector goalStates = mc.check(*goalStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
432 for (auto choice : relevantChoices) {
433 objRew[choice] += getProduct().getTransitionMatrix().getConstrainedRowSum(choice, goalStates);
434 }
435 }
436 }
437
438 objectiveRewards.push_back(std::move(objRew));
439
440 } else if (formula.isRewardOperatorFormula()) {
441 auto const& rewModel = getProduct().getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
442 STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
443 "Reward model has transition rewards which is not expected.");
444 bool rewardCollectedInEpoch = true;
445 if (formula.getSubformula().isCumulativeRewardFormula()) {
446 for (auto dim : objectiveDimensions[objIndex]) {
447 if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
448 rewardCollectedInEpoch = false;
449 break;
450 }
451 }
452 } else {
453 STORM_LOG_THROW(formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException,
454 "Unexpected type of formula " << formula);
455 }
456 if (rewardCollectedInEpoch) {
457 objectiveRewards.push_back(rewModel.getTotalRewardVector(getProduct().getTransitionMatrix()));
458 } else {
459 objectiveRewards.emplace_back(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
460 }
461 } else {
462 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula);
463 }
464 }
465
466 return objectiveRewards;
467}
468
469template<typename ValueType>
471 STORM_LOG_ASSERT(inStates.find(epochClass) != inStates.end(), "Could not find InStates for the given epoch class");
472 return inStates.find(epochClass)->second;
473}
474
475template<typename ValueType>
477 std::set<Epoch> possibleSteps(steps.begin(), steps.end());
478 std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(
479 std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
480
481 collectReachableEpochClasses(reachableEpochClasses, possibleSteps);
482
483 for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) {
484 std::vector<EpochClass> predecessors;
485 for (auto predecessorIt = reachableEpochClasses.rbegin(); predecessorIt != epochClassIt; ++predecessorIt) {
486 if (epochManager.isPredecessorEpochClass(*predecessorIt, *epochClassIt)) {
487 predecessors.push_back(*predecessorIt);
488 }
489 }
490 computeReachableStates(*epochClassIt, predecessors);
491 }
492}
493
494template<typename ValueType>
495void ProductModel<ValueType>::collectReachableEpochClasses(
496 std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>>& reachableEpochClasses, std::set<Epoch> const& possibleSteps) const {
497 // Get the start epoch according to the given bounds.
498 // For dimensions for which no bound (aka. maxValue) is known, we will later overapproximate the set of reachable classes.
499 Epoch startEpoch = epochManager.getZeroEpoch();
500 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
501 if (dimensions[dim].maxValue) {
502 epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get());
503 } else {
504 epochManager.setBottomDimension(startEpoch, dim);
505 }
506 }
507
508 std::set<Epoch> seenEpochs({startEpoch});
509 std::vector<Epoch> dfsStack({startEpoch});
510
511 reachableEpochClasses.insert(epochManager.getEpochClass(startEpoch));
512
513 // Perform a DFS to find all the reachable epochs
514 while (!dfsStack.empty()) {
515 Epoch currentEpoch = dfsStack.back();
516 dfsStack.pop_back();
517 for (auto const& step : possibleSteps) {
518 Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step);
519 if (seenEpochs.insert(successorEpoch).second) {
520 reachableEpochClasses.insert(epochManager.getEpochClass(successorEpoch));
521 dfsStack.push_back(std::move(successorEpoch));
522 }
523 }
524 }
525
526 // Also treat dimensions without a priori bound. Unbounded dimensions need no further treatment as for these only the 'bottom' class is relevant.
527 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
528 if (dimensions[dim].boundType != DimensionBoundType::Unbounded && !dimensions[dim].maxValue) {
529 std::vector<EpochClass> newClasses;
530 for (auto const& c : reachableEpochClasses) {
531 auto newClass = c;
532 epochManager.setDimensionOfEpochClass(newClass, dim, false);
533 newClasses.push_back(newClass);
534 }
535 for (auto const& c : newClasses) {
536 reachableEpochClasses.insert(c);
537 }
538 }
539 }
540}
541
542template<typename ValueType>
543void ProductModel<ValueType>::computeReachableStates(EpochClass const& epochClass, std::vector<EpochClass> const& predecessors) {
544 storm::storage::BitVector bottomDimensions(epochManager.getDimensionCount(), false);
545 bool considerInitialStates = true;
546 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
547 if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
548 bottomDimensions.set(dim, true);
549 if (dimensions[dim].boundType != DimensionBoundType::Unbounded && dimensions[dim].maxValue) {
550 considerInitialStates = false;
551 }
552 }
553 }
554 storm::storage::BitVector nonBottomDimensions = ~bottomDimensions;
555
556 storm::storage::BitVector ecInStates(getProduct().getNumberOfStates(), false);
557 if (considerInitialStates) {
558 for (auto initState : getProduct().getInitialStates()) {
559 uint64_t transformedInitState = transformProductState(initState, epochClass, memoryStateManager.getInitialMemoryState());
560 ecInStates.set(transformedInitState, true);
561 }
562 }
563 for (auto const& predecessor : predecessors) {
564 storm::storage::BitVector positiveStepDimensions(epochManager.getDimensionCount(), false);
565 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
566 if (!epochManager.isBottomDimensionEpochClass(predecessor, dim) && bottomDimensions.get(dim)) {
567 positiveStepDimensions.set(dim, true);
568 }
569 }
570 STORM_LOG_ASSERT(reachableStates.find(predecessor) != reachableStates.end(), "Could not find reachable states of predecessor epoch class.");
571 storm::storage::BitVector predecessorStates = reachableStates.find(predecessor)->second;
572 for (auto predecessorState : predecessorStates) {
573 uint64_t predecessorMemoryState = getMemoryState(predecessorState);
574 for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState];
575 choice < getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState + 1]; ++choice) {
576 bool choiceLeadsToThisClass = false;
577 Epoch const& choiceStep = getSteps()[choice];
578 for (auto dim : positiveStepDimensions) {
579 if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) {
580 choiceLeadsToThisClass = true;
581 }
582 }
583
584 if (choiceLeadsToThisClass) {
585 for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) {
586 uint64_t successorState = transformProductState(transition.getColumn(), epochClass, predecessorMemoryState);
587
588 ecInStates.set(successorState, true);
589 }
590 }
591 }
592 }
593 }
594
595 // Find all states reachable from an InState via DFS.
596 storm::storage::BitVector ecReachableStates = ecInStates;
597 std::vector<uint64_t> dfsStack(ecReachableStates.begin(), ecReachableStates.end());
598
599 while (!dfsStack.empty()) {
600 uint64_t currentState = dfsStack.back();
601 uint64_t currentMemoryState = getMemoryState(currentState);
602 dfsStack.pop_back();
603
604 for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[currentState];
605 choice != getProduct().getTransitionMatrix().getRowGroupIndices()[currentState + 1]; ++choice) {
606 bool choiceLeadsOutsideOfEpoch = false;
607 Epoch const& choiceStep = getSteps()[choice];
608 for (auto dim : nonBottomDimensions) {
609 if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) {
610 choiceLeadsOutsideOfEpoch = true;
611 break;
612 }
613 }
614
615 for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) {
616 uint64_t successorState = transformProductState(transition.getColumn(), epochClass, currentMemoryState);
617 if (choiceLeadsOutsideOfEpoch) {
618 ecInStates.set(successorState, true);
619 }
620 if (!ecReachableStates.get(successorState)) {
621 ecReachableStates.set(successorState, true);
622 dfsStack.push_back(successorState);
623 }
624 }
625 }
626 }
627
628 reachableStates[epochClass] = std::move(ecReachableStates);
629
630 inStates[epochClass] = std::move(ecInStates);
631}
632
633template<typename ValueType>
635 MemoryState const& predecessorMemoryState) const {
636 MemoryState memoryStatePrime = memoryState;
637
638 for (auto const& objDimensions : objectiveDimensions) {
639 for (auto dim : objDimensions) {
640 auto const& dimension = dimensions[dim];
641 if (dimension.memoryLabel) {
642 bool dimUpperBounded = dimension.boundType == DimensionBoundType::UpperBound;
643 bool dimBottom = epochManager.isBottomDimensionEpochClass(epochClass, dim);
644 if (dimUpperBounded && dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) {
645 STORM_LOG_ASSERT(objDimensions == dimension.dependentDimensions, "Unexpected set of dependent dimensions");
646 memoryStateManager.setRelevantDimensions(memoryStatePrime, objDimensions, false);
647 break;
648 } else if (!dimUpperBounded && !dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) {
649 memoryStateManager.setRelevantDimensions(memoryStatePrime, dimension.dependentDimensions, true);
650 }
651 }
652 }
653 }
654
655 // std::cout << "Transformed memory state " << memoryStateManager.toString(memoryState) << " at epoch class " << epochClass << " with predecessor " <<
656 // memoryStateManager.toString(predecessorMemoryState) << " to " << memoryStateManager.toString(memoryStatePrime) << '\n';
657
658 return memoryStatePrime;
659}
660
661template<typename ValueType>
662uint64_t ProductModel<ValueType>::transformProductState(uint64_t const& productState, EpochClass const& epochClass,
663 MemoryState const& predecessorMemoryState) const {
664 return getProductState(getModelState(productState), transformMemoryState(getMemoryState(productState), epochClass, predecessorMemoryState));
665}
666
667template<typename ValueType>
668boost::optional<storm::storage::BitVector> const& ProductModel<ValueType>::getProb1InitialStates(uint64_t objectiveIndex) const {
669 return prob1InitialStates[objectiveIndex];
670}
671
672template class ProductModel<double>;
674} // namespace rewardbounded
675} // namespace helper
676} // namespace modelchecker
677} // namespace storm
std::shared_ptr< Formula > clone(Formula const &f) const
static std::shared_ptr< Formula const > getTrueFormula()
Definition Formula.cpp:205
virtual std::unique_ptr< CheckResult > check(Environment const &env, CheckTask< storm::logic::Formula, SolutionType > const &checkTask)
Checks the provided formula.
bool epochClassOrder(EpochClass const &epochClass1, EpochClass const &epochClass2) const
MemoryState transformMemoryState(MemoryState const &memoryState, EpochClass const &epochClass, MemoryState const &predecessorMemoryState) const
MemoryState getMemoryState(uint64_t const &productState) const
ProductModel(storm::models::sparse::Model< ValueType > const &model, std::vector< storm::modelchecker::multiobjective::Objective< ValueType > > const &objectives, std::vector< Dimension< ValueType > > const &dimensions, std::vector< storm::storage::BitVector > const &objectiveDimensions, EpochManager const &epochManager, std::vector< Epoch > const &originalModelSteps)
uint64_t getInitialProductState(uint64_t const &initialModelState, storm::storage::BitVector const &initialModelStates, EpochClass const &epochClass) const
uint64_t transformProductState(uint64_t const &productState, EpochClass const &epochClass, MemoryState const &predecessorMemoryState) const
std::vector< std::vector< ValueType > > computeObjectiveRewards(EpochClass const &epochClass, std::vector< storm::modelchecker::multiobjective::Objective< ValueType > > const &objectives) const
uint64_t getModelState(uint64_t const &productState) const
uint64_t getProductStateFromChoice(uint64_t const &productChoice) const
storm::models::sparse::Model< ValueType > const & getProduct() const
storm::storage::BitVector const & getInStates(EpochClass const &epochClass) const
boost::optional< storm::storage::BitVector > const & getProb1InitialStates(uint64_t objectiveIndex) const
returns the initial states (with respect to the original model) that already satisfy the given object...
bool productStateExists(uint64_t const &modelState, uint64_t const &memoryState) const
uint64_t getProductState(uint64_t const &modelState, uint64_t const &memoryState) const
MemoryStateManager const & getMemoryStateManager() const
Base class for all sparse models.
Definition Model.h:33
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:197
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:162
storm::storage::BitVector const & getInitialStates() const
Retrieves the initial states of the model.
Definition Model.cpp:177
std::set< std::string > getLabelsOfState(storm::storage::sparse::state_type state) const
Retrieves the set of labels attached to the given state.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
bool full() const
Retrieves whether all bits are set in this bit vector.
const_iterator end() const
Returns an iterator pointing at the element past the back of the bit vector.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
void increment()
Increments the (unsigned) number represented by this BitVector by one.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
uint_fast64_t getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const
Retrieves the number of bits set in this bit vector with an index strictly smaller than the given one...
static MemoryStructure buildTrivialMemoryStructure(storm::models::sparse::Model< ValueType, RewardModelType > const &model)
Builds a trivial memory structure for the given model (consisting of a single memory state)
This class represents a (deterministic) memory structure that can be used to encode certain events (s...
MemoryStructure product(MemoryStructure const &rhs) const
Builds the product of this memory structure and the given memory structure.
std::vector< uint_fast64_t > const & getInitialMemoryStates() const
uint_fast64_t getSuccessorMemoryState(uint_fast64_t const &currentMemoryState, uint_fast64_t const &modelTransitionIndex) const
storm::models::sparse::StateLabeling const & getStateLabeling() const
uint_fast64_t getNumberOfStates() const
This class builds the product of the given sparse model and the given memory structure.
storm::storage::MemoryStructure const & getMemory() const
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > build()
void addReachableState(uint64_t const &modelState, uint64_t const &memoryState)
storm::models::sparse::Model< ValueType, RewardModelType > const & getOriginalModel() const
bool isStateReachable(uint64_t const &modelState, uint64_t const &memoryState)
uint64_t const & getResultState(uint64_t const &modelState, uint64_t const &memoryState)
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
LabParser.cpp.
Definition cli.cpp:18