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
16
18
23
24namespace storm {
25namespace modelchecker {
26namespace helper {
27namespace rewardbounded {
28
29template<typename ValueType, bool SingleObjectiveMode>
35
36template<typename ValueType, bool SingleObjectiveMode>
38 storm::models::sparse::Model<ValueType> const& model, std::shared_ptr<storm::logic::OperatorFormula const> objectiveFormula,
39 std::set<storm::expressions::Variable> const& infinityBoundVariables)
40 : model(model) {
41 STORM_LOG_TRACE("initializing multi-dimensional reward unfolding for formula " << *objectiveFormula << " and " << infinityBoundVariables.size()
42 << " bound variables should approach infinity.");
43
44 if (objectiveFormula->isProbabilityOperatorFormula()) {
45 if (objectiveFormula->getSubformula().isMultiObjectiveFormula()) {
46 for (auto const& subFormula : objectiveFormula->getSubformula().asMultiObjectiveFormula().getSubformulas()) {
47 STORM_LOG_THROW(subFormula->isBoundedUntilFormula(), storm::exceptions::InvalidPropertyException,
48 "Formula " << objectiveFormula << " is not supported. Invalid subformula " << *subFormula << ".");
49 }
50 } else {
51 STORM_LOG_THROW(objectiveFormula->getSubformula().isBoundedUntilFormula(), storm::exceptions::InvalidPropertyException,
52 "Formula " << objectiveFormula << " is not supported. Invalid subformula " << objectiveFormula->getSubformula() << ".");
53 }
54 } else {
55 STORM_LOG_THROW(objectiveFormula->isRewardOperatorFormula() && objectiveFormula->getSubformula().isCumulativeRewardFormula(),
56 storm::exceptions::InvalidPropertyException, "Formula " << objectiveFormula << " is not supported.");
57 }
58
59 // Build an objective from the formula.
61 objective.formula = objectiveFormula;
62 objective.originalFormula = objective.formula;
63 objective.considersComplementaryEvent = false;
64 objectives.push_back(std::move(objective));
66 initialize(infinityBoundVariables);
68
69template<typename ValueType, bool SingleObjectiveMode>
70void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initialize(std::set<storm::expressions::Variable> const& infinityBoundVariables) {
71 STORM_LOG_ASSERT(!SingleObjectiveMode || (this->objectives.size() == 1), "Enabled single objective mode but there are multiple objectives.");
72 std::vector<Epoch> epochSteps;
73 initializeObjectives(epochSteps, infinityBoundVariables);
74 initializeMemoryProduct(epochSteps);
75
76 // collect which epoch steps are possible
77 possibleEpochSteps.clear();
78 for (auto const& step : epochSteps) {
79 possibleEpochSteps.insert(step);
80 }
82
83template<typename ValueType, bool SingleObjectiveMode>
84void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initializeObjectives(
85 std::vector<Epoch>& epochSteps, std::set<storm::expressions::Variable> const& infinityBoundVariables) {
86 std::vector<std::vector<uint64_t>> dimensionWiseEpochSteps;
87 // collect the time-bounded subobjectives
88 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
89 auto const& formula = *this->objectives[objIndex].formula;
90 if (formula.isProbabilityOperatorFormula()) {
91 STORM_LOG_THROW(formula.getSubformula().isBoundedUntilFormula(), storm::exceptions::NotSupportedException,
92 "Unexpected type of subformula for formula " << formula << ".");
93 auto const& subformula = formula.getSubformula().asBoundedUntilFormula();
94 for (uint64_t dim = 0; dim < subformula.getDimension(); ++dim) {
95 Dimension<ValueType> dimension;
96 dimension.formula = subformula.restrictToDimension(dim);
97 dimension.objectiveIndex = objIndex;
98 std::string memLabel = "dim" + std::to_string(dimensions.size()) + "_maybe";
99 while (model.getStateLabeling().containsLabel(memLabel)) {
100 memLabel = "_" + memLabel;
101 }
102 dimension.memoryLabel = memLabel;
103 // for simplicity we do not allow interval formulas.
104 STORM_LOG_THROW(!subformula.hasLowerBound(dim) || !subformula.hasUpperBound(dim), storm::exceptions::NotSupportedException,
105 "Bounded until formulas are only supported by this method if they consider either an upper bound or a lower bound. Got "
106 << subformula << " instead.");
107 // lower bounded until formulas with non-trivial left hand side are excluded as this would require some additional effort (in particular the
108 // ProductModel::transformMemoryState method).
110 subformula.hasUpperBound(dim) || subformula.getLeftSubformula(dim).isTrueFormula(), storm::exceptions::NotSupportedException,
111 "Lower bounded until formulas are only supported by this method if the left subformula is 'true'. Got " << subformula << " instead.");
112
113 // Treat formulas that aren't acutally bounded differently
114 bool formulaUnbounded =
115 (!subformula.hasLowerBound(dim) && !subformula.hasUpperBound(dim)) ||
116 (subformula.hasLowerBound(dim) && !subformula.isLowerBoundStrict(dim) && !subformula.getLowerBound(dim).containsVariables() &&
117 storm::utility::isZero(subformula.getLowerBound(dim).evaluateAsRational())) ||
118 (subformula.hasUpperBound(dim) && subformula.getUpperBound(dim).isVariable() &&
119 infinityBoundVariables.count(subformula.getUpperBound(dim).getBaseExpression().asVariableExpression().getVariable()) > 0);
120 if (formulaUnbounded) {
121 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 0));
124 } else {
125 if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) {
126 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 1));
128 } else {
129 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).isRewardBound(), "Unexpected type of time bound.");
130 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).hasRewardModelName() || this->model.hasUniqueRewardModel(),
131 "Model has several reward models, but no reward model has been specified in the formula.");
132 std::string const& rewardName = subformula.getTimeBoundReference(dim).hasRewardModelName()
133 ? subformula.getTimeBoundReference(dim).getRewardName()
134 : this->model.getUniqueRewardModelName();
135 STORM_LOG_THROW(this->model.hasRewardModel(rewardName), storm::exceptions::IllegalArgumentException,
136 "No reward model with name '" << rewardName << "' found.");
137 auto const& rewardModel = this->model.getRewardModel(rewardName);
138 STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
139 "Transition rewards are currently not supported as reward bounds.");
140 std::vector<ValueType> actionRewards = rewardModel.getTotalRewardVector(this->model.getTransitionMatrix());
141 auto discretizedRewardsAndFactor = storm::utility::vector::toIntegralVector<ValueType, uint64_t>(actionRewards);
142 dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first));
143 dimension.scalingFactor = std::move(discretizedRewardsAndFactor.second);
144 }
145 if (subformula.hasLowerBound(dim)) {
146 if (subformula.getLowerBound(dim).isVariable() &&
147 infinityBoundVariables.count(subformula.getLowerBound(dim).getBaseExpression().asVariableExpression().getVariable()) > 0) {
149 } else {
151 }
152 } else {
154 }
155 }
156 dimensions.emplace_back(std::move(dimension));
157 }
158 } else if (formula.isRewardOperatorFormula() && formula.getSubformula().isCumulativeRewardFormula()) {
159 auto const& subformula = formula.getSubformula().asCumulativeRewardFormula();
160 for (uint64_t dim = 0; dim < subformula.getDimension(); ++dim) {
161 Dimension<ValueType> dimension;
162 dimension.formula = subformula.restrictToDimension(dim);
164 !dimension.formula->asCumulativeRewardFormula().getBound().isVariable() ||
165 infinityBoundVariables.count(
166 dimension.formula->asCumulativeRewardFormula().getBound().getBaseExpression().asVariableExpression().getVariable()) == 0,
167 storm::exceptions::NotSupportedException, "Letting cumulative reward bounds approach infinite is not supported.");
168 dimension.objectiveIndex = objIndex;
170 if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) {
171 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 1));
173 } else {
174 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).isRewardBound(), "Unexpected type of time bound.");
175 std::string const& rewardName = subformula.getTimeBoundReference(dim).getRewardName();
176 STORM_LOG_THROW(this->model.hasRewardModel(rewardName), storm::exceptions::IllegalArgumentException,
177 "No reward model with name '" << rewardName << "' found.");
178 auto const& rewardModel = this->model.getRewardModel(rewardName);
179 STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
180 "Transition rewards are currently not supported as reward bounds.");
181 std::vector<ValueType> actionRewards = rewardModel.getTotalRewardVector(this->model.getTransitionMatrix());
182 auto discretizedRewardsAndFactor = storm::utility::vector::toIntegralVector<ValueType, uint64_t>(actionRewards);
183 dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first));
184 dimension.scalingFactor = std::move(discretizedRewardsAndFactor.second);
185 }
186 dimensions.emplace_back(std::move(dimension));
187 }
188 }
189 }
190
191 // Compute a mapping for each objective to the set of dimensions it considers
192 // Also store for each dimension dim, which dimensions should be unsatisfiable whenever the bound of dim is violated
193 uint64_t dim = 0;
194 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
195 storm::storage::BitVector objDimensions(dimensions.size(), false);
196 uint64_t objDimensionCount = 0;
197 bool objDimensionsCanBeSatisfiedIndividually = false;
198 if (objectives[objIndex].formula->isProbabilityOperatorFormula() && objectives[objIndex].formula->getSubformula().isBoundedUntilFormula()) {
199 objDimensionCount = objectives[objIndex].formula->getSubformula().asBoundedUntilFormula().getDimension();
200 objDimensionsCanBeSatisfiedIndividually = objectives[objIndex].formula->getSubformula().asBoundedUntilFormula().hasMultiDimensionalSubformulas();
201 } else if (objectives[objIndex].formula->isRewardOperatorFormula() && objectives[objIndex].formula->getSubformula().isCumulativeRewardFormula()) {
202 objDimensionCount = objectives[objIndex].formula->getSubformula().asCumulativeRewardFormula().getDimension();
203 }
204 for (uint64_t currDim = dim; currDim < dim + objDimensionCount; ++currDim) {
205 objDimensions.set(currDim);
206 }
207 for (uint64_t currDim = dim; currDim < dim + objDimensionCount; ++currDim) {
208 if (!objDimensionsCanBeSatisfiedIndividually || dimensions[currDim].boundType == DimensionBoundType::UpperBound) {
209 dimensions[currDim].dependentDimensions = objDimensions;
210 } else {
211 dimensions[currDim].dependentDimensions = storm::storage::BitVector(dimensions.size(), false);
212 dimensions[currDim].dependentDimensions.set(currDim, true);
213 }
214 }
215 dim += objDimensionCount;
216 objectiveDimensions.push_back(std::move(objDimensions));
217 }
218 STORM_LOG_ASSERT(dim == dimensions.size(), "Dimension count mismatch.");
219
220 // Initialize the epoch manager
221 epochManager = EpochManager(dimensions.size());
222
223 // Convert the epoch steps to a choice-wise representation
224 epochSteps.reserve(model.getTransitionMatrix().getRowCount());
225 for (uint64_t choice = 0; choice < model.getTransitionMatrix().getRowCount(); ++choice) {
226 Epoch step;
227 uint64_t dim = 0;
228 for (auto const& dimensionSteps : dimensionWiseEpochSteps) {
229 epochManager.setDimensionOfEpoch(step, dim, dimensionSteps[choice]);
230 ++dim;
231 }
232 epochSteps.push_back(step);
233 }
234
235 // Set the maximal values we need to consider for each dimension
236 computeMaxDimensionValues();
237 translateLowerBoundInfinityDimensions(epochSteps);
238}
239
240template<typename ValueType, bool SingleObjectiveMode>
241void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::initializeMemoryProduct(std::vector<Epoch> const& epochSteps) {
242 productModel = std::make_unique<ProductModel<ValueType>>(model, objectives, dimensions, objectiveDimensions, epochManager, epochSteps);
243}
244
245template<typename ValueType, bool SingleObjectiveMode>
246void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::computeMaxDimensionValues() {
247 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
249 bool isStrict = false;
250 storm::logic::Formula const& dimFormula = *dimensions[dim].formula;
251 if (dimFormula.isBoundedUntilFormula()) {
252 STORM_LOG_ASSERT(!dimFormula.asBoundedUntilFormula().hasMultiDimensionalSubformulas(), "Expected single-dimensional subformula.");
253 if (dimFormula.asBoundedUntilFormula().hasUpperBound()) {
254 STORM_LOG_ASSERT(!dimFormula.asBoundedUntilFormula().hasLowerBound(), "Bounded until formulas with interval bounds are not supported.");
255 bound = dimFormula.asBoundedUntilFormula().getUpperBound();
256 isStrict = dimFormula.asBoundedUntilFormula().isUpperBoundStrict();
257 } else {
258 STORM_LOG_ASSERT(dimFormula.asBoundedUntilFormula().hasLowerBound(), "Bounded until formulas without any bounds are not supported.");
259 bound = dimFormula.asBoundedUntilFormula().getLowerBound();
260 isStrict = dimFormula.asBoundedUntilFormula().isLowerBoundStrict();
261 }
262 } else if (dimFormula.isCumulativeRewardFormula()) {
263 STORM_LOG_ASSERT(!dimFormula.asCumulativeRewardFormula().isMultiDimensional(), "Expected single-dimensional cumulative reward.");
264 bound = dimFormula.asCumulativeRewardFormula().getBound();
265 isStrict = dimFormula.asCumulativeRewardFormula().isBoundStrict();
266 }
267
268 if (!bound.containsVariables()) {
269 // We always consider upper bounds to be non-strict and lower bounds to be strict.
270 // Thus, >=N would become >N-1. However, note that the case N=0 is treated separately.
271 if (dimensions[dim].boundType == DimensionBoundType::LowerBound || dimensions[dim].boundType == DimensionBoundType::UpperBound) {
273 discretizedBound /= dimensions[dim].scalingFactor;
274 if (storm::utility::isInteger(discretizedBound)) {
275 if (isStrict == (dimensions[dim].boundType == DimensionBoundType::UpperBound)) {
276 discretizedBound -= storm::utility::one<ValueType>();
277 }
278 } else {
279 discretizedBound = storm::utility::floor(discretizedBound);
280 }
281 uint64_t dimensionValue = storm::utility::convertNumber<uint64_t>(discretizedBound);
282 STORM_LOG_THROW(epochManager.isValidDimensionValue(dimensionValue), storm::exceptions::NotSupportedException,
283 "The bound " << bound << " is too high for the considered number of dimensions.");
284 dimensions[dim].maxValue = dimensionValue;
285 }
286 }
287 }
288}
289
290template<typename ValueType, bool SingleObjectiveMode>
291void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::translateLowerBoundInfinityDimensions(std::vector<Epoch>& epochSteps) {
292 // Translate lowerBoundedByInfinity dimensions to finite bounds
293 storm::storage::BitVector infLowerBoundedDimensions(dimensions.size(), false);
294 storm::storage::BitVector upperBoundedDimensions(dimensions.size(), false);
295 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
296 infLowerBoundedDimensions.set(dim, dimensions[dim].boundType == DimensionBoundType::LowerBoundInfinity);
297 upperBoundedDimensions.set(dim, dimensions[dim].boundType == DimensionBoundType::UpperBound);
298 }
299 if (!infLowerBoundedDimensions.empty()) {
300 // We can currently only handle this case for single maximizing bounded until probability objectives.
301 // The approach is to erase all epochSteps that are not part of an end component and then change the reward bound to '> 0'.
302 // Then, reaching a reward means reaching an end component where arbitrarily many reward can be collected.
304 SingleObjectiveMode, storm::exceptions::NotSupportedException,
305 "Letting lower bounds approach infinity is only supported in single objective mode."); // It most likely also works with multiple objectives with
306 // the same shape. However, we haven't checked this yet.
307 STORM_LOG_THROW(objectives.front().formula->isProbabilityOperatorFormula(), storm::exceptions::NotSupportedException,
308 "Letting lower bounds approach infinity is only supported for probability operator formulas.");
309 auto const& probabilityOperatorFormula = objectives.front().formula->asProbabilityOperatorFormula();
310 STORM_LOG_THROW(probabilityOperatorFormula.getSubformula().isBoundedUntilFormula(), storm::exceptions::NotSupportedException,
311 "Letting lower bounds approach infinity is only supported for bounded until probabilities.");
313 (probabilityOperatorFormula.hasOptimalityType() && storm::solver::maximize(probabilityOperatorFormula.getOptimalityType())),
314 storm::exceptions::NotSupportedException,
315 "Letting lower bounds approach infinity is only supported for maximizing bounded until probabilities.");
316
317 STORM_LOG_THROW(upperBoundedDimensions.empty() || !probabilityOperatorFormula.getSubformula().asBoundedUntilFormula().hasMultiDimensionalSubformulas(),
318 storm::exceptions::NotSupportedException,
319 "Letting lower bounds approach infinity is only supported if the formula has either only lower bounds or if it has a single goal "
320 "state."); // This would fail because the upper bounded dimension(s) might be satisfied already. One should erase epoch steps in the
321 // epoch model (after applying the goal-unfolding).
322 storm::storage::BitVector choicesWithoutUpperBoundedStep(model.getNumberOfChoices(), true);
323 if (!upperBoundedDimensions.empty()) {
324 // To not invalidate upper-bounded dimensions, one needs to consider MECS where no reward for such a dimension is collected.
325 for (uint64_t choiceIndex = 0; choiceIndex < model.getNumberOfChoices(); ++choiceIndex) {
326 for (uint64_t dim : upperBoundedDimensions) {
327 if (epochManager.getDimensionOfEpoch(epochSteps[choiceIndex], dim) != 0) {
328 choicesWithoutUpperBoundedStep.set(choiceIndex, false);
329 break;
330 }
331 }
332 }
333 }
334 storm::storage::MaximalEndComponentDecomposition<ValueType> mecDecomposition(model.getTransitionMatrix(), model.getBackwardTransitions(),
335 storm::storage::BitVector(model.getNumberOfStates(), true),
336 choicesWithoutUpperBoundedStep);
337 storm::storage::BitVector nonMecChoices(model.getNumberOfChoices(), true);
338 for (auto const& mec : mecDecomposition) {
339 for (auto const& stateChoicesPair : mec) {
340 for (auto const& choice : stateChoicesPair.second) {
341 nonMecChoices.set(choice, false);
342 }
343 }
344 }
345 for (uint64_t choice : nonMecChoices) {
346 for (uint64_t dim : infLowerBoundedDimensions) {
347 epochManager.setDimensionOfEpoch(epochSteps[choice], dim, 0);
348 }
349 }
350
351 // Translate the dimension to '>0'
352 for (uint64_t dim : infLowerBoundedDimensions) {
353 dimensions[dim].boundType = DimensionBoundType::LowerBound;
354 dimensions[dim].maxValue = 0;
355 }
356 }
357}
358
359template<typename ValueType, bool SingleObjectiveMode>
361 bool setUnknownDimsToBottom) {
362 Epoch startEpoch = epochManager.getZeroEpoch();
363 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
364 if (dimensions[dim].maxValue) {
365 epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get());
366 } else {
367 STORM_LOG_THROW(setUnknownDimsToBottom || dimensions[dim].boundType == DimensionBoundType::Unbounded, storm::exceptions::UnexpectedException,
368 "Tried to obtain the start epoch although no bound on dimension " << dim << " is known.");
369 epochManager.setBottomDimension(startEpoch, dim);
370 }
371 }
372 STORM_LOG_TRACE("Start epoch is " << epochManager.toString(startEpoch));
373 return startEpoch;
374}
375
376template<typename ValueType, bool SingleObjectiveMode>
377std::vector<typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::Epoch>
379 // Perform a DFS to find all the reachable epochs
380 std::vector<Epoch> dfsStack;
381 std::set<Epoch, std::function<bool(Epoch const&, Epoch const&)>> collectedEpochs(
382 std::bind(&EpochManager::epochClassZigZagOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
383
384 if (!stopAtComputedEpochs || epochSolutions.count(startEpoch) == 0) {
385 collectedEpochs.insert(startEpoch);
386 dfsStack.push_back(startEpoch);
387 }
388 while (!dfsStack.empty()) {
389 Epoch currentEpoch = dfsStack.back();
390 dfsStack.pop_back();
391 for (auto const& step : possibleEpochSteps) {
392 Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step);
393 if (!stopAtComputedEpochs || epochSolutions.count(successorEpoch) == 0) {
394 if (collectedEpochs.insert(successorEpoch).second) {
395 dfsStack.push_back(std::move(successorEpoch));
396 }
397 }
398 }
399 }
400 return std::vector<Epoch>(collectedEpochs.begin(), collectedEpochs.end());
401}
402
403template<typename ValueType, bool SingleObjectiveMode>
405 STORM_LOG_DEBUG("Setting model for epoch " << epochManager.toString(epoch));
406
407 // Check if we need to update the current epoch class
408 if (!currentEpoch || !epochManager.compareEpochClass(epoch, currentEpoch.get())) {
409 setCurrentEpochClass(epoch);
410 epochModel.epochMatrixChanged = true;
411 STORM_LOG_STATISTICS_LAZY([this, &epoch](std::ostream& stream) {
412 if (storm::utility::graph::hasCycle(epochModel.epochMatrix)) {
413 stream << "Epoch model for epoch " << epochManager.toString(epoch) << " is cyclic.";
414 return true;
415 }
416 return false;
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:363
virtual bool isCumulativeRewardFormula() const
Definition Formula.cpp:158
virtual bool isBoundedUntilFormula() const
Definition Formula.cpp:90
CumulativeRewardFormula & asCumulativeRewardFormula()
Definition Formula.cpp:459
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:34
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:226
virtual uint_fast64_t getNumberOfChoices() const override
Returns the number of choices ine the model.
Definition Model.cpp:201
storm::storage::SparseMatrix< ValueType > getBackwardTransitions() const
Retrieves the backward transition relation of the model, i.e.
Definition Model.cpp:186
virtual bool hasRewardModel(std::string const &rewardModelName) const override
Retrieves whether the model has a reward model with the given name.
Definition Model.cpp:236
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:191
RewardModelType const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name, if one exists.
Definition Model.cpp:247
virtual std::string const & getUniqueRewardModelName() const override
Retrieves the name of the unique reward model, if there exists exactly one.
Definition Model.cpp:321
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:28
#define STORM_LOG_STATISTICS_LAZY(...)
Definition logging.h:75
#define STORM_LOG_TRACE(message)
Definition logging.h:22
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
SFTBDDChecker::ValueType ValueType
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:42
storm::storage::BitVector performProbGreater0(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Performs a backward depth-first search trough the underlying graph structure of the given model to de...
Definition graph.cpp:316
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:137
storm::storage::BitVector performProb1A(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 1 of satisfying phi until psi under all possible re...
Definition graph.cpp:1019
storm::storage::BitVector performProbGreater0E(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under at leas...
Definition graph.cpp:674
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:176
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< S > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:79
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:461
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:520
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:1008
ValueType floor(ValueType const &number)
bool isZero(ValueType const &a)
Definition constants.cpp:44
bool isInteger(ValueType const &number)
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
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