Storm 1.13.0.1
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>
102storm::storage::MemoryStructure ProductModel<ValueType>::computeMemoryStructure(
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 &= (mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula())
136 ->template asExplicitQualitativeCheckResult<ValueType>()
137 .getTruthValuesVector() |
138 mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())
139 ->template asExplicitQualitativeCheckResult<ValueType>()
140 .getTruthValuesVector());
141 }
142
143 // Build the transitions between the memory states
144 for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) {
145 auto const& memStateBV = objMemStates[memState];
146 for (uint64_t memStatePrime = 0; memStatePrime < objMemStates.size(); ++memStatePrime) {
147 auto const& memStatePrimeBV = objMemStates[memStatePrime];
148 if (memStatePrimeBV.isSubsetOf(memStateBV)) {
149 std::shared_ptr<storm::logic::Formula const> transitionFormula = storm::logic::Formula::getTrueFormula();
150 for (auto subObjIndex : memStateBV) {
151 std::shared_ptr<storm::logic::Formula const> subObjFormula =
152 dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer();
153 if (memStatePrimeBV.get(subObjIndex)) {
154 subObjFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not,
155 subObjFormula);
156 }
157 transitionFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(
158 storm::logic::BinaryBooleanStateFormula::OperatorType::And, transitionFormula, subObjFormula);
159 }
160
161 storm::storage::BitVector transitionStates =
162 mc.check(*transitionFormula)->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
163 if (memStatePrimeBV.empty()) {
164 transitionStates |= ~constraintStates;
165 } else {
166 transitionStates &= constraintStates;
167 }
168 objMemoryBuilder.setTransition(memState, memStatePrime, transitionStates);
169
170 // Set the initial states
171 if (memStateBV.full()) {
172 storm::storage::BitVector initialTransitionStates = model.getInitialStates() & transitionStates;
173 // At this point we can check whether there is an initial state that already satisfies all subObjectives.
174 // Such a situation can not be reduced (easily) to an expected reward computation and thus requires special treatment
175 if (memStatePrimeBV.empty() && !initialTransitionStates.empty()) {
176 prob1InitialStates[objIndex] = initialTransitionStates;
177 }
178
179 for (auto initState : initialTransitionStates) {
180 objMemoryBuilder.setInitialMemoryState(initState, memStatePrime);
181 }
182 }
183 }
184 }
185 }
186
187 // Build the memory labels
188 for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) {
189 auto const& memStateBV = objMemStates[memState];
190 for (auto subObjIndex : memStateBV) {
191 objMemoryBuilder.setLabel(memState, dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get());
192 }
193 }
194 auto objMemory = objMemoryBuilder.build();
195 memory = memory.product(objMemory);
196 }
197 return memory;
198}
199
200template<typename ValueType>
201std::vector<typename ProductModel<ValueType>::MemoryState> ProductModel<ValueType>::computeMemoryStateMap(storm::storage::MemoryStructure const& memory) const {
202 // Compute a mapping between the different representations of memory states
203 std::vector<MemoryState> result;
204 result.reserve(memory.getNumberOfStates());
205 for (uint64_t memStateIndex = 0; memStateIndex < memory.getNumberOfStates(); ++memStateIndex) {
206 MemoryState memState = memoryStateManager.getInitialMemoryState();
207 std::set<std::string> stateLabels = memory.getStateLabeling().getLabelsOfState(memStateIndex);
208 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
209 if (dimensions[dim].memoryLabel) {
210 if (stateLabels.find(dimensions[dim].memoryLabel.get()) != stateLabels.end()) {
211 memoryStateManager.setRelevantDimension(memState, dim, true);
212 } else {
213 memoryStateManager.setRelevantDimension(memState, dim, false);
214 }
215 }
216 }
217 result.push_back(std::move(memState));
218 }
219 return result;
220}
221
222template<typename ValueType>
223void ProductModel<ValueType>::setReachableProductStates(storm::storage::SparseModelMemoryProduct<ValueType>& productBuilder,
224 std::vector<Epoch> const& originalModelSteps, std::vector<MemoryState> const& memoryStateMap) const {
225 std::vector<uint64_t> inverseMemoryStateMap(memoryStateManager.getUpperMemoryStateBound(), std::numeric_limits<uint64_t>::max());
226 for (uint64_t memStateIndex = 0; memStateIndex < memoryStateMap.size(); ++memStateIndex) {
227 inverseMemoryStateMap[memoryStateMap[memStateIndex]] = memStateIndex;
228 }
229
230 auto const& memory = productBuilder.getMemory();
231 auto const& model = productBuilder.getOriginalModel();
232 auto const& modelTransitions = model.getTransitionMatrix();
233
234 std::vector<storm::storage::BitVector> reachableProductStates(memoryStateManager.getUpperMemoryStateBound());
235 for (auto const& memState : memoryStateMap) {
236 reachableProductStates[memState] = storm::storage::BitVector(model.getNumberOfStates(), false);
237 }
238
239 // Initialize the reachable states with the initial states
240 // If the bound is not known (e.g., when computing quantiles) we don't know the initial epoch class. Hence, we consider all possibilities.
241 std::vector<EpochClass> initEpochClasses;
242 initEpochClasses.push_back(epochManager.getEpochClass(epochManager.getZeroEpoch()));
243 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
244 Dimension<ValueType> const& dimension = dimensions[dim];
245 if (dimension.boundType == DimensionBoundType::Unbounded) {
246 // For unbounded dimensions we are only interested in the bottom class.
247 for (auto& ec : initEpochClasses) {
248 epochManager.setDimensionOfEpochClass(ec, dim, true);
249 }
250 } else if (!dimension.maxValue) {
251 // If no max value is known, we have to consider all possibilities
252 std::vector<EpochClass> newEcs = initEpochClasses;
253 for (auto& ec : newEcs) {
254 epochManager.setDimensionOfEpochClass(ec, dim, true);
255 }
256 initEpochClasses.insert(initEpochClasses.end(), newEcs.begin(), newEcs.end());
257 }
258 }
259 for (auto const& initEpochClass : initEpochClasses) {
260 auto memStateIt = memory.getInitialMemoryStates().begin();
261 for (auto initState : model.getInitialStates()) {
262 uint64_t transformedMemoryState = transformMemoryState(memoryStateMap[*memStateIt], initEpochClass, memoryStateManager.getInitialMemoryState());
263 reachableProductStates[transformedMemoryState].set(initState, true);
264 ++memStateIt;
265 }
266 assert(memStateIt == memory.getInitialMemoryStates().end());
267 }
268
269 // Find the reachable epoch classes
270 std::set<Epoch> possibleSteps(originalModelSteps.begin(), originalModelSteps.end());
271 std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(
272 std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
273 collectReachableEpochClasses(reachableEpochClasses, possibleSteps);
274
275 // Iterate over all epoch classes starting from the initial one (i.e., no bottom dimension).
276 for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) {
277 auto const& epochClass = *epochClassIt;
278
279 // Find the remaining set of reachable states via DFS.
280 std::vector<std::pair<uint64_t, MemoryState>> dfsStack;
281 for (MemoryState const& memState : memoryStateMap) {
282 for (auto modelState : reachableProductStates[memState]) {
283 dfsStack.emplace_back(modelState, memState);
284 }
285 }
286
287 while (!dfsStack.empty()) {
288 uint64_t currentModelState = dfsStack.back().first;
289 MemoryState currentMemoryState = dfsStack.back().second;
290 uint64_t currentMemoryStateIndex = inverseMemoryStateMap[currentMemoryState];
291 dfsStack.pop_back();
292
293 for (uint64_t choice = modelTransitions.getRowGroupIndices()[currentModelState];
294 choice != modelTransitions.getRowGroupIndices()[currentModelState + 1]; ++choice) {
295 for (auto transitionIt = modelTransitions.getRow(choice).begin(); transitionIt < modelTransitions.getRow(choice).end(); ++transitionIt) {
296 MemoryState successorMemoryState =
297 memoryStateMap[memory.getSuccessorMemoryState(currentMemoryStateIndex, transitionIt - modelTransitions.begin())];
298 successorMemoryState = transformMemoryState(successorMemoryState, epochClass, currentMemoryState);
299 if (!reachableProductStates[successorMemoryState].get(transitionIt->getColumn())) {
300 reachableProductStates[successorMemoryState].set(transitionIt->getColumn(), true);
301 dfsStack.emplace_back(transitionIt->getColumn(), successorMemoryState);
302 }
303 }
304 }
305 }
306 }
307
308 for (uint64_t memStateIndex = 0; memStateIndex < memoryStateManager.getMemoryStateCount(); ++memStateIndex) {
309 for (auto modelState : reachableProductStates[memoryStateMap[memStateIndex]]) {
310 productBuilder.addReachableState(modelState, memStateIndex);
311 }
312 }
313}
314
315template<typename ValueType>
319
320template<typename ValueType>
321std::vector<typename ProductModel<ValueType>::Epoch> const& ProductModel<ValueType>::getSteps() const {
322 return steps;
323}
324
325template<typename ValueType>
326bool ProductModel<ValueType>::productStateExists(uint64_t const& modelState, MemoryState const& memoryState) const {
327 return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState] < getProduct().getNumberOfStates();
328}
329
330template<typename ValueType>
331uint64_t ProductModel<ValueType>::getProductState(uint64_t const& modelState, MemoryState const& memoryState) const {
332 STORM_LOG_ASSERT(productStateExists(modelState, memoryState), "Tried to obtain state (" << modelState << ", " << memoryStateManager.toString(memoryState)
333 << ") in the model-memory-product which does not exist");
334 return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState];
335}
336
337template<typename ValueType>
338uint64_t ProductModel<ValueType>::getInitialProductState(uint64_t const& initialModelState, storm::storage::BitVector const& initialModelStates,
339 EpochClass const& epochClass) const {
340 auto productInitStateIt = getProduct().getInitialStates().begin();
341 productInitStateIt += initialModelStates.getNumberOfSetBitsBeforeIndex(initialModelState);
342 STORM_LOG_ASSERT(getModelState(*productInitStateIt) == initialModelState, "Could not find the corresponding initial state in the product model.");
343 return transformProductState(*productInitStateIt, epochClass, memoryStateManager.getInitialMemoryState());
344}
345
346template<typename ValueType>
347uint64_t ProductModel<ValueType>::getModelState(uint64_t const& productState) const {
348 return productToModelStateMap[productState];
349}
350
351template<typename ValueType>
353 return productToMemoryStateMap[productState];
354}
355
356template<typename ValueType>
358 return memoryStateManager;
359}
360
361template<typename ValueType>
362uint64_t ProductModel<ValueType>::getProductStateFromChoice(uint64_t const& productChoice) const {
363 return choiceToStateMap[productChoice];
364}
365
366template<typename ValueType>
367std::vector<std::vector<ValueType>> ProductModel<ValueType>::computeObjectiveRewards(
368 EpochClass const& epochClass, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const {
369 std::vector<std::vector<ValueType>> objectiveRewards;
370 objectiveRewards.reserve(objectives.size());
371
372 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
373 auto const& formula = *objectives[objIndex].formula;
374 if (formula.isProbabilityOperatorFormula()) {
376 std::vector<uint64_t> dimensionIndexMap;
377 for (auto globalDimensionIndex : objectiveDimensions[objIndex]) {
378 dimensionIndexMap.push_back(globalDimensionIndex);
379 }
380
381 std::shared_ptr<storm::logic::Formula const> sinkStatesFormula;
382 for (auto dim : objectiveDimensions[objIndex]) {
383 auto memLabelFormula = std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dim].memoryLabel.get());
384 if (sinkStatesFormula) {
385 sinkStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or,
386 sinkStatesFormula, memLabelFormula);
387 } else {
388 sinkStatesFormula = memLabelFormula;
389 }
390 }
391 sinkStatesFormula =
392 std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, sinkStatesFormula);
393
394 std::vector<ValueType> objRew(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
395 storm::storage::BitVector relevantObjectives(objectiveDimensions[objIndex].getNumberOfSetBits());
396
397 while (!relevantObjectives.full()) {
398 relevantObjectives.increment();
399
400 // find out whether objective reward should be earned within this epoch class
401 bool collectRewardInEpoch = true;
402 for (auto subObjIndex : relevantObjectives) {
403 if (dimensions[dimensionIndexMap[subObjIndex]].boundType == DimensionBoundType::UpperBound &&
404 epochManager.isBottomDimensionEpochClass(epochClass, dimensionIndexMap[subObjIndex])) {
405 collectRewardInEpoch = false;
406 break;
407 }
408 }
409
410 if (collectRewardInEpoch) {
411 std::shared_ptr<storm::logic::Formula const> relevantStatesFormula;
412 std::shared_ptr<storm::logic::Formula const> goalStatesFormula = storm::logic::CloneVisitor().clone(*sinkStatesFormula);
413 for (uint64_t subObjIndex = 0; subObjIndex < dimensionIndexMap.size(); ++subObjIndex) {
414 std::shared_ptr<storm::logic::Formula> memLabelFormula =
415 std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get());
416 if (relevantObjectives.get(subObjIndex)) {
417 auto rightSubFormula =
418 dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer();
419 goalStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(
420 storm::logic::BinaryBooleanStateFormula::OperatorType::And, goalStatesFormula, rightSubFormula);
421 } else {
422 memLabelFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(
423 storm::logic::UnaryBooleanStateFormula::OperatorType::Not, memLabelFormula);
424 }
425 if (relevantStatesFormula) {
426 relevantStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(
427 storm::logic::BinaryBooleanStateFormula::OperatorType::And, relevantStatesFormula, memLabelFormula);
428 } else {
429 relevantStatesFormula = memLabelFormula;
430 }
431 }
432
433 storm::storage::BitVector relevantStates =
434 mc.check(*relevantStatesFormula)->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
435 storm::storage::BitVector relevantChoices = getProduct().getTransitionMatrix().getRowFilter(relevantStates);
436 storm::storage::BitVector goalStates =
437 mc.check(*goalStatesFormula)->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
438 for (auto choice : relevantChoices) {
439 objRew[choice] += getProduct().getTransitionMatrix().getConstrainedRowSum(choice, goalStates);
440 }
441 }
442 }
443
444 objectiveRewards.push_back(std::move(objRew));
445
446 } else if (formula.isRewardOperatorFormula()) {
447 auto const& rewModel = getProduct().getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
448 STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
449 "Reward model has transition rewards which is not expected.");
450 bool rewardCollectedInEpoch = true;
451 if (formula.getSubformula().isCumulativeRewardFormula()) {
452 for (auto dim : objectiveDimensions[objIndex]) {
453 if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
454 rewardCollectedInEpoch = false;
455 break;
456 }
457 }
458 } else {
459 STORM_LOG_THROW(formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException,
460 "Unexpected type of formula " << formula);
461 }
462 if (rewardCollectedInEpoch) {
463 objectiveRewards.push_back(rewModel.getTotalRewardVector(getProduct().getTransitionMatrix()));
464 } else {
465 objectiveRewards.emplace_back(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
466 }
467 } else {
468 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula);
469 }
470 }
471
472 return objectiveRewards;
473}
474
475template<typename ValueType>
477 STORM_LOG_ASSERT(inStates.find(epochClass) != inStates.end(), "Could not find InStates for the given epoch class");
478 return inStates.find(epochClass)->second;
479}
480
481template<typename ValueType>
482void ProductModel<ValueType>::computeReachableStatesInEpochClasses() {
483 std::set<Epoch> possibleSteps(steps.begin(), steps.end());
484 std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(
485 std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
486
487 collectReachableEpochClasses(reachableEpochClasses, possibleSteps);
488
489 for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) {
490 std::vector<EpochClass> predecessors;
491 for (auto predecessorIt = reachableEpochClasses.rbegin(); predecessorIt != epochClassIt; ++predecessorIt) {
492 if (epochManager.isPredecessorEpochClass(*predecessorIt, *epochClassIt)) {
493 predecessors.push_back(*predecessorIt);
494 }
495 }
496 computeReachableStates(*epochClassIt, predecessors);
497 }
498}
499
500template<typename ValueType>
501void ProductModel<ValueType>::collectReachableEpochClasses(
502 std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>>& reachableEpochClasses, std::set<Epoch> const& possibleSteps) const {
503 // Get the start epoch according to the given bounds.
504 // For dimensions for which no bound (aka. maxValue) is known, we will later overapproximate the set of reachable classes.
505 Epoch startEpoch = epochManager.getZeroEpoch();
506 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
507 if (dimensions[dim].maxValue) {
508 epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get());
509 } else {
510 epochManager.setBottomDimension(startEpoch, dim);
511 }
512 }
513
514 std::set<Epoch> seenEpochs({startEpoch});
515 std::vector<Epoch> dfsStack({startEpoch});
516
517 reachableEpochClasses.insert(epochManager.getEpochClass(startEpoch));
518
519 // Perform a DFS to find all the reachable epochs
520 while (!dfsStack.empty()) {
521 Epoch currentEpoch = dfsStack.back();
522 dfsStack.pop_back();
523 for (auto const& step : possibleSteps) {
524 Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step);
525 if (seenEpochs.insert(successorEpoch).second) {
526 reachableEpochClasses.insert(epochManager.getEpochClass(successorEpoch));
527 dfsStack.push_back(std::move(successorEpoch));
528 }
529 }
530 }
531
532 // Also treat dimensions without a priori bound. Unbounded dimensions need no further treatment as for these only the 'bottom' class is relevant.
533 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
534 if (dimensions[dim].boundType != DimensionBoundType::Unbounded && !dimensions[dim].maxValue) {
535 std::vector<EpochClass> newClasses;
536 for (auto const& c : reachableEpochClasses) {
537 auto newClass = c;
538 epochManager.setDimensionOfEpochClass(newClass, dim, false);
539 newClasses.push_back(newClass);
540 }
541 for (auto const& c : newClasses) {
542 reachableEpochClasses.insert(c);
543 }
544 }
545 }
546}
547
548template<typename ValueType>
549void ProductModel<ValueType>::computeReachableStates(EpochClass const& epochClass, std::vector<EpochClass> const& predecessors) {
550 storm::storage::BitVector bottomDimensions(epochManager.getDimensionCount(), false);
551 bool considerInitialStates = true;
552 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
553 if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
554 bottomDimensions.set(dim, true);
555 if (dimensions[dim].boundType != DimensionBoundType::Unbounded && dimensions[dim].maxValue) {
556 considerInitialStates = false;
557 }
558 }
559 }
560 storm::storage::BitVector nonBottomDimensions = ~bottomDimensions;
561
562 storm::storage::BitVector ecInStates(getProduct().getNumberOfStates(), false);
563 if (considerInitialStates) {
564 for (auto initState : getProduct().getInitialStates()) {
565 uint64_t transformedInitState = transformProductState(initState, epochClass, memoryStateManager.getInitialMemoryState());
566 ecInStates.set(transformedInitState, true);
567 }
568 }
569 for (auto const& predecessor : predecessors) {
570 storm::storage::BitVector positiveStepDimensions(epochManager.getDimensionCount(), false);
571 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
572 if (!epochManager.isBottomDimensionEpochClass(predecessor, dim) && bottomDimensions.get(dim)) {
573 positiveStepDimensions.set(dim, true);
574 }
575 }
576 STORM_LOG_ASSERT(reachableStates.find(predecessor) != reachableStates.end(), "Could not find reachable states of predecessor epoch class.");
577 storm::storage::BitVector predecessorStates = reachableStates.find(predecessor)->second;
578 for (auto predecessorState : predecessorStates) {
579 uint64_t predecessorMemoryState = getMemoryState(predecessorState);
580 for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState];
581 choice < getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState + 1]; ++choice) {
582 bool choiceLeadsToThisClass = false;
583 Epoch const& choiceStep = getSteps()[choice];
584 for (auto dim : positiveStepDimensions) {
585 if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) {
586 choiceLeadsToThisClass = true;
587 }
588 }
589
590 if (choiceLeadsToThisClass) {
591 for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) {
592 uint64_t successorState = transformProductState(transition.getColumn(), epochClass, predecessorMemoryState);
593
594 ecInStates.set(successorState, true);
595 }
596 }
597 }
598 }
599 }
600
601 // Find all states reachable from an InState via DFS.
602 storm::storage::BitVector ecReachableStates = ecInStates;
603 std::vector<uint64_t> dfsStack(ecReachableStates.begin(), ecReachableStates.end());
604
605 while (!dfsStack.empty()) {
606 uint64_t currentState = dfsStack.back();
607 uint64_t currentMemoryState = getMemoryState(currentState);
608 dfsStack.pop_back();
609
610 for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[currentState];
611 choice != getProduct().getTransitionMatrix().getRowGroupIndices()[currentState + 1]; ++choice) {
612 bool choiceLeadsOutsideOfEpoch = false;
613 Epoch const& choiceStep = getSteps()[choice];
614 for (auto dim : nonBottomDimensions) {
615 if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) {
616 choiceLeadsOutsideOfEpoch = true;
617 break;
618 }
619 }
620
621 for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) {
622 uint64_t successorState = transformProductState(transition.getColumn(), epochClass, currentMemoryState);
623 if (choiceLeadsOutsideOfEpoch) {
624 ecInStates.set(successorState, true);
625 }
626 if (!ecReachableStates.get(successorState)) {
627 ecReachableStates.set(successorState, true);
628 dfsStack.push_back(successorState);
629 }
630 }
631 }
632 }
633
634 reachableStates[epochClass] = std::move(ecReachableStates);
635
636 inStates[epochClass] = std::move(ecInStates);
637}
638
639template<typename ValueType>
641 MemoryState const& predecessorMemoryState) const {
642 MemoryState memoryStatePrime = memoryState;
643
644 for (auto const& objDimensions : objectiveDimensions) {
645 for (auto dim : objDimensions) {
646 auto const& dimension = dimensions[dim];
647 if (dimension.memoryLabel) {
648 bool dimUpperBounded = dimension.boundType == DimensionBoundType::UpperBound;
649 bool dimBottom = epochManager.isBottomDimensionEpochClass(epochClass, dim);
650 if (dimUpperBounded && dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) {
651 STORM_LOG_ASSERT(objDimensions == dimension.dependentDimensions, "Unexpected set of dependent dimensions");
652 memoryStateManager.setRelevantDimensions(memoryStatePrime, objDimensions, false);
653 break;
654 } else if (!dimUpperBounded && !dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) {
655 memoryStateManager.setRelevantDimensions(memoryStatePrime, dimension.dependentDimensions, true);
656 }
657 }
658 }
659 }
660
661 // std::cout << "Transformed memory state " << memoryStateManager.toString(memoryState) << " at epoch class " << epochClass << " with predecessor " <<
662 // memoryStateManager.toString(predecessorMemoryState) << " to " << memoryStateManager.toString(memoryStatePrime) << '\n';
663
664 return memoryStatePrime;
665}
666
667template<typename ValueType>
668uint64_t ProductModel<ValueType>::transformProductState(uint64_t const& productState, EpochClass const& epochClass,
669 MemoryState const& predecessorMemoryState) const {
670 return getProductState(getModelState(productState), transformMemoryState(getMemoryState(productState), epochClass, predecessorMemoryState));
671}
672
673template<typename ValueType>
674boost::optional<storm::storage::BitVector> const& ProductModel<ValueType>::getProb1InitialStates(uint64_t objectiveIndex) const {
675 return prob1InitialStates[objectiveIndex];
676}
677
678template class ProductModel<double>;
680} // namespace rewardbounded
681} // namespace helper
682} // namespace modelchecker
683} // namespace storm
std::shared_ptr< Formula > clone(Formula const &f) const
static std::shared_ptr< Formula const > getTrueFormula()
Definition Formula.cpp:213
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:30
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:198
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:163
storm::storage::BitVector const & getInitialStates() const
Retrieves the initial states of the model.
Definition Model.cpp:178
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:16
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 set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
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.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
uint64_t getNumberOfSetBitsBeforeIndex(uint64_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(bool preserveModelType=false)
Invokes the building of the product under the specified scheduler (if given).
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
ValueType zero()
Definition constants.cpp:24