#include "storm/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h" #include "storm/adapters/CarlAdapter.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/utility/macros.h" #include "storm/utility/vector.h" #include "storm/exceptions/InvalidPropertyException.h" namespace storm { namespace modelchecker { namespace multiobjective { template SparseMdpPcaaWeightVectorChecker::SparseMdpPcaaWeightVectorChecker(SparseMdpModelType const& model, std::vector> const& objectives, storm::storage::BitVector const& actionsWithNegativeReward, storm::storage::BitVector const& ecActions, storm::storage::BitVector const& possiblyRecurrentStates) : SparsePcaaWeightVectorChecker(model, objectives, actionsWithNegativeReward, ecActions, possiblyRecurrentStates) { // set the state action rewards for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { typename SparseMdpModelType::RewardModelType const& rewModel = this->model.getRewardModel(this->objectives[objIndex].rewardModelName); STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Reward model has transition rewards which is not expected."); this->discreteActionRewards[objIndex] = rewModel.getTotalRewardVector(this->model.getTransitionMatrix()); } } template void SparseMdpPcaaWeightVectorChecker::boundedPhase(std::vector const& weightVector, std::vector& weightedRewardVector) { // Allocate some memory so this does not need to happen for each time epoch std::vector optimalChoicesInCurrentEpoch(this->model.getNumberOfStates()); std::vector choiceValues(weightedRewardVector.size()); std::vector temporaryResult(this->model.getNumberOfStates()); std::vector zeroReward(weightedRewardVector.size(), storm::utility::zero()); // Get for each occurring timeBound the indices of the objectives with that bound. std::map> stepBounds; for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { auto const& obj = this->objectives[objIndex]; STORM_LOG_THROW(!obj.lowerTimeBound, storm::exceptions::InvalidPropertyException, "Lower step bounds are not supported by this model checker"); if (obj.upperTimeBound) { STORM_LOG_THROW(!obj.upperTimeBound->containsVariables(), storm::exceptions::InvalidPropertyException, "The step bound '" << * obj.upperTimeBound << " contains undefined variables"); uint_fast64_t stepBound = (uint_fast64_t) obj.upperTimeBound->evaluateAsInt(); if (obj.upperTimeBoundStrict) { --stepBound; } auto stepBoundIt = stepBounds.insert(std::make_pair(stepBound, storm::storage::BitVector(this->objectives.size(), false))).first; stepBoundIt->second.set(objIndex); // There is no error for the values of these objectives. this->offsetsToLowerBound[objIndex] = storm::utility::zero(); this->offsetsToUpperBound[objIndex] = storm::utility::zero(); } } // Stores the objectives for which we need to compute values in the current time epoch. storm::storage::BitVector consideredObjectives = this->objectivesWithNoUpperTimeBound; // Stores objectives for which the current epoch passed their lower bound storm::storage::BitVector lowerBoundViolatedObjectives(consideredObjectives.size(), false); auto stepBoundIt = stepBounds.begin(); uint_fast64_t currentEpoch = stepBounds.empty() ? 0 : stepBoundIt->first; while (currentEpoch > 0) { if (stepBoundIt != stepBounds.end() && currentEpoch == stepBoundIt->first) { consideredObjectives |= stepBoundIt->second; for(auto objIndex : stepBoundIt->second) { // This objective now plays a role in the weighted sum storm::utility::vector::addScaledVector(weightedRewardVector, this->discreteActionRewards[objIndex], weightVector[objIndex]); } ++stepBoundIt; } // Get values and scheduler for weighted sum of objectives this->model.getTransitionMatrix().multiplyWithVector(this->weightedResult, choiceValues); storm::utility::vector::addVectors(choiceValues, weightedRewardVector, choiceValues); storm::utility::vector::reduceVectorMax(choiceValues, this->weightedResult, this->model.getTransitionMatrix().getRowGroupIndices(), &optimalChoicesInCurrentEpoch); // get values for individual objectives // TODO we could compute the result for one of the objectives from the weighted result, the given weight vector, and the remaining objective results. for (auto objIndex : consideredObjectives) { std::vector& objectiveResult = this->objectiveResults[objIndex]; std::vector const& objectiveRewards = lowerBoundViolatedObjectives.get(objIndex) ? zeroReward : this->discreteActionRewards[objIndex]; auto rowGroupIndexIt = this->model.getTransitionMatrix().getRowGroupIndices().begin(); auto optimalChoiceIt = optimalChoicesInCurrentEpoch.begin(); for(ValueType& stateValue : temporaryResult){ uint_fast64_t row = (*rowGroupIndexIt) + (*optimalChoiceIt); ++rowGroupIndexIt; ++optimalChoiceIt; stateValue = objectiveRewards[row]; for(auto const& entry : this->model.getTransitionMatrix().getRow(row)) { stateValue += entry.getValue() * objectiveResult[entry.getColumn()]; } } objectiveResult.swap(temporaryResult); } --currentEpoch; } } template class SparseMdpPcaaWeightVectorChecker>; #ifdef STORM_HAVE_CARL template class SparseMdpPcaaWeightVectorChecker>; #endif } } }