Browse Source

Moved reduction of the preprocessed model into the weightvectorchecker

main
TimQu 8 years ago
parent
commit
721dd37a62
  1. 31
      src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessor.cpp
  2. 2
      src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessorResult.h
  3. 38
      src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp
  4. 2
      src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.h
  5. 32
      src/storm/modelchecker/multiobjective/constraintbased/SparseCbQuery.cpp
  6. 8
      src/storm/modelchecker/multiobjective/constraintbased/SparseCbQuery.h
  7. 25
      src/storm/modelchecker/multiobjective/pcaa/PcaaWeightVectorChecker.cpp
  8. 16
      src/storm/modelchecker/multiobjective/pcaa/PcaaWeightVectorChecker.h
  9. 77
      src/storm/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.cpp
  10. 13
      src/storm/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h
  11. 148
      src/storm/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.cpp
  12. 31
      src/storm/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h
  13. 8
      src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp
  14. 1
      src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h
  15. 193
      src/storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.cpp
  16. 47
      src/storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h

31
src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessor.cpp

@ -12,7 +12,6 @@
#include "storm/storage/memorystructure/MemoryStructureBuilder.h" #include "storm/storage/memorystructure/MemoryStructureBuilder.h"
#include "storm/storage/memorystructure/SparseModelMemoryProduct.h" #include "storm/storage/memorystructure/SparseModelMemoryProduct.h"
#include "storm/storage/expressions/ExpressionManager.h" #include "storm/storage/expressions/ExpressionManager.h"
#include "storm/transformer/GoalStateMerger.h"
#include "storm/utility/macros.h" #include "storm/utility/macros.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
#include "storm/utility/graph.h" #include "storm/utility/graph.h"
@ -388,36 +387,6 @@ namespace storm {
setReward0States(result, backwardTransitions); setReward0States(result, backwardTransitions);
checkRewardFiniteness(result, data.finiteRewardCheckObjectives, backwardTransitions); checkRewardFiniteness(result, data.finiteRewardCheckObjectives, backwardTransitions);
/////
std::set<std::string> relevantRewardModels;
for (auto const& obj : result.objectives) {
obj.formula->gatherReferencedRewardModels(relevantRewardModels);
}
STORM_LOG_THROW(result.rewardFinitenessType != ReturnType::RewardFinitenessType::Infinite, storm::exceptions::InvalidPropertyException, "Infinite rewards unsupported.");
if (result.containsOnlyRewardObjectives()) {
// Build a subsystem that discards states that yield infinite reward for all schedulers.
// We can also merge the states that will have reward zero anyway.
storm::storage::BitVector maybeStates = result.rewardLessInfinityEStates.get() & ~result.reward0AStates;
storm::transformer::GoalStateMerger<SparseModelType> merger(*result.preprocessedModel);
typename storm::transformer::GoalStateMerger<SparseModelType>::ReturnType mergerResult = merger.mergeTargetAndSinkStates(maybeStates, result.reward0AStates, storm::storage::BitVector(maybeStates.size(), false), std::vector<std::string>(relevantRewardModels.begin(), relevantRewardModels.end()));
result.preprocessedModel = mergerResult.model;
result.reward0EStates = result.reward0EStates % maybeStates;
if (mergerResult.targetState) {
storm::storage::BitVector targetStateAsVector(result.preprocessedModel->getNumberOfStates(), false);
targetStateAsVector.set(*mergerResult.targetState, true);
// The overapproximation for the possible ec choices consists of the states that can reach the target states with prob. 0 and the target state itself.
//result.ecChoicesHint = result.preprocessedModel->getTransitionMatrix().getRowFilter(storm::utility::graph::performProb0E(*result.preprocessedModel, result.preprocessedModel->getBackwardTransitions(), storm::storage::BitVector(targetStateAsVector.size(), true), targetStateAsVector));
//result.ecChoicesHint->set(result.preprocessedModel->getTransitionMatrix().getRowGroupIndices()[*mergerResult.targetState], true);
// There is an additional state in the result
result.reward0EStates.resize(result.reward0EStates.size() + 1, true);
}
assert(result.reward0EStates.size() == result.preprocessedModel->getNumberOfStates());
}
return result; return result;
} }

2
src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessorResult.h

