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