Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MultiDimensionalRewardUnfolding.cpp
Go to the documentation of this file.
2
3#include <functional>
4#include <set>
5#include <string>
6
9
18
20
25
26namespace storm {
27namespace modelchecker {
28namespace helper {
29namespace rewardbounded {
30
31template<typename ValueType, bool SingleObjectiveMode>
38template<typename ValueType, bool SingleObjectiveMode>
40 storm::models::sparse::Model<ValueType> const& model, std::shared_ptr<storm::logic::OperatorFormula const> objectiveFormula,
41 std::set<storm::expressions::Variable> const& infinityBoundVariables)
42 : model(model) {
43 STORM_LOG_TRACE("initializing multi-dimensional reward unfolding for formula " << *objectiveFormula << " and " << infinityBoundVariables.size()
44 << " bound variables should approach infinity.");
45
46 if (objectiveFormula->isProbabilityOperatorFormula()) {
47 if (objectiveFormula->getSubformula().isMultiObjectiveFormula()) {
48 for (auto const& subFormula : objectiveFormula->getSubformula().asMultiObjectiveFormula().getSubformulas()) {
49 STORM_LOG_THROW(subFormula->isBoundedUntilFormula(), storm::exceptions::InvalidPropertyException,
50 "Formula " << objectiveFormula << " is not supported. Invalid subformula " << *subFormula << ".");
51 }
52 } else {
53 STORM_LOG_THROW(objectiveFormula->getSubformula().isBoundedUntilFormula(), storm::exceptions::InvalidPropertyException,
54 "Formula " << objectiveFormula << " is not supported. Invalid subformula " << objectiveFormula->getSubformula() << ".");
55 }
56 } else {
57 STORM_LOG_THROW(objectiveFormula->isRewardOperatorFormula() && objectiveFormula->getSubformula().isCumulativeRewardFormula(),
58 storm::exceptions::InvalidPropertyException, "Formula " << objectiveFormula << " is not supported.");
59 }
60
61 // Build an objective from the formula.
63 objective.formula = objectiveFormula;
64 objective.originalFormula = objective.formula;
65 objective.considersComplementaryEvent = false;
66 objectives.push_back(std::move(objective));
68 initialize(infinityBoundVariables);
69}
70
71template<typename ValueType, bool SingleObjectiveMode>
72void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initialize(std::set<storm::expressions::Variable> const& infinityBoundVariables) {
73 STORM_LOG_ASSERT(!SingleObjectiveMode || (this->objectives.size() == 1), "Enabled single objective mode but there are multiple objectives.");
74 std::vector<Epoch> epochSteps;
75 initializeObjectives(epochSteps, infinityBoundVariables);
76 initializeMemoryProduct(epochSteps);
78 // collect which epoch steps are possible
79 possibleEpochSteps.clear();
80 for (auto const& step : epochSteps) {
81 possibleEpochSteps.insert(step);
82 }
83}
85template<typename ValueType, bool SingleObjectiveMode>
86void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initializeObjectives(
87 std::vector<Epoch>& epochSteps, std::set<storm::expressions::Variable> const& infinityBoundVariables) {
88 std::vector<std::vector<uint64_t>> dimensionWiseEpochSteps;
89 // collect the time-bounded subobjectives
90 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
91 auto const& formula = *this->objectives[objIndex].formula;
92 if (formula.isProbabilityOperatorFormula()) {
93 STORM_LOG_THROW(formula.getSubformula().isBoundedUntilFormula(), storm::exceptions::NotSupportedException,
94 "Unexpected type of subformula for formula " << formula << ".");
95 auto const& subformula = formula.getSubformula().asBoundedUntilFormula();
96 for (uint64_t dim = 0; dim < subformula.getDimension(); ++dim) {
97 Dimension<ValueType> dimension;
98 dimension.formula = subformula.restrictToDimension(dim);
99 dimension.objectiveIndex = objIndex;
100 std::string memLabel = "dim" + std::to_string(dimensions.size()) + "_maybe";
101 while (model.getStateLabeling().containsLabel(memLabel)) {
102 memLabel = "_" + memLabel;
103 }
104 dimension.memoryLabel = memLabel;
105 // for simplicity we do not allow interval formulas.
106 STORM_LOG_THROW(!subformula.hasLowerBound(dim) || !subformula.hasUpperBound(dim), storm::exceptions::NotSupportedException,
107 "Bounded until formulas are only supported by this method if they consider either an upper bound or a lower bound. Got "
108 << subformula << " instead.");
109 // lower bounded until formulas with non-trivial left hand side are excluded as this would require some additional effort (in particular the
110 // ProductModel::transformMemoryState method).
112 subformula.hasUpperBound(dim) || subformula.getLeftSubformula(dim).isTrueFormula(), storm::exceptions::NotSupportedException,
113 "Lower bounded until formulas are only supported by this method if the left subformula is 'true'. Got " << subformula << " instead.");
114
115 // Treat formulas that aren't acutally bounded differently
116 bool formulaUnbounded =
117 (!subformula.hasLowerBound(dim) && !subformula.hasUpperBound(dim)) ||
118 (subformula.hasLowerBound(dim) && !subformula.isLowerBoundStrict(dim) && !subformula.getLowerBound(dim).containsVariables() &&
119 storm::utility::isZero(subformula.getLowerBound(dim).evaluateAsRational())) ||
120 (subformula.hasUpperBound(dim) && subformula.getUpperBound(dim).isVariable() &&
121 infinityBoundVariables.count(subformula.getUpperBound(dim).getBaseExpression().asVariableExpression().getVariable()) > 0);
122 if (formulaUnbounded) {
123 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 0));
126 } else {
127 if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) {
128 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 1));
130 } else {
131 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).isRewardBound(), "Unexpected type of time bound.");
132 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).hasRewardModelName() || this->model.hasUniqueRewardModel(),
133 "Model has several reward models, but no reward model has been specified in the formula.");
134 std::string const& rewardName = subformula.getTimeBoundReference(dim).hasRewardModelName()
135 ? subformula.getTimeBoundReference(dim).getRewardName()
136 : this->model.getUniqueRewardModelName();
137 STORM_LOG_THROW(this->model.hasRewardModel(rewardName), storm::exceptions::IllegalArgumentException,
138 "No reward model with name '" << rewardName << "' found.");
139 auto const& rewardModel = this->model.getRewardModel(rewardName);
140 STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
141 "Transition rewards are currently not supported as reward bounds.");
142 std::vector<ValueType> actionRewards = rewardModel.getTotalRewardVector(this->model.getTransitionMatrix());
143 auto discretizedRewardsAndFactor = storm::utility::vector::toIntegralVector<ValueType, uint64_t>(actionRewards);
144 dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first));
145 dimension.scalingFactor = std::move(discretizedRewardsAndFactor.second);
146 }
147 if (subformula.hasLowerBound(dim)) {
148 if (subformula.getLowerBound(dim).isVariable() &&
149 infinityBoundVariables.count(subformula.getLowerBound(dim).getBaseExpression().asVariableExpression().getVariable()) > 0) {
151 } else {
153 }
154 } else {
156 }
157 }
158 dimensions.emplace_back(std::move(dimension));
159 }
160 } else if (formula.isRewardOperatorFormula() && formula.getSubformula().isCumulativeRewardFormula()) {
161 auto const& subformula = formula.getSubformula().asCumulativeRewardFormula();
162 for (uint64_t dim = 0; dim < subformula.getDimension(); ++dim) {
163 Dimension<ValueType> dimension;
164 dimension.formula = subformula.restrictToDimension(dim);
166 !dimension.formula->asCumulativeRewardFormula().getBound().isVariable() ||
167 infinityBoundVariables.count(
168 dimension.formula->asCumulativeRewardFormula().getBound().getBaseExpression().asVariableExpression().getVariable()) == 0,
169 storm::exceptions::NotSupportedException, "Letting cumulative reward bounds approach infinite is not supported.");
170 dimension.objectiveIndex = objIndex;
172 if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) {
173 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 1));
175 } else {
176 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).isRewardBound(), "Unexpected type of time bound.");
177 std::string const& rewardName = subformula.getTimeBoundReference(dim).getRewardName();
178 STORM_LOG_THROW(this->model.hasRewardModel(rewardName), storm::exceptions::IllegalArgumentException,
179 "No reward model with name '" << rewardName << "' found.");
180 auto const& rewardModel = this->model.getRewardModel(rewardName);
181 STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
182 "Transition rewards are currently not supported as reward bounds.");
183 std::vector<ValueType> actionRewards = rewardModel.getTotalRewardVector(this->model.getTransitionMatrix());
184 auto discretizedRewardsAndFactor = storm::utility::vector::toIntegralVector<ValueType, uint64_t>(actionRewards);
185 dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first));
186 dimension.scalingFactor = std::move(discretizedRewardsAndFactor.second);
187 }
188 dimensions.emplace_back(std::move(dimension));
189 }
190 }
191 }
192
193 // Compute a mapping for each objective to the set of dimensions it considers
194 // Also store for each dimension dim, which dimensions should be unsatisfiable whenever the bound of dim is violated
195 uint64_t dim = 0;
196 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
197 storm::storage::BitVector objDimensions(dimensions.size(), false);
198 uint64_t objDimensionCount = 0;
199 bool objDimensionsCanBeSatisfiedIndividually = false;
200 if (objectives[objIndex].formula->isProbabilityOperatorFormula() && objectives[objIndex].formula->getSubformula().isBoundedUntilFormula()) {
201 objDimensionCount = objectives[objIndex].formula->getSubformula().asBoundedUntilFormula().getDimension();
202 objDimensionsCanBeSatisfiedIndividually = objectives[objIndex].formula->getSubformula().asBoundedUntilFormula().hasMultiDimensionalSubformulas();
203 } else if (objectives[objIndex].formula->isRewardOperatorFormula() && objectives[objIndex].formula->getSubformula().isCumulativeRewardFormula()) {
204 objDimensionCount = objectives[objIndex].formula->getSubformula().asCumulativeRewardFormula().getDimension();
205 }
206 for (uint64_t currDim = dim; currDim < dim + objDimensionCount; ++currDim) {
207 objDimensions.set(currDim);
208 }
209 for (uint64_t currDim = dim; currDim < dim + objDimensionCount; ++currDim) {
210 if (!objDimensionsCanBeSatisfiedIndividually || dimensions[currDim].boundType == DimensionBoundType::UpperBound) {
211 dimensions[currDim].dependentDimensions = objDimensions;
212 } else {
213 dimensions[currDim].dependentDimensions = storm::storage::BitVector(dimensions.size(), false);
214 dimensions[currDim].dependentDimensions.set(currDim, true);
215 }
216 }
217 dim += objDimensionCount;
218 objectiveDimensions.push_back(std::move(objDimensions));
219 }
220 STORM_LOG_ASSERT(dim == dimensions.size(), "Dimension count mismatch.");
221
222 // Initialize the epoch manager
223 epochManager = EpochManager(dimensions.size());
224
225 // Convert the epoch steps to a choice-wise representation
226 epochSteps.reserve(model.getTransitionMatrix().getRowCount());
227 for (uint64_t choice = 0; choice < model.getTransitionMatrix().getRowCount(); ++choice) {
228 Epoch step;
229 uint64_t dim = 0;
230 for (auto const& dimensionSteps : dimensionWiseEpochSteps) {
231 epochManager.setDimensionOfEpoch(step, dim, dimensionSteps[choice]);
232 ++dim;
233 }
234 epochSteps.push_back(step);
235 }
236
237 // Set the maximal values we need to consider for each dimension
238 computeMaxDimensionValues();
239 translateLowerBoundInfinityDimensions(epochSteps);
240}
241
242template<typename ValueType, bool SingleObjectiveMode>
243void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initializeMemoryProduct(std::vector<Epoch> const& epochSteps) {
244 productModel = std::make_unique<ProductModel<ValueType>>(model, objectives, dimensions, objectiveDimensions, epochManager, epochSteps);
245}
246
247template<typename ValueType, bool SingleObjectiveMode>
248void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::computeMaxDimensionValues() {
249 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
251 bool isStrict = false;
252 storm::logic::Formula const& dimFormula = *dimensions[dim].formula;
253 if (dimFormula.isBoundedUntilFormula()) {
254 STORM_LOG_ASSERT(!dimFormula.asBoundedUntilFormula().hasMultiDimensionalSubformulas(), "Expected single-dimensional subformula.");
255 if (dimFormula.asBoundedUntilFormula().hasUpperBound()) {
256 STORM_LOG_ASSERT(!dimFormula.asBoundedUntilFormula().hasLowerBound(), "Bounded until formulas with interval bounds are not supported.");
257 bound = dimFormula.asBoundedUntilFormula().getUpperBound();
258 isStrict = dimFormula.asBoundedUntilFormula().isUpperBoundStrict();
259 } else {
260 STORM_LOG_ASSERT(dimFormula.asBoundedUntilFormula().hasLowerBound(), "Bounded until formulas without any bounds are not supported.");
261 bound = dimFormula.asBoundedUntilFormula().getLowerBound();
262 isStrict = dimFormula.asBoundedUntilFormula().isLowerBoundStrict();
263 }
264 } else if (dimFormula.isCumulativeRewardFormula()) {
265 STORM_LOG_ASSERT(!dimFormula.asCumulativeRewardFormula().isMultiDimensional(), "Expected single-dimensional cumulative reward.");
266 bound = dimFormula.asCumulativeRewardFormula().getBound();
267 isStrict = dimFormula.asCumulativeRewardFormula().isBoundStrict();
268 }
269
270 if (!bound.containsVariables()) {
271 // We always consider upper bounds to be non-strict and lower bounds to be strict.
272 // Thus, >=N would become >N-1. However, note that the case N=0 is treated separately.
273 if (dimensions[dim].boundType == DimensionBoundType::LowerBound || dimensions[dim].boundType == DimensionBoundType::UpperBound) {
275 discretizedBound /= dimensions[dim].scalingFactor;
276 if (storm::utility::isInteger(discretizedBound)) {
277 if (isStrict == (dimensions[dim].boundType == DimensionBoundType::UpperBound)) {
278 discretizedBound -= storm::utility::one<ValueType>();
279 }
280 } else {
281 discretizedBound = storm::utility::floor(discretizedBound);
282 }
283 uint64_t dimensionValue = storm::utility::convertNumber<uint64_t>(discretizedBound);
284 STORM_LOG_THROW(epochManager.isValidDimensionValue(dimensionValue), storm::exceptions::NotSupportedException,
285 "The bound " << bound << " is too high for the considered number of dimensions.");
286 dimensions[dim].maxValue = dimensionValue;
287 }
288 }
289 }
290}
291
292template<typename ValueType, bool SingleObjectiveMode>
293void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::translateLowerBoundInfinityDimensions(std::vector<Epoch>& epochSteps) {
294 // Translate lowerBoundedByInfinity dimensions to finite bounds
295 storm::storage::BitVector infLowerBoundedDimensions(dimensions.size(), false);
296 storm::storage::BitVector upperBoundedDimensions(dimensions.size(), false);
297 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
298 infLowerBoundedDimensions.set(dim, dimensions[dim].boundType == DimensionBoundType::LowerBoundInfinity);
299 upperBoundedDimensions.set(dim, dimensions[dim].boundType == DimensionBoundType::UpperBound);
300 }
301 if (!infLowerBoundedDimensions.empty()) {
302 // We can currently only handle this case for single maximizing bounded until probability objectives.
303 // The approach is to erase all epochSteps that are not part of an end component and then change the reward bound to '> 0'.
304 // Then, reaching a reward means reaching an end component where arbitrarily many reward can be collected.
306 SingleObjectiveMode, storm::exceptions::NotSupportedException,
307 "Letting lower bounds approach infinity is only supported in single objective mode."); // It most likely also works with multiple objectives with
308 // the same shape. However, we haven't checked this yet.
309 STORM_LOG_THROW(objectives.front().formula->isProbabilityOperatorFormula(), storm::exceptions::NotSupportedException,
310 "Letting lower bounds approach infinity is only supported for probability operator formulas.");
311 auto const& probabilityOperatorFormula = objectives.front().formula->asProbabilityOperatorFormula();
312 STORM_LOG_THROW(probabilityOperatorFormula.getSubformula().isBoundedUntilFormula(), storm::exceptions::NotSupportedException,
313 "Letting lower bounds approach infinity is only supported for bounded until probabilities.");
315 (probabilityOperatorFormula.hasOptimalityType() && storm::solver::maximize(probabilityOperatorFormula.getOptimalityType())),
316 storm::exceptions::NotSupportedException,
317 "Letting lower bounds approach infinity is only supported for maximizing bounded until probabilities.");
318
319 STORM_LOG_THROW(upperBoundedDimensions.empty() || !probabilityOperatorFormula.getSubformula().asBoundedUntilFormula().hasMultiDimensionalSubformulas(),
320 storm::exceptions::NotSupportedException,
321 "Letting lower bounds approach infinity is only supported if the formula has either only lower bounds or if it has a single goal "
322 "state."); // This would fail because the upper bounded dimension(s) might be satisfied already. One should erase epoch steps in the
323 // epoch model (after applying the goal-unfolding).
324 storm::storage::BitVector choicesWithoutUpperBoundedStep(model.getNumberOfChoices(), true);
325 if (!upperBoundedDimensions.empty()) {
326 // To not invalidate upper-bounded dimensions, one needs to consider MECS where no reward for such a dimension is collected.
327 for (uint64_t choiceIndex = 0; choiceIndex < model.getNumberOfChoices(); ++choiceIndex) {
328 for (uint64_t dim : upperBoundedDimensions) {
329 if (epochManager.getDimensionOfEpoch(epochSteps[choiceIndex], dim) != 0) {
330 choicesWithoutUpperBoundedStep.set(choiceIndex, false);
331 break;
332 }
333 }
334 }
335 }
336 storm::storage::MaximalEndComponentDecomposition<ValueType> mecDecomposition(model.getTransitionMatrix(), model.getBackwardTransitions(),
337 storm::storage::BitVector(model.getNumberOfStates(), true),
338 choicesWithoutUpperBoundedStep);
339 storm::storage::BitVector nonMecChoices(model.getNumberOfChoices(), true);
340 for (auto const& mec : mecDecomposition) {
341 for (auto const& stateChoicesPair : mec) {
342 for (auto const& choice : stateChoicesPair.second) {
343 nonMecChoices.set(choice, false);
344 }
345 }
346 }
347 for (uint64_t choice : nonMecChoices) {
348 for (uint64_t dim : infLowerBoundedDimensions) {
349 epochManager.setDimensionOfEpoch(epochSteps[choice], dim, 0);
350 }
351 }
352
353 // Translate the dimension to '>0'
354 for (uint64_t dim : infLowerBoundedDimensions) {
355 dimensions[dim].boundType = DimensionBoundType::LowerBound;
356 dimensions[dim].maxValue = 0;
357 }
358 }
359}
360
361template<typename ValueType, bool SingleObjectiveMode>
363 bool setUnknownDimsToBottom) {
364 Epoch startEpoch = epochManager.getZeroEpoch();
365 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
366 if (dimensions[dim].maxValue) {
367 epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get());
368 } else {
369 STORM_LOG_THROW(setUnknownDimsToBottom || dimensions[dim].boundType == DimensionBoundType::Unbounded, storm::exceptions::UnexpectedException,
370 "Tried to obtain the start epoch although no bound on dimension " << dim << " is known.");
371 epochManager.setBottomDimension(startEpoch, dim);
372 }
373 }
374 STORM_LOG_TRACE("Start epoch is " << epochManager.toString(startEpoch));
375 return startEpoch;
376}
377
378template<typename ValueType, bool SingleObjectiveMode>
379std::vector<typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::Epoch>
381 // Perform a DFS to find all the reachable epochs
382 std::vector<Epoch> dfsStack;
383 std::set<Epoch, std::function<bool(Epoch const&, Epoch const&)>> collectedEpochs(
384 std::bind(&EpochManager::epochClassZigZagOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
385
386 if (!stopAtComputedEpochs || epochSolutions.count(startEpoch) == 0) {
387 collectedEpochs.insert(startEpoch);
388 dfsStack.push_back(startEpoch);
389 }
390 while (!dfsStack.empty()) {
391 Epoch currentEpoch = dfsStack.back();
392 dfsStack.pop_back();
393 for (auto const& step : possibleEpochSteps) {
394 Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step);
395 if (!stopAtComputedEpochs || epochSolutions.count(successorEpoch) == 0) {
396 if (collectedEpochs.insert(successorEpoch).second) {
397 dfsStack.push_back(std::move(successorEpoch));
398 }
399 }
400 }
401 }
402 return std::vector<Epoch>(collectedEpochs.begin(), collectedEpochs.end());
403}
404
405template<typename ValueType, bool SingleObjectiveMode>
407 STORM_LOG_DEBUG("Setting model for epoch " << epochManager.toString(epoch));
408
409 // Check if we need to update the current epoch class
410 if (!currentEpoch || !epochManager.compareEpochClass(epoch, currentEpoch.get())) {
411 setCurrentEpochClass(epoch);
412 epochModel.epochMatrixChanged = true;
414 if (storm::utility::graph::hasCycle(epochModel.epochMatrix)) {
415 std::cout << "Epoch model for epoch " << epochManager.toString(epoch) << " is cyclic.\n";
416 }
417 }
418 } else {
419 epochModel.epochMatrixChanged = false;
420 }
421
422 bool containsLowerBoundedObjective = false;
423 for (auto const& dimension : dimensions) {
424 if (dimension.boundType == DimensionBoundType::LowerBound) {
425 containsLowerBoundedObjective = true;
426 break;
427 }
428 }
429 std::map<Epoch, EpochSolution const*> subSolutions;
430 for (auto const& step : possibleEpochSteps) {
431 Epoch successorEpoch = epochManager.getSuccessorEpoch(epoch, step);
432 if (successorEpoch != epoch) {
433 auto successorSolIt = epochSolutions.find(successorEpoch);
434 STORM_LOG_ASSERT(successorSolIt != epochSolutions.end(), "Solution for successor epoch does not exist (anymore).");
435 subSolutions.emplace(successorEpoch, &successorSolIt->second);
436 }
437 }
438 epochModel.stepSolutions.resize(epochModel.stepChoices.getNumberOfSetBits());
439 auto stepSolIt = epochModel.stepSolutions.begin();
440 for (auto reducedChoice : epochModel.stepChoices) {
441 uint64_t productChoice = epochModelToProductChoiceMap[reducedChoice];
442 uint64_t productState = productModel->getProductStateFromChoice(productChoice);
443 auto const& memoryState = productModel->getMemoryState(productState);
444 Epoch successorEpoch = epochManager.getSuccessorEpoch(epoch, productModel->getSteps()[productChoice]);
445 EpochClass successorEpochClass = epochManager.getEpochClass(successorEpoch);
446 // Find out whether objective reward is earned for the current choice
447 // Objective reward is not earned if
448 // a) there is an upper bounded subObjective that is __still_relevant__ but the corresponding reward bound is passed after taking the choice
449 // b) there is a lower bounded subObjective and the corresponding reward bound is not passed yet.
450 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
451 bool rewardEarned = !storm::utility::isZero(epochModel.objectiveRewards[objIndex][reducedChoice]);
452 if (rewardEarned) {
453 for (auto dim : objectiveDimensions[objIndex]) {
454 if ((dimensions[dim].boundType == DimensionBoundType::UpperBound) == epochManager.isBottomDimension(successorEpoch, dim) &&
455 productModel->getMemoryStateManager().isRelevantDimension(memoryState, dim)) {
456 rewardEarned = false;
457 break;
458 }
459 }
460 }
461 epochModel.objectiveRewardFilter[objIndex].set(reducedChoice, rewardEarned);
462 }
463 // compute the solution for the stepChoices
464 // For optimization purposes, we distinguish the case where the memory state does not have to be transformed
465 EpochSolution const& successorEpochSolution = getEpochSolution(subSolutions, successorEpoch);
466 SolutionType choiceSolution;
467 bool firstSuccessor = true;
468 if (!containsLowerBoundedObjective && epochManager.compareEpochClass(epoch, successorEpoch)) {
469 for (auto const& successor : productModel->getProduct().getTransitionMatrix().getRow(productChoice)) {
470 if (firstSuccessor) {
471 choiceSolution = getScaledSolution(getStateSolution(successorEpochSolution, successor.getColumn()), successor.getValue());
472 firstSuccessor = false;
473 } else {
474 addScaledSolution(choiceSolution, getStateSolution(successorEpochSolution, successor.getColumn()), successor.getValue());
475 }
476 }
477 } else {
478 for (auto const& successor : productModel->getProduct().getTransitionMatrix().getRow(productChoice)) {
479 uint64_t successorProductState = productModel->transformProductState(successor.getColumn(), successorEpochClass, memoryState);
480 SolutionType const& successorSolution = getStateSolution(successorEpochSolution, successorProductState);
481 if (firstSuccessor) {
482 choiceSolution = getScaledSolution(successorSolution, successor.getValue());
483 firstSuccessor = false;
484 } else {
485 addScaledSolution(choiceSolution, successorSolution, successor.getValue());
486 }
487 }
488 }
489 STORM_LOG_ASSERT(!firstSuccessor, "Expected no first successor.");
490 *stepSolIt = std::move(choiceSolution);
491 ++stepSolIt;
492 }
493
494 STORM_LOG_ASSERT(epochModel.objectiveRewards.size() == objectives.size(), "Objective rewards size mismatch.");
495 STORM_LOG_ASSERT(epochModel.objectiveRewardFilter.size() == objectives.size(), "Objective reward filter size mismatch.");
496 STORM_LOG_ASSERT(epochModel.epochMatrix.getRowCount() == epochModel.stepChoices.size(), "Row count / step choices mismatch.");
497 STORM_LOG_ASSERT(epochModel.stepChoices.size() == epochModel.objectiveRewards.front().size(), "Step choices / objective rewards mismatch.");
498 STORM_LOG_ASSERT(epochModel.objectiveRewards.front().size() == epochModel.objectiveRewards.back().size(), "Front/back objective rewards size mismatch.");
499 STORM_LOG_ASSERT(epochModel.objectiveRewards.front().size() == epochModel.objectiveRewardFilter.front().size(),
500 "Objective rewards / filter size mismatch (front).");
501 STORM_LOG_ASSERT(epochModel.objectiveRewards.back().size() == epochModel.objectiveRewardFilter.back().size(),
502 "Objective rewards / filter size mismatch (back).");
503 STORM_LOG_ASSERT(epochModel.stepChoices.getNumberOfSetBits() == epochModel.stepSolutions.size(), "Step choices bits / solutions size mismatch.");
504
505 currentEpoch = epoch;
506 /*
507 std::cout << "Epoch model for epoch " << storm::utility::vector::toString(epoch) << '\n';
508 std::cout << "Matrix: \n" << epochModel.epochMatrix << '\n';
509 std::cout << "ObjectiveRewards: " << storm::utility::vector::toString(epochModel.objectiveRewards[0]) << '\n';
510 std::cout << "steps: " << epochModel.stepChoices << '\n';
511 std::cout << "step solutions: ";
512 for (int i = 0; i < epochModel.stepSolutions.size(); ++i) {
513 std::cout << " " << epochModel.stepSolutions[i].weightedValue;
514 }
515 std::cout << '\n';
516 */
517 return epochModel;
518}
519
520template<typename ValueType, bool SingleObjectiveMode>
521void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setCurrentEpochClass(Epoch const& epoch) {
522 EpochClass epochClass = epochManager.getEpochClass(epoch);
523 // std::cout << "Setting epoch class for epoch " << epochManager.toString(epoch) << '\n';
524 auto productObjectiveRewards = productModel->computeObjectiveRewards(epochClass, objectives);
525
526 storm::storage::BitVector stepChoices(productModel->getProduct().getTransitionMatrix().getRowCount(), false);
527 uint64_t choice = 0;
528 for (auto const& step : productModel->getSteps()) {
529 if (!epochManager.isZeroEpoch(step) && epochManager.getSuccessorEpoch(epoch, step) != epoch) {
530 stepChoices.set(choice, true);
531 }
532 ++choice;
533 }
534 epochModel.epochMatrix = productModel->getProduct().getTransitionMatrix().filterEntries(~stepChoices);
535 // redirect transitions for the case where the lower reward bounds are not met yet
536 storm::storage::BitVector violatedLowerBoundedDimensions(dimensions.size(), false);
537 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
538 if (dimensions[dim].boundType == DimensionBoundType::LowerBound && !epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
539 violatedLowerBoundedDimensions.set(dim);
540 }
541 }
542 if (!violatedLowerBoundedDimensions.empty()) {
543 for (uint64_t state = 0; state < epochModel.epochMatrix.getRowGroupCount(); ++state) {
544 auto const& memoryState = productModel->getMemoryState(state);
545 for (auto& entry : epochModel.epochMatrix.getRowGroup(state)) {
546 entry.setColumn(productModel->transformProductState(entry.getColumn(), epochClass, memoryState));
547 }
548 }
549 }
550
551 storm::storage::BitVector zeroObjRewardChoices(productModel->getProduct().getTransitionMatrix().getRowCount(), true);
552 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
553 if (violatedLowerBoundedDimensions.isDisjointFrom(objectiveDimensions[objIndex])) {
554 zeroObjRewardChoices &= storm::utility::vector::filterZero(productObjectiveRewards[objIndex]);
555 }
556 }
557 storm::storage::BitVector allProductStates(productModel->getProduct().getNumberOfStates(), true);
558
559 // Get the relevant states for this epoch.
560 storm::storage::BitVector productInStates = productModel->getInStates(epochClass);
561 // The epoch model only needs to consider the states that are reachable from a relevant state
562 storm::storage::BitVector consideredStates =
563 storm::utility::graph::getReachableStates(epochModel.epochMatrix, productInStates, allProductStates, ~allProductStates);
564
565 // We assume that there is no end component in which objective reward is earned
566 STORM_LOG_ASSERT(!storm::utility::graph::checkIfECWithChoiceExists(epochModel.epochMatrix, epochModel.epochMatrix.transpose(true), allProductStates,
567 ~zeroObjRewardChoices & ~stepChoices),
568 "There is a scheduler that yields infinite reward for one objective. This case should be excluded");
569
570 // Create the epoch model matrix
571 std::vector<uint64_t> productToEpochModelStateMapping;
573 STORM_LOG_ASSERT(zeroObjRewardChoices.size() == productModel->getProduct().getNumberOfStates(), "Zero reward choices size mismatch.");
574 STORM_LOG_ASSERT(stepChoices.size() == productModel->getProduct().getNumberOfStates(), "Step choices size mismatch.");
575 STORM_LOG_ASSERT(epochModel.equationSolverProblemFormat.is_initialized(), "Linear equation problem format was not set.");
576 bool convertToEquationSystem = epochModel.equationSolverProblemFormat.get() == storm::solver::LinearEquationSolverProblemFormat::EquationSystem;
577 // For DTMCs we consider the subsystem induced by the considered states.
578 // The transitions for states with zero reward are filtered out to guarantee a unique solution of the eq-system.
579 auto backwardTransitions = epochModel.epochMatrix.transpose(true);
580 storm::storage::BitVector nonZeroRewardStates =
581 storm::utility::graph::performProbGreater0(backwardTransitions, consideredStates, consideredStates & (~zeroObjRewardChoices | stepChoices));
582 // If there is at least one considered state with reward zero, we have to add a 'zero-reward-state' to the epoch model.
583 bool requiresZeroRewardState = nonZeroRewardStates != consideredStates;
584 uint64_t numEpochModelStates = nonZeroRewardStates.getNumberOfSetBits();
585 uint64_t zeroRewardInState = numEpochModelStates;
586 if (requiresZeroRewardState) {
587 ++numEpochModelStates;
588 }
589 storm::storage::SparseMatrixBuilder<ValueType> builder;
590 if (!nonZeroRewardStates.empty()) {
591 builder = storm::storage::SparseMatrixBuilder<ValueType>(
592 epochModel.epochMatrix.getSubmatrix(true, nonZeroRewardStates, nonZeroRewardStates, convertToEquationSystem));
593 }
594 if (requiresZeroRewardState) {
595 if (convertToEquationSystem) {
596 // add a diagonal entry
597 builder.addNextValue(zeroRewardInState, zeroRewardInState, storm::utility::zero<ValueType>());
598 }
599 epochModel.epochMatrix = builder.build(numEpochModelStates, numEpochModelStates);
600 } else {
601 STORM_LOG_ASSERT(!nonZeroRewardStates.empty(), "Expected non-zero reward states.");
602 epochModel.epochMatrix = builder.build();
603 }
604 if (convertToEquationSystem) {
605 epochModel.epochMatrix.convertToEquationSystem();
606 }
607
608 epochModelToProductChoiceMap.clear();
609 epochModelToProductChoiceMap.reserve(numEpochModelStates);
610 productToEpochModelStateMapping.assign(nonZeroRewardStates.size(), zeroRewardInState);
611 for (uint64_t productState : nonZeroRewardStates) {
612 productToEpochModelStateMapping[productState] = epochModelToProductChoiceMap.size();
613 epochModelToProductChoiceMap.push_back(productState);
614 }
615 if (requiresZeroRewardState) {
616 uint64_t zeroRewardProductState = (consideredStates & ~nonZeroRewardStates).getNextSetIndex(0);
617 STORM_LOG_ASSERT(zeroRewardProductState < consideredStates.size(), "Zero reward product state out of range.");
618 epochModelToProductChoiceMap.push_back(zeroRewardProductState);
619 }
620 } else if (model.isOfType(storm::models::ModelType::Mdp)) {
621 // Eliminate zero-reward end components
622 auto ecElimResult = storm::transformer::EndComponentEliminator<ValueType>::transform(epochModel.epochMatrix, consideredStates,
623 zeroObjRewardChoices & ~stepChoices, consideredStates);
624 epochModel.epochMatrix = std::move(ecElimResult.matrix);
625 epochModelToProductChoiceMap = std::move(ecElimResult.newToOldRowMapping);
626 productToEpochModelStateMapping = std::move(ecElimResult.oldToNewStateMapping);
627 } else {
628 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unsupported model type.");
629 }
630
631 epochModel.stepChoices = storm::storage::BitVector(epochModel.epochMatrix.getRowCount(), false);
632 for (uint64_t choice = 0; choice < epochModel.epochMatrix.getRowCount(); ++choice) {
633 if (stepChoices.get(epochModelToProductChoiceMap[choice])) {
634 epochModel.stepChoices.set(choice, true);
635 }
636 }
637
638 epochModel.objectiveRewards.clear();
639 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
640 std::vector<ValueType> const& productObjRew = productObjectiveRewards[objIndex];
641 std::vector<ValueType> reducedModelObjRewards;
642 reducedModelObjRewards.reserve(epochModel.epochMatrix.getRowCount());
643 for (auto const& productChoice : epochModelToProductChoiceMap) {
644 reducedModelObjRewards.push_back(productObjRew[productChoice]);
645 }
646 // Check if the objective is violated in the current epoch
647 if (!violatedLowerBoundedDimensions.isDisjointFrom(objectiveDimensions[objIndex])) {
648 storm::utility::vector::setVectorValues(reducedModelObjRewards, ~epochModel.stepChoices, storm::utility::zero<ValueType>());
649 }
650 epochModel.objectiveRewards.push_back(std::move(reducedModelObjRewards));
651 }
652
653 epochModel.epochInStates = storm::storage::BitVector(epochModel.epochMatrix.getRowGroupCount(), false);
654 for (uint64_t productState : productInStates) {
655 STORM_LOG_ASSERT(productToEpochModelStateMapping[productState] < epochModel.epochMatrix.getRowGroupCount(),
656 "Selected product state does not exist in the epoch model.");
657 epochModel.epochInStates.set(productToEpochModelStateMapping[productState], true);
658 }
659
660 std::vector<uint64_t> toEpochModelInStatesMap(productModel->getProduct().getNumberOfStates(), std::numeric_limits<uint64_t>::max());
661 std::vector<uint64_t> epochModelStateToInStateMap = epochModel.epochInStates.getNumberOfSetBitsBeforeIndices();
662 for (uint64_t productState : productInStates) {
663 toEpochModelInStatesMap[productState] = epochModelStateToInStateMap[productToEpochModelStateMapping[productState]];
664 }
665 productStateToEpochModelInStateMap = std::make_shared<std::vector<uint64_t> const>(std::move(toEpochModelInStatesMap));
666
667 epochModel.objectiveRewardFilter.clear();
668 for (auto const& objRewards : epochModel.objectiveRewards) {
669 epochModel.objectiveRewardFilter.push_back(storm::utility::vector::filterZero(objRewards));
670 epochModel.objectiveRewardFilter.back().complement();
671 }
672}
673
674template<typename ValueType, bool SingleObjectiveMode>
677 STORM_LOG_ASSERT(model.isOfType(storm::models::ModelType::Dtmc), "Trying to set the equation problem format although the model is not deterministic.");
678 epochModel.equationSolverProblemFormat = eqSysFormat;
679}
680
681template<typename ValueType, bool SingleObjectiveMode>
682template<bool SO, typename std::enable_if<SO, int>::type>
684MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getScaledSolution(SolutionType const& solution, ValueType const& scalingFactor) const {
685 return solution * scalingFactor;
686}
687
688template<typename ValueType, bool SingleObjectiveMode>
689template<bool SO, typename std::enable_if<!SO, int>::type>
691MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getScaledSolution(SolutionType const& solution, ValueType const& scalingFactor) const {
692 SolutionType res;
693 res.reserve(solution.size());
694 for (auto const& sol : solution) {
695 res.push_back(sol * scalingFactor);
696 }
697 return res;
698}
699
700template<typename ValueType, bool SingleObjectiveMode>
701template<bool SO, typename std::enable_if<SO, int>::type>
702void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::addScaledSolution(SolutionType& solution, SolutionType const& solutionToAdd,
703 ValueType const& scalingFactor) const {
704 solution += solutionToAdd * scalingFactor;
705}
706
707template<typename ValueType, bool SingleObjectiveMode>
708template<bool SO, typename std::enable_if<!SO, int>::type>
709void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::addScaledSolution(SolutionType& solution, SolutionType const& solutionToAdd,
710 ValueType const& scalingFactor) const {
711 storm::utility::vector::addScaledVector(solution, solutionToAdd, scalingFactor);
712}
713
714template<typename ValueType, bool SingleObjectiveMode>
715template<bool SO, typename std::enable_if<SO, int>::type>
716void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setSolutionEntry(SolutionType& solution, uint64_t objIndex,
717 ValueType const& value) const {
718 STORM_LOG_ASSERT(objIndex == 0, "Invalid objective index in single objective mode.");
719 solution = value;
720}
721
722template<typename ValueType, bool SingleObjectiveMode>
723template<bool SO, typename std::enable_if<!SO, int>::type>
724void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setSolutionEntry(SolutionType& solution, uint64_t objIndex,
725 ValueType const& value) const {
726 STORM_LOG_ASSERT(objIndex < solution.size(), "Invalid objective index " << objIndex << ".");
727 solution[objIndex] = value;
728}
729
730template<typename ValueType, bool SingleObjectiveMode>
731template<bool SO, typename std::enable_if<SO, int>::type>
732std::string MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::solutionToString(SolutionType const& solution) const {
733 std::stringstream stringstream;
734 stringstream << solution;
735 return stringstream.str();
736}
737
738template<typename ValueType, bool SingleObjectiveMode>
739template<bool SO, typename std::enable_if<!SO, int>::type>
740std::string MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::solutionToString(SolutionType const& solution) const {
741 std::stringstream stringstream;
742 stringstream << "(";
743 bool first = true;
744 for (auto const& s : solution) {
745 if (first) {
746 first = false;
747 } else {
748 stringstream << ", ";
749 }
750 stringstream << s;
751 }
752 stringstream << ")";
753 return stringstream.str();
754}
755
756template<typename ValueType, bool SingleObjectiveMode>
758 return precision / storm::utility::convertNumber<ValueType>(epochManager.getSumOfDimensions(startEpoch) + 1);
759}
760
761template<typename ValueType, bool SingleObjectiveMode>
763 auto& objective = this->objectives[objectiveIndex];
764 if (!objective.upperResultBound) {
765 if (objective.formula->isProbabilityOperatorFormula()) {
767 } else if (objective.formula->isRewardOperatorFormula()) {
768 auto const& rewModel = this->model.getRewardModel(objective.formula->asRewardOperatorFormula().getRewardModelName());
769 auto actionRewards = rewModel.getTotalRewardVector(this->model.getTransitionMatrix());
770 if (objective.formula->getSubformula().isCumulativeRewardFormula()) {
771 // Try to get an upper bound by computing the maximal reward achievable within one epoch step
772 auto const& cumulativeRewardFormula = objective.formula->getSubformula().asCumulativeRewardFormula();
773 for (uint64_t objDim = 0; objDim < cumulativeRewardFormula.getDimension(); ++objDim) {
774 boost::optional<ValueType> resBound;
775 ValueType rewardBound = cumulativeRewardFormula.template getBound<ValueType>(objDim);
776 if (cumulativeRewardFormula.getTimeBoundReference(objDim).isRewardBound()) {
777 auto const& costModel = this->model.getRewardModel(cumulativeRewardFormula.getTimeBoundReference(objDim).getRewardName());
778 if (!costModel.hasTransitionRewards()) {
779 auto actionCosts = costModel.getTotalRewardVector(this->model.getTransitionMatrix());
780 ValueType largestRewardPerCost = storm::utility::zero<ValueType>();
781 bool isFinite = true;
782 for (auto rewIt = actionRewards.begin(), costIt = actionCosts.begin(); rewIt != actionRewards.end(); ++rewIt, ++costIt) {
783 if (!storm::utility::isZero(*rewIt)) {
784 if (storm::utility::isZero(*costIt)) {
785 isFinite = false;
786 break;
787 }
788 ValueType rewardPerCost = *rewIt / *costIt;
789 largestRewardPerCost = std::max(largestRewardPerCost, rewardPerCost);
790 }
791 }
792 if (isFinite) {
793 resBound = largestRewardPerCost * rewardBound;
794 }
795 }
796 } else {
797 resBound = (*std::max_element(actionRewards.begin(), actionRewards.end())) * rewardBound;
798 }
799 if (resBound && (!objective.upperResultBound || objective.upperResultBound.get() > resBound.get())) {
800 objective.upperResultBound = resBound;
801 }
802 }
803
804 // If we could not find an upper bound, try to get an upper bound for the unbounded case
805 if (!objective.upperResultBound) {
806 storm::storage::BitVector allStates(model.getNumberOfStates(), true);
807 // Get the set of states from which reward is reachable
808 auto nonZeroRewardStates = rewModel.getStatesWithZeroReward(model.getTransitionMatrix());
809 nonZeroRewardStates.complement();
810 auto expRewGreater0EStates = storm::utility::graph::performProbGreater0E(model.getBackwardTransitions(), allStates, nonZeroRewardStates);
811 // Eliminate zero-reward ECs
812 auto zeroRewardChoices = rewModel.getChoicesWithZeroReward(model.getTransitionMatrix());
813 auto ecElimRes = storm::transformer::EndComponentEliminator<ValueType>::transform(model.getTransitionMatrix(), expRewGreater0EStates,
814 zeroRewardChoices, ~allStates);
815 allStates.resize(ecElimRes.matrix.getRowGroupCount());
816 storm::storage::BitVector outStates(allStates.size(), false);
817 std::vector<ValueType> rew0StateProbs;
818 rew0StateProbs.reserve(ecElimRes.matrix.getRowCount());
819 for (uint64_t state = 0; state < allStates.size(); ++state) {
820 for (uint64_t choice = ecElimRes.matrix.getRowGroupIndices()[state]; choice < ecElimRes.matrix.getRowGroupIndices()[state + 1];
821 ++choice) {
822 // Check whether the choice lead to a state with expRew 0 in the original model
823 bool isOutChoice = false;
824 uint64_t originalModelChoice = ecElimRes.newToOldRowMapping[choice];
825 for (auto const& entry : model.getTransitionMatrix().getRow(originalModelChoice)) {
826 if (!expRewGreater0EStates.get(entry.getColumn())) {
827 isOutChoice = true;
828 outStates.set(state, true);
829 rew0StateProbs.push_back(storm::utility::one<ValueType>() - ecElimRes.matrix.getRowSum(choice));
830 STORM_LOG_ASSERT(!storm::utility::isZero(rew0StateProbs.back()), "Expected non-zero reward state probability.");
831 break;
832 }
833 }
834 if (!isOutChoice) {
835 rew0StateProbs.push_back(storm::utility::zero<ValueType>());
836 }
837 }
838 }
839 // An upper reward bound can only be computed if it is below infinity
840 if (storm::utility::graph::performProb1A(ecElimRes.matrix, ecElimRes.matrix.getRowGroupIndices(), ecElimRes.matrix.transpose(true),
841 allStates, outStates)
842 .full()) {
843 std::vector<ValueType> rewards;
844 rewards.reserve(ecElimRes.matrix.getRowCount());
845 for (auto row : ecElimRes.newToOldRowMapping) {
846 rewards.push_back(actionRewards[row]);
847 }
849 objective.upperResultBound = baier.computeTotalRewardBounds(rewards).upper;
850 }
851 }
852 }
853 }
854 }
855 return objective.upperResultBound;
856}
857
858template<typename ValueType, bool SingleObjectiveMode>
860 auto& objective = this->objectives[objectiveIndex];
861 if (!objective.lowerResultBound) {
863 }
864 return objective.lowerResultBound;
865}
866
867template<typename ValueType, bool SingleObjectiveMode>
869 STORM_LOG_ASSERT(currentEpoch, "Tried to set a solution for the current epoch, but no epoch was specified before.");
870 STORM_LOG_ASSERT(inStateSolutions.size() == epochModel.epochInStates.getNumberOfSetBits(), "Invalid number of solutions.");
871
872 std::set<Epoch> predecessorEpochs, successorEpochs;
873 for (auto const& step : possibleEpochSteps) {
874 epochManager.gatherPredecessorEpochs(predecessorEpochs, currentEpoch.get(), step);
875 successorEpochs.insert(epochManager.getSuccessorEpoch(currentEpoch.get(), step));
876 }
877 predecessorEpochs.erase(currentEpoch.get());
878 successorEpochs.erase(currentEpoch.get());
879
880 // clean up solutions that are not needed anymore
881 for (auto const& successorEpoch : successorEpochs) {
882 auto successorEpochSolutionIt = epochSolutions.find(successorEpoch);
883 STORM_LOG_ASSERT(successorEpochSolutionIt != epochSolutions.end(), "Solution for successor epoch does not exist (anymore).");
884 --successorEpochSolutionIt->second.count;
885 if (successorEpochSolutionIt->second.count == 0) {
886 epochSolutions.erase(successorEpochSolutionIt);
887 }
888 }
889
890 // add the new solution
891 EpochSolution solution;
892 solution.count = predecessorEpochs.size();
893 solution.productStateToSolutionVectorMap = productStateToEpochModelInStateMap;
894 solution.solutions = std::move(inStateSolutions);
895 epochSolutions[currentEpoch.get()] = std::move(solution);
896}
897
898template<typename ValueType, bool SingleObjectiveMode>
900MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getStateSolution(Epoch const& epoch, uint64_t const& productState) {
901 auto epochSolutionIt = epochSolutions.find(epoch);
902 STORM_LOG_ASSERT(epochSolutionIt != epochSolutions.end(), "Requested unexisting solution for epoch " << epochManager.toString(epoch) << ".");
903 return getStateSolution(epochSolutionIt->second, productState);
904}
905
906template<typename ValueType, bool SingleObjectiveMode>
907typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::EpochSolution const&
908MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getEpochSolution(std::map<Epoch, EpochSolution const*> const& solutions, Epoch const& epoch) {
909 auto epochSolutionIt = solutions.find(epoch);
910 STORM_LOG_ASSERT(epochSolutionIt != solutions.end(), "Requested unexisting solution for epoch " << epochManager.toString(epoch) << ".");
911 return *epochSolutionIt->second;
912}
913
914template<typename ValueType, bool SingleObjectiveMode>
916MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::getStateSolution(EpochSolution const& epochSolution, uint64_t const& productState) {
917 STORM_LOG_ASSERT(productState < epochSolution.productStateToSolutionVectorMap->size(), "Requested solution at an unexisting product state.");
918 STORM_LOG_ASSERT((*epochSolution.productStateToSolutionVectorMap)[productState] < epochSolution.solutions.size(),
919 "Requested solution for epoch at product state " << productState << " for which no solution was stored.");
920 return epochSolution.solutions[(*epochSolution.productStateToSolutionVectorMap)[productState]];
921}
922
923template<typename ValueType, bool SingleObjectiveMode>
926 STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1, "The model has multiple initial states.");
927 return getInitialStateResult(epoch, *model.getInitialStates().begin());
928}
929
930template<typename ValueType, bool SingleObjectiveMode>
933 STORM_LOG_ASSERT(model.getInitialStates().get(initialStateIndex), "The given model state is not an initial state.");
934
935 auto result = getStateSolution(epoch, productModel->getInitialProductState(initialStateIndex, model.getInitialStates(), epochManager.getEpochClass(epoch)));
936 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
937 if (productModel->getProb1InitialStates(objIndex) && productModel->getProb1InitialStates(objIndex)->get(initialStateIndex)) {
938 // Check whether the objective can actually hold in this epoch
939 bool objectiveHolds = true;
940 for (auto dim : objectiveDimensions[objIndex]) {
941 if (dimensions[dim].boundType == DimensionBoundType::LowerBound && !epochManager.isBottomDimension(epoch, dim)) {
942 objectiveHolds = false;
943 } else if (dimensions[dim].boundType == DimensionBoundType::UpperBound && epochManager.isBottomDimension(epoch, dim)) {
944 objectiveHolds = false;
945 }
946 STORM_LOG_ASSERT(dimensions[dim].boundType != DimensionBoundType::LowerBoundInfinity, "Unexpected bound type at this point.");
947 }
948 if (objectiveHolds) {
949 setSolutionEntry(result, objIndex, storm::utility::one<ValueType>());
950 }
951 }
952 }
953 return result;
954}
955
956template<typename ValueType, bool SingleObjectiveMode>
960
961template<typename ValueType, bool SingleObjectiveMode>
965
970} // namespace rewardbounded
971} // namespace helper
972} // namespace modelchecker
973} // namespace storm
storm::RationalNumber evaluateAsRational() const
Evaluates the expression and returns the resulting rational number.
bool containsVariables() const
Retrieves whether the expression contains a variable.
bool isLowerBoundStrict(unsigned i=0) const
storm::expressions::Expression const & getUpperBound(unsigned i=0) const
storm::expressions::Expression const & getLowerBound(unsigned i=0) const
bool isUpperBoundStrict(unsigned i=0) const
storm::expressions::Expression const & getBound() const
BoundedUntilFormula & asBoundedUntilFormula()
Definition Formula.cpp:333
virtual bool isCumulativeRewardFormula() const
Definition Formula.cpp:144
virtual bool isBoundedUntilFormula() const
Definition Formula.cpp:84
CumulativeRewardFormula & asCumulativeRewardFormula()
Definition Formula.cpp:429
Bounds computeTotalRewardBounds(std::vector< ValueType > const &rewards)
Computes a lower and an upper bound on the expected total rewards.
bool epochClassZigZagOrder(Epoch const &epoch1, Epoch const &epoch2) const
Epoch getStartEpoch(bool setUnknownDimsToBottom=false)
Retrieves the desired epoch that needs to be analyzed to compute the reward bounded values.
std::conditional< SingleObjectiveMode, ValueType, std::vector< ValueType > >::type SolutionType
EpochModel< ValueType, SingleObjectiveMode > & setCurrentEpoch(Epoch const &epoch)
std::vector< Epoch > getEpochComputationOrder(Epoch const &startEpoch, bool stopAtComputedEpochs=false)
Computes a sequence of epochs that need to be analyzed to get a result at the start epoch.
ValueType getRequiredEpochModelPrecision(Epoch const &startEpoch, ValueType const &precision)
Returns the precision required for the analyzis of each epoch model in order to achieve the given ove...
boost::optional< ValueType > getUpperObjectiveBound(uint64_t objectiveIndex=0)
Returns an upper/lower bound for the objective result in every state (if this bound could be computed...
void setEquationSystemFormatForEpochModel(storm::solver::LinearEquationSolverProblemFormat eqSysFormat)
MultiDimensionalRewardUnfolding(storm::models::sparse::Model< ValueType > const &model, std::vector< storm::modelchecker::multiobjective::Objective< ValueType > > const &objectives)
bool isNondeterministicModel() const
Returns true if the model is a nondeterministic model.
Definition ModelBase.cpp:31
bool isOfType(storm::models::ModelType const &modelType) const
Checks whether the model is of the given type.
Definition ModelBase.cpp:27
Base class for all sparse models.
Definition Model.h: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 getNumberOfChoices() const override
Returns the number of choices ine the model.
Definition Model.cpp:173
storm::storage::SparseMatrix< ValueType > getBackwardTransitions() const
Retrieves the backward transition relation of the model, i.e.
Definition Model.cpp:158
virtual bool hasRewardModel(std::string const &rewardModelName) const override
Retrieves whether the model has a reward model with the given name.
Definition Model.cpp:208
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:163
RewardModelType const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name, if one exists.
Definition Model.cpp:219
virtual std::string const & getUniqueRewardModelName() const override
Retrieves the name of the unique reward model, if there exists exactly one.
Definition Model.cpp:293
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
void resize(uint64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix< ValueType > const &originalMatrix, storm::storage::MaximalEndComponentDecomposition< ValueType > ecs, storm::storage::BitVector const &subsystemStates, storm::storage::BitVector const &addSinkRowStates, bool addSelfLoopAtSinkStates=false)
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
SettingsType const & getModule()
Get module.
bool constexpr maximize(OptimizationDirection d)
storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::BitVector const &initialStates, storm::storage::BitVector const &constraintStates, storm::storage::BitVector const &targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceFilter)
Performs a forward depth-first search through the underlying graph structure to identify the states t...
Definition graph.cpp:41
storm::storage::BitVector performProbGreater0(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Performs a backward depth-first search trough the underlying graph structure of the given model to de...
Definition graph.cpp:315
bool hasCycle(storm::storage::SparseMatrix< T > const &transitionMatrix, boost::optional< storm::storage::BitVector > const &subsystem)
Returns true if the graph represented by the given matrix has a cycle.
Definition graph.cpp:136
storm::storage::BitVector performProb1A(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 1 of satisfying phi until psi under all possible re...
Definition graph.cpp:981
storm::storage::BitVector performProbGreater0E(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under at leas...
Definition graph.cpp:673
bool checkIfECWithChoiceExists(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &subsystem, storm::storage::BitVector const &choices)
Checks whether there is an End Component that.
Definition graph.cpp:175
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:78
void addScaledVector(std::vector< InValueType1 > &firstOperand, std::vector< InValueType2 > const &secondOperand, InValueType3 const &factor)
Computes x:= x + a*y, i.e., adds each element of the first vector and (the corresponding element of t...
Definition vector.h:460
storm::storage::BitVector filterZero(std::vector< T > const &values)
Retrieves a bit vector containing all the indices for which the value at this position is equal to ze...
Definition vector.h:519
std::enable_if< std::is_same< ValueType, storm::RationalNumber >::value, std::pair< std::vector< TargetValueType >, ValueType > >::type toIntegralVector(std::vector< ValueType > const &vec)
Definition vector.h:1004
ValueType floor(ValueType const &number)
bool isZero(ValueType const &a)
Definition constants.cpp:42
bool isInteger(ValueType const &number)
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
boost::optional< std::string > memoryLabel
A label that indicates the states where this dimension is still relevant (i.e., it is yet unknown whe...
Definition Dimension.h:33
DimensionBoundType boundType
The type of the bound on this dimension.
Definition Dimension.h:36
std::shared_ptr< storm::logic::Formula const > formula
The formula describing this dimension.
Definition Dimension.h:27
uint64_t objectiveIndex
The index of the associated objective.
Definition Dimension.h:30
ValueType scalingFactor
Multiplying an epoch value with this factor yields the reward/cost in the original domain.
Definition Dimension.h:39
boost::optional< ValueType > upperResultBound
Definition Objective.h:28
boost::optional< ValueType > lowerResultBound
Definition Objective.h:28
std::shared_ptr< storm::logic::Formula const > originalFormula
Definition Objective.h:17
std::shared_ptr< storm::logic::OperatorFormula const > formula
Definition Objective.h:20