@ -49,7 +49,7 @@ namespace storm {
// Intentionally left empty // Intentionally left empty
} }
bool containsOnlyRewardObjectives() { bool containsOnlyRewardObjectives() const {
for (auto const& obj : objectives) { for (auto const& obj : objectives) {
if (!(obj.formula->isRewardOperatorFormula() && (obj.formula->getSubformula().isTotalRewardFormula() || obj.formula->getSubformula().isCumulativeRewardFormula()))) { if (!(obj.formula->isRewardOperatorFormula() && (obj.formula->getSubformula().isTotalRewardFormula() || obj.formula->getSubformula().isCumulativeRewardFormula()))) {
return false; return false;

38
src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp

@ -25,7 +25,7 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
SparseCbAchievabilityQuery<SparseModelType>::SparseCbAchievabilityQuery(SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult) : SparseCbQuery<SparseModelType>(preprocessorResult) { SparseCbAchievabilityQuery<SparseModelType>::SparseCbAchievabilityQuery(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult) : SparseCbQuery<SparseModelType>(preprocessorResult) {
STORM_LOG_ASSERT(preprocessorResult.queryType == SparseMultiObjectivePreprocessorResult<SparseModelType>::QueryType::Achievability, "Invalid query Type"); STORM_LOG_ASSERT(preprocessorResult.queryType == SparseMultiObjectivePreprocessorResult<SparseModelType>::QueryType::Achievability, "Invalid query Type");
solver = storm::utility::solver::SmtSolverFactory().create(*this->expressionManager); solver = storm::utility::solver::SmtSolverFactory().create(*this->expressionManager);
} }
@ -42,7 +42,7 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
bool SparseCbAchievabilityQuery<SparseModelType>::checkAchievability() { bool SparseCbAchievabilityQuery<SparseModelType>::checkAchievability() {
STORM_LOG_INFO("Building constraint system to check achievability."); STORM_LOG_INFO("Building constraint system to check achievability.");
//this->preprocessedModel.writeDotToStream(std::cout); //this->preprocessedModel->writeDotToStream(std::cout);
storm::utility::Stopwatch swInitialization(true); storm::utility::Stopwatch swInitialization(true);
initializeConstraintSystem(); initializeConstraintSystem();
STORM_LOG_INFO("Constraint system consists of " << expectedChoiceVariables.size() << " + " << bottomStateVariables.size() << " variables"); STORM_LOG_INFO("Constraint system consists of " << expectedChoiceVariables.size() << " + " << bottomStateVariables.size() << " variables");
@ -78,9 +78,9 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
void SparseCbAchievabilityQuery<SparseModelType>::initializeConstraintSystem() { void SparseCbAchievabilityQuery<SparseModelType>::initializeConstraintSystem() {
uint_fast64_t numStates = this->preprocessedModel.getNumberOfStates(); uint_fast64_t numStates = this->preprocessedModel->getNumberOfStates();
uint_fast64_t numChoices = this->preprocessedModel.getNumberOfChoices(); uint_fast64_t numChoices = this->preprocessedModel->getNumberOfChoices();
uint_fast64_t numBottomStates = this->possibleBottomStates.getNumberOfSetBits(); uint_fast64_t numBottomStates = this->reward0EStates.getNumberOfSetBits();
storm::expressions::Expression zero = this->expressionManager->rational(storm::utility::zero<ValueType>()); storm::expressions::Expression zero = this->expressionManager->rational(storm::utility::zero<ValueType>());
storm::expressions::Expression one = this->expressionManager->rational(storm::utility::one<ValueType>()); storm::expressions::Expression one = this->expressionManager->rational(storm::utility::one<ValueType>());
@ -107,20 +107,20 @@ namespace storm {
solver->add(bottomStateSum == one); solver->add(bottomStateSum == one);
// assert that the "incoming" value of each state equals the "outgoing" value // assert that the "incoming" value of each state equals the "outgoing" value
storm::storage::SparseMatrix<ValueType> backwardsTransitions = this->preprocessedModel.getTransitionMatrix().transpose(); storm::storage::SparseMatrix<ValueType> backwardsTransitions = this->preprocessedModel->getTransitionMatrix().transpose();
auto bottomStateVariableIt = bottomStateVariables.begin(); auto bottomStateVariableIt = bottomStateVariables.begin();
for (uint_fast64_t state = 0; state < numStates; ++state) { for (uint_fast64_t state = 0; state < numStates; ++state) {
// get the "incomming" value // get the "incomming" value
storm::expressions::Expression value = this->preprocessedModel.getInitialStates().get(state) ? one : zero; storm::expressions::Expression value = this->preprocessedModel->getInitialStates().get(state) ? one : zero;
for (auto const& backwardsEntry : backwardsTransitions.getRow(state)) { for (auto const& backwardsEntry : backwardsTransitions.getRow(state)) {
value = value + (this->expressionManager->rational(backwardsEntry.getValue()) * expectedChoiceVariables[backwardsEntry.getColumn()].getExpression()); value = value + (this->expressionManager->rational(backwardsEntry.getValue()) * expectedChoiceVariables[backwardsEntry.getColumn()].getExpression());
} }
// subtract the "outgoing" value // subtract the "outgoing" value
for (uint_fast64_t choice = this->preprocessedModel.getTransitionMatrix().getRowGroupIndices()[state]; choice < this->preprocessedModel.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) { for (uint_fast64_t choice = this->preprocessedModel->getTransitionMatrix().getRowGroupIndices()[state]; choice < this->preprocessedModel->getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
value = value - expectedChoiceVariables[choice]; value = value - expectedChoiceVariables[choice];
} }
if (this->possibleBottomStates.get(state)) { if (this->reward0EStates.get(state)) {
value = value - (*bottomStateVariableIt); value = value - (*bottomStateVariableIt);
++bottomStateVariableIt; ++bottomStateVariableIt;
} }
@ -167,18 +167,18 @@ namespace storm {
template <> template <>
std::vector<double> SparseCbAchievabilityQuery<storm::models::sparse::Mdp<double>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const { std::vector<double> SparseCbAchievabilityQuery<storm::models::sparse::Mdp<double>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const {
return this->preprocessedModel.getRewardModel(rewardModelName).getTotalRewardVector(this->preprocessedModel.getTransitionMatrix()); return this->preprocessedModel->getRewardModel(rewardModelName).getTotalRewardVector(this->preprocessedModel->getTransitionMatrix());
} }
template <> template <>
std::vector<double> SparseCbAchievabilityQuery<storm::models::sparse::MarkovAutomaton<double>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const { std::vector<double> SparseCbAchievabilityQuery<storm::models::sparse::MarkovAutomaton<double>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const {
auto const& rewModel = this->preprocessedModel.getRewardModel(rewardModelName); auto const& rewModel = this->preprocessedModel->getRewardModel(rewardModelName);
STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected."); STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected.");
std::vector<double> result = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector<double>(this->preprocessedModel.getNumberOfChoices(), storm::utility::zero<ValueType>()); std::vector<double> result = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector<double>(this->preprocessedModel->getNumberOfChoices(), storm::utility::zero<ValueType>());
if(rewModel.hasStateRewards()) { if(rewModel.hasStateRewards()) {
// Note that state rewards are earned over time and thus play no role for probabilistic states // Note that state rewards are earned over time and thus play no role for probabilistic states
for(auto markovianState : this->preprocessedModel.getMarkovianStates()) { for(auto markovianState : this->preprocessedModel->getMarkovianStates()) {
result[this->preprocessedModel.getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / this->preprocessedModel.getExitRate(markovianState); result[this->preprocessedModel->getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / this->preprocessedModel->getExitRate(markovianState);
} }
} }
return result; return result;
@ -186,13 +186,13 @@ namespace storm {
template <> template <>
std::vector<storm::RationalNumber> SparseCbAchievabilityQuery<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const { std::vector<storm::RationalNumber> SparseCbAchievabilityQuery<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const {
auto const& rewModel = this->preprocessedModel.getRewardModel(rewardModelName); auto const& rewModel = this->preprocessedModel->getRewardModel(rewardModelName);
STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected."); STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected.");
std::vector<storm::RationalNumber> result = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector<storm::RationalNumber>(this->preprocessedModel.getNumberOfChoices(), storm::utility::zero<ValueType>()); std::vector<storm::RationalNumber> result = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector<storm::RationalNumber>(this->preprocessedModel->getNumberOfChoices(), storm::utility::zero<ValueType>());
if(rewModel.hasStateRewards()) { if(rewModel.hasStateRewards()) {
// Note that state rewards are earned over time and thus play no role for probabilistic states // Note that state rewards are earned over time and thus play no role for probabilistic states
for(auto markovianState : this->preprocessedModel.getMarkovianStates()) { for(auto markovianState : this->preprocessedModel->getMarkovianStates()) {
result[this->preprocessedModel.getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / this->preprocessedModel.getExitRate(markovianState); result[this->preprocessedModel->getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / this->preprocessedModel->getExitRate(markovianState);
} }
} }
return result; return result;
@ -200,7 +200,7 @@ namespace storm {
template <> template <>
std::vector<storm::RationalNumber> SparseCbAchievabilityQuery<storm::models::sparse::Mdp<storm::RationalNumber>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const { std::vector<storm::RationalNumber> SparseCbAchievabilityQuery<storm::models::sparse::Mdp<storm::RationalNumber>>::getActionBasedExpectedRewards(std::string const& rewardModelName) const {
return this->preprocessedModel.getRewardModel(rewardModelName).getTotalRewardVector(this->preprocessedModel.getTransitionMatrix()); return this->preprocessedModel->getRewardModel(rewardModelName).getTotalRewardVector(this->preprocessedModel->getTransitionMatrix());
} }

2
src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.h

@ -19,7 +19,7 @@ namespace storm {
typedef typename SparseModelType::ValueType ValueType; typedef typename SparseModelType::ValueType ValueType;
SparseCbAchievabilityQuery(SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult); SparseCbAchievabilityQuery(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult);
virtual ~SparseCbAchievabilityQuery() = default; virtual ~SparseCbAchievabilityQuery() = default;

32
src/storm/modelchecker/multiobjective/constraintbased/SparseCbQuery.cpp

@ -9,8 +9,10 @@
#include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h"
#include "storm/utility/constants.h" #include "storm/utility/constants.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
#include "storm/transformer/GoalStateMerger.h"
#include "storm/exceptions/UnexpectedException.h" #include "storm/exceptions/UnexpectedException.h"
#include "storm/exceptions/NotSupportedException.h"
namespace storm { namespace storm {
namespace modelchecker { namespace modelchecker {
@ -18,13 +20,31 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
SparseCbQuery<SparseModelType>::SparseCbQuery(SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult) : SparseCbQuery<SparseModelType>::SparseCbQuery(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult) :
originalModel(preprocessorResult.originalModel), originalFormula(preprocessorResult.originalFormula), originalModel(preprocessorResult.originalModel), originalFormula(preprocessorResult.originalFormula), objectives(std::move(preprocessorResult.objectives)) {
preprocessedModel(std::move(*preprocessorResult.preprocessedModel)), objectives(std::move(preprocessorResult.objectives)), STORM_LOG_THROW(preprocessorResult.rewardFinitenessType != SparseMultiObjectivePreprocessorResult<SparseModelType>::RewardFinitenessType::Infinite, storm::exceptions::NotSupportedException, "There is no Pareto optimal scheduler that yields finite reward for all objectives. This is not supported.");
possibleBottomStates(std::move(preprocessorResult.reward0EStates)) { STORM_LOG_THROW(preprocessorResult.rewardLessInfinityEStates, storm::exceptions::UnexpectedException, "The set of states with reward < infinity for some scheduler has not been computed during preprocessing.");
STORM_LOG_THROW(preprocessorResult.containsOnlyRewardObjectives(), storm::exceptions::NotSupportedException, "At least one objective was not reduced to an expected (total or cumulative) reward objective during preprocessing. This is not supported by the considered weight vector checker.");
STORM_LOG_THROW(preprocessorResult.preprocessedModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "The model has multiple initial states.");
// Build a subsystem of the preprocessor result model that discards states that yield infinite reward for all schedulers.
// We can also merge the states that will have reward zero anyway.
storm::storage::BitVector maybeStates = preprocessorResult.rewardLessInfinityEStates.get() & ~preprocessorResult.reward0AStates;
std::set<std::string> relevantRewardModels;
for (auto const& obj : this->objectives) {
obj.formula->gatherReferencedRewardModels(relevantRewardModels);
}
storm::transformer::GoalStateMerger<SparseModelType> merger(*preprocessorResult.preprocessedModel);
auto mergerResult = merger.mergeTargetAndSinkStates(maybeStates, preprocessorResult.reward0AStates, storm::storage::BitVector(maybeStates.size(), false), std::vector<std::string>(relevantRewardModels.begin(), relevantRewardModels.end()));
preprocessedModel = mergerResult.model;
reward0EStates = preprocessorResult.reward0EStates % maybeStates;
if (mergerResult.targetState) {
// There is an additional state in the result
reward0EStates.resize(reward0EStates.size() + 1, true);
}
expressionManager = std::make_shared<storm::expressions::ExpressionManager>(); expressionManager = std::make_shared<storm::expressions::ExpressionManager>();
STORM_LOG_WARN("TODO");
std::cout << "TODO" << std::endl;
} }

8
src/storm/modelchecker/multiobjective/constraintbased/SparseCbQuery.h

@ -27,17 +27,15 @@ namespace storm {
protected: protected:
SparseCbQuery(SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult); SparseCbQuery(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult);
SparseModelType const& originalModel; SparseModelType const& originalModel;
storm::logic::MultiObjectiveFormula const& originalFormula; storm::logic::MultiObjectiveFormula const& originalFormula;
SparseModelType preprocessedModel;
std::vector<Objective<typename SparseModelType::ValueType>> objectives; std::vector<Objective<typename SparseModelType::ValueType>> objectives;
std::shared_ptr<SparseModelType> preprocessedModel;
storm::storage::BitVector reward0EStates;
std::shared_ptr<storm::expressions::ExpressionManager> expressionManager; std::shared_ptr<storm::expressions::ExpressionManager> expressionManager;
storm::storage::BitVector possibleBottomStates;
}; };

25
src/storm/modelchecker/multiobjective/pcaa/PcaaWeightVectorChecker.cpp

@ -11,7 +11,7 @@ namespace storm {
namespace multiobjective { namespace multiobjective {
template <typename ModelType> template <typename ModelType>
PcaaWeightVectorChecker<ModelType>::PcaaWeightVectorChecker(ModelType const& model, std::vector<Objective<ValueType>> const& objectives) : model(model), objectives(objectives) { PcaaWeightVectorChecker<ModelType>::PcaaWeightVectorChecker(std::vector<Objective<ValueType>> const& objectives) : objectives(objectives) {
// Intentionally left empty // Intentionally left empty
} }
@ -32,21 +32,14 @@ namespace storm {
template <typename ModelType> template <typename ModelType>
template<typename VT, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::Mdp<VT>>::value, int>::type> template<typename VT, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::Mdp<VT>>::value, int>::type>
std::unique_ptr<PcaaWeightVectorChecker<ModelType>> WeightVectorCheckerFactory<ModelType>::create(ModelType const& model, std::unique_ptr<PcaaWeightVectorChecker<ModelType>> WeightVectorCheckerFactory<ModelType>::create(SparseMultiObjectivePreprocessorResult<ModelType> const& preprocessorResult) {
std::vector<Objective<typename ModelType::ValueType>> const& objectives, return std::make_unique<SparseMdpPcaaWeightVectorChecker<ModelType>>(preprocessorResult);
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates) {
return std::make_unique<SparseMdpPcaaWeightVectorChecker<ModelType>>(model, objectives, possibleECActions, possibleBottomStates);
} }
template <typename ModelType> template <typename ModelType>
template<typename VT, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::MarkovAutomaton<VT>>::value, int>::type> template<typename VT, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::MarkovAutomaton<VT>>::value, int>::type>
std::unique_ptr<PcaaWeightVectorChecker<ModelType>> WeightVectorCheckerFactory<ModelType>::create(ModelType const& model, std::unique_ptr<PcaaWeightVectorChecker<ModelType>> WeightVectorCheckerFactory<ModelType>::create(SparseMultiObjectivePreprocessorResult<ModelType> const& preprocessorResult) {
std::vector<Objective<typename ModelType::ValueType>> const& objectives, return std::make_unique<SparseMaPcaaWeightVectorChecker<ModelType>>(preprocessorResult);
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates) {
return std::make_unique<SparseMaPcaaWeightVectorChecker<ModelType>>(model, objectives, possibleECActions, possibleBottomStates);
} }
template class PcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>; template class PcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>;
@ -59,10 +52,10 @@ namespace storm {
template class WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<double>>; template class WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<double>>;
template class WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>; template class WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>;
template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>> WeightVectorCheckerFactory<storm::models::sparse::Mdp<double>>::create(storm::models::sparse::Mdp<double> const& model, std::vector<Objective<double>> const& objectives, storm::storage::BitVector const& possibleECActions, storm::storage::BitVector const& possibleBottomStates); template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>> WeightVectorCheckerFactory<storm::models::sparse::Mdp<double>>::create(SparseMultiObjectivePreprocessorResult<storm::models::sparse::Mdp<double>> const& preprocessorResult);
template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::Mdp<storm::RationalNumber>>> WeightVectorCheckerFactory<storm::models::sparse::Mdp<storm::RationalNumber>>::create(storm::models::sparse::Mdp<storm::RationalNumber> const& model, std::vector<Objective<storm::RationalNumber>> const& objectives, storm::storage::BitVector const& possibleECActions, storm::storage::BitVector const& possibleBottomStates); template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::Mdp<storm::RationalNumber>>> WeightVectorCheckerFactory<storm::models::sparse::Mdp<storm::RationalNumber>>::create(SparseMultiObjectivePreprocessorResult<storm::models::sparse::Mdp<storm::RationalNumber>> const& preprocessorResult);
template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>> WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<double>>::create(storm::models::sparse::MarkovAutomaton<double> const& model, std::vector<Objective<double>> const& objectives, storm::storage::BitVector const& possibleECActions, storm::storage::BitVector const& possibleBottomStates); template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<double>>> WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<double>>::create(SparseMultiObjectivePreprocessorResult<storm::models::sparse::MarkovAutomaton<double>> const& preprocessorResult);
template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>> WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::create(storm::models::sparse::MarkovAutomaton<storm::RationalNumber> const& model, std::vector<Objective<storm::RationalNumber>> const& objectives, storm::storage::BitVector const& possibleECActions, storm::storage::BitVector const& possibleBottomStates); template std::unique_ptr<PcaaWeightVectorChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>> WeightVectorCheckerFactory<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::create(SparseMultiObjectivePreprocessorResult<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>> const& preprocessorResult);
} }

16
src/storm/modelchecker/multiobjective/pcaa/PcaaWeightVectorChecker.h

@ -31,7 +31,7 @@ namespace storm {
* *
*/ */
PcaaWeightVectorChecker(ModelType const& model, std::vector<Objective<ValueType>> const& objectives); PcaaWeightVectorChecker(std::vector<Objective<ValueType>> const& objectives);
virtual ~PcaaWeightVectorChecker() = default; virtual ~PcaaWeightVectorChecker() = default;
@ -70,10 +70,8 @@ namespace storm {
protected: protected:
// The (preprocessed) model
ModelType const& model;
// The (preprocessed) objectives // The (preprocessed) objectives
std::vector<Objective<ValueType>> const& objectives; std::vector<Objective<ValueType>> objectives;
// The precision of this weight vector checker. // The precision of this weight vector checker.
ValueType weightedPrecision; ValueType weightedPrecision;
@ -84,16 +82,10 @@ namespace storm {
public: public:
template<typename VT = typename ModelType::ValueType, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::Mdp<VT>>::value, int>::type = 0> template<typename VT = typename ModelType::ValueType, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::Mdp<VT>>::value, int>::type = 0>
static std::unique_ptr<PcaaWeightVectorChecker<ModelType>> create(ModelType const& model, static std::unique_ptr<PcaaWeightVectorChecker<ModelType>> create(SparseMultiObjectivePreprocessorResult<ModelType> const& preprocessorResult);
std::vector<Objective<typename ModelType::ValueType>> const& objectives,
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates);
template<typename VT = typename ModelType::ValueType, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::MarkovAutomaton<VT>>::value, int>::type = 0> template<typename VT = typename ModelType::ValueType, typename std::enable_if<std::is_same<ModelType, storm::models::sparse::MarkovAutomaton<VT>>::value, int>::type = 0>
static std::unique_ptr<PcaaWeightVectorChecker<ModelType>> create(ModelType const& model, static std::unique_ptr<PcaaWeightVectorChecker<ModelType>> create(SparseMultiObjectivePreprocessorResult<ModelType> const& preprocessorResult);
std::vector<Objective<typename ModelType::ValueType>> const& objectives,
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates);
}; };
} }

77
src/storm/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.cpp

@ -20,30 +20,41 @@ namespace storm {
template <class SparseMaModelType> template <class SparseMaModelType>
SparseMaPcaaWeightVectorChecker<SparseMaModelType>::SparseMaPcaaWeightVectorChecker(SparseMaModelType const& model, SparseMaPcaaWeightVectorChecker<SparseMaModelType>::SparseMaPcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseMaModelType> const& preprocessorResult) :
std::vector<Objective<ValueType>> const& objectives, SparsePcaaWeightVectorChecker<SparseMaModelType>(preprocessorResult) {
storm::storage::BitVector const& possibleECActions, this->initialize(preprocessorResult);
storm::storage::BitVector const& possibleBottomStates) : }
SparsePcaaWeightVectorChecker<SparseMaModelType>(model, objectives, possibleECActions, possibleBottomStates) { template <class SparseMaModelType>
void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::initializeModelTypeSpecificData(SparseMaModelType const& model) {
markovianStates = model.getMarkovianStates();
exitRates = model.getExitRates();
// Set the (discretized) state action rewards. // Set the (discretized) state action rewards.
this->discreteActionRewards.resize(objectives.size()); this->actionRewards.resize(this->objectives.size());
for (auto objIndex : this->objectivesWithNoUpperTimeBound) { for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
auto const& formula = *objectives[objIndex].formula; auto const& formula = *this->objectives[objIndex].formula;
STORM_LOG_THROW(formula.isRewardOperatorFormula() && formula.asRewardOperatorFormula().hasRewardModelName(), storm::exceptions::UnexpectedException, "Unexpected type of operator formula: " << formula); STORM_LOG_THROW(formula.isRewardOperatorFormula() && formula.asRewardOperatorFormula().hasRewardModelName(), storm::exceptions::UnexpectedException, "Unexpected type of operator formula: " << formula);
STORM_LOG_THROW(formula.getSubformula().isTotalRewardFormula() || (formula.getSubformula().isCumulativeRewardFormula() && formula.getSubformula().asCumulativeRewardFormula().isTimeBounded()), storm::exceptions::UnexpectedException, "Unexpected type of sub-formula: " << formula.getSubformula()); typename SparseMaModelType::RewardModelType const& rewModel = model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
STORM_LOG_WARN_COND(!formula.getSubformula().isCumulativeRewardFormula() || (objectives[objIndex].originalFormula->isProbabilityOperatorFormula() && objectives[objIndex].originalFormula->asProbabilityOperatorFormula().getSubformula().isBoundedUntilFormula()), "Objective " << objectives[objIndex].originalFormula << " was simplified to a cumulative reward formula. Correctness of the algorithm is unknown for this type of property.");
typename SparseMaModelType::RewardModelType const& rewModel = this->model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected."); STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected.");
this->discreteActionRewards[objIndex] = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector<ValueType>(this->model.getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>()); this->actionRewards[objIndex] = rewModel.hasStateActionRewards() ? rewModel.getStateActionRewardVector() : std::vector<ValueType>(model.getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
if (rewModel.hasStateRewards()) { if (formula.getSubformula().isTotalRewardFormula()) {
// Note that state rewards are earned over time and thus play no role for probabilistic states if (rewModel.hasStateRewards()) {
for (auto markovianState : this->model.getMarkovianStates()) { // Note that state rewards are earned over time and thus play no role for probabilistic states
this->discreteActionRewards[objIndex][this->model.getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / this->model.getExitRate(markovianState); for (auto markovianState : markovianStates) {
this->actionRewards[objIndex][model.getTransitionMatrix().getRowGroupIndices()[markovianState]] += rewModel.getStateReward(markovianState) / exitRates[markovianState];
}
} }
} else {
STORM_LOG_THROW(formula.getSubformula().isCumulativeRewardFormula() && formula.getSubformula().asCumulativeRewardFormula().isTimeBounded(), storm::exceptions::UnexpectedException, "Unexpected type of sub-formula: " << formula.getSubformula());
STORM_LOG_THROW(!rewModel.hasStateRewards(), storm::exceptions::InvalidPropertyException, "Found state rewards for time bounded objective " << this->objectives[objIndex].originalFormula << ". This is not supported.");
STORM_LOG_WARN_COND(this->objectives[objIndex].originalFormula->isProbabilityOperatorFormula() && this->objectives[objIndex].originalFormula->asProbabilityOperatorFormula().getSubformula().isBoundedUntilFormula(), "Objective " << this->objectives[objIndex].originalFormula << " was simplified to a cumulative reward formula. Correctness of the algorithm is unknown for this type of property.");
} }
} }
} }
template <class SparseMaModelType> template <class SparseMaModelType>
void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) { void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) {
@ -106,33 +117,27 @@ namespace storm {
typename SparseMaPcaaWeightVectorChecker<SparseMaModelType>::SubModel SparseMaPcaaWeightVectorChecker<SparseMaModelType>::createSubModel(bool createMS, std::vector<ValueType> const& weightedRewardVector) const { typename SparseMaPcaaWeightVectorChecker<SparseMaModelType>::SubModel SparseMaPcaaWeightVectorChecker<SparseMaModelType>::createSubModel(bool createMS, std::vector<ValueType> const& weightedRewardVector) const {
SubModel result; SubModel result;
storm::storage::BitVector probabilisticStates = ~this->model.getMarkovianStates(); storm::storage::BitVector probabilisticStates = ~markovianStates;
result.states = createMS ? this->model.getMarkovianStates() : probabilisticStates; result.states = createMS ? markovianStates : probabilisticStates;
result.choices = this->model.getTransitionMatrix().getRowFilter(result.states); result.choices = this->transitionMatrix.getRowFilter(result.states);
STORM_LOG_ASSERT(!createMS || result.states.getNumberOfSetBits() == result.choices.getNumberOfSetBits(), "row groups for Markovian states should consist of exactly one row"); STORM_LOG_ASSERT(!createMS || result.states.getNumberOfSetBits() == result.choices.getNumberOfSetBits(), "row groups for Markovian states should consist of exactly one row");
//We need to add diagonal entries for selfloops on Markovian states. //We need to add diagonal entries for selfloops on Markovian states.
result.toMS = this->model.getTransitionMatrix().getSubmatrix(true, result.states, this->model.getMarkovianStates(), createMS); result.toMS = this->transitionMatrix.getSubmatrix(true, result.states, markovianStates, createMS);
result.toPS = this->model.getTransitionMatrix().getSubmatrix(true, result.states, probabilisticStates, false); result.toPS = this->transitionMatrix.getSubmatrix(true, result.states, probabilisticStates, false);
STORM_LOG_ASSERT(result.getNumberOfStates() == result.states.getNumberOfSetBits() && result.getNumberOfStates() == result.toMS.getRowGroupCount() && result.getNumberOfStates() == result.toPS.getRowGroupCount(), "Invalid state count for subsystem"); STORM_LOG_ASSERT(result.getNumberOfStates() == result.states.getNumberOfSetBits() && result.getNumberOfStates() == result.toMS.getRowGroupCount() && result.getNumberOfStates() == result.toPS.getRowGroupCount(), "Invalid state count for subsystem");
STORM_LOG_ASSERT(result.getNumberOfChoices() == result.choices.getNumberOfSetBits() && result.getNumberOfChoices() == result.toMS.getRowCount() && result.getNumberOfChoices() == result.toPS.getRowCount(), "Invalid choice count for subsystem"); STORM_LOG_ASSERT(result.getNumberOfChoices() == result.choices.getNumberOfSetBits() && result.getNumberOfChoices() == result.toMS.getRowCount() && result.getNumberOfChoices() == result.toPS.getRowCount(), "Invalid choice count for subsystem");
result.weightedRewardVector.resize(result.getNumberOfChoices()); result.weightedRewardVector.resize(result.getNumberOfChoices());
storm::utility::vector::selectVectorValues(result.weightedRewardVector, result.choices, weightedRewardVector); storm::utility::vector::selectVectorValues(result.weightedRewardVector, result.choices, weightedRewardVector);
result.objectiveRewardVectors.resize(this->objectives.size());
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
std::vector<ValueType>& objVector = result.objectiveRewardVectors[objIndex]; std::vector<ValueType> const& objRewards = this->actionRewards[objIndex];
objVector = std::vector<ValueType>(result.weightedRewardVector.size(), storm::utility::zero<ValueType>()); std::vector<ValueType> subModelObjRewards;
if (this->objectivesWithNoUpperTimeBound.get(objIndex)) { subModelObjRewards.reserve(result.getNumberOfChoices());
storm::utility::vector::selectVectorValues(objVector, result.choices, this->discreteActionRewards[objIndex]); for (auto const& choice : result.choices) {
} else { subModelObjRewards.push_back(objRewards[choice]);
typename SparseMaModelType::RewardModelType const& rewModel = this->model.getRewardModel(this->objectives[objIndex].formula->asRewardOperatorFormula().getRewardModelName());
STORM_LOG_ASSERT(!rewModel.hasTransitionRewards(), "Preprocessed Reward model has transition rewards which is not expected.");
STORM_LOG_ASSERT(!rewModel.hasStateRewards(), "State rewards for bounded objectives for MAs are not expected (bounded rewards are not supported).");
if (rewModel.hasStateActionRewards()) {
storm::utility::vector::selectVectorValues(objVector, result.choices, rewModel.getStateActionRewardVector());
}
} }
result.objectiveRewardVectors.push_back(std::move(subModelObjRewards));
} }
result.weightedSolutionVector.resize(result.getNumberOfStates()); result.weightedSolutionVector.resize(result.getNumberOfStates());
@ -161,7 +166,7 @@ namespace storm {
// ) <= this->maximumLowerUpperDistance // ) <= this->maximumLowerUpperDistance
// Initialize some data for fast and easy access // Initialize some data for fast and easy access
VT const maxRate = this->model.getMaximalExitRate(); VT const maxRate = storm::utility::vector::max_if(exitRates, markovianStates);
std::vector<VT> timeBounds; std::vector<VT> timeBounds;
std::vector<VT> eToPowerOfMinusMaxRateTimesBound; std::vector<VT> eToPowerOfMinusMaxRateTimesBound;
VT smallestNonZeroBound = storm::utility::zero<VT>(); VT smallestNonZeroBound = storm::utility::zero<VT>();
@ -229,7 +234,7 @@ namespace storm {
template <typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type> template <typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type>
void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::digitize(SubModel& MS, VT const& digitizationConstant) const { void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::digitize(SubModel& MS, VT const& digitizationConstant) const {
std::vector<VT> rateVector(MS.getNumberOfChoices()); std::vector<VT> rateVector(MS.getNumberOfChoices());
storm::utility::vector::selectVectorValues(rateVector, MS.states, this->model.getExitRates()); storm::utility::vector::selectVectorValues(rateVector, MS.states, exitRates);
for (uint_fast64_t row = 0; row < rateVector.size(); ++row) { for (uint_fast64_t row = 0; row < rateVector.size(); ++row) {
VT const eToMinusRateTimesDelta = std::exp(-rateVector[row] * digitizationConstant); VT const eToMinusRateTimesDelta = std::exp(-rateVector[row] * digitizationConstant);
for (auto& entry : MS.toMS.getRow(row)) { for (auto& entry : MS.toMS.getRow(row)) {
@ -258,7 +263,7 @@ namespace storm {
template <typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type> template <typename VT, typename std::enable_if<storm::NumberTraits<VT>::SupportsExponential, int>::type>
void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::digitizeTimeBounds(TimeBoundMap& upperTimeBounds, VT const& digitizationConstant) { void SparseMaPcaaWeightVectorChecker<SparseMaModelType>::digitizeTimeBounds(TimeBoundMap& upperTimeBounds, VT const& digitizationConstant) {
VT const maxRate = this->model.getMaximalExitRate(); VT const maxRate = storm::utility::vector::max_if(exitRates, markovianStates);
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
auto const& obj = this->objectives[objIndex]; auto const& obj = this->objectives[objIndex];
VT errorTowardsZero = storm::utility::zero<VT>(); VT errorTowardsZero = storm::utility::zero<VT>();

13
src/storm/modelchecker/multiobjective/pcaa/SparseMaPcaaWeightVectorChecker.h

@ -24,13 +24,13 @@ namespace storm {
public: public:
typedef typename SparseMaModelType::ValueType ValueType; typedef typename SparseMaModelType::ValueType ValueType;
SparseMaPcaaWeightVectorChecker(SparseMaModelType const& model, SparseMaPcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseMaModelType> const& preprocessorResult);
std::vector<Objective<ValueType>> const& objectives,
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates);
virtual ~SparseMaPcaaWeightVectorChecker() = default; virtual ~SparseMaPcaaWeightVectorChecker() = default;
protected:
virtual void initializeModelTypeSpecificData(SparseMaModelType const& model) override;
private: private:
/* /*
@ -151,6 +151,11 @@ namespace storm {
*/ */
void performMSStep(SubModel& MS, SubModel const& PS, storm::storage::BitVector const& consideredObjectives, std::vector<ValueType> const& weightVector) const; void performMSStep(SubModel& MS, SubModel const& PS, storm::storage::BitVector const& consideredObjectives, std::vector<ValueType> const& weightVector) const;
// Data regarding the given Markov automaton
storm::storage::BitVector markovianStates;
std::vector<ValueType> exitRates;
}; };
} }

148
src/storm/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.cpp

@ -21,48 +21,31 @@ namespace storm {
namespace multiobjective { namespace multiobjective {
template <class SparseMdpModelType> template <class SparseMdpModelType>
SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::SparseMdpPcaaWeightVectorChecker(SparseMdpModelType const& model, SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::SparseMdpPcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseMdpModelType> const& preprocessorResult) :
std::vector<Objective<ValueType>> const& objectives, SparsePcaaWeightVectorChecker<SparseMdpModelType>(preprocessorResult) {
storm::storage::BitVector const& possibleECActions, this->initialize(preprocessorResult);
storm::storage::BitVector const& possibleBottomStates) :
SparsePcaaWeightVectorChecker<SparseMdpModelType>(model, objectives, possibleECActions, possibleBottomStates) {
// set the state action rewards. Also do some sanity checks on the objectives.
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
auto const& formula = *objectives[objIndex].formula;
if (!(formula.isProbabilityOperatorFormula() && (formula.getSubformula().isBoundedUntilFormula() || formula.getSubformula().isMultiObjectiveFormula()))) {
STORM_LOG_THROW(formula.isRewardOperatorFormula() && formula.asRewardOperatorFormula().hasRewardModelName(), storm::exceptions::UnexpectedException, "Unexpected type of operator formula: " << formula);
STORM_LOG_THROW(formula.getSubformula().isCumulativeRewardFormula() || formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException, "Unexpected type of sub-formula: " << formula.getSubformula());
typename SparseMdpModelType::RewardModelType const& rewModel = this->model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Reward model has transition rewards which is not expected.");
this->discreteActionRewards[objIndex] = rewModel.getTotalRewardVector(this->model.getTransitionMatrix());
}
}
} }
template <class SparseMdpModelType> template <class SparseMdpModelType>
void SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) { void SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::initializeModelTypeSpecificData(SparseMdpModelType const& model) {
// Currently, only step bounds are considered. // set the state action rewards. Also do some sanity checks on the objectives.
bool containsRewardBoundedObjectives = false; this->actionRewards.resize(this->objectives.size());
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
if (this->objectives[objIndex].formula->isProbabilityOperatorFormula()) { auto const& formula = *this->objectives[objIndex].formula;
containsRewardBoundedObjectives = true; STORM_LOG_THROW(formula.isRewardOperatorFormula() && formula.asRewardOperatorFormula().hasRewardModelName(), storm::exceptions::UnexpectedException, "Unexpected type of operator formula: " << formula);
break; STORM_LOG_THROW(formula.getSubformula().isCumulativeRewardFormula() || formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException, "Unexpected type of sub-formula: " << formula.getSubformula());
} typename SparseMdpModelType::RewardModelType const& rewModel = model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
} STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Reward model has transition rewards which is not expected.");
this->actionRewards[objIndex] = rewModel.getTotalRewardVector(model.getTransitionMatrix());
if (containsRewardBoundedObjectives) {
boundedPhaseWithRewardBounds(weightVector, weightedRewardVector);
} else {
boundedPhaseOnlyStepBounds(weightVector, weightedRewardVector);
} }
} }
template <class SparseMdpModelType> template <class SparseMdpModelType>
void SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::boundedPhaseOnlyStepBounds(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) { void SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) {
// Allocate some memory so this does not need to happen for each time epoch // Allocate some memory so this does not need to happen for each time epoch
std::vector<uint_fast64_t> optimalChoicesInCurrentEpoch(this->model.getNumberOfStates()); std::vector<uint_fast64_t> optimalChoicesInCurrentEpoch(this->transitionMatrix.getRowGroupCount());
std::vector<ValueType> choiceValues(weightedRewardVector.size()); std::vector<ValueType> choiceValues(weightedRewardVector.size());
std::vector<ValueType> temporaryResult(this->model.getNumberOfStates()); std::vector<ValueType> temporaryResult(this->transitionMatrix.getRowGroupCount());
// Get for each occurring timeBound the indices of the objectives with that bound. // Get for each occurring timeBound the indices of the objectives with that bound.
std::map<uint_fast64_t, storm::storage::BitVector, std::greater<uint_fast64_t>> stepBounds; std::map<uint_fast64_t, storm::storage::BitVector, std::greater<uint_fast64_t>> stepBounds;
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
@ -94,29 +77,29 @@ namespace storm {
for(auto objIndex : stepBoundIt->second) { for(auto objIndex : stepBoundIt->second) {
// This objective now plays a role in the weighted sum // This objective now plays a role in the weighted sum
ValueType factor = storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex]; ValueType factor = storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex];
storm::utility::vector::addScaledVector(weightedRewardVector, this->discreteActionRewards[objIndex], factor); storm::utility::vector::addScaledVector(weightedRewardVector, this->actionRewards[objIndex], factor);
} }
++stepBoundIt; ++stepBoundIt;
} }
// Get values and scheduler for weighted sum of objectives // Get values and scheduler for weighted sum of objectives
this->model.getTransitionMatrix().multiplyWithVector(this->weightedResult, choiceValues); this->transitionMatrix.multiplyWithVector(this->weightedResult, choiceValues);
storm::utility::vector::addVectors(choiceValues, weightedRewardVector, choiceValues); storm::utility::vector::addVectors(choiceValues, weightedRewardVector, choiceValues);
storm::utility::vector::reduceVectorMax(choiceValues, this->weightedResult, this->model.getTransitionMatrix().getRowGroupIndices(), &optimalChoicesInCurrentEpoch); storm::utility::vector::reduceVectorMax(choiceValues, this->weightedResult, this->transitionMatrix.getRowGroupIndices(), &optimalChoicesInCurrentEpoch);
// get values for individual objectives // 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. // 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) { for (auto objIndex : consideredObjectives) {
std::vector<ValueType>& objectiveResult = this->objectiveResults[objIndex]; std::vector<ValueType>& objectiveResult = this->objectiveResults[objIndex];
std::vector<ValueType> const& objectiveRewards = this->discreteActionRewards[objIndex]; std::vector<ValueType> const& objectiveRewards = this->actionRewards[objIndex];
auto rowGroupIndexIt = this->model.getTransitionMatrix().getRowGroupIndices().begin(); auto rowGroupIndexIt = this->transitionMatrix.getRowGroupIndices().begin();
auto optimalChoiceIt = optimalChoicesInCurrentEpoch.begin(); auto optimalChoiceIt = optimalChoicesInCurrentEpoch.begin();
for(ValueType& stateValue : temporaryResult){ for(ValueType& stateValue : temporaryResult){
uint_fast64_t row = (*rowGroupIndexIt) + (*optimalChoiceIt); uint_fast64_t row = (*rowGroupIndexIt) + (*optimalChoiceIt);
++rowGroupIndexIt; ++rowGroupIndexIt;
++optimalChoiceIt; ++optimalChoiceIt;
stateValue = objectiveRewards[row]; stateValue = objectiveRewards[row];
for(auto const& entry : this->model.getTransitionMatrix().getRow(row)) { for(auto const& entry : this->transitionMatrix.getRow(row)) {
stateValue += entry.getValue() * objectiveResult[entry.getColumn()]; stateValue += entry.getValue() * objectiveResult[entry.getColumn()];
} }
} }
@ -126,95 +109,6 @@ namespace storm {
} }
} }
template <class SparseMdpModelType>
void SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::boundedPhaseWithRewardBounds(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) {
if (!rewardUnfolding) {
rewardUnfolding = std::make_unique<MultiDimensionalRewardUnfolding<ValueType>>(this->model, this->objectives, this->possibleECActions, this->possibleBottomStates);
}
auto initEpoch = rewardUnfolding->getStartEpoch();
auto epochOrder = rewardUnfolding->getEpochComputationOrder(initEpoch);
for (auto const& epoch : epochOrder) {
computeEpochSolution(epoch, weightVector);
}
auto solution = rewardUnfolding->getInitialStateResult(initEpoch);
this->weightedResult[*this->model.getInitialStates().begin()] = solution.weightedValue;
for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
this->objectiveResults[objIndex][*this->model.getInitialStates().begin()] = solution.objectiveValues[objIndex];
// Todo: we currently assume precise results...
this->offsetsToUnderApproximation[objIndex] = storm::utility::zero<ValueType>();
this->offsetsToOverApproximation[objIndex] = storm::utility::zero<ValueType>();
}
}
template <class SparseMdpModelType>
void SparseMdpPcaaWeightVectorChecker<SparseMdpModelType>::computeEpochSolution(typename MultiDimensionalRewardUnfolding<ValueType>::Epoch const& epoch, std::vector<ValueType> const& weightVector) {
auto const& epochModel = rewardUnfolding->setCurrentEpoch(epoch);
std::vector<typename MultiDimensionalRewardUnfolding<ValueType>::SolutionType> result(epochModel.epochMatrix.getRowGroupCount());
// Formulate a min-max equation system max(A*x+b)=x for the weighted sum of the objectives
std::vector<ValueType> b(epochModel.epochMatrix.getRowCount(), storm::utility::zero<ValueType>());
for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
ValueType weight = storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()) ? -weightVector[objIndex] : weightVector[objIndex];
if (!storm::utility::isZero(weight)) {
std::vector<ValueType> const& objectiveReward = epochModel.objectiveRewards[objIndex];
for (auto const& choice : epochModel.objectiveRewardFilter[objIndex]) {
b[choice] += weight * objectiveReward[choice];
}
}
}
auto stepSolutionIt = epochModel.stepSolutions.begin();
for (auto const& choice : epochModel.stepChoices) {
b[choice] += stepSolutionIt->weightedValue;
++stepSolutionIt;
}
// Invoke the min max solver
storm::solver::GeneralMinMaxLinearEquationSolverFactory<ValueType> minMaxSolverFactory;
auto minMaxSolver = minMaxSolverFactory.create(epochModel.epochMatrix);
minMaxSolver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
minMaxSolver->setTrackScheduler(true);
//minMaxSolver->setCachingEnabled(true);
std::vector<ValueType> x(result.size(), storm::utility::zero<ValueType>());
minMaxSolver->solveEquations(x, b);
for (uint64_t state = 0; state < x.size(); ++state) {
result[state].weightedValue = x[state];
}
// Formulate for each objective the linear equation system induced by the performed choices
auto const& choices = minMaxSolver->getSchedulerChoices();
storm::storage::SparseMatrix<ValueType> subMatrix = epochModel.epochMatrix.selectRowsFromRowGroups(choices, true);
subMatrix.convertToEquationSystem();
storm::solver::GeneralLinearEquationSolverFactory<ValueType> linEqSolverFactory;
auto linEqSolver = linEqSolverFactory.create(std::move(subMatrix));
b.resize(choices.size());
// TODO: start with a better initial guess
x.resize(choices.size());
for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
std::vector<ValueType> const& objectiveReward = epochModel.objectiveRewards[objIndex];
for (uint64_t state = 0; state < choices.size(); ++state) {
uint64_t choice = epochModel.epochMatrix.getRowGroupIndices()[state] + choices[state];
if (epochModel.objectiveRewardFilter[objIndex].get(choice)) {
b[state] = objectiveReward[choice];
} else {
b[state] = storm::utility::zero<ValueType>();
}
if (epochModel.stepChoices.get(choice)) {
b[state] += epochModel.stepSolutions[epochModel.stepChoices.getNumberOfSetBitsBeforeIndex(choice)].objectiveValues[objIndex];
}
}
linEqSolver->solveEquations(x, b);
for (uint64_t state = 0; state < choices.size(); ++state) {
result[state].objectiveValues.push_back(x[state]);
}
}
rewardUnfolding->setSolutionForCurrentEpoch(result);
}
template class SparseMdpPcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>; template class SparseMdpPcaaWeightVectorChecker<storm::models::sparse::Mdp<double>>;
#ifdef STORM_HAVE_CARL #ifdef STORM_HAVE_CARL
template class SparseMdpPcaaWeightVectorChecker<storm::models::sparse::Mdp<storm::RationalNumber>>; template class SparseMdpPcaaWeightVectorChecker<storm::models::sparse::Mdp<storm::RationalNumber>>;

31
src/storm/modelchecker/multiobjective/pcaa/SparseMdpPcaaWeightVectorChecker.h

@ -32,17 +32,17 @@ namespace storm {
* *
*/ */
SparseMdpPcaaWeightVectorChecker(SparseMdpModelType const& model, SparseMdpPcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseMdpModelType> const& preprocessorResult);
std::vector<Objective<ValueType>> const& objectives,
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates);
virtual ~SparseMdpPcaaWeightVectorChecker() = default; virtual ~SparseMdpPcaaWeightVectorChecker() = default;
protected:
virtual void initializeModelTypeSpecificData(SparseMdpModelType const& model) override;
private: private:
/*! /*!
* Computes the maximizing scheduler for the weighted sum of the objectives, including also step or reward bounded objectives. * Computes the maximizing scheduler for the weighted sum of the objectives, including also step bounded objectives.
* Moreover, the values of the individual objectives are computed w.r.t. this scheduler. * Moreover, the values of the individual objectives are computed w.r.t. this scheduler.
* *
* @param weightVector the weight vector of the current check * @param weightVector the weight vector of the current check
@ -50,27 +50,6 @@ namespace storm {
*/ */
virtual void boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) override; virtual void boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) override;
/*!
* Computes the bounded phase for the case that only step bounded objectives are considered.
*
* @param weightVector the weight vector of the current check
* @param weightedRewardVector the weighted rewards considering the unbounded objectives. Will be invalidated after calling this.
*/
void boundedPhaseOnlyStepBounds(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector);
/*!
* Computes the bounded phase for the case that also reward bounded objectives occurr.
*
* @param weightVector the weight vector of the current check
* @param weightedRewardVector the weighted rewards considering the unbounded objectives. Will be invalidated after calling this.
*/
void boundedPhaseWithRewardBounds(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector);
void computeEpochSolution(typename MultiDimensionalRewardUnfolding<ValueType>::Epoch const& epoch, std::vector<ValueType> const& weightVector);
std::unique_ptr<MultiDimensionalRewardUnfolding<ValueType>> rewardUnfolding;
}; };
} }

8
src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp

@ -21,13 +21,9 @@ namespace storm {
template <class SparseModelType, typename GeometryValueType> template <class SparseModelType, typename GeometryValueType>
SparsePcaaQuery<SparseModelType, GeometryValueType>::SparsePcaaQuery(SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult) : SparsePcaaQuery<SparseModelType, GeometryValueType>::SparsePcaaQuery(SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult) :
originalModel(preprocessorResult.originalModel), originalFormula(preprocessorResult.originalFormula), originalModel(preprocessorResult.originalModel), originalFormula(preprocessorResult.originalFormula), objectives(preprocessorResult.objectives) {
preprocessedModel(std::move(*preprocessorResult.preprocessedModel)), objectives(std::move(preprocessorResult.objectives)) {
STORM_LOG_WARN("TODO"); this->weightVectorChecker = WeightVectorCheckerFactory<SparseModelType>::create(preprocessorResult);
std::cout << "TODO" << std::endl;
this->weightVectorChecker = WeightVectorCheckerFactory<SparseModelType>::create(preprocessedModel, objectives, storm::storage::BitVector(preprocessedModel.getNumberOfChoices(), true), preprocessorResult.reward0EStates);
this->diracWeightVectorsToBeChecked = storm::storage::BitVector(this->objectives.size(), true); this->diracWeightVectorsToBeChecked = storm::storage::BitVector(this->objectives.size(), true);
this->overApproximation = storm::storage::geometry::Polytope<GeometryValueType>::createUniversalPolytope(); this->overApproximation = storm::storage::geometry::Polytope<GeometryValueType>::createUniversalPolytope();

1
src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h

@ -96,7 +96,6 @@ namespace storm {
SparseModelType const& originalModel; SparseModelType const& originalModel;
storm::logic::MultiObjectiveFormula const& originalFormula; storm::logic::MultiObjectiveFormula const& originalFormula;
SparseModelType preprocessedModel;
std::vector<Objective<typename SparseModelType::ValueType>> objectives; std::vector<Objective<typename SparseModelType::ValueType>> objectives;
// The corresponding weight vector checker // The corresponding weight vector checker

193
src/storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.cpp

@ -1,6 +1,7 @@
#include "storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h" #include "storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h"
#include <map> #include <map>
#include <set>
#include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/RationalFunctionAdapter.h"
#include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/Mdp.h"
@ -12,10 +13,12 @@
#include "storm/utility/macros.h" #include "storm/utility/macros.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
#include "storm/logic/Formulas.h" #include "storm/logic/Formulas.h"
#include "storm/transformer/GoalStateMerger.h"
#include "storm/exceptions/IllegalFunctionCallException.h" #include "storm/exceptions/IllegalFunctionCallException.h"
#include "storm/exceptions/UnexpectedException.h" #include "storm/exceptions/UnexpectedException.h"
#include "storm/exceptions/NotImplementedException.h" #include "storm/exceptions/NotImplementedException.h"
#include "storm/exceptions/NotSupportedException.h"
namespace storm { namespace storm {
namespace modelchecker { namespace modelchecker {
@ -23,31 +26,65 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
SparsePcaaWeightVectorChecker<SparseModelType>::SparsePcaaWeightVectorChecker(SparseModelType const& model, SparsePcaaWeightVectorChecker<SparseModelType>::SparsePcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult) :
std::vector<Objective<typename SparseModelType::ValueType>> const& objectives, PcaaWeightVectorChecker<SparseModelType>(preprocessorResult.objectives) {
storm::storage::BitVector const& possibleECActions, STORM_LOG_THROW(preprocessorResult.rewardFinitenessType != SparseMultiObjectivePreprocessorResult<SparseModelType>::RewardFinitenessType::Infinite, storm::exceptions::NotSupportedException, "There is no Pareto optimal scheduler that yields finite reward for all objectives. This is not supported.");
storm::storage::BitVector const& possibleBottomStates) : STORM_LOG_THROW(preprocessorResult.rewardLessInfinityEStates, storm::exceptions::UnexpectedException, "The set of states with reward < infinity for some scheduler has not been computed during preprocessing.");
PcaaWeightVectorChecker<SparseModelType>(model, objectives), STORM_LOG_THROW(preprocessorResult.containsOnlyRewardObjectives(), storm::exceptions::NotSupportedException, "At least one objective was not reduced to an expected (total or cumulative) reward objective during preprocessing. This is not supported by the considered weight vector checker.");
possibleECActions(possibleECActions), STORM_LOG_THROW(preprocessorResult.preprocessedModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "The model has multiple initial states.");
actionsWithoutRewardInUnboundedPhase(this->model.getNumberOfChoices(), true), }
possibleBottomStates(possibleBottomStates), template <class SparseModelType>
objectivesWithNoUpperTimeBound(objectives.size(), false), void SparsePcaaWeightVectorChecker<SparseModelType>::initialize(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult) {
discreteActionRewards(objectives.size()), // Build a subsystem of the preprocessor result model that discards states that yield infinite reward for all schedulers.
checkHasBeenCalled(false), // We can also merge the states that will have reward zero anyway.
objectiveResults(objectives.size()), storm::storage::BitVector maybeStates = preprocessorResult.rewardLessInfinityEStates.get() & ~preprocessorResult.reward0AStates;
offsetsToUnderApproximation(objectives.size()), std::set<std::string> relevantRewardModels;
offsetsToOverApproximation(objectives.size()), for (auto const& obj : this->objectives) {
optimalChoices(this->model.getNumberOfStates(), 0) { obj.formula->gatherReferencedRewardModels(relevantRewardModels);
}
storm::transformer::GoalStateMerger<SparseModelType> merger(*preprocessorResult.preprocessedModel);
auto mergerResult = merger.mergeTargetAndSinkStates(maybeStates, preprocessorResult.reward0AStates, storm::storage::BitVector(maybeStates.size(), false), std::vector<std::string>(relevantRewardModels.begin(), relevantRewardModels.end()));
// Initialize data specific for the considered model type
initializeModelTypeSpecificData(*mergerResult.model);
// Initilize general data of the model
transitionMatrix = std::move(mergerResult.model->getTransitionMatrix());
initialState = *mergerResult.model->getInitialStates().begin();
reward0EStates = preprocessorResult.reward0EStates % maybeStates;
if (mergerResult.targetState) {
// There is an additional state in the result
reward0EStates.resize(reward0EStates.size() + 1, true);
// The overapproximation for the possible ec choices consists of the states that can reach the target states with prob. 0 and the target state itself.
storm::storage::BitVector targetStateAsVector(transitionMatrix.getRowGroupCount(), false);
targetStateAsVector.set(*mergerResult.targetState, true);
ecChoicesHint = transitionMatrix.getRowFilter(storm::utility::graph::performProb0E(transitionMatrix, transitionMatrix.getRowGroupIndices(), transitionMatrix.transpose(true), storm::storage::BitVector(targetStateAsVector.size(), true), targetStateAsVector));
ecChoicesHint.set(transitionMatrix.getRowGroupIndices()[*mergerResult.targetState], true);
} else {
ecChoicesHint = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
}
// set data for unbounded objectives // set data for unbounded objectives
for(uint_fast64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { objectivesWithNoUpperTimeBound = storm::storage::BitVector(this->objectives.size(), false);
actionsWithoutRewardInUnboundedPhase = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
auto const& formula = *this->objectives[objIndex].formula; auto const& formula = *this->objectives[objIndex].formula;
if (formula.getSubformula().isTotalRewardFormula()) { if (formula.getSubformula().isTotalRewardFormula()) {
objectivesWithNoUpperTimeBound.set(objIndex, true); objectivesWithNoUpperTimeBound.set(objIndex, true);
STORM_LOG_ASSERT(formula.isRewardOperatorFormula() && formula.asRewardOperatorFormula().hasRewardModelName(), "Unexpected type of operator formula."); actionsWithoutRewardInUnboundedPhase &= storm::utility::vector::filterZero(actionRewards[objIndex]);
actionsWithoutRewardInUnboundedPhase &= this->model.getRewardModel(formula.asRewardOperatorFormula().getRewardModelName()).getChoicesWithZeroReward(this->model.getTransitionMatrix());
} }
} }
// initialize data for the results
checkHasBeenCalled = false;
objectiveResults.resize(this->objectives.size());
offsetsToUnderApproximation.resize(this->objectives.size(), storm::utility::zero<ValueType>());
offsetsToOverApproximation.resize(this->objectives.size(), storm::utility::zero<ValueType>());
optimalChoices.resize(transitionMatrix.getRowGroupCount(), 0);
} }
@ -55,7 +92,7 @@ namespace storm {
void SparsePcaaWeightVectorChecker<SparseModelType>::check(std::vector<ValueType> const& weightVector) { void SparsePcaaWeightVectorChecker<SparseModelType>::check(std::vector<ValueType> const& weightVector) {
checkHasBeenCalled = true; checkHasBeenCalled = true;
STORM_LOG_INFO("Invoked WeightVectorChecker with weights " << std::endl << "\t" << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(weightVector))); STORM_LOG_INFO("Invoked WeightVectorChecker with weights " << std::endl << "\t" << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(weightVector)));
std::vector<ValueType> weightedRewardVector(this->model.getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>()); std::vector<ValueType> weightedRewardVector(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
boost::optional<ValueType> weightedLowerResultBound = storm::utility::zero<ValueType>(); boost::optional<ValueType> weightedLowerResultBound = storm::utility::zero<ValueType>();
boost::optional<ValueType> weightedUpperResultBound = storm::utility::zero<ValueType>(); boost::optional<ValueType> weightedUpperResultBound = storm::utility::zero<ValueType>();
for (auto objIndex : objectivesWithNoUpperTimeBound) { for (auto objIndex : objectivesWithNoUpperTimeBound) {
@ -71,7 +108,7 @@ namespace storm {
} else { } else {
weightedLowerResultBound = boost::none; weightedLowerResultBound = boost::none;
} }
storm::utility::vector::addScaledVector(weightedRewardVector, discreteActionRewards[objIndex], -weightVector[objIndex]); storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], -weightVector[objIndex]);
} else { } else {
if (obj.lowerResultBound && weightedLowerResultBound) { if (obj.lowerResultBound && weightedLowerResultBound) {
weightedLowerResultBound.get() += weightVector[objIndex] * obj.lowerResultBound.get(); weightedLowerResultBound.get() += weightVector[objIndex] * obj.lowerResultBound.get();
@ -83,7 +120,7 @@ namespace storm {
} else { } else {
weightedUpperResultBound = boost::none; weightedUpperResultBound = boost::none;
} }
storm::utility::vector::addScaledVector(weightedRewardVector, discreteActionRewards[objIndex], weightVector[objIndex]); storm::utility::vector::addScaledVector(weightedRewardVector, actionRewards[objIndex], weightVector[objIndex]);
} }
} }
@ -107,11 +144,10 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
std::vector<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::getUnderApproximationOfInitialStateResults() const { std::vector<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::getUnderApproximationOfInitialStateResults() const {
STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before."); STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
uint_fast64_t initstate = *this->model.getInitialStates().begin();
std::vector<ValueType> res; std::vector<ValueType> res;
res.reserve(this->objectives.size()); res.reserve(this->objectives.size());
for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
res.push_back(this->objectiveResults[objIndex][initstate] + this->offsetsToUnderApproximation[objIndex]); res.push_back(this->objectiveResults[objIndex][initialState] + this->offsetsToUnderApproximation[objIndex]);
} }
return res; return res;
} }
@ -119,11 +155,10 @@ namespace storm {
template <class SparseModelType> template <class SparseModelType>
std::vector<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::getOverApproximationOfInitialStateResults() const { std::vector<typename SparsePcaaWeightVectorChecker<SparseModelType>::ValueType> SparsePcaaWeightVectorChecker<SparseModelType>::getOverApproximationOfInitialStateResults() const {
STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before."); STORM_LOG_THROW(checkHasBeenCalled, storm::exceptions::IllegalFunctionCallException, "Tried to retrieve results but check(..) has not been called before.");
uint_fast64_t initstate = *this->model.getInitialStates().begin();
std::vector<ValueType> res; std::vector<ValueType> res;
res.reserve(this->objectives.size()); res.reserve(this->objectives.size());
for(uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) { for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
res.push_back(this->objectiveResults[objIndex][initstate] + this->offsetsToOverApproximation[objIndex]); res.push_back(this->objectiveResults[objIndex][initialState] + this->offsetsToOverApproximation[objIndex]);
} }
return res; return res;
} }
@ -148,19 +183,17 @@ namespace storm {
void SparsePcaaWeightVectorChecker<SparseModelType>::unboundedWeightedPhase(std::vector<ValueType> const& weightedRewardVector, boost::optional<ValueType> const& lowerResultBound, boost::optional<ValueType> const& upperResultBound) { void SparsePcaaWeightVectorChecker<SparseModelType>::unboundedWeightedPhase(std::vector<ValueType> const& weightedRewardVector, boost::optional<ValueType> const& lowerResultBound, boost::optional<ValueType> const& upperResultBound) {
if (this->objectivesWithNoUpperTimeBound.empty() || !storm::utility::vector::hasNonZeroEntry(weightedRewardVector)) { if (this->objectivesWithNoUpperTimeBound.empty() || !storm::utility::vector::hasNonZeroEntry(weightedRewardVector)) {
this->weightedResult = std::vector<ValueType>(this->model.getNumberOfStates(), storm::utility::zero<ValueType>()); this->weightedResult = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
this->optimalChoices = std::vector<uint_fast64_t>(this->model.getNumberOfStates(), 0); this->optimalChoices = std::vector<uint_fast64_t>(transitionMatrix.getRowGroupCount(), 0);
return; return;
} }
updateEcElimResult(weightedRewardVector); updateEcQuotient(weightedRewardVector);
std::vector<ValueType> subRewardVector(cachedEcElimResult->newToOldRowMapping.size()); storm::utility::vector::selectVectorValues(ecQuotient->auxChoiceValues, ecQuotient->ecqToOriginalChoiceMapping, weightedRewardVector);
storm::utility::vector::selectVectorValues(subRewardVector, cachedEcElimResult->newToOldRowMapping, weightedRewardVector);
std::vector<ValueType> subResult(cachedEcElimResult->matrix.getRowGroupCount());
storm::solver::GeneralMinMaxLinearEquationSolverFactory<ValueType> solverFactory; storm::solver::GeneralMinMaxLinearEquationSolverFactory<ValueType> solverFactory;
std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver = solverFactory.create(cachedEcElimResult->matrix); std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver = solverFactory.create(ecQuotient->matrix);
solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize); solver->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
solver->setTrackScheduler(true); solver->setTrackScheduler(true);
if (lowerResultBound) { if (lowerResultBound) {
@ -169,11 +202,15 @@ namespace storm {
if (upperResultBound) { if (upperResultBound) {
solver->setUpperBound(*upperResultBound); solver->setUpperBound(*upperResultBound);
} }
solver->solveEquations(subResult, subRewardVector);
this->weightedResult = std::vector<ValueType>(this->model.getNumberOfStates()); // Use the (0...0) vector as initial guess for the solution.
std::fill(ecQuotient->auxStateValues.begin(), ecQuotient->auxStateValues.end(), storm::utility::zero<ValueType>());
solver->solveEquations(ecQuotient->auxStateValues, ecQuotient->auxChoiceValues);
this->weightedResult = std::vector<ValueType>(transitionMatrix.getRowGroupCount());
transformReducedSolutionToOriginalModel(cachedEcElimResult->matrix, subResult, solver->getSchedulerChoices(), cachedEcElimResult->newToOldRowMapping, cachedEcElimResult->oldToNewStateMapping, this->weightedResult, this->optimalChoices); transformReducedSolutionToOriginalModel(ecQuotient->matrix, ecQuotient->auxStateValues, solver->getSchedulerChoices(), ecQuotient->ecqToOriginalChoiceMapping, ecQuotient->originalToEcqStateMapping, this->weightedResult, this->optimalChoices);
} }
template <class SparseModelType> template <class SparseModelType>
@ -186,11 +223,11 @@ namespace storm {
} }
for (uint_fast64_t objIndex2 = 0; objIndex2 < this->objectives.size(); ++objIndex2) { for (uint_fast64_t objIndex2 = 0; objIndex2 < this->objectives.size(); ++objIndex2) {
if (objIndex != objIndex2) { if (objIndex != objIndex2) {
objectiveResults[objIndex2] = std::vector<ValueType>(this->model.getNumberOfStates(), storm::utility::zero<ValueType>()); objectiveResults[objIndex2] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
} }
} }
} else { } else {
storm::storage::SparseMatrix<ValueType> deterministicMatrix = this->model.getTransitionMatrix().selectRowsFromRowGroups(this->optimalChoices, true); storm::storage::SparseMatrix<ValueType> deterministicMatrix = transitionMatrix.selectRowsFromRowGroups(this->optimalChoices, true);
storm::storage::SparseMatrix<ValueType> deterministicBackwardTransitions = deterministicMatrix.transpose(); storm::storage::SparseMatrix<ValueType> deterministicBackwardTransitions = deterministicMatrix.transpose();
std::vector<ValueType> deterministicStateRewards(deterministicMatrix.getRowCount()); std::vector<ValueType> deterministicStateRewards(deterministicMatrix.getRowCount());
storm::solver::GeneralLinearEquationSolverFactory<ValueType> linearEquationSolverFactory; storm::solver::GeneralLinearEquationSolverFactory<ValueType> linearEquationSolverFactory;
@ -205,7 +242,7 @@ namespace storm {
if (objectivesWithNoUpperTimeBound.get(objIndex)) { if (objectivesWithNoUpperTimeBound.get(objIndex)) {
offsetsToUnderApproximation[objIndex] = storm::utility::zero<ValueType>(); offsetsToUnderApproximation[objIndex] = storm::utility::zero<ValueType>();
offsetsToOverApproximation[objIndex] = storm::utility::zero<ValueType>(); offsetsToOverApproximation[objIndex] = storm::utility::zero<ValueType>();
storm::utility::vector::selectVectorValues(deterministicStateRewards, this->optimalChoices, this->model.getTransitionMatrix().getRowGroupIndices(), discreteActionRewards[objIndex]); storm::utility::vector::selectVectorValues(deterministicStateRewards, this->optimalChoices, transitionMatrix.getRowGroupIndices(), actionRewards[objIndex]);
storm::storage::BitVector statesWithRewards = ~storm::utility::vector::filterZero(deterministicStateRewards); storm::storage::BitVector statesWithRewards = ~storm::utility::vector::filterZero(deterministicStateRewards);
// As maybestates we pick the states from which a state with reward is reachable // As maybestates we pick the states from which a state with reward is reachable
storm::storage::BitVector maybeStates = storm::utility::graph::performProbGreater0(deterministicBackwardTransitions, storm::storage::BitVector(deterministicMatrix.getRowCount(), true), statesWithRewards); storm::storage::BitVector maybeStates = storm::utility::graph::performProbGreater0(deterministicBackwardTransitions, storm::storage::BitVector(deterministicMatrix.getRowCount(), true), statesWithRewards);
@ -221,7 +258,7 @@ namespace storm {
storm::utility::vector::clip(objectiveResults[objIndex], obj.lowerResultBound, obj.upperResultBound); storm::utility::vector::clip(objectiveResults[objIndex], obj.lowerResultBound, obj.upperResultBound);
} }
// Make sure that the objectiveResult is initialized correctly // Make sure that the objectiveResult is initialized correctly
objectiveResults[objIndex].resize(this->model.getNumberOfStates(), storm::utility::zero<ValueType>()); objectiveResults[objIndex].resize(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
if (!maybeStates.empty()) { if (!maybeStates.empty()) {
storm::storage::SparseMatrix<ValueType> submatrix = deterministicMatrix.getSubmatrix( storm::storage::SparseMatrix<ValueType> submatrix = deterministicMatrix.getSubmatrix(
@ -255,32 +292,40 @@ namespace storm {
sumOfWeightsOfUncheckedObjectives -= weightVector[objIndex]; sumOfWeightsOfUncheckedObjectives -= weightVector[objIndex];
} }
} else { } else {
objectiveResults[objIndex] = std::vector<ValueType>(this->model.getNumberOfStates(), storm::utility::zero<ValueType>()); objectiveResults[objIndex] = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
} }
} }
} }
} }
template <class SparseModelType> template <class SparseModelType>
void SparsePcaaWeightVectorChecker<SparseModelType>::updateEcElimResult(std::vector<ValueType> const& weightedRewardVector) { void SparsePcaaWeightVectorChecker<SparseModelType>::updateEcQuotient(std::vector<ValueType> const& weightedRewardVector) {
// Check whether we need to update the currently cached ecElimResult // Check whether we need to update the currently cached ecElimResult
storm::storage::BitVector newZeroRewardChoices = storm::utility::vector::filterZero(weightedRewardVector); storm::storage::BitVector newReward0Choices = storm::utility::vector::filterZero(weightedRewardVector);
if (!cachedZeroRewardChoices || cachedZeroRewardChoices.get() != newZeroRewardChoices) { if (!ecQuotient || ecQuotient->origReward0Choices != newReward0Choices) {
cachedZeroRewardChoices = std::move(newZeroRewardChoices);
// It is sufficient to consider the states from which a transition with non-zero reward is reachable. (The remaining states always have reward zero). // It is sufficient to consider the states from which a transition with non-zero reward is reachable. (The remaining states always have reward zero).
storm::storage::BitVector nonZeroRewardStates(this->model.getNumberOfStates(), false); storm::storage::BitVector nonZeroRewardStates(transitionMatrix.getRowGroupCount(), false);
for (uint_fast64_t state = 0; state < this->model.getNumberOfStates(); ++state){ for (uint_fast64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state){
if (cachedZeroRewardChoices->getNextUnsetIndex(this->model.getTransitionMatrix().getRowGroupIndices()[state]) < this->model.getTransitionMatrix().getRowGroupIndices()[state+1]) { if (newReward0Choices.getNextUnsetIndex(transitionMatrix.getRowGroupIndices()[state]) < transitionMatrix.getRowGroupIndices()[state+1]) {
nonZeroRewardStates.set(state); nonZeroRewardStates.set(state);
} }
} }
storm::storage::BitVector subsystemStates = storm::utility::graph::performProbGreater0E(this->model.getTransitionMatrix().transpose(true), storm::storage::BitVector(this->model.getNumberOfStates(), true), nonZeroRewardStates); storm::storage::BitVector subsystemStates = storm::utility::graph::performProbGreater0E(transitionMatrix.transpose(true), storm::storage::BitVector(transitionMatrix.getRowGroupCount(), true), nonZeroRewardStates);
// Remove neutral end components, i.e., ECs in which no reward is earned. // Remove neutral end components, i.e., ECs in which no reward is earned.
cachedEcElimResult = storm::transformer::EndComponentEliminator<ValueType>::transform(this->model.getTransitionMatrix(), subsystemStates, possibleECActions & cachedZeroRewardChoices.get(), possibleBottomStates); auto ecElimResult = storm::transformer::EndComponentEliminator<ValueType>::transform(transitionMatrix, subsystemStates, ecChoicesHint & newReward0Choices, reward0EStates);
ecQuotient = EcQuotient();
ecQuotient->matrix = std::move(ecElimResult.matrix);
ecQuotient->ecqToOriginalChoiceMapping = std::move(ecElimResult.newToOldRowMapping);
ecQuotient->originalToEcqStateMapping = std::move(ecElimResult.oldToNewStateMapping);
ecQuotient->origReward0Choices = std::move(newReward0Choices);
ecQuotient->auxStateValues.reserve(transitionMatrix.getRowGroupCount());
ecQuotient->auxStateValues.resize(ecQuotient->matrix.getRowGroupCount());
ecQuotient->auxChoiceValues.reserve(transitionMatrix.getRowCount());
ecQuotient->auxChoiceValues.resize(ecQuotient->matrix.getRowCount());
} }
STORM_LOG_ASSERT(cachedEcElimResult, "Updating the ecElimResult was not successfull.");
} }
@ -293,32 +338,32 @@ namespace storm {
std::vector<ValueType>& originalSolution, std::vector<ValueType>& originalSolution,
std::vector<uint_fast64_t>& originalOptimalChoices) const { std::vector<uint_fast64_t>& originalOptimalChoices) const {
storm::storage::BitVector bottomStates(this->model.getTransitionMatrix().getRowGroupCount(), false); storm::storage::BitVector bottomStates(transitionMatrix.getRowGroupCount(), false);
storm::storage::BitVector statesThatShouldStayInTheirEC(this->model.getTransitionMatrix().getRowGroupCount(), false); storm::storage::BitVector statesThatShouldStayInTheirEC(transitionMatrix.getRowGroupCount(), false);
storm::storage::BitVector statesWithUndefSched(this->model.getTransitionMatrix().getRowGroupCount(), false); storm::storage::BitVector statesWithUndefSched(transitionMatrix.getRowGroupCount(), false);
// Handle all the states for which the choice in the original model is uniquely given by the choice in the reduced model // Handle all the states for which the choice in the original model is uniquely given by the choice in the reduced model
// Also store some information regarding the remaining states // Also store some information regarding the remaining states
for(uint_fast64_t state = 0; state < this->model.getTransitionMatrix().getRowGroupCount(); ++state) { for (uint_fast64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) {
// Check if the state exists in the reduced model, i.e., the mapping retrieves a valid index // Check if the state exists in the reduced model, i.e., the mapping retrieves a valid index
uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state]; uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state];
if(stateInReducedModel < reducedMatrix.getRowGroupCount()) { if (stateInReducedModel < reducedMatrix.getRowGroupCount()) {
originalSolution[state] = reducedSolution[stateInReducedModel]; originalSolution[state] = reducedSolution[stateInReducedModel];
uint_fast64_t chosenRowInReducedModel = reducedMatrix.getRowGroupIndices()[stateInReducedModel] + reducedOptimalChoices[stateInReducedModel]; uint_fast64_t chosenRowInReducedModel = reducedMatrix.getRowGroupIndices()[stateInReducedModel] + reducedOptimalChoices[stateInReducedModel];
uint_fast64_t chosenRowInOriginalModel = reducedToOriginalChoiceMapping[chosenRowInReducedModel]; uint_fast64_t chosenRowInOriginalModel = reducedToOriginalChoiceMapping[chosenRowInReducedModel];
// Check if the state is a bottom state, i.e., the chosen row stays inside its EC. // Check if the state is a bottom state, i.e., the chosen row stays inside its EC.
bool stateIsBottom = possibleBottomStates.get(state); bool stateIsBottom = reward0EStates.get(state);
for(auto const& entry : this->model.getTransitionMatrix().getRow(chosenRowInOriginalModel)) { for (auto const& entry : transitionMatrix.getRow(chosenRowInOriginalModel)) {
stateIsBottom &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel; stateIsBottom &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel;
} }
if(stateIsBottom) { if (stateIsBottom) {
bottomStates.set(state); bottomStates.set(state);
statesThatShouldStayInTheirEC.set(state); statesThatShouldStayInTheirEC.set(state);
} else { } else {
// Check if the chosen row originaly belonged to the current state (and not to another state of the EC) // Check if the chosen row originaly belonged to the current state (and not to another state of the EC)
if(chosenRowInOriginalModel >= this->model.getTransitionMatrix().getRowGroupIndices()[state] && if (chosenRowInOriginalModel >= transitionMatrix.getRowGroupIndices()[state] &&
chosenRowInOriginalModel < this->model.getTransitionMatrix().getRowGroupIndices()[state+1]) { chosenRowInOriginalModel < transitionMatrix.getRowGroupIndices()[state+1]) {
originalOptimalChoices[state] = chosenRowInOriginalModel - this->model.getTransitionMatrix().getRowGroupIndices()[state]; originalOptimalChoices[state] = chosenRowInOriginalModel - transitionMatrix.getRowGroupIndices()[state];
} else { } else {
statesWithUndefSched.set(state); statesWithUndefSched.set(state);
statesThatShouldStayInTheirEC.set(state); statesThatShouldStayInTheirEC.set(state);
@ -329,7 +374,7 @@ namespace storm {
originalSolution[state] = storm::utility::zero<ValueType>(); originalSolution[state] = storm::utility::zero<ValueType>();
// However, it might be the case that infinite reward is induced for an objective with weight 0. // However, it might be the case that infinite reward is induced for an objective with weight 0.
// To avoid this, all possible bottom states are made bottom and the remaining states have to reach a bottom state with prob. one // To avoid this, all possible bottom states are made bottom and the remaining states have to reach a bottom state with prob. one
if(possibleBottomStates.get(state)) { if (reward0EStates.get(state)) {
bottomStates.set(state); bottomStates.set(state);
} else { } else {
statesWithUndefSched.set(state); statesWithUndefSched.set(state);
@ -338,21 +383,21 @@ namespace storm {
} }
// Handle bottom states // Handle bottom states
for(auto state : bottomStates) { for (auto state : bottomStates) {
bool foundRowForState = false; bool foundRowForState = false;
// Find a row with zero rewards that only leads to bottom states. // Find a row with zero rewards that only leads to bottom states.
// If the state should stay in its EC, we also need to make sure that all successors map to the same state in the reduced model // If the state should stay in its EC, we also need to make sure that all successors map to the same state in the reduced model
uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state]; uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state];
for(uint_fast64_t row = this->model.getTransitionMatrix().getRowGroupIndices()[state]; row < this->model.getTransitionMatrix().getRowGroupIndices()[state+1]; ++row) { for (uint_fast64_t row = transitionMatrix.getRowGroupIndices()[state]; row < transitionMatrix.getRowGroupIndices()[state+1]; ++row) {
bool rowOnlyLeadsToBottomStates = true; bool rowOnlyLeadsToBottomStates = true;
bool rowStaysInEC = true; bool rowStaysInEC = true;
for( auto const& entry : this->model.getTransitionMatrix().getRow(row)) { for ( auto const& entry : transitionMatrix.getRow(row)) {
rowOnlyLeadsToBottomStates &= bottomStates.get(entry.getColumn()); rowOnlyLeadsToBottomStates &= bottomStates.get(entry.getColumn());
rowStaysInEC &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel; rowStaysInEC &= originalToReducedStateMapping[entry.getColumn()] == stateInReducedModel;
} }
if(rowOnlyLeadsToBottomStates && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state)) && actionsWithoutRewardInUnboundedPhase.get(row)) { if (rowOnlyLeadsToBottomStates && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state)) && actionsWithoutRewardInUnboundedPhase.get(row)) {
foundRowForState = true; foundRowForState = true;
originalOptimalChoices[state] = row - this->model.getTransitionMatrix().getRowGroupIndices()[state]; originalOptimalChoices[state] = row - transitionMatrix.getRowGroupIndices()[state];
break; break;
} }
} }
@ -361,18 +406,18 @@ namespace storm {
// Handle remaining states with still undef. scheduler (either EC states or non-subsystem states) // Handle remaining states with still undef. scheduler (either EC states or non-subsystem states)
while(!statesWithUndefSched.empty()) { while(!statesWithUndefSched.empty()) {
for(auto state : statesWithUndefSched) { for (auto state : statesWithUndefSched) {
// Iteratively Try to find a choice such that at least one successor has a defined scheduler. // Iteratively Try to find a choice such that at least one successor has a defined scheduler.
uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state]; uint_fast64_t stateInReducedModel = originalToReducedStateMapping[state];
for(uint_fast64_t row = this->model.getTransitionMatrix().getRowGroupIndices()[state]; row < this->model.getTransitionMatrix().getRowGroupIndices()[state+1]; ++row) { for (uint_fast64_t row = transitionMatrix.getRowGroupIndices()[state]; row < transitionMatrix.getRowGroupIndices()[state+1]; ++row) {
bool rowStaysInEC = true; bool rowStaysInEC = true;
bool rowLeadsToDefinedScheduler = false; bool rowLeadsToDefinedScheduler = false;
for(auto const& entry : this->model.getTransitionMatrix().getRow(row)) { for (auto const& entry : transitionMatrix.getRow(row)) {
rowStaysInEC &= ( stateInReducedModel == originalToReducedStateMapping[entry.getColumn()]); rowStaysInEC &= ( stateInReducedModel == originalToReducedStateMapping[entry.getColumn()]);
rowLeadsToDefinedScheduler |= !statesWithUndefSched.get(entry.getColumn()); rowLeadsToDefinedScheduler |= !statesWithUndefSched.get(entry.getColumn());
} }
if(rowLeadsToDefinedScheduler && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state))) { if (rowLeadsToDefinedScheduler && (rowStaysInEC || !statesThatShouldStayInTheirEC.get(state))) {
originalOptimalChoices[state] = row - this->model.getTransitionMatrix().getRowGroupIndices()[state]; originalOptimalChoices[state] = row - transitionMatrix.getRowGroupIndices()[state];
statesWithUndefSched.set(state, false); statesWithUndefSched.set(state, false);
} }
} }

47
src/storm/modelchecker/multiobjective/pcaa/SparsePcaaWeightVectorChecker.h

@ -6,6 +6,7 @@
#include "storm/transformer/EndComponentEliminator.h" #include "storm/transformer/EndComponentEliminator.h"
#include "storm/modelchecker/multiobjective/Objective.h" #include "storm/modelchecker/multiobjective/Objective.h"
#include "storm/modelchecker/multiobjective/pcaa/PcaaWeightVectorChecker.h" #include "storm/modelchecker/multiobjective/pcaa/PcaaWeightVectorChecker.h"
#include "storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessorResult.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
namespace storm { namespace storm {
@ -33,10 +34,7 @@ namespace storm {
* *
*/ */
SparsePcaaWeightVectorChecker(SparseModelType const& model, SparsePcaaWeightVectorChecker(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult);
std::vector<Objective<ValueType>> const& objectives,
storm::storage::BitVector const& possibleECActions,
storm::storage::BitVector const& possibleBottomStates);
virtual ~SparsePcaaWeightVectorChecker() = default; virtual ~SparsePcaaWeightVectorChecker() = default;
@ -66,6 +64,9 @@ namespace storm {
protected: protected:
void initialize(SparseMultiObjectivePreprocessorResult<SparseModelType> const& preprocessorResult);
virtual void initializeModelTypeSpecificData(SparseModelType const& model) = 0;
/*! /*!
* Determines the scheduler that optimizes the weighted reward vector of the unbounded objectives * Determines the scheduler that optimizes the weighted reward vector of the unbounded objectives
* *
@ -91,7 +92,7 @@ namespace storm {
*/ */
virtual void boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) = 0; virtual void boundedPhase(std::vector<ValueType> const& weightVector, std::vector<ValueType>& weightedRewardVector) = 0;
void updateEcElimResult(std::vector<ValueType> const& weightedRewardVector); void updateEcQuotient(std::vector<ValueType> const& weightedRewardVector);
/*! /*!
* Transforms the results of a min-max-solver that considers a reduced model (without end components) to a result for the original (unreduced) model * Transforms the results of a min-max-solver that considers a reduced model (without end components) to a result for the original (unreduced) model
@ -105,18 +106,23 @@ namespace storm {
std::vector<uint_fast64_t>& originalOptimalChoices) const; std::vector<uint_fast64_t>& originalOptimalChoices) const;
// Data regarding the given model
// The transition matrix of the considered model
storm::storage::SparseMatrix<ValueType> transitionMatrix;
// The initial state of the considered model
uint64_t initialState;
// Overapproximation of the set of choices that are part of an end component. // Overapproximation of the set of choices that are part of an end component.
storm::storage::BitVector possibleECActions; storm::storage::BitVector ecChoicesHint;
// The actions that have reward assigned for at least one objective without upper timeBound // The actions that have reward assigned for at least one objective without upper timeBound
storm::storage::BitVector actionsWithoutRewardInUnboundedPhase; storm::storage::BitVector actionsWithoutRewardInUnboundedPhase;
// The states for which it is allowed to visit them infinitely often // The states for which there is a scheduler yielding reward 0 for each objective
// Put differently, if one of the states is part of a neutral EC, it is possible to storm::storage::BitVector reward0EStates;
// stay in this EC forever (withoud inducing infinite reward for some objective). // stores the state action rewards for each objective.
storm::storage::BitVector possibleBottomStates; std::vector<std::vector<ValueType>> actionRewards;
// stores the indices of the objectives for which there is no upper time bound // stores the indices of the objectives for which there is no upper time bound
storm::storage::BitVector objectivesWithNoUpperTimeBound; storm::storage::BitVector objectivesWithNoUpperTimeBound;
// stores the (discretized) state action rewards for each objective.
std::vector<std::vector<ValueType>> discreteActionRewards;
// Memory for the solution of the most recent call of check(..) // Memory for the solution of the most recent call of check(..)
// becomes true after the first call of check(..) // becomes true after the first call of check(..)
bool checkHasBeenCalled; bool checkHasBeenCalled;
@ -130,10 +136,19 @@ namespace storm {
std::vector<ValueType> offsetsToOverApproximation; std::vector<ValueType> offsetsToOverApproximation;
// The scheduler choices that optimize the weighted rewards of undounded objectives. // The scheduler choices that optimize the weighted rewards of undounded objectives.
std::vector<uint_fast64_t> optimalChoices; std::vector<uint_fast64_t> optimalChoices;
// Caches the result of the ec elimination (avoiding recomputations for each weightvector) struct EcQuotient {
boost::optional<typename storm::transformer::EndComponentEliminator<ValueType>::EndComponentEliminatorReturnType> cachedEcElimResult; storm::storage::SparseMatrix<ValueType> matrix;
// Stores which choices are considered to have zero reward in the current cachedEcElimiResult. std::vector<uint_fast64_t> ecqToOriginalChoiceMapping;
boost::optional<storm::storage::BitVector> cachedZeroRewardChoices; std::vector<uint_fast64_t> originalToEcqStateMapping;
storm::storage::BitVector origReward0Choices;
std::vector<ValueType> auxStateValues;
std::vector<ValueType> auxChoiceValues;
};
boost::optional<EcQuotient> ecQuotient;
}; };

|||||||
100:0
Loading…
Cancel
Save