You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
608 lines
40 KiB
608 lines
40 KiB
#include "storm/modelchecker/multiobjective/rewardbounded/ProductModel.h"
|
|
|
|
|
|
#include "storm/utility/macros.h"
|
|
#include "storm/logic/Formulas.h"
|
|
#include "storm/logic/CloneVisitor.h"
|
|
#include "storm/storage/memorystructure/SparseModelMemoryProduct.h"
|
|
#include "storm/storage/memorystructure/MemoryStructureBuilder.h"
|
|
|
|
#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
|
|
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
|
|
|
|
#include "storm/exceptions/UnexpectedException.h"
|
|
#include "storm/exceptions/NotSupportedException.h"
|
|
|
|
namespace storm {
|
|
namespace modelchecker {
|
|
namespace multiobjective {
|
|
|
|
template<typename ValueType>
|
|
ProductModel<ValueType>::ProductModel(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives, std::vector<Dimension<ValueType>> const& dimensions, std::vector<storm::storage::BitVector> const& objectiveDimensions, EpochManager const& epochManager, std::vector<Epoch> const& originalModelSteps) : dimensions(dimensions), objectiveDimensions(objectiveDimensions), epochManager(epochManager), memoryStateManager(dimensions.size()) {
|
|
|
|
for (uint64_t dim = 0; dim < dimensions.size(); ++dim) {
|
|
if (!dimensions[dim].memoryLabel) {
|
|
memoryStateManager.setDimensionWithoutMemory(dim);
|
|
}
|
|
}
|
|
|
|
storm::storage::MemoryStructure memory = computeMemoryStructure(model, objectives);
|
|
assert(memoryStateManager.getMemoryStateCount() == memory.getNumberOfStates());
|
|
std::vector<MemoryState> memoryStateMap = computeMemoryStateMap(memory);
|
|
|
|
storm::storage::SparseModelMemoryProduct<ValueType> productBuilder(memory.product(model));
|
|
|
|
setReachableProductStates(productBuilder, originalModelSteps, memoryStateMap);
|
|
product = productBuilder.build()->template as<storm::models::sparse::Mdp<ValueType>>();
|
|
|
|
uint64_t numModelStates = productBuilder.getOriginalModel().getNumberOfStates();
|
|
MemoryState upperMemStateBound = memoryStateManager.getUpperMemoryStateBound();
|
|
uint64_t numMemoryStates = memoryStateManager.getMemoryStateCount();
|
|
uint64_t numProductStates = getProduct().getNumberOfStates();
|
|
|
|
// Compute a mappings from product states to model/memory states and back
|
|
modelMemoryToProductStateMap.resize(upperMemStateBound * numModelStates, std::numeric_limits<uint64_t>::max());
|
|
productToModelStateMap.resize(numProductStates, std::numeric_limits<uint64_t>::max());
|
|
productToMemoryStateMap.resize(numProductStates, std::numeric_limits<uint64_t>::max());
|
|
for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) {
|
|
for (uint64_t memoryStateIndex = 0; memoryStateIndex < numMemoryStates; ++memoryStateIndex) {
|
|
if (productBuilder.isStateReachable(modelState, memoryStateIndex)) {
|
|
uint64_t productState = productBuilder.getResultState(modelState, memoryStateIndex);
|
|
modelMemoryToProductStateMap[modelState * upperMemStateBound + memoryStateMap[memoryStateIndex]] = productState;
|
|
productToModelStateMap[productState] = modelState;
|
|
productToMemoryStateMap[productState] = memoryStateMap[memoryStateIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Map choice indices of the product to the state where it origins
|
|
choiceToStateMap.reserve(getProduct().getNumberOfChoices());
|
|
for (uint64_t productState = 0; productState < numProductStates; ++productState) {
|
|
uint64_t groupSize = getProduct().getTransitionMatrix().getRowGroupSize(productState);
|
|
for (uint64_t i = 0; i < groupSize; ++i) {
|
|
choiceToStateMap.push_back(productState);
|
|
}
|
|
}
|
|
|
|
// Compute the epoch steps for the product
|
|
steps.resize(getProduct().getNumberOfChoices(), 0);
|
|
for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) {
|
|
uint64_t numChoices = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupSize(modelState);
|
|
uint64_t firstChoice = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupIndices()[modelState];
|
|
for (uint64_t choiceOffset = 0; choiceOffset < numChoices; ++choiceOffset) {
|
|
Epoch const& step = originalModelSteps[firstChoice + choiceOffset];
|
|
if (step != 0) {
|
|
for (MemoryState const& memoryState : memoryStateMap) {
|
|
if (productStateExists(modelState, memoryState)) {
|
|
uint64_t productState = getProductState(modelState, memoryState);
|
|
uint64_t productChoice = getProduct().getTransitionMatrix().getRowGroupIndices()[productState] + choiceOffset;
|
|
assert(productChoice < getProduct().getTransitionMatrix().getRowGroupIndices()[productState + 1]);
|
|
steps[productChoice] = step;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// getProduct().writeDotToStream(std::cout);
|
|
|
|
computeReachableStatesInEpochClasses();
|
|
}
|
|
|
|
|
|
template<typename ValueType>
|
|
storm::storage::MemoryStructure ProductModel<ValueType>::computeMemoryStructure(storm::models::sparse::Mdp<ValueType> const& model, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const {
|
|
|
|
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<ValueType>> mc(model);
|
|
|
|
// Create a memory structure that remembers whether (sub)objectives are satisfied
|
|
storm::storage::MemoryStructure memory = storm::storage::MemoryStructureBuilder<ValueType>::buildTrivialMemoryStructure(model);
|
|
for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
|
|
if (!objectives[objIndex].formula->isProbabilityOperatorFormula()) {
|
|
continue;
|
|
}
|
|
|
|
std::vector<uint64_t> dimensionIndexMap;
|
|
for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) {
|
|
dimensionIndexMap.push_back(globalDimensionIndex);
|
|
}
|
|
|
|
bool objectiveContainsLowerBound = false;
|
|
for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) {
|
|
if (!dimensions[globalDimensionIndex].isUpperBounded) {
|
|
objectiveContainsLowerBound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// collect the memory states for this objective
|
|
std::vector<storm::storage::BitVector> objMemStates;
|
|
storm::storage::BitVector m(dimensionIndexMap.size(), false);
|
|
for (; !m.full(); m.increment()) {
|
|
objMemStates.push_back(~m);
|
|
}
|
|
objMemStates.push_back(~m);
|
|
assert(objMemStates.size() == 1ull << dimensionIndexMap.size());
|
|
|
|
// build objective memory
|
|
auto objMemoryBuilder = storm::storage::MemoryStructureBuilder<ValueType>(objMemStates.size(), model);
|
|
|
|
// Get the set of states that for all subobjectives satisfy either the left or the right subformula
|
|
storm::storage::BitVector constraintStates(model.getNumberOfStates(), true);
|
|
for (auto const& dim : objectiveDimensions[objIndex]) {
|
|
auto const& dimension = dimensions[dim];
|
|
STORM_LOG_ASSERT(dimension.formula->isBoundedUntilFormula(), "Unexpected Formula type");
|
|
constraintStates &=
|
|
(mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector() |
|
|
mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
|
|
}
|
|
|
|
// Build the transitions between the memory states
|
|
for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) {
|
|
auto const& memStateBV = objMemStates[memState];
|
|
for (uint64_t memStatePrime = 0; memStatePrime < objMemStates.size(); ++memStatePrime) {
|
|
auto const& memStatePrimeBV = objMemStates[memStatePrime];
|
|
if (memStatePrimeBV.isSubsetOf(memStateBV)) {
|
|
|
|
std::shared_ptr<storm::logic::Formula const> transitionFormula = storm::logic::Formula::getTrueFormula();
|
|
for (auto const& subObjIndex : memStateBV) {
|
|
std::shared_ptr<storm::logic::Formula const> subObjFormula = dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer();
|
|
if (memStatePrimeBV.get(subObjIndex)) {
|
|
subObjFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, subObjFormula);
|
|
}
|
|
transitionFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::And, transitionFormula, subObjFormula);
|
|
}
|
|
|
|
storm::storage::BitVector transitionStates = mc.check(*transitionFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
|
|
if (memStatePrimeBV.empty()) {
|
|
transitionStates |= ~constraintStates;
|
|
} else {
|
|
transitionStates &= constraintStates;
|
|
}
|
|
objMemoryBuilder.setTransition(memState, memStatePrime, transitionStates);
|
|
|
|
// Set the initial states
|
|
if (memStateBV.full()) {
|
|
storm::storage::BitVector initialTransitionStates = model.getInitialStates() & transitionStates;
|
|
// At this point we can check whether there is an initial state that already satisfies all subObjectives.
|
|
// Such a situation is not supported as we can not reduce this (easily) to an expected reward computation.
|
|
STORM_LOG_THROW(!memStatePrimeBV.empty() || initialTransitionStates.empty() || objectiveContainsLowerBound || initialTransitionStates.isDisjointFrom(constraintStates), storm::exceptions::NotSupportedException, "The objective " << *objectives[objIndex].formula << " is already satisfied in an initial state. This special case is not supported.");
|
|
for (auto const& initState : initialTransitionStates) {
|
|
objMemoryBuilder.setInitialMemoryState(initState, memStatePrime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build the memory labels
|
|
for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) {
|
|
auto const& memStateBV = objMemStates[memState];
|
|
for (auto const& subObjIndex : memStateBV) {
|
|
objMemoryBuilder.setLabel(memState, dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get());
|
|
}
|
|
}
|
|
auto objMemory = objMemoryBuilder.build();
|
|
memory = memory.product(objMemory);
|
|
}
|
|
return memory;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
std::vector<typename ProductModel<ValueType>::MemoryState> ProductModel<ValueType>::computeMemoryStateMap(storm::storage::MemoryStructure const& memory) const {
|
|
// Compute a mapping between the different representations of memory states
|
|
std::vector<MemoryState> result;
|
|
result.reserve(memory.getNumberOfStates());
|
|
for (uint64_t memStateIndex = 0; memStateIndex < memory.getNumberOfStates(); ++memStateIndex) {
|
|
MemoryState memState = memoryStateManager.getInitialMemoryState();
|
|
std::set<std::string> stateLabels = memory.getStateLabeling().getLabelsOfState(memStateIndex);
|
|
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
|
|
if (dimensions[dim].memoryLabel) {
|
|
if (stateLabels.find(dimensions[dim].memoryLabel.get()) != stateLabels.end()) {
|
|
memoryStateManager.setRelevantDimension(memState, dim, true);
|
|
} else {
|
|
memoryStateManager.setRelevantDimension(memState, dim, false);
|
|
}
|
|
}
|
|
}
|
|
result.push_back(std::move(memState));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
void ProductModel<ValueType>::setReachableProductStates(storm::storage::SparseModelMemoryProduct<ValueType>& productBuilder, std::vector<Epoch> const& originalModelSteps, std::vector<MemoryState> const& memoryStateMap) const {
|
|
|
|
std::vector<uint64_t> inverseMemoryStateMap(memoryStateManager.getUpperMemoryStateBound(), std::numeric_limits<uint64_t>::max());
|
|
for (uint64_t memStateIndex = 0; memStateIndex < memoryStateMap.size(); ++memStateIndex) {
|
|
inverseMemoryStateMap[memoryStateMap[memStateIndex]] = memStateIndex;
|
|
}
|
|
|
|
auto const& memory = productBuilder.getMemory();
|
|
auto const& model = productBuilder.getOriginalModel();
|
|
auto const& modelTransitions = model.getTransitionMatrix();
|
|
|
|
std::vector<storm::storage::BitVector> reachableProductStates(memoryStateManager.getUpperMemoryStateBound());
|
|
for (auto const& memState : memoryStateMap) {
|
|
reachableProductStates[memState] = storm::storage::BitVector(model.getNumberOfStates(), false);
|
|
}
|
|
|
|
// Initialize the reachable states with the initial states
|
|
EpochClass initEpochClass = epochManager.getEpochClass(epochManager.getZeroEpoch());
|
|
auto memStateIt = memory.getInitialMemoryStates().begin();
|
|
for (auto const& initState : model.getInitialStates()) {
|
|
uint64_t transformedMemoryState = transformMemoryState(memoryStateMap[*memStateIt], initEpochClass, memoryStateManager.getInitialMemoryState());
|
|
reachableProductStates[transformedMemoryState].set(initState, true);
|
|
++memStateIt;
|
|
}
|
|
assert(memStateIt == memory.getInitialMemoryStates().end());
|
|
|
|
// Find the reachable epoch classes
|
|
std::set<Epoch> possibleSteps(originalModelSteps.begin(), originalModelSteps.end());
|
|
std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
|
|
collectReachableEpochClasses(reachableEpochClasses, possibleSteps);
|
|
|
|
|
|
// Iterate over all epoch classes starting from the initial one (i.e., no bottom dimension).
|
|
for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) {
|
|
auto const& epochClass = *epochClassIt;
|
|
|
|
// Find the remaining set of reachable states via DFS.
|
|
std::vector<std::pair<uint64_t, MemoryState>> dfsStack;
|
|
for (MemoryState const& memState : memoryStateMap) {
|
|
for (auto const& modelState : reachableProductStates[memState]) {
|
|
dfsStack.emplace_back(modelState, memState);
|
|
}
|
|
}
|
|
|
|
while (!dfsStack.empty()) {
|
|
uint64_t currentModelState = dfsStack.back().first;
|
|
MemoryState currentMemoryState = dfsStack.back().second;
|
|
uint64_t currentMemoryStateIndex = inverseMemoryStateMap[currentMemoryState];
|
|
dfsStack.pop_back();
|
|
|
|
for (uint64_t choice = modelTransitions.getRowGroupIndices()[currentModelState]; choice != modelTransitions.getRowGroupIndices()[currentModelState + 1]; ++choice) {
|
|
|
|
for (auto transitionIt = modelTransitions.getRow(choice).begin(); transitionIt < modelTransitions.getRow(choice).end(); ++transitionIt) {
|
|
|
|
MemoryState successorMemoryState = memoryStateMap[memory.getSuccessorMemoryState(currentMemoryStateIndex, transitionIt - modelTransitions.begin())];
|
|
successorMemoryState = transformMemoryState(successorMemoryState, epochClass, currentMemoryState);
|
|
if (!reachableProductStates[successorMemoryState].get(transitionIt->getColumn())) {
|
|
reachableProductStates[successorMemoryState].set(transitionIt->getColumn(), true);
|
|
dfsStack.emplace_back(transitionIt->getColumn(), successorMemoryState);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint64_t memStateIndex = 0; memStateIndex < memoryStateManager.getMemoryStateCount(); ++memStateIndex) {
|
|
for (auto const& modelState : reachableProductStates[memoryStateMap[memStateIndex]]) {
|
|
productBuilder.addReachableState(modelState, memStateIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename ValueType>
|
|
storm::models::sparse::Mdp<ValueType> const& ProductModel<ValueType>::getProduct() const {
|
|
return *product;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
std::vector<typename ProductModel<ValueType>::Epoch> const& ProductModel<ValueType>::getSteps() const {
|
|
return steps;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
bool ProductModel<ValueType>::productStateExists(uint64_t const& modelState, MemoryState const& memoryState) const {
|
|
return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState] < getProduct().getNumberOfStates();
|
|
}
|
|
|
|
template<typename ValueType>
|
|
uint64_t ProductModel<ValueType>::getProductState(uint64_t const& modelState, MemoryState const& memoryState) const {
|
|
STORM_LOG_ASSERT(productStateExists(modelState, memoryState), "Tried to obtain a state in the model-memory-product that does not exist");
|
|
return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState];
|
|
}
|
|
|
|
template<typename ValueType>
|
|
uint64_t ProductModel<ValueType>::getInitialProductState(uint64_t const& initialModelState, storm::storage::BitVector const& initialModelStates) const {
|
|
auto productInitStateIt = getProduct().getInitialStates().begin();
|
|
productInitStateIt += initialModelStates.getNumberOfSetBitsBeforeIndex(initialModelState);
|
|
STORM_LOG_ASSERT(getModelState(*productInitStateIt) == initialModelState, "Could not find the corresponding initial state in the product model.");
|
|
return transformProductState(*productInitStateIt, epochManager.getEpochClass(epochManager.getZeroEpoch()), memoryStateManager.getInitialMemoryState());
|
|
}
|
|
|
|
template<typename ValueType>
|
|
uint64_t ProductModel<ValueType>::getModelState(uint64_t const& productState) const {
|
|
return productToModelStateMap[productState];
|
|
}
|
|
|
|
template<typename ValueType>
|
|
typename ProductModel<ValueType>::MemoryState ProductModel<ValueType>::getMemoryState(uint64_t const& productState) const {
|
|
return productToMemoryStateMap[productState];
|
|
}
|
|
|
|
template<typename ValueType>
|
|
MemoryStateManager const& ProductModel<ValueType>::getMemoryStateManager() const {
|
|
return memoryStateManager;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
uint64_t ProductModel<ValueType>::getProductStateFromChoice(uint64_t const& productChoice) const {
|
|
return choiceToStateMap[productChoice];
|
|
}
|
|
|
|
template<typename ValueType>
|
|
std::vector<std::vector<ValueType>> ProductModel<ValueType>::computeObjectiveRewards(EpochClass const& epochClass, std::vector<storm::modelchecker::multiobjective::Objective<ValueType>> const& objectives) const {
|
|
std::vector<std::vector<ValueType>> objectiveRewards;
|
|
objectiveRewards.reserve(objectives.size());
|
|
|
|
for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
|
|
auto const& formula = *objectives[objIndex].formula;
|
|
if (formula.isProbabilityOperatorFormula()) {
|
|
storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<ValueType>> mc(getProduct());
|
|
std::vector<uint64_t> dimensionIndexMap;
|
|
for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) {
|
|
dimensionIndexMap.push_back(globalDimensionIndex);
|
|
}
|
|
|
|
std::shared_ptr<storm::logic::Formula const> sinkStatesFormula;
|
|
for (auto const& dim : objectiveDimensions[objIndex]) {
|
|
auto memLabelFormula = std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dim].memoryLabel.get());
|
|
if (sinkStatesFormula) {
|
|
sinkStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or, sinkStatesFormula, memLabelFormula);
|
|
} else {
|
|
sinkStatesFormula = memLabelFormula;
|
|
}
|
|
}
|
|
sinkStatesFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, sinkStatesFormula);
|
|
|
|
std::vector<ValueType> objRew(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
|
|
storm::storage::BitVector relevantObjectives(objectiveDimensions[objIndex].getNumberOfSetBits());
|
|
|
|
while (!relevantObjectives.full()) {
|
|
relevantObjectives.increment();
|
|
|
|
// find out whether objective reward should be earned within this epoch class
|
|
bool collectRewardInEpoch = true;
|
|
for (auto const& subObjIndex : relevantObjectives) {
|
|
if (dimensions[dimensionIndexMap[subObjIndex]].isUpperBounded && epochManager.isBottomDimensionEpochClass(epochClass, dimensionIndexMap[subObjIndex])) {
|
|
collectRewardInEpoch = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (collectRewardInEpoch) {
|
|
std::shared_ptr<storm::logic::Formula const> relevantStatesFormula;
|
|
std::shared_ptr<storm::logic::Formula const> goalStatesFormula = storm::logic::CloneVisitor().clone(*sinkStatesFormula);
|
|
for (uint64_t subObjIndex = 0; subObjIndex < dimensionIndexMap.size(); ++subObjIndex) {
|
|
std::shared_ptr<storm::logic::Formula> memLabelFormula = std::make_shared<storm::logic::AtomicLabelFormula>(dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get());
|
|
if (relevantObjectives.get(subObjIndex)) {
|
|
auto rightSubFormula = dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer();
|
|
goalStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::And, goalStatesFormula, rightSubFormula);
|
|
} else {
|
|
memLabelFormula = std::make_shared<storm::logic::UnaryBooleanStateFormula>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, memLabelFormula);
|
|
}
|
|
if (relevantStatesFormula) {
|
|
relevantStatesFormula = std::make_shared<storm::logic::BinaryBooleanStateFormula>(storm::logic::BinaryBooleanStateFormula::OperatorType::And, relevantStatesFormula, memLabelFormula);
|
|
} else {
|
|
relevantStatesFormula = memLabelFormula;
|
|
}
|
|
}
|
|
|
|
storm::storage::BitVector relevantStates = mc.check(*relevantStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
|
|
storm::storage::BitVector relevantChoices = getProduct().getTransitionMatrix().getRowFilter(relevantStates);
|
|
storm::storage::BitVector goalStates = mc.check(*goalStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
|
|
for (auto const& choice : relevantChoices) {
|
|
objRew[choice] += getProduct().getTransitionMatrix().getConstrainedRowSum(choice, goalStates);
|
|
}
|
|
}
|
|
}
|
|
|
|
objectiveRewards.push_back(std::move(objRew));
|
|
|
|
} else if (formula.isRewardOperatorFormula()) {
|
|
auto const& rewModel = getProduct().getRewardModel(formula.asRewardOperatorFormula().getRewardModelName());
|
|
STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Reward model has transition rewards which is not expected.");
|
|
bool rewardCollectedInEpoch = true;
|
|
if (formula.getSubformula().isCumulativeRewardFormula()) {
|
|
for (auto const& dim : objectiveDimensions[objIndex]) {
|
|
if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
|
|
rewardCollectedInEpoch = false;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
STORM_LOG_THROW(formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula);
|
|
}
|
|
if (rewardCollectedInEpoch) {
|
|
objectiveRewards.push_back(rewModel.getTotalRewardVector(getProduct().getTransitionMatrix()));
|
|
} else {
|
|
objectiveRewards.emplace_back(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero<ValueType>());
|
|
}
|
|
} else {
|
|
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula);
|
|
}
|
|
}
|
|
|
|
return objectiveRewards;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
storm::storage::BitVector const& ProductModel<ValueType>::getInStates(EpochClass const& epochClass) const {
|
|
STORM_LOG_ASSERT(inStates.find(epochClass) != inStates.end(), "Could not find InStates for the given epoch class");
|
|
return inStates.find(epochClass)->second;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
void ProductModel<ValueType>::computeReachableStatesInEpochClasses() {
|
|
std::set<Epoch> possibleSteps(steps.begin(), steps.end());
|
|
std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>> reachableEpochClasses(std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2));
|
|
|
|
collectReachableEpochClasses(reachableEpochClasses, possibleSteps);
|
|
|
|
for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) {
|
|
std::vector<EpochClass> predecessors;
|
|
for (auto predecessorIt = reachableEpochClasses.rbegin(); predecessorIt != epochClassIt; ++predecessorIt) {
|
|
if (epochManager.isPredecessorEpochClass(*predecessorIt, *epochClassIt)) {
|
|
predecessors.push_back(*predecessorIt);
|
|
}
|
|
}
|
|
computeReachableStates(*epochClassIt, predecessors);
|
|
}
|
|
}
|
|
|
|
template<typename ValueType>
|
|
void ProductModel<ValueType>::collectReachableEpochClasses(std::set<EpochClass, std::function<bool(EpochClass const&, EpochClass const&)>>& reachableEpochClasses, std::set<Epoch> const& possibleSteps) const {
|
|
|
|
Epoch startEpoch = epochManager.getZeroEpoch();
|
|
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
|
|
STORM_LOG_ASSERT(dimensions[dim].maxValue, "No max-value for dimension " << dim << " was given.");
|
|
epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get());
|
|
}
|
|
|
|
std::set<Epoch> seenEpochs({startEpoch});
|
|
std::vector<Epoch> dfsStack({startEpoch});
|
|
|
|
reachableEpochClasses.insert(epochManager.getEpochClass(startEpoch));
|
|
|
|
// Perform a DFS to find all the reachable epochs
|
|
while (!dfsStack.empty()) {
|
|
Epoch currentEpoch = dfsStack.back();
|
|
dfsStack.pop_back();
|
|
for (auto const& step : possibleSteps) {
|
|
Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step);
|
|
if (seenEpochs.insert(successorEpoch).second) {
|
|
reachableEpochClasses.insert(epochManager.getEpochClass(successorEpoch));
|
|
dfsStack.push_back(std::move(successorEpoch));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename ValueType>
|
|
void ProductModel<ValueType>::computeReachableStates(EpochClass const& epochClass, std::vector<EpochClass> const& predecessors) {
|
|
|
|
storm::storage::BitVector bottomDimensions(epochManager.getDimensionCount(), false);
|
|
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
|
|
if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) {
|
|
bottomDimensions.set(dim, true);
|
|
}
|
|
}
|
|
storm::storage::BitVector nonBottomDimensions = ~bottomDimensions;
|
|
|
|
storm::storage::BitVector ecInStates(getProduct().getNumberOfStates(), false);
|
|
|
|
if (!epochManager.hasBottomDimensionEpochClass(epochClass)) {
|
|
for (auto const& initState : getProduct().getInitialStates()) {
|
|
uint64_t transformedInitState = transformProductState(initState, epochClass, memoryStateManager.getInitialMemoryState());
|
|
ecInStates.set(transformedInitState, true);
|
|
}
|
|
}
|
|
|
|
for (auto const& predecessor : predecessors) {
|
|
storm::storage::BitVector positiveStepDimensions(epochManager.getDimensionCount(), false);
|
|
for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) {
|
|
if (!epochManager.isBottomDimensionEpochClass(predecessor, dim) && bottomDimensions.get(dim)) {
|
|
positiveStepDimensions.set(dim, true);
|
|
}
|
|
}
|
|
STORM_LOG_ASSERT(reachableStates.find(predecessor) != reachableStates.end(), "Could not find reachable states of predecessor epoch class.");
|
|
storm::storage::BitVector predecessorStates = reachableStates.find(predecessor)->second;
|
|
for (auto const& predecessorState : predecessorStates) {
|
|
uint64_t predecessorMemoryState = getMemoryState(predecessorState);
|
|
for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState]; choice < getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState + 1]; ++choice) {
|
|
bool choiceLeadsToThisClass = false;
|
|
Epoch const& choiceStep = getSteps()[choice];
|
|
for (auto const& dim : positiveStepDimensions) {
|
|
if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) {
|
|
choiceLeadsToThisClass = true;
|
|
}
|
|
}
|
|
|
|
if (choiceLeadsToThisClass) {
|
|
for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) {
|
|
uint64_t successorState = transformProductState(transition.getColumn(), epochClass, predecessorMemoryState);
|
|
|
|
ecInStates.set(successorState, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find all states reachable from an InState via DFS.
|
|
storm::storage::BitVector ecReachableStates = ecInStates;
|
|
std::vector<uint64_t> dfsStack(ecReachableStates.begin(), ecReachableStates.end());
|
|
|
|
while (!dfsStack.empty()) {
|
|
uint64_t currentState = dfsStack.back();
|
|
uint64_t currentMemoryState = getMemoryState(currentState);
|
|
dfsStack.pop_back();
|
|
|
|
for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[currentState]; choice != getProduct().getTransitionMatrix().getRowGroupIndices()[currentState + 1]; ++choice) {
|
|
|
|
bool choiceLeadsOutsideOfEpoch = false;
|
|
Epoch const& choiceStep = getSteps()[choice];
|
|
for (auto const& dim : nonBottomDimensions) {
|
|
if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) {
|
|
choiceLeadsOutsideOfEpoch = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) {
|
|
uint64_t successorState = transformProductState(transition.getColumn(), epochClass, currentMemoryState);
|
|
if (choiceLeadsOutsideOfEpoch) {
|
|
ecInStates.set(successorState, true);
|
|
}
|
|
if (!ecReachableStates.get(successorState)) {
|
|
ecReachableStates.set(successorState, true);
|
|
dfsStack.push_back(successorState);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
reachableStates[epochClass] = std::move(ecReachableStates);
|
|
|
|
inStates[epochClass] = std::move(ecInStates);
|
|
}
|
|
|
|
template<typename ValueType>
|
|
typename ProductModel<ValueType>::MemoryState ProductModel<ValueType>::transformMemoryState(MemoryState const& memoryState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const {
|
|
MemoryState memoryStatePrime = memoryState;
|
|
|
|
for (auto const& objDimensions : objectiveDimensions) {
|
|
for (auto const& dim : objDimensions) {
|
|
auto const& dimension = dimensions[dim];
|
|
if (dimension.memoryLabel) {
|
|
bool dimUpperBounded = dimension.isUpperBounded;
|
|
bool dimBottom = epochManager.isBottomDimensionEpochClass(epochClass, dim);
|
|
if (dimUpperBounded && dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) {
|
|
STORM_LOG_ASSERT(objDimensions == dimension.dependentDimensions, "Unexpected set of dependent dimensions");
|
|
memoryStateManager.setRelevantDimensions(memoryStatePrime, objDimensions, false);
|
|
break;
|
|
} else if (!dimUpperBounded && !dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) {
|
|
memoryStateManager.setRelevantDimensions(memoryStatePrime, dimension.dependentDimensions, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// std::cout << "Transformed memory state " << memoryStateManager.toString(memoryState) << " at epoch class " << epochClass << " with predecessor " << memoryStateManager.toString(predecessorMemoryState) << " to " << memoryStateManager.toString(memoryStatePrime) << std::endl;
|
|
|
|
return memoryStatePrime;
|
|
}
|
|
|
|
template<typename ValueType>
|
|
uint64_t ProductModel<ValueType>::transformProductState(uint64_t const& productState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const {
|
|
return getProductState(getModelState(productState), transformMemoryState(getMemoryState(productState), epochClass, predecessorMemoryState));
|
|
}
|
|
|
|
template class ProductModel<double>;
|
|
template class ProductModel<storm::RationalNumber>;
|
|
|
|
}
|
|
}
|
|
}
|