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;
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) {
98 dimension.
formula = subformula.restrictToDimension(dim);
100 std::string memLabel =
"dim" + std::to_string(dimensions.size()) +
"_maybe";
101 while (model.getStateLabeling().containsLabel(memLabel)) {
102 memLabel =
"_" + memLabel;
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.");
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.");
116 bool formulaUnbounded =
117 (!subformula.hasLowerBound(dim) && !subformula.hasUpperBound(dim)) ||
118 (subformula.hasLowerBound(dim) && !subformula.isLowerBoundStrict(dim) && !subformula.getLowerBound(dim).containsVariables() &&
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));
127 if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) {
128 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 1));
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()
138 "No reward model with name '" << rewardName <<
"' found.");
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());
144 dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first));
145 dimension.
scalingFactor = std::move(discretizedRewardsAndFactor.second);
147 if (subformula.hasLowerBound(dim)) {
148 if (subformula.getLowerBound(dim).isVariable() &&
149 infinityBoundVariables.count(subformula.getLowerBound(dim).getBaseExpression().asVariableExpression().getVariable()) > 0) {
158 dimensions.emplace_back(std::move(dimension));
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) {
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.");
172 if (subformula.getTimeBoundReference(dim).isTimeBound() || subformula.getTimeBoundReference(dim).isStepBound()) {
173 dimensionWiseEpochSteps.push_back(std::vector<uint64_t>(model.getTransitionMatrix().getRowCount(), 1));
176 STORM_LOG_ASSERT(subformula.getTimeBoundReference(dim).isRewardBound(),
"Unexpected type of time bound.");
177 std::string
const& rewardName = subformula.getTimeBoundReference(dim).getRewardName();
179 "No reward model with name '" << rewardName <<
"' found.");
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());
185 dimensionWiseEpochSteps.push_back(std::move(discretizedRewardsAndFactor.first));
186 dimension.
scalingFactor = std::move(discretizedRewardsAndFactor.second);
188 dimensions.emplace_back(std::move(dimension));
196 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
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();
206 for (uint64_t currDim = dim; currDim < dim + objDimensionCount; ++currDim) {
207 objDimensions.
set(currDim);
209 for (uint64_t currDim = dim; currDim < dim + objDimensionCount; ++currDim) {
211 dimensions[currDim].dependentDimensions = objDimensions;
214 dimensions[currDim].dependentDimensions.set(currDim,
true);
217 dim += objDimensionCount;
218 objectiveDimensions.push_back(std::move(objDimensions));
226 epochSteps.reserve(model.getTransitionMatrix().getRowCount());
227 for (uint64_t choice = 0; choice < model.getTransitionMatrix().getRowCount(); ++choice) {
230 for (
auto const& dimensionSteps : dimensionWiseEpochSteps) {
231 epochManager.setDimensionOfEpoch(step, dim, dimensionSteps[choice]);
234 epochSteps.push_back(step);
238 computeMaxDimensionValues();
239 translateLowerBoundInfinityDimensions(epochSteps);
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);
247template<
typename ValueType,
bool SingleObjectiveMode>
248void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::computeMaxDimensionValues() {
249 for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
251 bool isStrict =
false;
275 discretizedBound /= dimensions[dim].scalingFactor;
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;
292template<
typename ValueType,
bool SingleObjectiveMode>
293void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::translateLowerBoundInfinityDimensions(std::vector<Epoch>& epochSteps) {
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) {
301 if (!infLowerBoundedDimensions.empty()) {
306 SingleObjectiveMode, storm::exceptions::NotSupportedException,
307 "Letting lower bounds approach infinity is only supported in single objective mode.");
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.");
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 "
324 storm::storage::BitVector choicesWithoutUpperBoundedStep(model.
getNumberOfChoices(),
true);
325 if (!upperBoundedDimensions.empty()) {
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);
338 choicesWithoutUpperBoundedStep);
340 for (
auto const& mec : mecDecomposition) {
341 for (
auto const& stateChoicesPair : mec) {
342 for (
auto const& choice : stateChoicesPair.second) {
343 nonMecChoices.set(choice,
false);
347 for (uint64_t choice : nonMecChoices) {
348 for (uint64_t dim : infLowerBoundedDimensions) {
349 epochManager.setDimensionOfEpoch(epochSteps[choice], dim, 0);
354 for (uint64_t dim : infLowerBoundedDimensions) {
356 dimensions[dim].maxValue = 0;
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());
370 "Tried to obtain the start epoch although no bound on dimension " << dim <<
" is known.");
371 epochManager.setBottomDimension(startEpoch, dim);
374 STORM_LOG_TRACE(
"Start epoch is " << epochManager.toString(startEpoch));
378template<
typename ValueType,
bool SingleObjectiveMode>
379std::vector<typename MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::Epoch>
382 std::vector<Epoch> dfsStack;
383 std::set<
Epoch, std::function<bool(
Epoch const&,
Epoch const&)>> collectedEpochs(
386 if (!stopAtComputedEpochs || epochSolutions.count(startEpoch) == 0) {
387 collectedEpochs.insert(startEpoch);
388 dfsStack.push_back(startEpoch);
390 while (!dfsStack.empty()) {
391 Epoch currentEpoch = dfsStack.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));
402 return std::vector<Epoch>(collectedEpochs.begin(), collectedEpochs.end());
405template<
typename ValueType,
bool SingleObjectiveMode>
407 STORM_LOG_DEBUG(
"Setting model for epoch " << epochManager.toString(epoch));
410 if (!currentEpoch || !epochManager.compareEpochClass(epoch, currentEpoch.get())) {
411 setCurrentEpochClass(epoch);
412 epochModel.epochMatrixChanged =
true;
415 std::cout <<
"Epoch model for epoch " << epochManager.toString(epoch) <<
" is cyclic.\n";
419 epochModel.epochMatrixChanged =
false;
422 bool containsLowerBoundedObjective =
false;
423 for (
auto const& dimension : dimensions) {
425 containsLowerBoundedObjective =
true;
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);
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);
450 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
453 for (
auto dim : objectiveDimensions[objIndex]) {
455 productModel->getMemoryStateManager().isRelevantDimension(memoryState, dim)) {
456 rewardEarned =
false;
461 epochModel.objectiveRewardFilter[objIndex].set(reducedChoice, rewardEarned);
465 EpochSolution
const& successorEpochSolution = getEpochSolution(subSolutions, successorEpoch);
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;
474 addScaledSolution(choiceSolution, getStateSolution(successorEpochSolution, successor.getColumn()), successor.getValue());
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;
485 addScaledSolution(choiceSolution, successorSolution, successor.getValue());
490 *stepSolIt = std::move(choiceSolution);
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.");
505 currentEpoch = epoch;
520template<
typename ValueType,
bool SingleObjectiveMode>
521void MultiDimensionalRewardUnfolding<ValueType, SingleObjectiveMode>::setCurrentEpochClass(Epoch
const& epoch) {
522 EpochClass epochClass = epochManager.getEpochClass(epoch);
524 auto productObjectiveRewards = productModel->computeObjectiveRewards(epochClass, objectives);
528 for (
auto const& step : productModel->getSteps()) {
529 if (!epochManager.isZeroEpoch(step) && epochManager.getSuccessorEpoch(epoch, step) != epoch) {
530 stepChoices.set(choice,
true);
534 epochModel.epochMatrix = productModel->getProduct().getTransitionMatrix().filterEntries(~stepChoices);
537 for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
539 violatedLowerBoundedDimensions.set(dim);
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));
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])) {
557 storm::storage::BitVector allProductStates(productModel->getProduct().getNumberOfStates(),
true);
560 storm::storage::BitVector productInStates = productModel->getInStates(epochClass);
562 storm::storage::BitVector consideredStates =
567 ~zeroObjRewardChoices & ~stepChoices),
568 "There is a scheduler that yields infinite reward for one objective. This case should be excluded");
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.");
579 auto backwardTransitions = epochModel.epochMatrix.transpose(
true);
580 storm::storage::BitVector nonZeroRewardStates =
583 bool requiresZeroRewardState = nonZeroRewardStates != consideredStates;
585 uint64_t zeroRewardInState = numEpochModelStates;
586 if (requiresZeroRewardState) {
587 ++numEpochModelStates;
589 storm::storage::SparseMatrixBuilder<ValueType> builder;
590 if (!nonZeroRewardStates.
empty()) {
591 builder = storm::storage::SparseMatrixBuilder<ValueType>(
592 epochModel.epochMatrix.getSubmatrix(
true, nonZeroRewardStates, nonZeroRewardStates, convertToEquationSystem));
594 if (requiresZeroRewardState) {
595 if (convertToEquationSystem) {
599 epochModel.epochMatrix = builder.
build(numEpochModelStates, numEpochModelStates);
602 epochModel.epochMatrix = builder.
build();
604 if (convertToEquationSystem) {
605 epochModel.epochMatrix.convertToEquationSystem();
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);
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);
623 zeroObjRewardChoices & ~stepChoices, consideredStates);
624 epochModel.epochMatrix = std::move(ecElimResult.matrix);
625 epochModelToProductChoiceMap = std::move(ecElimResult.newToOldRowMapping);
626 productToEpochModelStateMapping = std::move(ecElimResult.oldToNewStateMapping);
628 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Unsupported model type.");
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);
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]);
647 if (!violatedLowerBoundedDimensions.isDisjointFrom(objectiveDimensions[objIndex])) {
650 epochModel.objectiveRewards.push_back(std::move(reducedModelObjRewards));
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);
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]];
665 productStateToEpochModelInStateMap = std::make_shared<std::vector<uint64_t>
const>(std::move(toEpochModelInStatesMap));
667 epochModel.objectiveRewardFilter.clear();
668 for (
auto const& objRewards : epochModel.objectiveRewards) {
670 epochModel.objectiveRewardFilter.back().complement();
674template<
typename ValueType,
bool SingleObjectiveMode>
678 epochModel.equationSolverProblemFormat = eqSysFormat;
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;
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 {
693 res.reserve(solution.size());
694 for (
auto const& sol : solution) {
695 res.push_back(sol * scalingFactor);
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;
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 {
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.");
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;
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();
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;
744 for (
auto const& s : solution) {
748 stringstream <<
", ";
753 return stringstream.str();
756template<
typename ValueType,
bool SingleObjectiveMode>
761template<
typename ValueType,
bool SingleObjectiveMode>
763 auto& objective = this->objectives[objectiveIndex];
765 if (objective.
formula->isProbabilityOperatorFormula()) {
767 }
else if (objective.
formula->isRewardOperatorFormula()) {
768 auto const& rewModel = this->model.
getRewardModel(objective.
formula->asRewardOperatorFormula().getRewardModelName());
770 if (objective.
formula->getSubformula().isCumulativeRewardFormula()) {
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()) {
781 bool isFinite =
true;
782 for (
auto rewIt = actionRewards.begin(), costIt = actionCosts.begin(); rewIt != actionRewards.end(); ++rewIt, ++costIt) {
788 ValueType rewardPerCost = *rewIt / *costIt;
789 largestRewardPerCost = std::max(largestRewardPerCost, rewardPerCost);
793 resBound = largestRewardPerCost * rewardBound;
797 resBound = (*std::max_element(actionRewards.begin(), actionRewards.end())) * rewardBound;
808 auto nonZeroRewardStates = rewModel.getStatesWithZeroReward(model.getTransitionMatrix());
809 nonZeroRewardStates.complement();
812 auto zeroRewardChoices = rewModel.getChoicesWithZeroReward(model.getTransitionMatrix());
814 zeroRewardChoices, ~allStates);
815 allStates.
resize(ecElimRes.matrix.getRowGroupCount());
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];
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())) {
828 outStates.
set(state,
true);
841 allStates, outStates)
843 std::vector<ValueType> rewards;
844 rewards.reserve(ecElimRes.matrix.getRowCount());
845 for (
auto row : ecElimRes.newToOldRowMapping) {
846 rewards.push_back(actionRewards[row]);
858template<
typename ValueType,
bool SingleObjectiveMode>
860 auto& objective = this->objectives[objectiveIndex];
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.");
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));
877 predecessorEpochs.erase(currentEpoch.get());
878 successorEpochs.erase(currentEpoch.get());
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);
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);
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);
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;
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]];
923template<
typename ValueType,
bool SingleObjectiveMode>
926 STORM_LOG_ASSERT(model.getInitialStates().getNumberOfSetBits() == 1,
"The model has multiple initial states.");
930template<
typename ValueType,
bool SingleObjectiveMode>
933 STORM_LOG_ASSERT(model.getInitialStates().get(initialStateIndex),
"The given model state is not an initial state.");
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)) {
939 bool objectiveHolds =
true;
940 for (
auto dim : objectiveDimensions[objIndex]) {
942 objectiveHolds =
false;
944 objectiveHolds =
false;
948 if (objectiveHolds) {
956template<
typename ValueType,
bool SingleObjectiveMode>
961template<
typename ValueType,
bool SingleObjectiveMode>
963 return dimensions.at(dim);